mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 16:57:46 +00:00
openspec/lightweight-tasks/task1-2-4.md
feat: implement stream-json session handling and control requests
This commit is contained in:
132
packages/cli/src/streamJson/input.ts
Normal file
132
packages/cli/src/streamJson/input.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { createInterface } from 'node:readline/promises';
|
||||
import process from 'node:process';
|
||||
import {
|
||||
parseStreamJsonEnvelope,
|
||||
serializeStreamJsonEnvelope,
|
||||
type StreamJsonControlRequestEnvelope,
|
||||
type StreamJsonOutputEnvelope,
|
||||
type StreamJsonUserEnvelope,
|
||||
} from './types.js';
|
||||
import { FatalInputError } from '@qwen-code/qwen-code-core';
|
||||
|
||||
export interface ParsedStreamJsonInput {
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
export async function readStreamJsonInput(): Promise<ParsedStreamJsonInput> {
|
||||
const rl = createInterface({
|
||||
input: process.stdin,
|
||||
crlfDelay: Number.POSITIVE_INFINITY,
|
||||
terminal: false,
|
||||
});
|
||||
|
||||
try {
|
||||
return await parseStreamJsonInputFromIterable(rl);
|
||||
} finally {
|
||||
rl.close();
|
||||
}
|
||||
}
|
||||
|
||||
export async function parseStreamJsonInputFromIterable(
|
||||
lines: AsyncIterable<string>,
|
||||
emitEnvelope: (envelope: StreamJsonOutputEnvelope) => void = writeEnvelope,
|
||||
): Promise<ParsedStreamJsonInput> {
|
||||
const promptParts: string[] = [];
|
||||
let receivedUserMessage = false;
|
||||
|
||||
for await (const rawLine of lines) {
|
||||
const line = rawLine.trim();
|
||||
if (!line) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const envelope = parseStreamJsonEnvelope(line);
|
||||
|
||||
switch (envelope.type) {
|
||||
case 'user':
|
||||
promptParts.push(extractUserMessageText(envelope));
|
||||
receivedUserMessage = true;
|
||||
break;
|
||||
case 'control_request':
|
||||
handleControlRequest(envelope, emitEnvelope);
|
||||
break;
|
||||
case 'control_response':
|
||||
case 'control_cancel_request':
|
||||
// Currently ignored on CLI side.
|
||||
break;
|
||||
default:
|
||||
throw new FatalInputError(
|
||||
`Unsupported stream-json input type: ${envelope.type}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!receivedUserMessage) {
|
||||
throw new FatalInputError(
|
||||
'No user message provided via stream-json input.',
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
prompt: promptParts.join('\n').trim(),
|
||||
};
|
||||
}
|
||||
|
||||
function handleControlRequest(
|
||||
envelope: StreamJsonControlRequestEnvelope,
|
||||
emitEnvelope: (envelope: StreamJsonOutputEnvelope) => void,
|
||||
) {
|
||||
const subtype = envelope.request?.subtype;
|
||||
if (subtype === 'initialize') {
|
||||
emitEnvelope({
|
||||
type: 'control_response',
|
||||
request_id: envelope.request_id,
|
||||
success: true,
|
||||
response: {
|
||||
subtype,
|
||||
capabilities: {},
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
emitEnvelope({
|
||||
type: 'control_response',
|
||||
request_id: envelope.request_id,
|
||||
success: false,
|
||||
error: `Unsupported control_request subtype: ${subtype ?? 'unknown'}`,
|
||||
});
|
||||
}
|
||||
|
||||
export function extractUserMessageText(
|
||||
envelope: StreamJsonUserEnvelope,
|
||||
): string {
|
||||
const content = envelope.message?.content;
|
||||
if (typeof content === 'string') {
|
||||
return content;
|
||||
}
|
||||
if (Array.isArray(content)) {
|
||||
return content
|
||||
.map((block) => {
|
||||
if (block && typeof block === 'object' && 'type' in block) {
|
||||
if (block.type === 'text' && 'text' in block) {
|
||||
return block.text ?? '';
|
||||
}
|
||||
return JSON.stringify(block);
|
||||
}
|
||||
return '';
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function writeEnvelope(envelope: StreamJsonOutputEnvelope): void {
|
||||
process.stdout.write(`${serializeStreamJsonEnvelope(envelope)}\n`);
|
||||
}
|
||||
Reference in New Issue
Block a user