@carboncode/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSES/DeepSeek-Reasonix-MIT.txt +21 -0
  3. package/README.md +109 -0
  4. package/README.zh-CN.md +91 -0
  5. package/THIRD_PARTY_NOTICES.md +14 -0
  6. package/dashboard/app.css +3233 -0
  7. package/dashboard/dist/app.js +30444 -0
  8. package/dashboard/dist/app.js.map +1 -0
  9. package/dashboard/dist/vendor-hljs.css +10 -0
  10. package/dashboard/dist/vendor-uplot.css +1 -0
  11. package/dashboard/index.html +19 -0
  12. package/data/deepseek-tokenizer.json.gz +0 -0
  13. package/dist/cli/acp-35C4ME6Y.js +711 -0
  14. package/dist/cli/acp-35C4ME6Y.js.map +1 -0
  15. package/dist/cli/chat-A6UJDPGV.js +51 -0
  16. package/dist/cli/chat-A6UJDPGV.js.map +1 -0
  17. package/dist/cli/chunk-2425HK6U.js +54 -0
  18. package/dist/cli/chunk-2425HK6U.js.map +1 -0
  19. package/dist/cli/chunk-25T6CVUP.js +172 -0
  20. package/dist/cli/chunk-25T6CVUP.js.map +1 -0
  21. package/dist/cli/chunk-2UQP6H6T.js +31 -0
  22. package/dist/cli/chunk-2UQP6H6T.js.map +1 -0
  23. package/dist/cli/chunk-3OAR6NVL.js +96 -0
  24. package/dist/cli/chunk-3OAR6NVL.js.map +1 -0
  25. package/dist/cli/chunk-3T6VBZCL.js +54 -0
  26. package/dist/cli/chunk-3T6VBZCL.js.map +1 -0
  27. package/dist/cli/chunk-4IBIPQVB.js +153 -0
  28. package/dist/cli/chunk-4IBIPQVB.js.map +1 -0
  29. package/dist/cli/chunk-4MQ3VURH.js +3106 -0
  30. package/dist/cli/chunk-4MQ3VURH.js.map +1 -0
  31. package/dist/cli/chunk-4TVNJWMA.js +11619 -0
  32. package/dist/cli/chunk-4TVNJWMA.js.map +1 -0
  33. package/dist/cli/chunk-4VR6XF4P.js +341 -0
  34. package/dist/cli/chunk-4VR6XF4P.js.map +1 -0
  35. package/dist/cli/chunk-5QCB62C4.js +25319 -0
  36. package/dist/cli/chunk-5QCB62C4.js.map +1 -0
  37. package/dist/cli/chunk-6OWJV3YW.js +390 -0
  38. package/dist/cli/chunk-6OWJV3YW.js.map +1 -0
  39. package/dist/cli/chunk-7EO27TB3.js +130 -0
  40. package/dist/cli/chunk-7EO27TB3.js.map +1 -0
  41. package/dist/cli/chunk-7L2WTRNU.js +308 -0
  42. package/dist/cli/chunk-7L2WTRNU.js.map +1 -0
  43. package/dist/cli/chunk-BHTZFEYE.js +47 -0
  44. package/dist/cli/chunk-BHTZFEYE.js.map +1 -0
  45. package/dist/cli/chunk-BSGCXZQN.js +343 -0
  46. package/dist/cli/chunk-BSGCXZQN.js.map +1 -0
  47. package/dist/cli/chunk-BSINVTTL.js +464 -0
  48. package/dist/cli/chunk-BSINVTTL.js.map +1 -0
  49. package/dist/cli/chunk-CPKCNHRR.js +323 -0
  50. package/dist/cli/chunk-CPKCNHRR.js.map +1 -0
  51. package/dist/cli/chunk-CXVWUPA3.js +96 -0
  52. package/dist/cli/chunk-CXVWUPA3.js.map +1 -0
  53. package/dist/cli/chunk-D5NFKRGO.js +160 -0
  54. package/dist/cli/chunk-D5NFKRGO.js.map +1 -0
  55. package/dist/cli/chunk-ECHSFYOY.js +109 -0
  56. package/dist/cli/chunk-ECHSFYOY.js.map +1 -0
  57. package/dist/cli/chunk-FEZK652I.js +3644 -0
  58. package/dist/cli/chunk-FEZK652I.js.map +1 -0
  59. package/dist/cli/chunk-GALC45Q2.js +696 -0
  60. package/dist/cli/chunk-GALC45Q2.js.map +1 -0
  61. package/dist/cli/chunk-IAUOP25G.js +2984 -0
  62. package/dist/cli/chunk-IAUOP25G.js.map +1 -0
  63. package/dist/cli/chunk-ILJOIQ5W.js +163 -0
  64. package/dist/cli/chunk-ILJOIQ5W.js.map +1 -0
  65. package/dist/cli/chunk-IX6XI2RG.js +225 -0
  66. package/dist/cli/chunk-IX6XI2RG.js.map +1 -0
  67. package/dist/cli/chunk-J5BYPUB5.js +62795 -0
  68. package/dist/cli/chunk-J5BYPUB5.js.map +1 -0
  69. package/dist/cli/chunk-J5XJHLWM.js +55 -0
  70. package/dist/cli/chunk-J5XJHLWM.js.map +1 -0
  71. package/dist/cli/chunk-JKGYMRX5.js +101 -0
  72. package/dist/cli/chunk-JKGYMRX5.js.map +1 -0
  73. package/dist/cli/chunk-JMBMLOBP.js +26 -0
  74. package/dist/cli/chunk-JMBMLOBP.js.map +1 -0
  75. package/dist/cli/chunk-LN3B5PMX.js +128 -0
  76. package/dist/cli/chunk-LN3B5PMX.js.map +1 -0
  77. package/dist/cli/chunk-M2UFZUX3.js +635 -0
  78. package/dist/cli/chunk-M2UFZUX3.js.map +1 -0
  79. package/dist/cli/chunk-PJS34556.js +809 -0
  80. package/dist/cli/chunk-PJS34556.js.map +1 -0
  81. package/dist/cli/chunk-QJG7OF27.js +655 -0
  82. package/dist/cli/chunk-QJG7OF27.js.map +1 -0
  83. package/dist/cli/chunk-QVC75MR3.js +232 -0
  84. package/dist/cli/chunk-QVC75MR3.js.map +1 -0
  85. package/dist/cli/chunk-S2KIUQKQ.js +378 -0
  86. package/dist/cli/chunk-S2KIUQKQ.js.map +1 -0
  87. package/dist/cli/chunk-S4XVGLRW.js +499 -0
  88. package/dist/cli/chunk-S4XVGLRW.js.map +1 -0
  89. package/dist/cli/chunk-T5TQ4NDT.js +190 -0
  90. package/dist/cli/chunk-T5TQ4NDT.js.map +1 -0
  91. package/dist/cli/chunk-TH756VLN.js +1924 -0
  92. package/dist/cli/chunk-TH756VLN.js.map +1 -0
  93. package/dist/cli/chunk-TUK7OWJA.js +51 -0
  94. package/dist/cli/chunk-TUK7OWJA.js.map +1 -0
  95. package/dist/cli/chunk-U4IJVG32.js +363 -0
  96. package/dist/cli/chunk-U4IJVG32.js.map +1 -0
  97. package/dist/cli/chunk-UI66BH6D.js +624 -0
  98. package/dist/cli/chunk-UI66BH6D.js.map +1 -0
  99. package/dist/cli/chunk-VPMBGAND.js +53 -0
  100. package/dist/cli/chunk-VPMBGAND.js.map +1 -0
  101. package/dist/cli/chunk-WLHH3OSR.js +522 -0
  102. package/dist/cli/chunk-WLHH3OSR.js.map +1 -0
  103. package/dist/cli/chunk-WRN65TRD.js +908 -0
  104. package/dist/cli/chunk-WRN65TRD.js.map +1 -0
  105. package/dist/cli/chunk-X53B3JIX.js +34320 -0
  106. package/dist/cli/chunk-X53B3JIX.js.map +1 -0
  107. package/dist/cli/chunk-XJ5SRLKK.js +50 -0
  108. package/dist/cli/chunk-XJ5SRLKK.js.map +1 -0
  109. package/dist/cli/chunk-YZSXRGFH.js +54 -0
  110. package/dist/cli/chunk-YZSXRGFH.js.map +1 -0
  111. package/dist/cli/code-4TUTAGO5.js +163 -0
  112. package/dist/cli/code-4TUTAGO5.js.map +1 -0
  113. package/dist/cli/commands-KMOZEYCF.js +356 -0
  114. package/dist/cli/commands-KMOZEYCF.js.map +1 -0
  115. package/dist/cli/commit-DTFA56VQ.js +292 -0
  116. package/dist/cli/commit-DTFA56VQ.js.map +1 -0
  117. package/dist/cli/desktop-7N3MHNBD.js +1274 -0
  118. package/dist/cli/desktop-7N3MHNBD.js.map +1 -0
  119. package/dist/cli/devtools-HW3WDT3Q.js +91 -0
  120. package/dist/cli/devtools-HW3WDT3Q.js.map +1 -0
  121. package/dist/cli/diff-E5OWTF4C.js +165 -0
  122. package/dist/cli/diff-E5OWTF4C.js.map +1 -0
  123. package/dist/cli/doctor-IEJQRJMN.js +27 -0
  124. package/dist/cli/doctor-IEJQRJMN.js.map +1 -0
  125. package/dist/cli/events-4625EGXI.js +340 -0
  126. package/dist/cli/events-4625EGXI.js.map +1 -0
  127. package/dist/cli/index.js +3536 -0
  128. package/dist/cli/index.js.map +1 -0
  129. package/dist/cli/mcp-PDI2PDLG.js +277 -0
  130. package/dist/cli/mcp-PDI2PDLG.js.map +1 -0
  131. package/dist/cli/mcp-browse-OSPXOFPZ.js +178 -0
  132. package/dist/cli/mcp-browse-OSPXOFPZ.js.map +1 -0
  133. package/dist/cli/mcp-inspect-QRFVTHMF.js +148 -0
  134. package/dist/cli/mcp-inspect-QRFVTHMF.js.map +1 -0
  135. package/dist/cli/package.json +3 -0
  136. package/dist/cli/prompt-3CDII3UO.js +16 -0
  137. package/dist/cli/prompt-3CDII3UO.js.map +1 -0
  138. package/dist/cli/prune-sessions-KZX4SXKW.js +44 -0
  139. package/dist/cli/prune-sessions-KZX4SXKW.js.map +1 -0
  140. package/dist/cli/replay-HYOSRQIV.js +291 -0
  141. package/dist/cli/replay-HYOSRQIV.js.map +1 -0
  142. package/dist/cli/run-2ZHADOUP.js +220 -0
  143. package/dist/cli/run-2ZHADOUP.js.map +1 -0
  144. package/dist/cli/server-X75PAZG5.js +3572 -0
  145. package/dist/cli/server-X75PAZG5.js.map +1 -0
  146. package/dist/cli/sessions-POOZA5CQ.js +120 -0
  147. package/dist/cli/sessions-POOZA5CQ.js.map +1 -0
  148. package/dist/cli/setup-YLPFI3OH.js +618 -0
  149. package/dist/cli/setup-YLPFI3OH.js.map +1 -0
  150. package/dist/cli/stats-NXJ3TO2D.js +16 -0
  151. package/dist/cli/stats-NXJ3TO2D.js.map +1 -0
  152. package/dist/cli/update-ZUO5MKQ6.js +15 -0
  153. package/dist/cli/update-ZUO5MKQ6.js.map +1 -0
  154. package/dist/cli/version-NXXWE3WN.js +33 -0
  155. package/dist/cli/version-NXXWE3WN.js.map +1 -0
  156. package/dist/index.d.ts +2523 -0
  157. package/dist/index.js +15408 -0
  158. package/dist/index.js.map +1 -0
  159. package/package.json +112 -0
@@ -0,0 +1,1274 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
3
+ import {
4
+ createMcpRuntime
5
+ } from "./chunk-CPKCNHRR.js";
6
+ import {
7
+ buildCodeToolset
8
+ } from "./chunk-S2KIUQKQ.js";
9
+ import {
10
+ Eventizer,
11
+ autoResolveVerdict
12
+ } from "./chunk-WLHH3OSR.js";
13
+ import "./chunk-XJ5SRLKK.js";
14
+ import "./chunk-VPMBGAND.js";
15
+ import {
16
+ CacheFirstLoop,
17
+ ImmutablePrefix,
18
+ listDirectory,
19
+ listFilesWithStatsAsync,
20
+ parseAtQuery,
21
+ rankPickerCandidates
22
+ } from "./chunk-4TVNJWMA.js";
23
+ import "./chunk-7L2WTRNU.js";
24
+ import "./chunk-T5TQ4NDT.js";
25
+ import "./chunk-GALC45Q2.js";
26
+ import {
27
+ MemoryStore,
28
+ codeSystemPrompt
29
+ } from "./chunk-QJG7OF27.js";
30
+ import {
31
+ canonicalPresetName,
32
+ resolvePreset
33
+ } from "./chunk-2425HK6U.js";
34
+ import {
35
+ countTokensBounded
36
+ } from "./chunk-6OWJV3YW.js";
37
+ import {
38
+ DeepSeekClient,
39
+ pickPrimaryBalance
40
+ } from "./chunk-BSGCXZQN.js";
41
+ import "./chunk-25T6CVUP.js";
42
+ import {
43
+ loadDotenv
44
+ } from "./chunk-2UQP6H6T.js";
45
+ import "./chunk-3OAR6NVL.js";
46
+ import {
47
+ pauseGate
48
+ } from "./chunk-TH756VLN.js";
49
+ import {
50
+ SkillStore
51
+ } from "./chunk-M2UFZUX3.js";
52
+ import "./chunk-BHTZFEYE.js";
53
+ import "./chunk-WRN65TRD.js";
54
+ import "./chunk-IX6XI2RG.js";
55
+ import "./chunk-S4XVGLRW.js";
56
+ import {
57
+ deleteSession,
58
+ listSessionsForWorkspace,
59
+ loadSessionMessages,
60
+ loadSessionMeta,
61
+ patchSessionMeta,
62
+ sessionPath,
63
+ timestampSuffix
64
+ } from "./chunk-U4IJVG32.js";
65
+ import "./chunk-QVC75MR3.js";
66
+ import "./chunk-ILJOIQ5W.js";
67
+ import "./chunk-IAUOP25G.js";
68
+ import {
69
+ isPlausibleKey,
70
+ loadApiKey,
71
+ loadBaseUrl,
72
+ loadDesktopOpenTabs,
73
+ loadEditMode,
74
+ loadEditor,
75
+ loadPreset,
76
+ loadReasoningEffort,
77
+ loadRecentWorkspaces,
78
+ loadResolvedSkillPaths,
79
+ loadWorkspaceDir,
80
+ parseMcpSpec,
81
+ pushRecentWorkspace,
82
+ readConfig,
83
+ saveApiKey,
84
+ saveBaseUrl,
85
+ saveDesktopOpenTabs,
86
+ saveEditMode,
87
+ saveEditor,
88
+ savePreset,
89
+ saveReasoningEffort,
90
+ saveWorkspaceDir,
91
+ writeConfig
92
+ } from "./chunk-4MQ3VURH.js";
93
+ import {
94
+ VERSION
95
+ } from "./chunk-LN3B5PMX.js";
96
+ import "./chunk-TUK7OWJA.js";
97
+
98
+ // src/cli/commands/desktop.ts
99
+ import { AsyncLocalStorage } from "async_hooks";
100
+ import { existsSync, statSync, writeSync } from "fs";
101
+ import { readFile } from "fs/promises";
102
+ import { isAbsolute, join, resolve } from "path";
103
+ import { stdin } from "process";
104
+ import { createInterface } from "readline";
105
+ function emit(ev, tabId) {
106
+ const payload = tabId ? { ...ev, tabId } : ev;
107
+ writeSync(1, Buffer.from(`${JSON.stringify(payload)}
108
+ `, "utf8"));
109
+ }
110
+ function tailLines(s, n) {
111
+ if (!s) return "";
112
+ const lines = s.split(/\r?\n/);
113
+ return lines.slice(-n).join("\n");
114
+ }
115
+ function buildLoadedMessages(records) {
116
+ const out = [];
117
+ let turn = 0;
118
+ let pendingAssistantIdx = -1;
119
+ for (const rec of records) {
120
+ if (rec.role === "system") continue;
121
+ if (rec.role === "user") {
122
+ out.push({ kind: "user", text: rec.content ?? "" });
123
+ pendingAssistantIdx = -1;
124
+ continue;
125
+ }
126
+ if (rec.role === "assistant") {
127
+ turn++;
128
+ const segments = [];
129
+ if (rec.reasoning_content) segments.push({ kind: "reasoning", text: rec.reasoning_content });
130
+ if (rec.content) segments.push({ kind: "text", text: rec.content });
131
+ if (rec.tool_calls) {
132
+ for (let i = 0; i < rec.tool_calls.length; i++) {
133
+ const tc = rec.tool_calls[i];
134
+ if (!tc) continue;
135
+ segments.push({
136
+ kind: "tool",
137
+ callId: tc.id ?? `tc-r-${turn}-${i}`,
138
+ name: tc.function?.name ?? "",
139
+ args: tc.function?.arguments ?? ""
140
+ });
141
+ }
142
+ }
143
+ out.push({ kind: "assistant", turn, segments, pending: false });
144
+ pendingAssistantIdx = out.length - 1;
145
+ continue;
146
+ }
147
+ if (rec.role === "tool") {
148
+ if (pendingAssistantIdx < 0) continue;
149
+ const host = out[pendingAssistantIdx];
150
+ if (host?.kind !== "assistant") continue;
151
+ const callId = rec.tool_call_id;
152
+ if (!callId) continue;
153
+ const seg = host.segments.find((s) => s.kind === "tool" && s.callId === callId);
154
+ if (seg && seg.kind === "tool") {
155
+ seg.result = rec.content ?? "";
156
+ seg.ok = !/error|failed/i.test(seg.result.slice(0, 200));
157
+ }
158
+ }
159
+ }
160
+ return out;
161
+ }
162
+ function emitSettings(tab) {
163
+ const apiKey = loadApiKey();
164
+ const recent = loadRecentWorkspaces().filter((p) => p !== tab.rootDir);
165
+ emit(
166
+ {
167
+ type: "$settings",
168
+ reasoningEffort: loadReasoningEffort(),
169
+ editMode: loadEditMode(),
170
+ budgetUsd: tab.runtime?.loop.budgetUsd ?? null,
171
+ baseUrl: loadBaseUrl(),
172
+ apiKeyPrefix: apiKey ? `${apiKey.slice(0, 6)}\u2026${apiKey.slice(-3)}` : void 0,
173
+ workspaceDir: tab.rootDir,
174
+ recentWorkspaces: recent,
175
+ model: tab.currentModel,
176
+ preset: tab.currentPreset,
177
+ editor: loadEditor(),
178
+ version: VERSION
179
+ },
180
+ tab.id
181
+ );
182
+ }
183
+ async function emitBalance(tab) {
184
+ if (!tab.runtime) return;
185
+ const bal = await tab.runtime.loop.client.getBalance().catch(() => null);
186
+ if (!bal) return;
187
+ const primary = pickPrimaryBalance(bal.balance_infos);
188
+ if (!primary) return;
189
+ emit(
190
+ {
191
+ type: "$balance",
192
+ currency: primary.currency,
193
+ total: Number(primary.total_balance),
194
+ isAvailable: bal.is_available
195
+ },
196
+ tab.id
197
+ );
198
+ }
199
+ function emitSessions(tab) {
200
+ try {
201
+ const items = listSessionsForWorkspace(tab.rootDir).map((s) => ({
202
+ name: s.name,
203
+ messageCount: s.messageCount,
204
+ mtime: s.mtime.toISOString(),
205
+ summary: s.meta.summary
206
+ }));
207
+ emit({ type: "$sessions", items }, tab.id);
208
+ } catch (err) {
209
+ emit({ type: "$error", message: `session_list failed: ${err.message}` }, tab.id);
210
+ }
211
+ }
212
+ function summarizeMcpSpec(raw) {
213
+ try {
214
+ const parsed = parseMcpSpec(raw);
215
+ if (parsed.transport === "stdio") {
216
+ const argv = [parsed.command, ...parsed.args].join(" ");
217
+ return {
218
+ raw,
219
+ name: parsed.name,
220
+ transport: "stdio",
221
+ summary: `stdio \xB7 ${argv}`,
222
+ status: "configured"
223
+ };
224
+ }
225
+ return {
226
+ raw,
227
+ name: parsed.name,
228
+ transport: parsed.transport,
229
+ summary: `${parsed.transport} \xB7 ${parsed.url}`,
230
+ status: "configured"
231
+ };
232
+ } catch (err) {
233
+ return {
234
+ raw,
235
+ name: null,
236
+ transport: "stdio",
237
+ summary: raw,
238
+ parseError: err.message,
239
+ status: "failed",
240
+ statusReason: err.message
241
+ };
242
+ }
243
+ }
244
+ function emitMcpSpecs(tab) {
245
+ const cfg = readConfig();
246
+ const specs = (cfg.mcp ?? []).map((raw) => {
247
+ const base = summarizeMcpSpec(raw);
248
+ const live = tab.mcpStatuses.get(raw);
249
+ if (!live) return base;
250
+ return { ...base, status: live.kind, statusReason: live.reason, toolCount: live.toolCount };
251
+ });
252
+ const bridged = specs.length > 0 && specs.every((s) => s.status === "connected");
253
+ emit({ type: "$mcp_specs", specs, bridged }, tab.id);
254
+ }
255
+ function emitMemory(tab) {
256
+ try {
257
+ const store = new MemoryStore({ projectRoot: tab.rootDir });
258
+ const entries = store.list().map((e) => ({
259
+ name: e.name,
260
+ scope: e.scope,
261
+ description: e.description
262
+ }));
263
+ emit({ type: "$memory", entries }, tab.id);
264
+ } catch (err) {
265
+ emit({ type: "$error", message: `memory_get failed: ${err.message}` }, tab.id);
266
+ }
267
+ }
268
+ function emitCtxBreakdown(tab) {
269
+ if (!tab.runtime) return;
270
+ try {
271
+ const sys = countTokensBounded(tab.runtime.loop.prefix.system);
272
+ const tools = countTokensBounded(JSON.stringify(tab.runtime.loop.prefix.toolSpecs));
273
+ emit({ type: "$ctx_breakdown", reservedTokens: sys + tools }, tab.id);
274
+ } catch {
275
+ }
276
+ }
277
+ function emitSkills(tab) {
278
+ try {
279
+ const store = new SkillStore({
280
+ projectRoot: tab.rootDir,
281
+ customSkillPaths: loadResolvedSkillPaths(tab.rootDir)
282
+ });
283
+ const items = store.list().map((s) => ({
284
+ name: s.name,
285
+ description: s.description,
286
+ scope: s.scope,
287
+ path: s.path,
288
+ runAs: s.runAs,
289
+ model: s.model
290
+ }));
291
+ emit({ type: "$skills", items }, tab.id);
292
+ } catch (err) {
293
+ emit({ type: "$error", message: `skills_get failed: ${err.message}` }, tab.id);
294
+ }
295
+ }
296
+ var tabCounter = 0;
297
+ function nextTabId() {
298
+ tabCounter++;
299
+ return `t${tabCounter}`;
300
+ }
301
+ function mintSessionFor(rootDir) {
302
+ const name = `desktop-${timestampSuffix()}-${tabCounter}`;
303
+ try {
304
+ patchSessionMeta(name, { workspace: rootDir });
305
+ } catch {
306
+ }
307
+ return name;
308
+ }
309
+ function buildRuntimeFor(tab) {
310
+ if (!tab.toolset) throw new Error("buildRuntimeFor called before initTabToolset finished");
311
+ const toolset = tab.toolset;
312
+ const client = new DeepSeekClient({ baseUrl: loadBaseUrl() });
313
+ const prefix = new ImmutablePrefix({ system: tab.system, toolSpecs: toolset.tools.specs() });
314
+ const reasoningEffort = loadReasoningEffort();
315
+ const { autoEscalate } = resolvePreset(tab.currentPreset);
316
+ const loop = new CacheFirstLoop({
317
+ client,
318
+ prefix,
319
+ tools: toolset.tools,
320
+ model: tab.currentModel,
321
+ budgetUsd: tab.budgetUsd,
322
+ session: tab.currentSession,
323
+ reasoningEffort,
324
+ autoEscalate
325
+ });
326
+ const eventizer = new Eventizer();
327
+ const ctx = { model: tab.currentModel, prefixHash: prefix.fingerprint, reasoningEffort };
328
+ return { loop, eventizer, ctx };
329
+ }
330
+ var TS_EXPORT_RE = /^export\s+(?:default\s+)?(?:async\s+)?(function|class|const|let|var|interface|type|enum)\s+\*?\s*(\w+)/;
331
+ var FILE_INDEX_TTL_MS = 1e4;
332
+ async function getFileIndexFor(tab) {
333
+ const fresh = tab.fileIndex && Date.now() - tab.fileIndexBuiltAt < FILE_INDEX_TTL_MS;
334
+ if (fresh) return tab.fileIndex;
335
+ if (tab.fileIndexBuilding) return tab.fileIndexBuilding;
336
+ tab.fileIndexBuilding = listFilesWithStatsAsync(tab.rootDir, { maxResults: 5e3 }).then((res) => {
337
+ tab.fileIndex = res;
338
+ tab.fileIndexBuiltAt = Date.now();
339
+ tab.fileIndexBuilding = null;
340
+ return res;
341
+ }).catch((err) => {
342
+ tab.fileIndexBuilding = null;
343
+ throw err;
344
+ });
345
+ return tab.fileIndexBuilding;
346
+ }
347
+ async function getSymbolIndexFor(tab) {
348
+ if (tab.symbolIndex) return tab.symbolIndex;
349
+ if (tab.symbolBuilding) return tab.symbolBuilding;
350
+ tab.symbolBuilding = (async () => {
351
+ const files = await getFileIndexFor(tab);
352
+ const sourceExts = /\.(?:ts|tsx|js|jsx|mts|cts)$/;
353
+ const candidates = files.filter((f) => sourceExts.test(f.path)).slice(0, 1500);
354
+ const out = [];
355
+ const PARALLEL = 16;
356
+ for (let i = 0; i < candidates.length; i += PARALLEL) {
357
+ const batch = candidates.slice(i, i + PARALLEL);
358
+ await Promise.all(
359
+ batch.map(async (entry) => {
360
+ const abs = isAbsolute(entry.path) ? entry.path : join(tab.rootDir, entry.path);
361
+ try {
362
+ const text = await readFile(abs, "utf8");
363
+ const lines = text.split(/\r?\n/);
364
+ for (let li = 0; li < lines.length; li++) {
365
+ const line = lines[li];
366
+ if (!line.startsWith("export ")) continue;
367
+ const m = TS_EXPORT_RE.exec(line);
368
+ if (m) out.push({ kind: m[1], name: m[2], path: entry.path, line: li + 1 });
369
+ }
370
+ } catch {
371
+ }
372
+ })
373
+ );
374
+ }
375
+ tab.symbolIndex = out;
376
+ tab.symbolBuilding = null;
377
+ return out;
378
+ })().catch((err) => {
379
+ tab.symbolBuilding = null;
380
+ throw err;
381
+ });
382
+ return tab.symbolBuilding;
383
+ }
384
+ function rankSymbols(syms, q, limit) {
385
+ const needle = q.toLowerCase();
386
+ const scored = [];
387
+ for (const s of syms) {
388
+ const lower = s.name.toLowerCase();
389
+ let score;
390
+ if (lower === needle) score = 0;
391
+ else if (lower.startsWith(needle)) score = 100;
392
+ else if (lower.includes(needle)) score = 500 + lower.indexOf(needle);
393
+ else continue;
394
+ scored.push({ entry: s, score });
395
+ }
396
+ scored.sort((a, b) => a.score - b.score || a.entry.name.localeCompare(b.entry.name));
397
+ return scored.slice(0, limit).map((s) => `${s.entry.path}:${s.entry.line}`);
398
+ }
399
+ function pushMentionRecent(tab, path) {
400
+ const MAX = 20;
401
+ const idx = tab.recentMentions.indexOf(path);
402
+ if (idx >= 0) tab.recentMentions.splice(idx, 1);
403
+ tab.recentMentions.unshift(path);
404
+ if (tab.recentMentions.length > MAX) tab.recentMentions.length = MAX;
405
+ }
406
+ function installDesktopCrashGuards(stderr = process.stderr) {
407
+ process.on("unhandledRejection", (reason) => {
408
+ const err = reason instanceof Error ? reason : new Error(String(reason));
409
+ stderr.write(`[desktop] unhandledRejection: ${err.stack ?? err.message}
410
+ `);
411
+ });
412
+ process.on("uncaughtException", (err) => {
413
+ stderr.write(`[desktop] uncaughtException: ${err.stack ?? err.message}
414
+ `);
415
+ });
416
+ }
417
+ async function desktopCommand(opts) {
418
+ loadDotenv();
419
+ installDesktopCrashGuards();
420
+ const tabs = /* @__PURE__ */ new Map();
421
+ const tabContext = new AsyncLocalStorage();
422
+ function activeRunningTab() {
423
+ const id = tabContext.getStore();
424
+ return id ? tabs.get(id) : void 0;
425
+ }
426
+ function createTabSkeleton(initialDir) {
427
+ const dir = resolve(initialDir ?? opts.dir ?? loadWorkspaceDir() ?? process.cwd());
428
+ pushRecentWorkspace(dir);
429
+ const preset = canonicalPresetName(loadPreset());
430
+ const resolved = resolvePreset(preset);
431
+ const model = opts.model || resolved.model;
432
+ const tab = {
433
+ id: nextTabId(),
434
+ rootDir: dir,
435
+ currentSession: "",
436
+ currentPreset: preset,
437
+ currentModel: model,
438
+ budgetUsd: opts.budgetUsd,
439
+ toolset: null,
440
+ system: "",
441
+ runtime: null,
442
+ aborter: null,
443
+ fileIndex: null,
444
+ fileIndexBuilding: null,
445
+ fileIndexBuiltAt: 0,
446
+ symbolIndex: null,
447
+ symbolBuilding: null,
448
+ recentMentions: [],
449
+ pendingGateIds: /* @__PURE__ */ new Set(),
450
+ completedStepIds: /* @__PURE__ */ new Set(),
451
+ planTotalSteps: 0,
452
+ mcpRuntime: null,
453
+ mcpStatuses: /* @__PURE__ */ new Map()
454
+ };
455
+ tab.currentSession = mintSessionFor(dir);
456
+ tabs.set(tab.id, tab);
457
+ return tab;
458
+ }
459
+ async function initTabToolset(tab) {
460
+ const toolset = await buildCodeToolset({
461
+ rootDir: tab.rootDir,
462
+ onSkillInstalled: () => emitSkills(tab),
463
+ onJobsChanged: () => emitJobs()
464
+ });
465
+ tab.toolset = toolset;
466
+ tab.system = codeSystemPrompt(tab.rootDir, {
467
+ hasSemanticSearch: toolset.semantic.enabled,
468
+ modelId: tab.currentModel
469
+ });
470
+ if (loadApiKey()) {
471
+ process.env.DEEPSEEK_API_KEY = loadApiKey();
472
+ tab.runtime = buildRuntimeFor(tab);
473
+ void bridgeTabMcp(tab);
474
+ }
475
+ }
476
+ function bridgeTabMcp(tab) {
477
+ if (!tab.runtime || !tab.toolset) return Promise.resolve();
478
+ if (tab.mcpRuntime) {
479
+ return tab.mcpRuntime.reloadFromConfig(tab.runtime.loop).then(() => emitMcpSpecs(tab)).catch((err) => {
480
+ emit({ type: "$error", message: `mcp reload failed: ${err.message}` }, tab.id);
481
+ });
482
+ }
483
+ const requested = (readConfig().mcp ?? []).length;
484
+ if (requested === 0) return Promise.resolve();
485
+ const runtime = createMcpRuntime({
486
+ getTools: () => {
487
+ if (!tab.toolset) throw new Error("toolset gone");
488
+ return tab.toolset.tools;
489
+ },
490
+ getMcpPrefix: () => void 0,
491
+ getRequestedCount: () => requested,
492
+ progressSink: { current: null }
493
+ });
494
+ tab.mcpRuntime = runtime;
495
+ runtime.setLifecycleSink((notice) => {
496
+ if (notice.kind === "slow") return;
497
+ const cfg = readConfig().mcp ?? [];
498
+ const target = cfg.find((raw) => {
499
+ try {
500
+ return parseMcpSpec(raw).name === notice.name;
501
+ } catch {
502
+ return false;
503
+ }
504
+ });
505
+ if (!target) return;
506
+ if (notice.kind === "handshake") {
507
+ tab.mcpStatuses.set(target, { kind: "handshake" });
508
+ } else if (notice.kind === "connected") {
509
+ tab.mcpStatuses.set(target, { kind: "connected", toolCount: notice.tools });
510
+ } else if (notice.kind === "failed") {
511
+ tab.mcpStatuses.set(target, { kind: "failed", reason: notice.reason });
512
+ } else if (notice.kind === "disabled") {
513
+ tab.mcpStatuses.set(target, { kind: "disabled" });
514
+ }
515
+ emitMcpSpecs(tab);
516
+ });
517
+ return runtime.reloadFromConfig(tab.runtime.loop).then(() => void 0).catch((err) => {
518
+ emit({ type: "$error", message: `mcp bridge failed: ${err.message}` }, tab.id);
519
+ });
520
+ }
521
+ function persistOpenTabs() {
522
+ try {
523
+ saveDesktopOpenTabs(Array.from(tabs.values()).map((t) => t.rootDir));
524
+ } catch {
525
+ }
526
+ }
527
+ async function closeTab(tab) {
528
+ abortTurn(tab);
529
+ try {
530
+ await tab.toolset?.jobs.shutdown();
531
+ } catch {
532
+ }
533
+ if (tab.mcpRuntime) {
534
+ try {
535
+ await tab.mcpRuntime.closeAll();
536
+ } catch {
537
+ }
538
+ }
539
+ tabs.delete(tab.id);
540
+ if (first && first.id === tab.id) {
541
+ const next = tabs.values().next().value;
542
+ if (next) first = next;
543
+ }
544
+ persistOpenTabs();
545
+ emit({ type: "$tab_closed" }, tab.id);
546
+ }
547
+ async function runTurn(tab, text) {
548
+ if (!tab.runtime) return;
549
+ const rt = tab.runtime;
550
+ tab.aborter = new AbortController();
551
+ if (tab.currentSession) {
552
+ const existing = loadSessionMeta(tab.currentSession).summary;
553
+ if (!existing || !existing.trim()) {
554
+ const summary = text.replace(/\s+/g, " ").trim().slice(0, 60);
555
+ if (summary) {
556
+ try {
557
+ patchSessionMeta(tab.currentSession, { summary });
558
+ } catch {
559
+ }
560
+ }
561
+ }
562
+ }
563
+ await tabContext.run(tab.id, async () => {
564
+ try {
565
+ for await (const ev of rt.loop.step(text)) {
566
+ for (const kev of rt.eventizer.consume(ev, rt.ctx)) emit(kev, tab.id);
567
+ if (ev.role === "tool" && (ev.toolName === "remember" || ev.toolName === "forget")) {
568
+ emitMemory(tab);
569
+ }
570
+ if (tab.aborter?.signal.aborted) break;
571
+ }
572
+ } catch (err) {
573
+ emit({ type: "$error", message: err.message }, tab.id);
574
+ } finally {
575
+ tab.aborter = null;
576
+ emit({ type: "$turn_complete" }, tab.id);
577
+ if (tab.planTotalSteps > 0 && tab.completedStepIds.size >= tab.planTotalSteps) {
578
+ tab.completedStepIds.clear();
579
+ tab.planTotalSteps = 0;
580
+ emit({ type: "$plan_cleared" }, tab.id);
581
+ }
582
+ emitSessions(tab);
583
+ void emitBalance(tab);
584
+ }
585
+ });
586
+ }
587
+ async function switchWorkspace(tab, nextDir) {
588
+ const target = resolve(nextDir);
589
+ if (target === tab.rootDir) {
590
+ emitSettings(tab);
591
+ return;
592
+ }
593
+ if (!existsSync(target) || !statSync(target).isDirectory()) {
594
+ emit({ type: "$error", message: `Workspace not found: ${target}` }, tab.id);
595
+ emitSettings(tab);
596
+ return;
597
+ }
598
+ abortTurn(tab);
599
+ try {
600
+ await tab.toolset?.jobs.shutdown();
601
+ } catch {
602
+ }
603
+ tab.rootDir = target;
604
+ saveWorkspaceDir(target);
605
+ pushRecentWorkspace(target);
606
+ tab.fileIndex = null;
607
+ tab.fileIndexBuilding = null;
608
+ tab.fileIndexBuiltAt = 0;
609
+ tab.symbolIndex = null;
610
+ tab.symbolBuilding = null;
611
+ tab.recentMentions.length = 0;
612
+ tab.currentSession = mintSessionFor(target);
613
+ tab.toolset = await buildCodeToolset({
614
+ rootDir: target,
615
+ onSkillInstalled: () => emitSkills(tab),
616
+ onJobsChanged: () => emitJobs()
617
+ });
618
+ tab.system = codeSystemPrompt(target, {
619
+ hasSemanticSearch: tab.toolset.semantic.enabled,
620
+ modelId: tab.currentModel
621
+ });
622
+ if (tab.runtime) tab.runtime = buildRuntimeFor(tab);
623
+ emitSessions(tab);
624
+ emitSettings(tab);
625
+ emitSkills(tab);
626
+ }
627
+ function forgetGate(id) {
628
+ for (const t of tabs.values()) {
629
+ if (t.pendingGateIds.delete(id)) return t;
630
+ }
631
+ return void 0;
632
+ }
633
+ function abortTurn(tab) {
634
+ tab.aborter?.abort();
635
+ tab.runtime?.loop.abort();
636
+ }
637
+ function tabSessionLabel(tab) {
638
+ if (tab.currentSession) {
639
+ try {
640
+ const summary = loadSessionMeta(tab.currentSession).summary?.trim();
641
+ if (summary) return summary;
642
+ } catch {
643
+ }
644
+ }
645
+ return tab.rootDir.split(/[\\/]/).filter(Boolean).pop() ?? tab.rootDir;
646
+ }
647
+ function emitJobs() {
648
+ const items = [];
649
+ for (const t of tabs.values()) {
650
+ const reg = t.toolset?.jobs;
651
+ if (!reg) continue;
652
+ const label = tabSessionLabel(t);
653
+ for (const j of reg.list()) {
654
+ items.push({
655
+ id: j.id,
656
+ tabId: t.id,
657
+ sessionLabel: label,
658
+ command: j.command,
659
+ pid: j.pid,
660
+ running: j.running,
661
+ exitCode: j.exitCode,
662
+ startedAt: j.startedAt,
663
+ outputTail: tailLines(j.output, 8),
664
+ spawnError: j.spawnError
665
+ });
666
+ }
667
+ }
668
+ items.sort((a, b) => {
669
+ if (a.running !== b.running) return a.running ? -1 : 1;
670
+ return b.startedAt - a.startedAt;
671
+ });
672
+ emit({ type: "$jobs", items });
673
+ }
674
+ async function stopJob(jobId) {
675
+ for (const t of tabs.values()) {
676
+ const reg = t.toolset?.jobs;
677
+ if (!reg) continue;
678
+ const hit = reg.list().find((j) => j.id === jobId);
679
+ if (!hit) continue;
680
+ await reg.stop(jobId);
681
+ return true;
682
+ }
683
+ return false;
684
+ }
685
+ async function stopAllJobs() {
686
+ const ops = [];
687
+ for (const t of tabs.values()) {
688
+ const reg = t.toolset?.jobs;
689
+ if (!reg) continue;
690
+ for (const j of reg.list()) {
691
+ if (j.running) ops.push(reg.stop(j.id));
692
+ }
693
+ }
694
+ await Promise.allSettled(ops);
695
+ }
696
+ function cancelPendingGates(tab) {
697
+ const hadActivePlan = tab.planTotalSteps > 0 || tab.completedStepIds.size > 0;
698
+ const ids = [...tab.pendingGateIds];
699
+ tab.pendingGateIds.clear();
700
+ for (const id of ids) pauseGate.cancel(id);
701
+ if (hadActivePlan) {
702
+ tab.completedStepIds.clear();
703
+ tab.planTotalSteps = 0;
704
+ emit({ type: "$plan_cleared" }, tab.id);
705
+ }
706
+ }
707
+ let first;
708
+ let shuttingDown = false;
709
+ async function gracefulShutdown() {
710
+ if (shuttingDown) return;
711
+ shuttingDown = true;
712
+ await Promise.allSettled(
713
+ [...tabs.values()].map((t) => t.toolset?.jobs.shutdown(1500) ?? Promise.resolve())
714
+ );
715
+ process.exit(0);
716
+ }
717
+ process.on("SIGTERM", () => {
718
+ void gracefulShutdown();
719
+ });
720
+ process.on("SIGINT", () => {
721
+ void gracefulShutdown();
722
+ });
723
+ pauseGate.on((req) => {
724
+ const tab = activeRunningTab();
725
+ const tabId = tab?.id;
726
+ if (tab) tab.pendingGateIds.add(req.id);
727
+ const auto = autoResolveVerdict(req, loadEditMode());
728
+ if (auto !== null) {
729
+ if (req.kind === "plan_checkpoint") {
730
+ const payload = req.payload;
731
+ if (tab) tab.completedStepIds.add(payload.stepId);
732
+ emit(
733
+ {
734
+ type: "$step_completed",
735
+ stepId: payload.stepId,
736
+ title: payload.title,
737
+ result: payload.result,
738
+ notes: payload.notes
739
+ },
740
+ tabId
741
+ );
742
+ }
743
+ if (tab) tab.pendingGateIds.delete(req.id);
744
+ pauseGate.resolve(req.id, auto);
745
+ return;
746
+ }
747
+ if (req.kind === "run_command" || req.kind === "run_background") {
748
+ const payload = req.payload;
749
+ emit(
750
+ { type: "$confirm_required", id: req.id, kind: req.kind, command: payload.command ?? "" },
751
+ tabId
752
+ );
753
+ return;
754
+ }
755
+ if (req.kind === "path_access") {
756
+ const payload = req.payload;
757
+ emit(
758
+ {
759
+ type: "$path_access_required",
760
+ id: req.id,
761
+ path: payload.path,
762
+ intent: payload.intent,
763
+ toolName: payload.toolName,
764
+ sandboxRoot: payload.sandboxRoot,
765
+ allowPrefix: payload.allowPrefix
766
+ },
767
+ tabId
768
+ );
769
+ return;
770
+ }
771
+ if (req.kind === "choice") {
772
+ const payload = req.payload;
773
+ emit(
774
+ {
775
+ type: "$choice_required",
776
+ id: req.id,
777
+ question: payload.question,
778
+ options: payload.options,
779
+ allowCustom: payload.allowCustom
780
+ },
781
+ tabId
782
+ );
783
+ return;
784
+ }
785
+ if (req.kind === "plan_proposed") {
786
+ const payload = req.payload;
787
+ if (tab) {
788
+ tab.completedStepIds.clear();
789
+ tab.planTotalSteps = payload.steps?.length ?? 0;
790
+ }
791
+ emit(
792
+ {
793
+ type: "$plan_required",
794
+ id: req.id,
795
+ plan: payload.plan,
796
+ steps: payload.steps,
797
+ summary: payload.summary
798
+ },
799
+ tabId
800
+ );
801
+ return;
802
+ }
803
+ if (req.kind === "plan_checkpoint") {
804
+ const payload = req.payload;
805
+ if (tab) tab.completedStepIds.add(payload.stepId);
806
+ emit(
807
+ {
808
+ type: "$step_completed",
809
+ stepId: payload.stepId,
810
+ title: payload.title,
811
+ result: payload.result,
812
+ notes: payload.notes
813
+ },
814
+ tabId
815
+ );
816
+ emit(
817
+ {
818
+ type: "$checkpoint_required",
819
+ id: req.id,
820
+ stepId: payload.stepId,
821
+ title: payload.title,
822
+ result: payload.result,
823
+ notes: payload.notes,
824
+ completed: tab?.completedStepIds.size ?? 0,
825
+ total: tab?.planTotalSteps ?? 0
826
+ },
827
+ tabId
828
+ );
829
+ return;
830
+ }
831
+ if (req.kind === "plan_revision") {
832
+ const payload = req.payload;
833
+ emit(
834
+ {
835
+ type: "$revision_required",
836
+ id: req.id,
837
+ reason: payload.reason,
838
+ remainingSteps: payload.remainingSteps,
839
+ summary: payload.summary
840
+ },
841
+ tabId
842
+ );
843
+ return;
844
+ }
845
+ const exhaustive = req.kind;
846
+ process.stderr.write(
847
+ `[desktop] no handler for pause kind "${String(exhaustive)}" \u2014 auto-cancelling gate id=${req.id}
848
+ `
849
+ );
850
+ if (tab) tab.pendingGateIds.delete(req.id);
851
+ pauseGate.cancel(req.id);
852
+ });
853
+ function bootstrapTab(initialDir) {
854
+ const tab = createTabSkeleton(initialDir);
855
+ emit({ type: "$tab_opened", workspaceDir: tab.rootDir }, tab.id);
856
+ emitSessions(tab);
857
+ emitSettings(tab);
858
+ emitMcpSpecs(tab);
859
+ emitSkills(tab);
860
+ emitMemory(tab);
861
+ if (!loadApiKey()) emit({ type: "$needs_setup", reason: "no_api_key" }, tab.id);
862
+ void emitBalance(tab);
863
+ void initTabToolset(tab).then(() => {
864
+ if (loadApiKey()) emit({ type: "$ready" }, tab.id);
865
+ emitCtxBreakdown(tab);
866
+ }).catch((err) => {
867
+ emit({ type: "$error", message: `init failed: ${err.message}` }, tab.id);
868
+ });
869
+ return tab;
870
+ }
871
+ const savedTabDirs = opts.dir ? [] : loadDesktopOpenTabs().filter((d) => {
872
+ try {
873
+ return existsSync(d) && statSync(d).isDirectory();
874
+ } catch {
875
+ return false;
876
+ }
877
+ });
878
+ first = bootstrapTab(savedTabDirs[0]);
879
+ for (const d of savedTabDirs.slice(1)) {
880
+ if (resolve(d) === first.rootDir) continue;
881
+ bootstrapTab(d);
882
+ }
883
+ persistOpenTabs();
884
+ const rl = createInterface({ input: stdin });
885
+ rl.on("line", (line) => {
886
+ const trimmed = line.trim();
887
+ if (!trimmed) return;
888
+ let msg;
889
+ try {
890
+ msg = JSON.parse(trimmed);
891
+ } catch {
892
+ emit({ type: "$error", message: `bad json on stdin: ${trimmed.slice(0, 80)}` });
893
+ return;
894
+ }
895
+ if (msg.cmd === "tab_open") {
896
+ try {
897
+ bootstrapTab(msg.workspaceDir);
898
+ persistOpenTabs();
899
+ } catch (err) {
900
+ emit({ type: "$error", message: `tab_open failed: ${err.message}` });
901
+ }
902
+ return;
903
+ }
904
+ if (msg.cmd === "confirm_response") {
905
+ forgetGate(msg.id);
906
+ pauseGate.resolve(msg.id, msg.response);
907
+ return;
908
+ }
909
+ if (msg.cmd === "choice_response") {
910
+ forgetGate(msg.id);
911
+ pauseGate.resolve(msg.id, msg.response);
912
+ return;
913
+ }
914
+ if (msg.cmd === "plan_response") {
915
+ const tab2 = forgetGate(msg.id);
916
+ if (tab2 && msg.response.type === "cancel") {
917
+ tab2.completedStepIds.clear();
918
+ tab2.planTotalSteps = 0;
919
+ emit({ type: "$plan_cleared" }, tab2.id);
920
+ }
921
+ pauseGate.resolve(msg.id, msg.response);
922
+ return;
923
+ }
924
+ if (msg.cmd === "checkpoint_response") {
925
+ const tab2 = forgetGate(msg.id);
926
+ if (tab2 && msg.response.type === "stop") {
927
+ tab2.completedStepIds.clear();
928
+ tab2.planTotalSteps = 0;
929
+ emit({ type: "$plan_cleared" }, tab2.id);
930
+ }
931
+ pauseGate.resolve(msg.id, msg.response);
932
+ return;
933
+ }
934
+ if (msg.cmd === "revision_response") {
935
+ forgetGate(msg.id);
936
+ pauseGate.resolve(msg.id, msg.response);
937
+ return;
938
+ }
939
+ if (msg.cmd === "setup_save_key") {
940
+ const key = msg.key.trim();
941
+ if (!isPlausibleKey(key)) {
942
+ emit({
943
+ type: "$error",
944
+ message: "Key looks too short \u2014 paste the full token (16+ chars, no spaces)."
945
+ });
946
+ return;
947
+ }
948
+ try {
949
+ saveApiKey(key);
950
+ process.env.DEEPSEEK_API_KEY = key;
951
+ for (const tab2 of tabs.values()) {
952
+ if (!tab2.toolset) {
953
+ emitSettings(tab2);
954
+ void emitBalance(tab2);
955
+ continue;
956
+ }
957
+ tab2.runtime = buildRuntimeFor(tab2);
958
+ emit({ type: "$ready" }, tab2.id);
959
+ emitSettings(tab2);
960
+ void emitBalance(tab2);
961
+ }
962
+ } catch (err) {
963
+ emit({ type: "$error", message: `saveApiKey failed: ${err.message}` });
964
+ }
965
+ return;
966
+ }
967
+ if (msg.cmd === "jobs_list") {
968
+ emitJobs();
969
+ return;
970
+ }
971
+ if (msg.cmd === "jobs_stop") {
972
+ void stopJob(msg.jobId).finally(() => emitJobs());
973
+ return;
974
+ }
975
+ if (msg.cmd === "jobs_stop_all") {
976
+ void stopAllJobs().finally(() => emitJobs());
977
+ return;
978
+ }
979
+ const tab = msg.tabId ? tabs.get(msg.tabId) : first;
980
+ if (!tab) {
981
+ process.stderr.write(
982
+ `rpc dispatch: unknown tabId=${msg.tabId} for cmd=${msg.cmd} \u2014 dropping
983
+ `
984
+ );
985
+ return;
986
+ }
987
+ if (msg.cmd === "abort") {
988
+ abortTurn(tab);
989
+ cancelPendingGates(tab);
990
+ return;
991
+ }
992
+ if (msg.cmd === "tab_close") {
993
+ void closeTab(tab);
994
+ return;
995
+ }
996
+ if (msg.cmd === "mcp_specs_get") {
997
+ emitMcpSpecs(tab);
998
+ return;
999
+ }
1000
+ if (msg.cmd === "mcp_specs_add") {
1001
+ const spec = msg.spec.trim();
1002
+ if (!spec) {
1003
+ emit({ type: "$error", message: "mcp_specs_add: spec is empty" }, tab.id);
1004
+ return;
1005
+ }
1006
+ try {
1007
+ parseMcpSpec(spec);
1008
+ } catch (err) {
1009
+ emit({ type: "$error", message: `mcp_specs_add: ${err.message}` }, tab.id);
1010
+ return;
1011
+ }
1012
+ try {
1013
+ const cfg = readConfig();
1014
+ const list = cfg.mcp ?? [];
1015
+ if (!list.includes(spec)) {
1016
+ cfg.mcp = [...list, spec];
1017
+ writeConfig(cfg);
1018
+ }
1019
+ emitMcpSpecs(tab);
1020
+ void bridgeTabMcp(tab);
1021
+ } catch (err) {
1022
+ emit({ type: "$error", message: `mcp_specs_add: ${err.message}` }, tab.id);
1023
+ }
1024
+ return;
1025
+ }
1026
+ if (msg.cmd === "mcp_specs_remove") {
1027
+ try {
1028
+ const cfg = readConfig();
1029
+ const list = cfg.mcp ?? [];
1030
+ if (list.includes(msg.spec)) {
1031
+ cfg.mcp = list.filter((s) => s !== msg.spec);
1032
+ writeConfig(cfg);
1033
+ }
1034
+ tab.mcpStatuses.delete(msg.spec);
1035
+ emitMcpSpecs(tab);
1036
+ void bridgeTabMcp(tab);
1037
+ } catch (err) {
1038
+ emit({ type: "$error", message: `mcp_specs_remove: ${err.message}` }, tab.id);
1039
+ }
1040
+ return;
1041
+ }
1042
+ if (msg.cmd === "skills_get") {
1043
+ emitSkills(tab);
1044
+ return;
1045
+ }
1046
+ if (msg.cmd === "skill_run") {
1047
+ if (!tab.runtime) {
1048
+ emit(
1049
+ { type: "$error", message: "Not configured yet \u2014 paste your DeepSeek API key first." },
1050
+ tab.id
1051
+ );
1052
+ return;
1053
+ }
1054
+ try {
1055
+ const store = new SkillStore({
1056
+ projectRoot: tab.rootDir,
1057
+ customSkillPaths: loadResolvedSkillPaths(tab.rootDir)
1058
+ });
1059
+ const found = store.read(msg.name);
1060
+ if (!found) {
1061
+ emit({ type: "$error", message: `skill not found: ${msg.name}` }, tab.id);
1062
+ return;
1063
+ }
1064
+ const extra = msg.args?.trim() ?? "";
1065
+ const header = `# Skill: ${found.name}${found.description ? `
1066
+ > ${found.description}` : ""}`;
1067
+ const argsLine = extra ? `
1068
+
1069
+ Arguments: ${extra}` : "";
1070
+ const payload = `${header}
1071
+
1072
+ ${found.body}${argsLine}`;
1073
+ void runTurn(tab, payload);
1074
+ } catch (err) {
1075
+ emit({ type: "$error", message: `skill_run: ${err.message}` }, tab.id);
1076
+ }
1077
+ return;
1078
+ }
1079
+ if (msg.cmd === "session_list") {
1080
+ emitSessions(tab);
1081
+ return;
1082
+ }
1083
+ if (msg.cmd === "session_delete") {
1084
+ deleteSession(msg.name);
1085
+ emitSessions(tab);
1086
+ return;
1087
+ }
1088
+ if (msg.cmd === "session_load") {
1089
+ try {
1090
+ const records = loadSessionMessages(msg.name);
1091
+ const meta = loadSessionMeta(msg.name);
1092
+ abortTurn(tab);
1093
+ cancelPendingGates(tab);
1094
+ tab.currentSession = msg.name;
1095
+ if (tab.runtime) tab.runtime = buildRuntimeFor(tab);
1096
+ const loadedMessages = buildLoadedMessages(records);
1097
+ if (loadedMessages.length === 0) {
1098
+ let sizeBytes = 0;
1099
+ try {
1100
+ sizeBytes = statSync(sessionPath(msg.name)).size;
1101
+ } catch {
1102
+ }
1103
+ process.stderr.write(
1104
+ `session_load: "${msg.name}" returned 0 messages (file size=${sizeBytes}B) \u2014 empty or unreadable jsonl
1105
+ `
1106
+ );
1107
+ emit({ type: "$session_empty", name: msg.name, sizeBytes }, tab.id);
1108
+ }
1109
+ emit(
1110
+ {
1111
+ type: "$session_loaded",
1112
+ name: msg.name,
1113
+ messages: loadedMessages,
1114
+ carryover: {
1115
+ totalCostUsd: meta.totalCostUsd ?? 0,
1116
+ cacheHitTokens: meta.cacheHitTokens ?? 0,
1117
+ cacheMissTokens: meta.cacheMissTokens ?? 0
1118
+ }
1119
+ },
1120
+ tab.id
1121
+ );
1122
+ } catch (err) {
1123
+ process.stderr.write(`session_load: "${msg.name}" threw \u2014 ${err.message}
1124
+ `);
1125
+ emit({ type: "$error", message: `session_load failed: ${err.message}` }, tab.id);
1126
+ }
1127
+ return;
1128
+ }
1129
+ if (msg.cmd === "new_chat") {
1130
+ abortTurn(tab);
1131
+ cancelPendingGates(tab);
1132
+ tab.currentSession = mintSessionFor(tab.rootDir);
1133
+ if (tab.runtime) tab.runtime = buildRuntimeFor(tab);
1134
+ emitSessions(tab);
1135
+ return;
1136
+ }
1137
+ if (msg.cmd === "settings_get") {
1138
+ emitSettings(tab);
1139
+ return;
1140
+ }
1141
+ if (msg.cmd === "settings_save") {
1142
+ try {
1143
+ if (msg.reasoningEffort !== void 0) {
1144
+ saveReasoningEffort(msg.reasoningEffort);
1145
+ tab.runtime?.loop.configure({ reasoningEffort: msg.reasoningEffort });
1146
+ }
1147
+ if (msg.editMode !== void 0) saveEditMode(msg.editMode);
1148
+ if (msg.budgetUsd !== void 0) {
1149
+ tab.budgetUsd = msg.budgetUsd ?? void 0;
1150
+ tab.runtime?.loop.setBudget(msg.budgetUsd);
1151
+ }
1152
+ if (msg.baseUrl !== void 0) saveBaseUrl(msg.baseUrl);
1153
+ if (msg.workspaceDir !== void 0) {
1154
+ void switchWorkspace(tab, msg.workspaceDir);
1155
+ return;
1156
+ }
1157
+ if (msg.editor !== void 0) saveEditor(msg.editor);
1158
+ if (msg.preset !== void 0) {
1159
+ tab.currentPreset = canonicalPresetName(msg.preset);
1160
+ const resolved = resolvePreset(tab.currentPreset);
1161
+ tab.currentModel = resolved.model;
1162
+ savePreset(tab.currentPreset);
1163
+ if (tab.toolset) {
1164
+ tab.system = codeSystemPrompt(tab.rootDir, {
1165
+ hasSemanticSearch: tab.toolset.semantic.enabled,
1166
+ modelId: tab.currentModel
1167
+ });
1168
+ if (tab.runtime) tab.runtime = buildRuntimeFor(tab);
1169
+ }
1170
+ }
1171
+ emitSettings(tab);
1172
+ } catch (err) {
1173
+ emit(
1174
+ { type: "$error", message: `settings_save failed: ${err.message}` },
1175
+ tab.id
1176
+ );
1177
+ }
1178
+ return;
1179
+ }
1180
+ if (msg.cmd === "mention_query") {
1181
+ const nonce = msg.nonce;
1182
+ const query = msg.query;
1183
+ const parsed = parseAtQuery(query);
1184
+ const treeWalk = parsed.trailingSlash || query.length === 0;
1185
+ if (treeWalk) {
1186
+ void listDirectory(tab.rootDir, parsed.dir).then((entries) => {
1187
+ const results = entries.map((e) => e.isDir ? `${e.path}/` : e.path);
1188
+ emit({ type: "$mention_results", nonce, query, results }, tab.id);
1189
+ }).catch((err) => {
1190
+ emit(
1191
+ { type: "$error", message: `mention_query (dir) failed: ${err.message}` },
1192
+ tab.id
1193
+ );
1194
+ emit({ type: "$mention_results", nonce, query, results: [] }, tab.id);
1195
+ });
1196
+ return;
1197
+ }
1198
+ const wantSymbols = query.length >= 2 && !query.includes("/");
1199
+ void (async () => {
1200
+ try {
1201
+ const files = await getFileIndexFor(tab);
1202
+ const fileResults = rankPickerCandidates(files, query, {
1203
+ limit: wantSymbols ? 19 : 25,
1204
+ recentlyUsed: tab.recentMentions
1205
+ });
1206
+ let symResults = [];
1207
+ if (wantSymbols) {
1208
+ const syms = await getSymbolIndexFor(tab);
1209
+ symResults = rankSymbols(syms, query, 6);
1210
+ }
1211
+ emit(
1212
+ { type: "$mention_results", nonce, query, results: [...symResults, ...fileResults] },
1213
+ tab.id
1214
+ );
1215
+ } catch (err) {
1216
+ emit(
1217
+ { type: "$error", message: `mention_query failed: ${err.message}` },
1218
+ tab.id
1219
+ );
1220
+ emit({ type: "$mention_results", nonce, query, results: [] }, tab.id);
1221
+ }
1222
+ })();
1223
+ return;
1224
+ }
1225
+ if (msg.cmd === "mention_picked") {
1226
+ pushMentionRecent(tab, msg.path);
1227
+ return;
1228
+ }
1229
+ if (msg.cmd === "mention_preview") {
1230
+ const nonce = msg.nonce;
1231
+ const rel = msg.path;
1232
+ const abs = isAbsolute(rel) ? rel : join(tab.rootDir, rel);
1233
+ const safeAbs = resolve(abs);
1234
+ const safeRoot = resolve(tab.rootDir);
1235
+ if (!safeAbs.startsWith(safeRoot)) {
1236
+ emit({ type: "$mention_preview", nonce, path: rel, head: "", totalLines: 0 }, tab.id);
1237
+ return;
1238
+ }
1239
+ void readFile(safeAbs, "utf8").then((text) => {
1240
+ const lines = text.split(/\r?\n/);
1241
+ if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
1242
+ const head = lines.slice(0, 12).join("\n");
1243
+ emit(
1244
+ { type: "$mention_preview", nonce, path: rel, head, totalLines: lines.length },
1245
+ tab.id
1246
+ );
1247
+ }).catch(() => {
1248
+ emit({ type: "$mention_preview", nonce, path: rel, head: "", totalLines: 0 }, tab.id);
1249
+ });
1250
+ return;
1251
+ }
1252
+ if (msg.cmd === "user_input") {
1253
+ if (!tab.runtime) {
1254
+ emit(
1255
+ { type: "$error", message: "Not configured yet \u2014 paste your DeepSeek API key first." },
1256
+ tab.id
1257
+ );
1258
+ return;
1259
+ }
1260
+ void runTurn(tab, msg.text);
1261
+ }
1262
+ });
1263
+ await new Promise((resolve2) => {
1264
+ rl.on("close", () => {
1265
+ void gracefulShutdown();
1266
+ resolve2();
1267
+ });
1268
+ });
1269
+ }
1270
+ export {
1271
+ desktopCommand,
1272
+ installDesktopCrashGuards
1273
+ };
1274
+ //# sourceMappingURL=desktop-7N3MHNBD.js.map