mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
fix: prevent sending control request when query is closed
This commit is contained in:
@@ -555,6 +555,15 @@ describe('Permission Control (E2E)', () => {
|
|||||||
...SHARED_TEST_OPTIONS,
|
...SHARED_TEST_OPTIONS,
|
||||||
cwd: testDir,
|
cwd: testDir,
|
||||||
permissionMode: 'default',
|
permissionMode: 'default',
|
||||||
|
timeout: {
|
||||||
|
/**
|
||||||
|
* We use a short control request timeout and
|
||||||
|
* wait till the time exceeded to test if
|
||||||
|
* an immediate close() will raise an query close
|
||||||
|
* error and no other uncaught timeout error
|
||||||
|
*/
|
||||||
|
controlRequest: 5000,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -563,7 +572,9 @@ describe('Permission Control (E2E)', () => {
|
|||||||
await expect(q.setPermissionMode('yolo')).rejects.toThrow(
|
await expect(q.setPermissionMode('yolo')).rejects.toThrow(
|
||||||
'Query is closed',
|
'Query is closed',
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 8000));
|
||||||
|
}, 10_000);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('canUseTool and setPermissionMode integration', () => {
|
describe('canUseTool and setPermissionMode integration', () => {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ npm install @qwen-code/sdk-typescript
|
|||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Node.js >= 20.0.0
|
- Node.js >= 20.0.0
|
||||||
- [Qwen Code](https://github.com/QwenLM/qwen-code) installed and accessible in PATH
|
- [Qwen Code](https://github.com/QwenLM/qwen-code) >= 0.4.0 (stable) installed and accessible in PATH
|
||||||
|
|
||||||
> **Note for nvm users**: If you use nvm to manage Node.js versions, the SDK may not be able to auto-detect the Qwen Code executable. You should explicitly set the `pathToQwenExecutable` option to the full path of the `qwen` binary.
|
> **Note for nvm users**: If you use nvm to manage Node.js versions, the SDK may not be able to auto-detect the Qwen Code executable. You should explicitly set the `pathToQwenExecutable` option to the full path of the `qwen` binary.
|
||||||
|
|
||||||
|
|||||||
@@ -620,6 +620,10 @@ export class Query implements AsyncIterable<SDKMessage> {
|
|||||||
subtype: string,
|
subtype: string,
|
||||||
data: Record<string, unknown> = {},
|
data: Record<string, unknown> = {},
|
||||||
): Promise<Record<string, unknown> | null> {
|
): Promise<Record<string, unknown> | null> {
|
||||||
|
if (this.closed) {
|
||||||
|
return Promise.reject(new Error('Query is closed'));
|
||||||
|
}
|
||||||
|
|
||||||
const requestId = randomUUID();
|
const requestId = randomUUID();
|
||||||
|
|
||||||
const request: CLIControlRequest = {
|
const request: CLIControlRequest = {
|
||||||
@@ -688,12 +692,13 @@ export class Query implements AsyncIterable<SDKMessage> {
|
|||||||
for (const pending of this.pendingControlRequests.values()) {
|
for (const pending of this.pendingControlRequests.values()) {
|
||||||
pending.abortController.abort();
|
pending.abortController.abort();
|
||||||
clearTimeout(pending.timeout);
|
clearTimeout(pending.timeout);
|
||||||
|
pending.reject(new Error('Query is closed'));
|
||||||
}
|
}
|
||||||
this.pendingControlRequests.clear();
|
this.pendingControlRequests.clear();
|
||||||
|
|
||||||
// Clean up pending MCP responses
|
// Clean up pending MCP responses
|
||||||
for (const pending of this.pendingMcpResponses.values()) {
|
for (const pending of this.pendingMcpResponses.values()) {
|
||||||
pending.reject(new Error('Query closed'));
|
pending.reject(new Error('Query is closed'));
|
||||||
}
|
}
|
||||||
this.pendingMcpResponses.clear();
|
this.pendingMcpResponses.clear();
|
||||||
|
|
||||||
@@ -719,7 +724,7 @@ export class Query implements AsyncIterable<SDKMessage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.sdkMcpTransports.clear();
|
this.sdkMcpTransports.clear();
|
||||||
logger.info('Query closed');
|
logger.info('Query is closed');
|
||||||
}
|
}
|
||||||
|
|
||||||
private async *readSdkMessages(): AsyncGenerator<SDKMessage> {
|
private async *readSdkMessages(): AsyncGenerator<SDKMessage> {
|
||||||
@@ -821,28 +826,16 @@ export class Query implements AsyncIterable<SDKMessage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async interrupt(): Promise<void> {
|
async interrupt(): Promise<void> {
|
||||||
if (this.closed) {
|
|
||||||
throw new Error('Query is closed');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.sendControlRequest(ControlRequestType.INTERRUPT);
|
await this.sendControlRequest(ControlRequestType.INTERRUPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setPermissionMode(mode: string): Promise<void> {
|
async setPermissionMode(mode: string): Promise<void> {
|
||||||
if (this.closed) {
|
|
||||||
throw new Error('Query is closed');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.sendControlRequest(ControlRequestType.SET_PERMISSION_MODE, {
|
await this.sendControlRequest(ControlRequestType.SET_PERMISSION_MODE, {
|
||||||
mode,
|
mode,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setModel(model: string): Promise<void> {
|
async setModel(model: string): Promise<void> {
|
||||||
if (this.closed) {
|
|
||||||
throw new Error('Query is closed');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.sendControlRequest(ControlRequestType.SET_MODEL, { model });
|
await this.sendControlRequest(ControlRequestType.SET_MODEL, { model });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -853,10 +846,6 @@ export class Query implements AsyncIterable<SDKMessage> {
|
|||||||
* @throws Error if query is closed
|
* @throws Error if query is closed
|
||||||
*/
|
*/
|
||||||
async supportedCommands(): Promise<Record<string, unknown> | null> {
|
async supportedCommands(): Promise<Record<string, unknown> | null> {
|
||||||
if (this.closed) {
|
|
||||||
throw new Error('Query is closed');
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.sendControlRequest(ControlRequestType.SUPPORTED_COMMANDS);
|
return this.sendControlRequest(ControlRequestType.SUPPORTED_COMMANDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -867,10 +856,6 @@ export class Query implements AsyncIterable<SDKMessage> {
|
|||||||
* @throws Error if query is closed
|
* @throws Error if query is closed
|
||||||
*/
|
*/
|
||||||
async mcpServerStatus(): Promise<Record<string, unknown> | null> {
|
async mcpServerStatus(): Promise<Record<string, unknown> | null> {
|
||||||
if (this.closed) {
|
|
||||||
throw new Error('Query is closed');
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.sendControlRequest(ControlRequestType.MCP_SERVER_STATUS);
|
return this.sendControlRequest(ControlRequestType.MCP_SERVER_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user