返回 CodeWhale
extension.ts
根目录 / extensions / vscode / src / extension.ts
1 import * as vscode from "vscode";
2 import {
3 checkRuntime,
4 listSnapshots,
5 listThreadSummaries,
6 openCodeWhaleTerminal,
7 readRuntimeConfig,
8 runtimeBaseUrl,
9 startRuntimeTerminal,
10 type RuntimeState,
11 } from "./runtime";
12 import { RuntimeStatusView } from "./status";
13
14 export function activate(context: vscode.ExtensionContext): void {
15 const output = vscode.window.createOutputChannel("CodeWhale");
16 const status = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100);
17 const statusView = new RuntimeStatusView();
18 let autoRefreshTimer: ReturnType<typeof setInterval> | undefined;
19 let autoRefreshInFlight = false;
20
21 status.command = "codewhale.checkRuntime";
22 context.subscriptions.push(output, status);
23 context.subscriptions.push(
24 vscode.window.registerWebviewViewProvider(RuntimeStatusView.viewType, statusView),
25 );
26
27 const refreshAgentView = async (): Promise<void> => {
28 const config = readRuntimeConfig();
29 const threads = await listThreadSummaries(config);
30 statusView.updateThreads(threads, "Showing recent runtime threads.");
31 output.appendLine(`Loaded ${threads.length} runtime thread summaries.`);
32 };
33
34 const refreshSnapshots = async (): Promise<void> => {
35 const config = readRuntimeConfig();
36 const snapshots = await listSnapshots(config);
37 statusView.updateSnapshots(snapshots, "Showing recent restore points.");
38 output.appendLine(`Loaded ${snapshots.length} runtime restore points.`);
39 };
40
41 const refreshAgentViewDetails = async (showWarning: boolean): Promise<void> => {
42 try {
43 await refreshAgentView();
44 } catch (error: unknown) {
45 const detail = error instanceof Error ? error.message : String(error);
46 statusView.updateThreads([], "Runtime thread summaries unavailable.");
47 output.appendLine(`Runtime thread summaries unavailable: ${detail}`);
48 if (showWarning) {
49 void vscode.window.showWarningMessage(detail);
50 }
51 }
52
53 try {
54 await refreshSnapshots();
55 } catch (error: unknown) {
56 const detail = error instanceof Error ? error.message : String(error);
57 statusView.updateSnapshots([], detail);
58 output.appendLine(`Runtime restore points unavailable: ${detail}`);
59 if (showWarning) {
60 void vscode.window.showWarningMessage(detail);
61 }
62 }
63 };
64
65 const updateStatus = (text: string, tooltip: string): void => {
66 status.text = text;
67 status.tooltip = tooltip;
68 status.show();
69 };
70
71 const checkAndRefreshRuntime = async (
72 showSpinner: boolean,
73 logResult: boolean,
74 ): Promise<RuntimeState> => {
75 const config = readRuntimeConfig();
76 if (showSpinner) {
77 updateStatus("$(sync~spin) CodeWhale", "Checking CodeWhale runtime...");
78 }
79
80 const state = await checkRuntime(config);
81 statusView.update(state);
82
83 switch (state.kind) {
84 case "connected":
85 updateStatus("$(check) CodeWhale", state.detail);
86 await refreshAgentViewDetails(false);
87 break;
88 case "auth-required":
89 updateStatus("$(lock) CodeWhale", state.detail);
90 statusView.updateThreads([], "Runtime token is required before threads can load.");
91 statusView.updateSnapshots([], "Runtime token is required before restore points can load.");
92 break;
93 case "offline":
94 case "error":
95 updateStatus("$(warning) CodeWhale", state.detail);
96 statusView.updateThreads([], "Connect to the runtime to load recent threads.");
97 statusView.updateSnapshots([], "Connect to the runtime to load restore points.");
98 break;
99 }
100
101 if (logResult) {
102 output.appendLine(`${new Date().toISOString()} ${state.kind}: ${state.detail}`);
103 }
104 return state;
105 };
106
107 const runAutoRefresh = async (): Promise<void> => {
108 if (autoRefreshInFlight) {
109 return;
110 }
111
112 autoRefreshInFlight = true;
113 try {
114 await checkAndRefreshRuntime(false, false);
115 } finally {
116 autoRefreshInFlight = false;
117 }
118 };
119
120 const scheduleAutoRefresh = (): void => {
121 if (autoRefreshTimer) {
122 clearInterval(autoRefreshTimer);
123 autoRefreshTimer = undefined;
124 }
125
126 const intervalSeconds = readRuntimeConfig().agentViewRefreshIntervalSeconds;
127 if (intervalSeconds === 0) {
128 output.appendLine("Agent View auto-refresh is disabled.");
129 return;
130 }
131
132 autoRefreshTimer = setInterval(() => {
133 void runAutoRefresh();
134 }, intervalSeconds * 1000);
135 output.appendLine(`Agent View auto-refresh scheduled every ${intervalSeconds}s.`);
136 };
137
138 updateStatus("$(terminal) CodeWhale", "Check CodeWhale runtime");
139 scheduleAutoRefresh();
140 context.subscriptions.push(
141 new vscode.Disposable(() => {
142 if (autoRefreshTimer) {
143 clearInterval(autoRefreshTimer);
144 }
145 }),
146 vscode.workspace.onDidChangeConfiguration((event) => {
147 if (event.affectsConfiguration("codewhale.agentViewRefreshIntervalSeconds")) {
148 scheduleAutoRefresh();
149 }
150 }),
151 );
152
153 context.subscriptions.push(
154 vscode.commands.registerCommand("codewhale.openTerminal", () => {
155 const config = readRuntimeConfig();
156 openCodeWhaleTerminal(config);
157 output.appendLine(`Opened CodeWhale terminal using ${config.commandPath}.`);
158 }),
159 );
160
161 context.subscriptions.push(
162 vscode.commands.registerCommand("codewhale.startRuntime", () => {
163 const config = readRuntimeConfig();
164 startRuntimeTerminal(config);
165 const baseUrl = runtimeBaseUrl(config);
166 updateStatus("$(sync~spin) CodeWhale", `Runtime terminal started for ${baseUrl}`);
167 output.appendLine(`Started CodeWhale runtime terminal at ${baseUrl}.`);
168 void vscode.window.showInformationMessage(`CodeWhale runtime starting at ${baseUrl}`);
169 }),
170 );
171
172 context.subscriptions.push(
173 vscode.commands.registerCommand("codewhale.checkRuntime", async () => {
174 return await checkAndRefreshRuntime(true, true);
175 }),
176 );
177
178 context.subscriptions.push(
179 vscode.commands.registerCommand("codewhale.refreshAgentView", async () => {
180 await refreshAgentViewDetails(true);
181 }),
182 );
183
184 context.subscriptions.push(
185 vscode.commands.registerCommand("codewhale.refreshSnapshots", async () => {
186 try {
187 await refreshSnapshots();
188 } catch (error: unknown) {
189 const detail = error instanceof Error ? error.message : String(error);
190 statusView.updateSnapshots([], detail);
191 output.appendLine(`Runtime restore points unavailable: ${detail}`);
192 void vscode.window.showWarningMessage(detail);
193 }
194 }),
195 );
196
197 context.subscriptions.push(
198 vscode.commands.registerCommand("codewhale.openRuntimeDocs", () => {
199 void vscode.env.openExternal(
200 vscode.Uri.parse(
201 "https://github.com/Hmbown/CodeWhale/blob/main/docs/RUNTIME_API.md",
202 ),
203 );
204 }),
205 );
206
207 void vscode.commands.executeCommand("codewhale.checkRuntime");
208 }
209
210 export function deactivate(): void {
211 // No background process is owned by the extension; runtime starts in a user-visible terminal.
212 }
213
213 lines TYPESCRIPT