chore(vscode): run dev cli from source (#4283)

This commit is contained in:
Dragon 2026-05-19 06:56:16 +08:00 committed by GitHub
parent f97cb680a0
commit 3605f81779
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 84 additions and 8 deletions

View file

@ -15,6 +15,12 @@ To run the extension locally for development, we recommend using the automatic w
```
4. **Launch Host:** Press **`F5`** (or **`fn+F5`** on Mac) to open a new **Extension Development Host** window with the extension running.
When the extension runs in VS Code's development mode, the chat panel starts
the CLI through the repository root `scripts/dev.js` entrypoint. CLI and core
source changes are picked up on the next ACP process start without rebuilding
or copying the bundled `dist/qwen-cli/cli.js`. Extension UI changes still need
the watch process above.
### Manual Build
If you only need to compile the extension once without watching for changes:

View file

@ -126,6 +126,11 @@ vi.mock('@qwen-code/qwen-code-core', async () => {
});
vi.mock('vscode', () => ({
ExtensionMode: {
Production: 1,
Development: 2,
Test: 3,
},
ConfigurationTarget: {
Global: 'global',
},
@ -330,11 +335,14 @@ vi.mock('../../utils/errorMessage.js', () => ({
getErrorMessage: vi.fn((error: unknown) => String(error)),
}));
import { WebViewProvider } from './WebViewProvider.js';
import { WebViewProvider, resolveQwenCliEntryPath } from './WebViewProvider.js';
import {
truncatePanelTitle,
MAX_PANEL_TITLE_LENGTH,
} from '../utils/panelTitleUtils.js';
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import * as path from 'node:path';
const createConfigChangeEvent = (...affectedSections: string[]) => ({
affectsConfiguration: (section: string) => affectedSections.includes(section),
@ -345,6 +353,38 @@ type WebViewMessageHandler = (message: {
data?: unknown;
}) => Promise<void>;
describe('resolveQwenCliEntryPath', () => {
it('uses the source dev entry when the extension runs in development mode', () => {
const tempRoot = mkdtempSync(path.join(tmpdir(), 'qwen-vscode-dev-'));
try {
const extensionRoot = path.join(
tempRoot,
'packages',
'vscode-ide-companion',
);
const devEntry = path.join(tempRoot, 'scripts', 'dev.js');
mkdirSync(extensionRoot, { recursive: true });
mkdirSync(path.dirname(devEntry), { recursive: true });
writeFileSync(devEntry, '');
expect(
resolveQwenCliEntryPath({ fsPath: extensionRoot } as never, 2),
).toBe(devEntry);
} finally {
rmSync(tempRoot, { recursive: true, force: true });
}
});
it('uses the bundled CLI outside development mode', () => {
expect(
resolveQwenCliEntryPath(
{ fsPath: '/extension-root' } as never,
undefined,
),
).toBe('/extension-root/dist/qwen-cli/cli.js');
});
});
/**
* Create a mock webview + provider and attach them.
* If `captureMessageHandler` is true, the `onDidReceiveMessage` handler is

View file

@ -6,6 +6,8 @@
import * as vscode from 'vscode';
import { execFile } from 'child_process';
import { existsSync } from 'node:fs';
import * as path from 'node:path';
import { QwenAgentManager } from '../../services/qwenAgentManager.js';
import { ConversationStore } from '../../services/conversationStore.js';
import type {
@ -60,6 +62,26 @@ const AUTH_RELATED_QWEN_SETTINGS = [
'qwen-code.codingPlanRegion',
] as const;
export function resolveQwenCliEntryPath(
extensionUri: vscode.Uri,
extensionMode: vscode.ExtensionMode | undefined,
): string {
if (extensionMode === vscode.ExtensionMode.Development) {
const devCliEntry = path.resolve(
extensionUri.fsPath,
'..',
'..',
'scripts',
'dev.js',
);
if (existsSync(devCliEntry)) {
return devCliEntry;
}
}
return vscode.Uri.joinPath(extensionUri, 'dist', 'qwen-cli', 'cli.js').fsPath;
}
function isInsightCommand(command: string): boolean {
const [firstToken = ''] = command.trim().split(/\s+/, 1);
return firstToken.replace(/^\/+/, '') === 'insight';
@ -1206,12 +1228,10 @@ export class WebViewProvider {
`[WebViewProvider] Using CLI-managed authentication (autoAuth=${autoAuthenticate})`,
);
const bundledCliEntry = vscode.Uri.joinPath(
const cliEntry = resolveQwenCliEntryPath(
this.extensionUri,
'dist',
'qwen-cli',
'cli.js',
).fsPath;
this.context.extensionMode,
);
try {
console.log('[WebViewProvider] Connecting to agent...');
@ -1219,7 +1239,7 @@ export class WebViewProvider {
// Pass the detected CLI path to ensure we use the correct installation
const connectResult = await this.agentManager.connect(
workingDir,
bundledCliEntry,
cliEntry,
options,
);
console.log('[WebViewProvider] Agent connected successfully');

View file

@ -109,7 +109,17 @@ const env = {
// On Windows, use tsx.cmd; on Unix, use tsx directly
const isWin = platform() === 'win32';
const tsxCmd = isWin ? 'tsx.cmd' : 'tsx';
const localTsxCmd = join(
root,
'node_modules',
'.bin',
isWin ? 'tsx.cmd' : 'tsx',
);
const tsxCmd = existsSync(localTsxCmd)
? localTsxCmd
: isWin
? 'tsx.cmd'
: 'tsx';
const tsxArgs = [cliEntry, ...process.argv.slice(2)];
const child = spawn(tsxCmd, tsxArgs, {