返回 CodeWhale
runtime.ts
根目录 / extensions / vscode / src / runtime.ts
1 import * as vscode from "vscode";
2 import type { SnapshotEntry, ThreadSummary } from "./api";
3
4 export type { SnapshotEntry, ThreadSummary };
5
6 export interface RuntimeState {
7 kind: "connected" | "offline" | "auth-required" | "error";
8 baseUrl: string;
9 detail: string;
10 version?: string;
11 }
12
13 export interface RuntimeConfig {
14 commandPath: string;
15 host: string;
16 port: number;
17 agentViewRefreshIntervalSeconds: number;
18 }
19
20 export function readRuntimeConfig(): RuntimeConfig {
21 const config = vscode.workspace.getConfiguration("codewhale");
22 const commandPath = config.get<string>("commandPath", "codewhale").trim() || "codewhale";
23 const host = config.get<string>("runtimeHost", "127.0.0.1").trim() || "127.0.0.1";
24 const port = config.get<number>("runtimePort", 7878);
25 const interval = config.get<number>("agentViewRefreshIntervalSeconds", 15);
26 // The bearer token is deliberately absent here: it resolves through
27 // `secrets.ts`, where SecretStorage wins over the deprecated setting.
28 return {
29 commandPath,
30 host,
31 port,
32 agentViewRefreshIntervalSeconds: clampRefreshInterval(interval),
33 };
34 }
35
36 export function runtimeBaseUrl(config: RuntimeConfig): string {
37 return `http://${config.host}:${config.port}`;
38 }
39
40 /**
41 * Start `codewhale serve` in a visible terminal.
42 *
43 * The bearer token never enters argv: a sent command line lands in the terminal
44 * buffer, the shell history file, and every local `ps`. The runtime accepts
45 * `CODEWHALE_RUNTIME_TOKEN` as the fallback for `--auth-token`
46 * (`crates/tui/src/lib.rs:1325-1327`), so it travels in the terminal's
47 * environment instead.
48 */
49 export function startRuntimeTerminal(config: RuntimeConfig, token?: string): vscode.Terminal {
50 const terminal = vscode.window.createTerminal({
51 name: "CodeWhale Runtime",
52 env: token ? { CODEWHALE_RUNTIME_TOKEN: token } : undefined,
53 });
54 const args = [
55 "serve",
56 "--http",
57 "--host",
58 shellQuote(config.host),
59 "--port",
60 String(config.port),
61 ];
62 terminal.sendText(`${shellQuote(config.commandPath)} ${args.join(" ")}`);
63 terminal.show();
64 return terminal;
65 }
66
67 export function openCodeWhaleTerminal(config: RuntimeConfig): vscode.Terminal {
68 const terminal = vscode.window.createTerminal("CodeWhale");
69 terminal.sendText(shellQuote(config.commandPath));
70 terminal.show();
71 return terminal;
72 }
73
74 function clampRefreshInterval(value: number): number {
75 if (!Number.isFinite(value)) {
76 return 15;
77 }
78 return Math.max(0, Math.min(300, Math.floor(value)));
79 }
80
81 function shellQuote(value: string): string {
82 if (/^[A-Za-z0-9_./:=+-]+$/.test(value)) {
83 return value;
84 }
85 return `'${value.replace(/'/g, "'\\''")}'`;
86 }
87
87 lines TYPESCRIPT