specrails-desktop 2.27.0 → 2.28.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 (135) hide show
  1. package/client/dist/assets/{ActivityFeedPage-BlkNVXCv.js → ActivityFeedPage-DcMmDAP3.js} +1 -1
  2. package/client/dist/assets/AgentBrowserCapture-CZ3oc1UL.js +1 -0
  3. package/client/dist/assets/AgentIntegrationsModal-DkqooCFn.js +1 -0
  4. package/client/dist/assets/{AgentModeCodePane-BUR_QxqR.js → AgentModeCodePane-CM77ljte.js} +2 -2
  5. package/client/dist/assets/{AgentModeJobsPane-CnRXelns.js → AgentModeJobsPane-DbZzogbi.js} +1 -1
  6. package/client/dist/assets/AgentsPage-BZIml7eL.js +86 -0
  7. package/client/dist/assets/{AnalyticsPage-C_eT3I3i.js → AnalyticsPage-B4qflNxi.js} +1 -1
  8. package/client/dist/assets/{BarChart-DuzYxiJ6.js → BarChart-Ckstq9fl.js} +1 -1
  9. package/client/dist/assets/CodePage-DFZXLbI1.js +2 -0
  10. package/client/dist/assets/{DesktopAnalyticsPage-BSeBkF2n.js → DesktopAnalyticsPage-DbgMa8OO.js} +1 -1
  11. package/client/dist/assets/{DocsDialog-CGzqDyN3.js → DocsDialog-BQc9Kwev.js} +2 -2
  12. package/client/dist/assets/DocsPage-OsV44ECy.js +11 -0
  13. package/client/dist/assets/{ExportDropdown-IehskH-7.js → ExportDropdown-a43XHO3Y.js} +1 -1
  14. package/client/dist/assets/IntegrationsPage-C3E8hZhB.js +1 -0
  15. package/client/dist/assets/InteractiveJobComposer-zj8SrOAl.js +19 -0
  16. package/client/dist/assets/JobDetailModal-Cm6ZeVoh.js +1 -0
  17. package/client/dist/assets/JobDetailPage-DN67LsdX.js +1 -0
  18. package/client/dist/assets/JobsPage-DF9wKGbw.js +1 -0
  19. package/client/dist/assets/LoopBuilderPage-RJ6GGjli.js +7 -0
  20. package/client/dist/assets/{LoopPreviewModal-BNUQ9iL8.js → LoopPreviewModal-B2wmifF6.js} +1 -1
  21. package/client/dist/assets/LoopsPage-DdlvH-dJ.js +1 -0
  22. package/client/dist/assets/PluginsPage-9DI0a6jI.js +2 -0
  23. package/client/dist/assets/ProjectSettingsDialog-dgy5hqPd.js +1 -0
  24. package/client/dist/assets/{TemplatePreviewModal-C6mndL8D.js → TemplatePreviewModal-CERf1IYB.js} +1 -1
  25. package/client/dist/assets/agent-BRJCESkc2.js +1 -0
  26. package/client/dist/assets/agent-BuYz6fJd.js +1 -0
  27. package/client/dist/assets/agent-CxnRzL6Z.js +1 -0
  28. package/client/dist/assets/agent-D4JAuEGN2.js +1 -0
  29. package/client/dist/assets/agent-DNQlI8EN.js +1 -0
  30. package/client/dist/assets/agent-DyzUH89R.js +1 -0
  31. package/client/dist/assets/agent-MaTxBlg5.js +1 -0
  32. package/client/dist/assets/agent-ostmMYNz2.js +1 -0
  33. package/client/dist/assets/{brain-Dia32foa.js → brain-BPbhM1Jx.js} +1 -1
  34. package/client/dist/assets/{dist-js-D_4k5prK.js → dist-js-Byjcju1W.js} +1 -1
  35. package/client/dist/assets/{dist-js-DN7Pxudf.js → dist-js-Dh-9lbLD.js} +1 -1
  36. package/client/dist/assets/index-89SuhD4Y.js +158 -0
  37. package/client/dist/assets/index-DOGzWJhO.css +2 -0
  38. package/client/dist/assets/integrations-By80EBkf.js +1 -0
  39. package/client/dist/assets/integrations-CAjq5tdW2.js +1 -0
  40. package/client/dist/assets/integrations-CG2dp3Kd.js +1 -0
  41. package/client/dist/assets/integrations-CP_aiV_8.js +1 -0
  42. package/client/dist/assets/integrations-CUNe3t9l2.js +1 -0
  43. package/client/dist/assets/integrations-CWjh5VRk.js +1 -0
  44. package/client/dist/assets/integrations-JOHSCvB3.js +1 -0
  45. package/client/dist/assets/integrations-RJfzXkPx2.js +1 -0
  46. package/client/dist/assets/{lib-BxSZPd0W.js → lib-yKyTNJ1c.js} +1 -1
  47. package/client/dist/assets/nav-B2_ToNqS.js +1 -0
  48. package/client/dist/assets/nav-BAMkVEId.js +1 -0
  49. package/client/dist/assets/nav-BTg8s-cD.js +1 -0
  50. package/client/dist/assets/nav-BY_4EWRv.js +1 -0
  51. package/client/dist/assets/nav-CVXtv0wX2.js +1 -0
  52. package/client/dist/assets/nav-D0Erv9ZJ2.js +1 -0
  53. package/client/dist/assets/nav-DUuw9VIU2.js +1 -0
  54. package/client/dist/assets/nav-S4HM94vW.js +1 -0
  55. package/client/dist/assets/{upload-4u055Eea.js → upload-Qh1NE3xi.js} +1 -1
  56. package/client/dist/assets/{value-C3Qe3VXm.js → value-BMPeyInx.js} +1 -1
  57. package/client/dist/index.html +29 -29
  58. package/package.json +1 -1
  59. package/server/dist/agent-chat-router.js +2 -0
  60. package/server/dist/agent-operator-prompt.js +10 -3
  61. package/server/dist/global-plugins-router.js +62 -0
  62. package/server/dist/headroom-manager.js +971 -0
  63. package/server/dist/headroom-routing.js +53 -0
  64. package/server/dist/index.js +7 -0
  65. package/server/dist/mcp/guide.js +3 -0
  66. package/server/dist/mcp/tools/jobs.js +52 -9
  67. package/server/dist/path-resolver.js +10 -3
  68. package/server/dist/project-router-jobs.js +56 -6
  69. package/server/dist/setup-manager.js +32 -29
  70. package/server/dist/setup-prerequisites.js +3 -1
  71. package/server/dist/transient-children.js +152 -13
  72. package/server/dist/util/cli-prompt.js +3 -0
  73. package/client/dist/assets/AgentBrowserCapture-76Ri2QR1.js +0 -1
  74. package/client/dist/assets/AgentIntegrationsModal-D9hVgaBR.js +0 -1
  75. package/client/dist/assets/AgentsPage-DEt9vpzh.js +0 -86
  76. package/client/dist/assets/CodePage-72-FLBNw.js +0 -2
  77. package/client/dist/assets/DocsPage-BB3VGFM9.js +0 -11
  78. package/client/dist/assets/IntegrationsPage-X8bLyuDk.js +0 -3
  79. package/client/dist/assets/InteractiveJobComposer-D1HUALME.js +0 -19
  80. package/client/dist/assets/JobDetailModal-CMg6aKSO.js +0 -1
  81. package/client/dist/assets/JobDetailPage-Bg01rzeu.js +0 -1
  82. package/client/dist/assets/JobsPage-Cwkm9yYc.js +0 -1
  83. package/client/dist/assets/LoopBuilderPage-4uPNN8Xn.js +0 -7
  84. package/client/dist/assets/LoopsPage-DLXESZxq.js +0 -1
  85. package/client/dist/assets/ProjectSettingsDialog-DP6hAug-.js +0 -1
  86. package/client/dist/assets/agent-B0Rg_eLw2.js +0 -1
  87. package/client/dist/assets/agent-B8f79myL.js +0 -1
  88. package/client/dist/assets/agent-BCH7a3z-.js +0 -1
  89. package/client/dist/assets/agent-BELAd4RF.js +0 -1
  90. package/client/dist/assets/agent-BrCnHlvX2.js +0 -1
  91. package/client/dist/assets/agent-CrcIj6WE2.js +0 -1
  92. package/client/dist/assets/agent-CtwLUmjl.js +0 -1
  93. package/client/dist/assets/agent-xG4qLg4R.js +0 -1
  94. package/client/dist/assets/index-BbVOi-dY.js +0 -158
  95. package/client/dist/assets/index-o3Q2Lii5.css +0 -2
  96. package/client/dist/assets/integrations-BQNkrfr7.js +0 -1
  97. package/client/dist/assets/integrations-BdN2f6bP.js +0 -1
  98. package/client/dist/assets/integrations-BrIUJmVf.js +0 -1
  99. package/client/dist/assets/integrations-CuWnMR5L.js +0 -1
  100. package/client/dist/assets/integrations-Cyp7cSYv2.js +0 -1
  101. package/client/dist/assets/integrations-DDR3OHdq.js +0 -1
  102. package/client/dist/assets/integrations-DVxi8b0j2.js +0 -1
  103. package/client/dist/assets/integrations-RuNEX3La2.js +0 -1
  104. package/client/dist/assets/nav-BFCUtHUQ2.js +0 -1
  105. package/client/dist/assets/nav-BouzorQ5.js +0 -1
  106. package/client/dist/assets/nav-Bpr-K0tD.js +0 -1
  107. package/client/dist/assets/nav-CpFrRDXd2.js +0 -1
  108. package/client/dist/assets/nav-CzKzuwH8.js +0 -1
  109. package/client/dist/assets/nav-Dkv4Vlr_2.js +0 -1
  110. package/client/dist/assets/nav-GG6azCbh.js +0 -1
  111. package/client/dist/assets/nav-prHkMqcr.js +0 -1
  112. /package/client/dist/assets/{activity-D8vQJrpW.js → activity-Dbl71S-E.js} +0 -0
  113. /package/client/dist/assets/{addspec-DxW6oPsk.js → addspec-DrXqP8ip.js} +0 -0
  114. /package/client/dist/assets/{agents-Cev2oFfX.js → agents-CGDn_3kB.js} +0 -0
  115. /package/client/dist/assets/{agentstudio-CZm2rOzt.js → agentstudio-Dxe_94PK.js} +0 -0
  116. /package/client/dist/assets/{aiedit-DYtnZU9H.js → aiedit-DrfHQnlz.js} +0 -0
  117. /package/client/dist/assets/{analytics-Ce-sPORu.js → analytics-CxDYcBl9.js} +0 -0
  118. /package/client/dist/assets/{attachments-Ci6_42Kh.js → attachments-DvxQbBTC.js} +0 -0
  119. /package/client/dist/assets/{auth-Cvsb7-Ek.js → auth-DpZq5p1e.js} +0 -0
  120. /package/client/dist/assets/{browser-CdS6O1Qb.js → browser-DM8G151q.js} +0 -0
  121. /package/client/dist/assets/{chat-VMflhzxn.js → chat-0dg5hOo_.js} +0 -0
  122. /package/client/dist/assets/{code-thSEptMx.js → code-B62fClk-.js} +0 -0
  123. /package/client/dist/assets/{commands-CckaBWdV.js → commands-DCJ-R9E4.js} +0 -0
  124. /package/client/dist/assets/{common-CYaT2yHp.js → common-DXrmj6WD.js} +0 -0
  125. /package/client/dist/assets/{dashboard-BawQCyrV.js → dashboard-DBuLfWo9.js} +0 -0
  126. /package/client/dist/assets/{explore-BKVV5JSu.js → explore-_XyU5e7R.js} +0 -0
  127. /package/client/dist/assets/{jira-B3613-gj.js → jira-DeIjwCK3.js} +0 -0
  128. /package/client/dist/assets/{jobs-D5sZzgLs.js → jobs-DrYmsKy5.js} +0 -0
  129. /package/client/dist/assets/{loops-Brnx4Fji.js → loops-CO6q75W8.js} +0 -0
  130. /package/client/dist/assets/{mcp-CIH0pH1K.js → mcp-HwBbQDk0.js} +0 -0
  131. /package/client/dist/assets/{settings-BFv6UbKV.js → settings-BYCMtxOz.js} +0 -0
  132. /package/client/dist/assets/{setup-RiUHzq7E.js → setup-D2CmgAro.js} +0 -0
  133. /package/client/dist/assets/{specs-CmEZY57v.js → specs-PGIOzMtc.js} +0 -0
  134. /package/client/dist/assets/{terminal-DFPNRLKp.js → terminal-CFt56A5f.js} +0 -0
  135. /package/client/dist/assets/{tickets-CkfbISuK.js → tickets-BHv3ewvq.js} +0 -0
@@ -3,13 +3,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BACKGROUND_LOG_RETENTION_MS = void 0;
6
7
  exports.trackTransientChild = trackTransientChild;
7
8
  exports.killTransientChildren = killTransientChildren;
8
9
  exports.startBackgroundProcess = startBackgroundProcess;
9
10
  exports.getBackgroundProcess = getBackgroundProcess;
11
+ exports.getBackgroundProcessLogs = getBackgroundProcessLogs;
10
12
  exports.listBackgroundProcesses = listBackgroundProcesses;
11
13
  exports.killBackgroundProcess = killBackgroundProcess;
12
14
  exports.killOwnedBackgroundProcess = killOwnedBackgroundProcess;
15
+ exports.killBackgroundProcessesForChat = killBackgroundProcessesForChat;
13
16
  const tree_kill_1 = __importDefault(require("tree-kill"));
14
17
  const child_process_1 = require("child_process");
15
18
  const readline_1 = __importDefault(require("readline"));
@@ -25,16 +28,34 @@ const readline_1 = __importDefault(require("readline"));
25
28
  */
26
29
  const byProject = new Map();
27
30
  const backgroundByPid = new Map();
31
+ const finishedBackgroundByPid = new Map();
28
32
  const backgroundTerminal = new Set(['exited', 'killed', 'failed']);
33
+ const BACKGROUND_KILL_GRACE_MS = 2500;
34
+ const BACKGROUND_OUTPUT_MAX_LINE_CHARS = 1000;
35
+ const BACKGROUND_OUTPUT_MAX_LINES_PER_SECOND = 20;
36
+ const BACKGROUND_LOG_MAX_LINES = 500;
37
+ exports.BACKGROUND_LOG_RETENTION_MS = 10 * 60 * 1000;
29
38
  function treeKillSafe(pid, signal) {
30
39
  try {
31
40
  (0, tree_kill_1.default)(pid, signal, () => undefined);
32
41
  }
33
42
  catch { /* best-effort */ }
34
43
  }
44
+ function treeKillWithEscalation(pid, isStillAlive = () => backgroundByPid.has(pid)) {
45
+ treeKillSafe(pid, 'SIGTERM');
46
+ const timer = setTimeout(() => {
47
+ if (isStillAlive())
48
+ treeKillSafe(pid, 'SIGKILL');
49
+ }, BACKGROUND_KILL_GRACE_MS);
50
+ timer.unref?.();
51
+ return timer;
52
+ }
35
53
  function cloneProcess(process) {
36
54
  return { ...process };
37
55
  }
56
+ function cloneLogLine(line) {
57
+ return { ...line };
58
+ }
38
59
  /** Track a child under a projectId; auto-removes itself on close. */
39
60
  function trackTransientChild(projectId, child) {
40
61
  let set = byProject.get(projectId);
@@ -54,13 +75,19 @@ function trackTransientChild(projectId, child) {
54
75
  child.once('close', drop);
55
76
  child.once('error', drop);
56
77
  }
57
- /** SIGTERM the whole subtree of every tracked child for a project, then forget. */
78
+ /** SIGTERM the whole subtree of every tracked child for a project, then escalate. */
58
79
  function killTransientChildren(projectId) {
59
80
  const set = byProject.get(projectId);
60
81
  if (set)
61
82
  for (const child of set) {
62
83
  if (child.pid) {
63
- treeKillSafe(child.pid, 'SIGTERM');
84
+ if (backgroundByPid.has(child.pid))
85
+ continue;
86
+ let alive = true;
87
+ const timer = treeKillWithEscalation(child.pid, () => alive);
88
+ const clear = () => { alive = false; clearTimeout(timer); };
89
+ child.once('close', clear);
90
+ child.once('error', clear);
64
91
  }
65
92
  }
66
93
  byProject.delete(projectId);
@@ -68,8 +95,7 @@ function killTransientChildren(projectId) {
68
95
  if (record.process.projectId !== projectId)
69
96
  continue;
70
97
  markTerminal(record, 'killed', null, 'SIGTERM');
71
- treeKillSafe(pid, 'SIGTERM');
72
- backgroundByPid.delete(pid);
98
+ terminateBackgroundRecord(record);
73
99
  }
74
100
  }
75
101
  function startBackgroundProcess(command, cwd, chatId, projectId, hooks) {
@@ -91,19 +117,32 @@ function startBackgroundProcess(command, cwd, chatId, projectId, hooks) {
91
117
  chatId,
92
118
  projectId,
93
119
  };
94
- const record = { child, process, hooks: hooks ?? {} };
120
+ const staleFinished = finishedBackgroundByPid.get(child.pid);
121
+ if (staleFinished) {
122
+ clearTimeout(staleFinished.cleanupTimer);
123
+ finishedBackgroundByPid.delete(child.pid);
124
+ }
125
+ const record = {
126
+ child,
127
+ process,
128
+ hooks: hooks ?? {},
129
+ outputLines: [],
130
+ droppedOutputLines: 0,
131
+ };
95
132
  backgroundByPid.set(child.pid, record);
96
133
  trackTransientChild(projectId, child);
97
134
  attachOutput(child, 'stdout', record);
98
135
  attachOutput(child, 'stderr', record);
99
136
  child.once('error', () => {
100
- markTerminal(record, 'failed', null, null);
137
+ markTerminal(record, process.status === 'killed' ? 'killed' : 'failed', null, null);
101
138
  backgroundByPid.delete(process.pid);
139
+ retainFinishedRecord(record);
102
140
  });
103
141
  child.once('close', (code, signal) => {
104
142
  const status = process.status === 'killed' ? 'killed' : code === 0 ? 'exited' : 'failed';
105
143
  markTerminal(record, status, code, signal);
106
144
  backgroundByPid.delete(process.pid);
145
+ retainFinishedRecord(record);
107
146
  });
108
147
  record.hooks.onStarted?.(cloneProcess(process));
109
148
  return cloneProcess(process);
@@ -111,27 +150,112 @@ function startBackgroundProcess(command, cwd, chatId, projectId, hooks) {
111
150
  function attachOutput(child, source, record) {
112
151
  const stream = source === 'stdout' ? child.stdout : child.stderr;
113
152
  const rl = readline_1.default.createInterface({ input: stream });
153
+ let windowStart = Date.now();
154
+ let emittedInWindow = 0;
114
155
  rl.on('line', (line) => {
156
+ const clipped = line.length > BACKGROUND_OUTPUT_MAX_LINE_CHARS ? `${line.slice(0, BACKGROUND_OUTPUT_MAX_LINE_CHARS)}...` : line;
157
+ appendOutputLine(record, source, clipped);
158
+ if (!record.hooks.onOutput)
159
+ return;
160
+ const now = Date.now();
161
+ if (now - windowStart >= 1000) {
162
+ windowStart = now;
163
+ emittedInWindow = 0;
164
+ }
165
+ if (emittedInWindow >= BACKGROUND_OUTPUT_MAX_LINES_PER_SECOND)
166
+ return;
167
+ emittedInWindow += 1;
115
168
  record.hooks.onOutput?.({
116
169
  pid: record.process.pid,
117
170
  chatId: record.process.chatId,
118
171
  projectId: record.process.projectId,
119
172
  source,
120
- line,
173
+ line: clipped,
121
174
  });
122
175
  });
123
176
  }
177
+ function appendOutputLine(record, source, line) {
178
+ record.outputLines.push({ at: Date.now(), source, line });
179
+ if (record.outputLines.length > BACKGROUND_LOG_MAX_LINES) {
180
+ const overflow = record.outputLines.length - BACKGROUND_LOG_MAX_LINES;
181
+ record.outputLines.splice(0, overflow);
182
+ record.droppedOutputLines += overflow;
183
+ }
184
+ }
185
+ function retainFinishedRecord(record) {
186
+ if (record.retained) {
187
+ const existing = finishedBackgroundByPid.get(record.process.pid);
188
+ if (existing) {
189
+ existing.process = cloneProcess(record.process);
190
+ existing.outputLines = record.outputLines.map(cloneLogLine);
191
+ existing.droppedOutputLines = record.droppedOutputLines;
192
+ }
193
+ return;
194
+ }
195
+ record.retained = true;
196
+ const previous = finishedBackgroundByPid.get(record.process.pid);
197
+ if (previous)
198
+ clearTimeout(previous.cleanupTimer);
199
+ const cleanupTimer = setTimeout(() => {
200
+ finishedBackgroundByPid.delete(record.process.pid);
201
+ }, exports.BACKGROUND_LOG_RETENTION_MS);
202
+ cleanupTimer.unref?.();
203
+ finishedBackgroundByPid.set(record.process.pid, {
204
+ process: cloneProcess(record.process),
205
+ outputLines: record.outputLines.map(cloneLogLine),
206
+ droppedOutputLines: record.droppedOutputLines,
207
+ cleanupTimer,
208
+ });
209
+ }
124
210
  function markTerminal(record, status, exitCode, signal) {
125
211
  if (record.killTimer)
126
212
  clearTimeout(record.killTimer);
127
213
  record.process.status = status;
128
214
  record.process.exitCode = exitCode;
129
215
  record.process.signal = signal;
216
+ if (record.terminalNotified)
217
+ return;
218
+ record.terminalNotified = true;
130
219
  record.hooks.onExited?.(cloneProcess(record.process));
131
220
  }
132
221
  function getBackgroundProcess(pid) {
133
222
  const record = backgroundByPid.get(pid);
134
- return record ? cloneProcess(record.process) : null;
223
+ if (record)
224
+ return cloneProcess(record.process);
225
+ const finished = finishedBackgroundByPid.get(pid);
226
+ return finished ? cloneProcess(finished.process) : null;
227
+ }
228
+ function getBackgroundProcessLogs(pid, filter = {}) {
229
+ const live = backgroundByPid.get(pid);
230
+ const record = live
231
+ ? {
232
+ process: live.process,
233
+ outputLines: live.outputLines,
234
+ droppedOutputLines: live.droppedOutputLines,
235
+ }
236
+ : finishedBackgroundByPid.get(pid);
237
+ if (!record)
238
+ return null;
239
+ if (filter.projectId && record.process.projectId !== filter.projectId)
240
+ return null;
241
+ if (filter.chatId && record.process.chatId !== filter.chatId)
242
+ return null;
243
+ const limit = typeof filter.limit === 'number' && Number.isFinite(filter.limit)
244
+ ? Math.max(1, Math.min(BACKGROUND_LOG_MAX_LINES, Math.floor(filter.limit)))
245
+ : BACKGROUND_LOG_MAX_LINES;
246
+ const start = Math.max(0, record.outputLines.length - limit);
247
+ const lines = record.outputLines.slice(start).map(cloneLogLine);
248
+ const droppedByLimit = record.outputLines.length - lines.length;
249
+ const droppedLines = record.droppedOutputLines + droppedByLimit;
250
+ return {
251
+ process: cloneProcess(record.process),
252
+ lines,
253
+ truncated: droppedLines > 0,
254
+ droppedLines,
255
+ maxLines: BACKGROUND_LOG_MAX_LINES,
256
+ maxLineChars: BACKGROUND_OUTPUT_MAX_LINE_CHARS,
257
+ retentionMs: exports.BACKGROUND_LOG_RETENTION_MS,
258
+ };
135
259
  }
136
260
  function listBackgroundProcesses(filter = {}) {
137
261
  const processes = [];
@@ -152,11 +276,7 @@ function killBackgroundProcess(pid) {
152
276
  if (!record)
153
277
  return;
154
278
  record.process.status = 'killed';
155
- treeKillSafe(pid, 'SIGTERM');
156
- record.killTimer = setTimeout(() => {
157
- if (backgroundByPid.has(pid))
158
- treeKillSafe(pid, 'SIGKILL');
159
- }, 2500);
279
+ terminateBackgroundRecord(record);
160
280
  }
161
281
  function killOwnedBackgroundProcess(pid, owner) {
162
282
  const record = backgroundByPid.get(pid);
@@ -169,3 +289,22 @@ function killOwnedBackgroundProcess(pid, owner) {
169
289
  killBackgroundProcess(pid);
170
290
  return true;
171
291
  }
292
+ function terminateBackgroundRecord(record) {
293
+ if (record.killTimer)
294
+ clearTimeout(record.killTimer);
295
+ record.killTimer = treeKillWithEscalation(record.process.pid);
296
+ }
297
+ function killBackgroundProcessesForChat(chatId, projectId) {
298
+ let killed = 0;
299
+ for (const record of backgroundByPid.values()) {
300
+ if (record.process.chatId !== chatId)
301
+ continue;
302
+ if (projectId && record.process.projectId !== projectId)
303
+ continue;
304
+ record.process.status = 'killed';
305
+ markTerminal(record, 'killed', null, 'SIGTERM');
306
+ terminateBackgroundRecord(record);
307
+ killed += 1;
308
+ }
309
+ return killed;
310
+ }
@@ -29,6 +29,7 @@ exports.spawnCodex = spawnCodex;
29
29
  exports.spawnGemini = spawnGemini;
30
30
  exports.spawnAiCli = spawnAiCli;
31
31
  const win_spawn_1 = require("./win-spawn");
32
+ const headroom_routing_1 = require("../headroom-routing");
32
33
  // Per-call (not a frozen module const) so a test can flip the platform with a
33
34
  // `process.platform` spy without re-importing this module — which removes a
34
35
  // flaky vi.doMock + resetModules + dynamic-import dance in the spawn tests.
@@ -147,6 +148,7 @@ function ensureStdinPipe(stdio) {
147
148
  * survive. POSIX call is identical to `spawnCli('claude', args, options)`.
148
149
  */
149
150
  function spawnClaude(args, options = {}) {
151
+ options = (0, headroom_routing_1.withHeadroomSpawnEnv)('claude', options);
150
152
  if (!isWin()) {
151
153
  return (0, win_spawn_1.spawnCli)('claude', args, options);
152
154
  }
@@ -169,6 +171,7 @@ function spawnClaude(args, options = {}) {
169
171
  * survive. POSIX call is identical to `spawnCli('codex', args, options)`.
170
172
  */
171
173
  function spawnCodex(args, options = {}) {
174
+ options = (0, headroom_routing_1.withHeadroomSpawnEnv)('codex', options);
172
175
  if (!isWin()) {
173
176
  return (0, win_spawn_1.spawnCli)('codex', args, options);
174
177
  }
@@ -1 +0,0 @@
1
- import{a as e}from"./chunk-BNv3lrIs.js";import{I as t,Rn as n,n as r,qn as i,s as a,wn as o,zn as s}from"./index-BbVOi-dY.js";var c=e(i(),1);function l(e,t){let n=e.indexOf(`,`);if(!e.startsWith(`data:`)||n===-1)throw Error(`dataUrlToFile: not a data URL`);let r=e.slice(0,n),i=/^data:([^;,]+)/.exec(r)?.[1]||`application/octet-stream`,a=e.slice(n+1);if(/;base64$/i.test(r)){let e=atob(a),n=new Uint8Array(e.length);for(let t=0;t<e.length;t++)n[t]=e.charCodeAt(t);return new File([n],t,{type:i})}return new File([decodeURIComponent(a)],t,{type:i})}var u=o();function d({projectId:e,conversationId:i}){let{t:o}=s(`agent`),{closeBrowser:d,queueCapture:f}=r(),p=(0,c.useMemo)(()=>`agent-${i??`home`}-${Math.round(performance.now())}`,[i]),m=async e=>{if(!i){n.error(o(`workspace.requiresConversation`));return}try{f(await a(i,l(e.screenshotDataUrl,`capture-${Date.now()}.png`))),n.success(o(`workspace.browserCaptured`)),d()}catch(e){n.error(o(`workspace.uploadFailed`),{description:e instanceof Error?e.message:void 0})}};return(0,u.jsx)(t,{open:!0,onClose:d,projectId:e,pendingSpecId:p,confirmLabel:o(`workspace.browserConfirm`),onCaptured:e=>{m(e)}})}export{d as AgentBrowserCapture};
@@ -1 +0,0 @@
1
- import{a as e}from"./chunk-BNv3lrIs.js";import{$ as t,H as n,Kn as r,Q as i,Tt as a,ot as o,qn as s,wn as c,zn as l}from"./index-BbVOi-dY.js";import u from"./IntegrationsPage-X8bLyuDk.js";var d=e(s(),1),f=e(r(),1),p=c();function m({onClose:e}){let{t:r}=l(`nav`),{t:s}=l(`common`),{panelRef:c,panelStyle:m,headerHandleProps:h,resizeHandles:g,isFloating:_,guardBackdrop:v}=t({minWidth:320,minHeight:200,persistKey:`specrails-desktop:agent-integrations-modal`});return(0,d.useEffect)(()=>{function t(t){t.key===`Escape`&&e()}return window.addEventListener(`keydown`,t),()=>window.removeEventListener(`keydown`,t)},[e]),(0,f.createPortal)((0,p.jsx)(n,{delayDuration:400,children:(0,p.jsxs)(`div`,{className:`fixed inset-0 z-[65] flex items-stretch justify-center`,children:[(0,p.jsx)(`div`,{className:`absolute inset-0 bg-black/50 backdrop-blur-sm`,onClick:v(e)}),(0,p.jsxs)(`div`,{ref:c,className:`relative m-3 flex w-full flex-col overflow-hidden rounded-xl border border-border/30 glass-card animate-in fade-in zoom-in-95 duration-200`,style:m,children:[(0,p.jsxs)(`div`,{...h,className:`flex items-center justify-between border-b border-border/30 px-4 py-3${_?` cursor-grab active:cursor-grabbing`:``}`,children:[(0,p.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,p.jsx)(a,{className:`h-4 w-4 flex-shrink-0 text-accent-primary`}),(0,p.jsx)(`h2`,{className:`truncate text-sm font-semibold text-foreground`,children:r(`rightSidebar.integrations`)})]}),(0,p.jsx)(`button`,{type:`button`,onClick:e,"aria-label":s(`actions.close`),className:`flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-surface/50 hover:text-foreground`,children:(0,p.jsx)(o,{className:`h-4 w-4`})})]}),(0,p.jsx)(`div`,{className:`min-h-0 flex-1 overflow-auto`,children:(0,p.jsx)(u,{})})]}),(0,p.jsx)(i,{handles:g})]})}),document.body)}export{m as AgentIntegrationsModal};