mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-04 22:51:08 +00:00
Merge pull request #2718 from QwenLM/fix/terminal-response-leak-ssh
fix(cli): prevent terminal response leakage on high-latency SSH
This commit is contained in:
commit
067430eef2
3 changed files with 23 additions and 122 deletions
|
|
@ -558,121 +558,4 @@ describe('AuthDialog', () => {
|
|||
expect(handleAuthSelect).toHaveBeenCalledWith(undefined);
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('shows API Key subtype menu and opens custom info', async () => {
|
||||
const settings: LoadedSettings = new LoadedSettings(
|
||||
{
|
||||
settings: { ui: { customThemes: {} }, mcpServers: {} },
|
||||
originalSettings: { ui: { customThemes: {} }, mcpServers: {} },
|
||||
path: '',
|
||||
},
|
||||
{
|
||||
settings: {},
|
||||
originalSettings: {},
|
||||
path: '',
|
||||
},
|
||||
{
|
||||
settings: {
|
||||
security: { auth: { selectedType: undefined } },
|
||||
ui: { customThemes: {} },
|
||||
mcpServers: {},
|
||||
},
|
||||
originalSettings: {
|
||||
security: { auth: { selectedType: undefined } },
|
||||
ui: { customThemes: {} },
|
||||
mcpServers: {},
|
||||
},
|
||||
path: '',
|
||||
},
|
||||
{
|
||||
settings: { ui: { customThemes: {} }, mcpServers: {} },
|
||||
originalSettings: { ui: { customThemes: {} }, mcpServers: {} },
|
||||
path: '',
|
||||
},
|
||||
true,
|
||||
new Set(),
|
||||
);
|
||||
|
||||
const { stdin, lastFrame, unmount } = renderAuthDialog(settings);
|
||||
await wait();
|
||||
|
||||
// Move from Qwen OAuth -> Coding Plan -> API Key, then enter
|
||||
stdin.write('\u001B[B');
|
||||
stdin.write('\u001B[B');
|
||||
stdin.write('\r');
|
||||
await wait();
|
||||
|
||||
expect(lastFrame()).toContain('Select API Key Type');
|
||||
expect(lastFrame()).toContain('Alibaba Cloud ModelStudio Standard API Key');
|
||||
expect(lastFrame()).toContain('Custom API Key');
|
||||
|
||||
// Move to Custom API Key and enter
|
||||
stdin.write('\u001B[B');
|
||||
stdin.write('\r');
|
||||
await wait();
|
||||
|
||||
expect(lastFrame()).toContain('Custom Configuration');
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('shows Alibaba Cloud ModelStudio Standard API Key region endpoint', async () => {
|
||||
const settings: LoadedSettings = new LoadedSettings(
|
||||
{
|
||||
settings: { ui: { customThemes: {} }, mcpServers: {} },
|
||||
originalSettings: { ui: { customThemes: {} }, mcpServers: {} },
|
||||
path: '',
|
||||
},
|
||||
{
|
||||
settings: {},
|
||||
originalSettings: {},
|
||||
path: '',
|
||||
},
|
||||
{
|
||||
settings: {
|
||||
security: { auth: { selectedType: undefined } },
|
||||
ui: { customThemes: {} },
|
||||
mcpServers: {},
|
||||
},
|
||||
originalSettings: {
|
||||
security: { auth: { selectedType: undefined } },
|
||||
ui: { customThemes: {} },
|
||||
mcpServers: {},
|
||||
},
|
||||
path: '',
|
||||
},
|
||||
{
|
||||
settings: { ui: { customThemes: {} }, mcpServers: {} },
|
||||
originalSettings: { ui: { customThemes: {} }, mcpServers: {} },
|
||||
path: '',
|
||||
},
|
||||
true,
|
||||
new Set(),
|
||||
);
|
||||
|
||||
const { stdin, lastFrame, unmount } = renderAuthDialog(settings, {}, {});
|
||||
await wait();
|
||||
|
||||
// Main -> API Key
|
||||
stdin.write('\u001B[B');
|
||||
stdin.write('\u001B[B');
|
||||
stdin.write('\r');
|
||||
await wait();
|
||||
|
||||
// API Key type -> Alibaba Cloud ModelStudio Standard API Key (default)
|
||||
stdin.write('\r');
|
||||
await wait();
|
||||
|
||||
// Region -> Singapore
|
||||
stdin.write('\u001B[B');
|
||||
stdin.write('\r');
|
||||
await wait();
|
||||
|
||||
expect(lastFrame()).toContain(
|
||||
'Enter Alibaba Cloud ModelStudio Standard API Key',
|
||||
);
|
||||
expect(lastFrame()).toContain(
|
||||
'https://dashscope-intl.aliyuncs.com/compatible-mode/v1',
|
||||
);
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -540,7 +540,16 @@ export function KeypressProvider({
|
|||
}
|
||||
};
|
||||
|
||||
// Matches terminal query responses (DA1, DA2, Kitty protocol query)
|
||||
// that may arrive late from startup detection in kittyProtocolDetector.
|
||||
// These are never valid user input.
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const TERMINAL_RESPONSE_RE = /^\x1b\[[?>][\d;]*[uc]$/;
|
||||
|
||||
const handleKeypress = async (_: unknown, key: Key) => {
|
||||
if (TERMINAL_RESPONSE_RE.test(key.sequence)) {
|
||||
return;
|
||||
}
|
||||
if (key.sequence === FOCUS_IN || key.sequence === FOCUS_OUT) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,11 +37,20 @@ export async function detectAndEnableKittyProtocol(): Promise<boolean> {
|
|||
const onTimeout = () => {
|
||||
timeoutId = undefined;
|
||||
process.stdin.removeListener('data', handleData);
|
||||
if (!originalRawMode) {
|
||||
process.stdin.setRawMode(false);
|
||||
}
|
||||
detectionComplete = true;
|
||||
resolve(false);
|
||||
|
||||
// Keep a drain handler briefly to consume any late-arriving terminal
|
||||
// responses that would otherwise leak into the application input.
|
||||
const drainHandler = () => {};
|
||||
process.stdin.on('data', drainHandler);
|
||||
|
||||
setTimeout(() => {
|
||||
process.stdin.removeListener('data', drainHandler);
|
||||
if (!originalRawMode) {
|
||||
process.stdin.setRawMode(false);
|
||||
}
|
||||
detectionComplete = true;
|
||||
resolve(false);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
const handleData = (data: Buffer) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue