@surething/cockpit 1.0.233 → 1.0.235

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 (225) hide show
  1. package/.next-prod/BUILD_ID +1 -1
  2. package/.next-prod/app-path-routes-manifest.json +3 -3
  3. package/.next-prod/build-manifest.json +3 -3
  4. package/.next-prod/prerender-manifest.json +3 -3
  5. package/.next-prod/react-loadable-manifest.json +7 -0
  6. package/.next-prod/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next-prod/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next-prod/server/app/_global-error.html +1 -1
  9. package/.next-prod/server/app/_global-error.rsc +1 -1
  10. package/.next-prod/server/app/_global-error.segments/_full.segment.rsc +1 -1
  11. package/.next-prod/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  12. package/.next-prod/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  13. package/.next-prod/server/app/_global-error.segments/_head.segment.rsc +1 -1
  14. package/.next-prod/server/app/_global-error.segments/_index.segment.rsc +1 -1
  15. package/.next-prod/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next-prod/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next-prod/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next-prod/server/app/_not-found.html +10 -2
  19. package/.next-prod/server/app/_not-found.rsc +29 -20
  20. package/.next-prod/server/app/_not-found.segments/_full.segment.rsc +29 -20
  21. package/.next-prod/server/app/_not-found.segments/_head.segment.rsc +1 -1
  22. package/.next-prod/server/app/_not-found.segments/_index.segment.rsc +12 -4
  23. package/.next-prod/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  24. package/.next-prod/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  25. package/.next-prod/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  26. package/.next-prod/server/app/api/agent/test/route.js.nft.json +1 -1
  27. package/.next-prod/server/app/api/bash/route.js.nft.json +1 -1
  28. package/.next-prod/server/app/api/chat/codex/route.js +1 -1
  29. package/.next-prod/server/app/api/chat/codex/route.js.nft.json +1 -1
  30. package/.next-prod/server/app/api/chat/deepseek/route.js +1 -1
  31. package/.next-prod/server/app/api/chat/deepseek/route.js.nft.json +1 -1
  32. package/.next-prod/server/app/api/chat/kimi/route.js +1 -1
  33. package/.next-prod/server/app/api/chat/kimi/route.js.nft.json +1 -1
  34. package/.next-prod/server/app/api/chat/ollama/route.js +1 -1
  35. package/.next-prod/server/app/api/chat/ollama/route.js.nft.json +1 -1
  36. package/.next-prod/server/app/api/chat/pty-input/route.js.nft.json +1 -1
  37. package/.next-prod/server/app/api/chat/route.js +1 -1
  38. package/.next-prod/server/app/api/chat/route.js.nft.json +1 -1
  39. package/.next-prod/server/app/api/chat/stop/route.js +1 -1
  40. package/.next-prod/server/app/api/chat/stop/route.js.nft.json +1 -1
  41. package/.next-prod/server/app/api/claude-stats/route.js.nft.json +1 -1
  42. package/.next-prod/server/app/api/commands/route.js.nft.json +1 -1
  43. package/.next-prod/server/app/api/comments/route.js.nft.json +1 -1
  44. package/.next-prod/server/app/api/db/columns/route.js.nft.json +1 -1
  45. package/.next-prod/server/app/api/db/connect/route.js.nft.json +1 -1
  46. package/.next-prod/server/app/api/db/disconnect/route.js.nft.json +1 -1
  47. package/.next-prod/server/app/api/db/export/route.js.nft.json +1 -1
  48. package/.next-prod/server/app/api/db/query/route.js.nft.json +1 -1
  49. package/.next-prod/server/app/api/db/schemas/route.js.nft.json +1 -1
  50. package/.next-prod/server/app/api/dev/spans/route.js.nft.json +1 -1
  51. package/.next-prod/server/app/api/extension/version/route.js.nft.json +1 -1
  52. package/.next-prod/server/app/api/file/route.js.nft.json +1 -1
  53. package/.next-prod/server/app/api/files/blame/route.js.nft.json +1 -1
  54. package/.next-prod/server/app/api/files/clipboard/route.js.nft.json +1 -1
  55. package/.next-prod/server/app/api/files/copy/route.js.nft.json +1 -1
  56. package/.next-prod/server/app/api/files/delete/route.js.nft.json +1 -1
  57. package/.next-prod/server/app/api/files/expanded/route.js.nft.json +1 -1
  58. package/.next-prod/server/app/api/files/index/route.js.nft.json +1 -1
  59. package/.next-prod/server/app/api/files/init/route.js.nft.json +1 -1
  60. package/.next-prod/server/app/api/files/paste/route.js.nft.json +1 -1
  61. package/.next-prod/server/app/api/files/read/route.js.nft.json +1 -1
  62. package/.next-prod/server/app/api/files/readdir/route.js.nft.json +1 -1
  63. package/.next-prod/server/app/api/files/recent/route.js.nft.json +1 -1
  64. package/.next-prod/server/app/api/files/save/route.js.nft.json +1 -1
  65. package/.next-prod/server/app/api/files/search/route.js.nft.json +1 -1
  66. package/.next-prod/server/app/api/files/stat/route.js.nft.json +1 -1
  67. package/.next-prod/server/app/api/files/text/route.js.nft.json +1 -1
  68. package/.next-prod/server/app/api/git/branch-diff/route.js.nft.json +1 -1
  69. package/.next-prod/server/app/api/git/branches/route.js.nft.json +1 -1
  70. package/.next-prod/server/app/api/git/commit-diff/route.js.nft.json +1 -1
  71. package/.next-prod/server/app/api/git/commits/route.js.nft.json +1 -1
  72. package/.next-prod/server/app/api/git/current-branch/route.js.nft.json +1 -1
  73. package/.next-prod/server/app/api/git/diff/route.js.nft.json +1 -1
  74. package/.next-prod/server/app/api/git/discard/route.js.nft.json +1 -1
  75. package/.next-prod/server/app/api/git/stage/route.js.nft.json +1 -1
  76. package/.next-prod/server/app/api/git/status/route.js.nft.json +1 -1
  77. package/.next-prod/server/app/api/git/unstage/route.js.nft.json +1 -1
  78. package/.next-prod/server/app/api/git/worktree/route.js.nft.json +1 -1
  79. package/.next-prod/server/app/api/global-state/route.js +1 -1
  80. package/.next-prod/server/app/api/global-state/route.js.nft.json +1 -1
  81. package/.next-prod/server/app/api/health/route.js.nft.json +1 -1
  82. package/.next-prod/server/app/api/jupyter/load/route.js.nft.json +1 -1
  83. package/.next-prod/server/app/api/jupyter/save/route.js.nft.json +1 -1
  84. package/.next-prod/server/app/api/jupyter/shutdown/route.js.nft.json +1 -1
  85. package/.next-prod/server/app/api/lsp/definition/route.js.nft.json +1 -1
  86. package/.next-prod/server/app/api/lsp/hover/route.js.nft.json +1 -1
  87. package/.next-prod/server/app/api/lsp/references/route.js.nft.json +1 -1
  88. package/.next-prod/server/app/api/lsp/status/route.js.nft.json +1 -1
  89. package/.next-prod/server/app/api/lsp/warmup/route.js.nft.json +1 -1
  90. package/.next-prod/server/app/api/mysql/columns/route.js.nft.json +1 -1
  91. package/.next-prod/server/app/api/mysql/connect/route.js.nft.json +1 -1
  92. package/.next-prod/server/app/api/mysql/disconnect/route.js.nft.json +1 -1
  93. package/.next-prod/server/app/api/mysql/export/route.js.nft.json +1 -1
  94. package/.next-prod/server/app/api/mysql/query/route.js.nft.json +1 -1
  95. package/.next-prod/server/app/api/mysql/schemas/route.js.nft.json +1 -1
  96. package/.next-prod/server/app/api/neo4j/connect/route.js.nft.json +1 -1
  97. package/.next-prod/server/app/api/neo4j/disconnect/route.js.nft.json +1 -1
  98. package/.next-prod/server/app/api/neo4j/query/route.js.nft.json +1 -1
  99. package/.next-prod/server/app/api/neo4j/schema/route.js.nft.json +1 -1
  100. package/.next-prod/server/app/api/note/route.js.nft.json +1 -1
  101. package/.next-prod/server/app/api/ollama/models/route.js.nft.json +1 -1
  102. package/.next-prod/server/app/api/ollama/start/route.js.nft.json +1 -1
  103. package/.next-prod/server/app/api/open-cursor/route.js.nft.json +1 -1
  104. package/.next-prod/server/app/api/open-vscode/route.js.nft.json +1 -1
  105. package/.next-prod/server/app/api/pick-folder/route.js.nft.json +1 -1
  106. package/.next-prod/server/app/api/pinned-sessions/route.js.nft.json +1 -1
  107. package/.next-prod/server/app/api/project-settings/route.js.nft.json +1 -1
  108. package/.next-prod/server/app/api/project-state/route.js.nft.json +1 -1
  109. package/.next-prod/server/app/api/projectGraph/affected/route.js.nft.json +1 -1
  110. package/.next-prod/server/app/api/projectGraph/callees/route.js.nft.json +1 -1
  111. package/.next-prod/server/app/api/projectGraph/callers/route.js.nft.json +1 -1
  112. package/.next-prod/server/app/api/projectGraph/coedit/route.js.nft.json +1 -1
  113. package/.next-prod/server/app/api/projectGraph/context/route.js.nft.json +1 -1
  114. package/.next-prod/server/app/api/projectGraph/file/route.js.nft.json +1 -1
  115. package/.next-prod/server/app/api/projectGraph/file-functions/route.js.nft.json +1 -1
  116. package/.next-prod/server/app/api/projectGraph/impact/route.js.nft.json +1 -1
  117. package/.next-prod/server/app/api/projectGraph/related/route.js.nft.json +1 -1
  118. package/.next-prod/server/app/api/projectGraph/risk/route.js.nft.json +1 -1
  119. package/.next-prod/server/app/api/projectGraph/search/route.js.nft.json +1 -1
  120. package/.next-prod/server/app/api/projects/route.js.nft.json +1 -1
  121. package/.next-prod/server/app/api/push/public-key/route.js +1 -1
  122. package/.next-prod/server/app/api/push/public-key/route.js.nft.json +1 -1
  123. package/.next-prod/server/app/api/push/subscribe/route.js +1 -1
  124. package/.next-prod/server/app/api/push/subscribe/route.js.nft.json +1 -1
  125. package/.next-prod/server/app/api/push/unsubscribe/route.js +1 -1
  126. package/.next-prod/server/app/api/push/unsubscribe/route.js.nft.json +1 -1
  127. package/.next-prod/server/app/api/redis/command/route.js.nft.json +1 -1
  128. package/.next-prod/server/app/api/redis/connect/route.js.nft.json +1 -1
  129. package/.next-prod/server/app/api/redis/delete/route.js.nft.json +1 -1
  130. package/.next-prod/server/app/api/redis/disconnect/route.js.nft.json +1 -1
  131. package/.next-prod/server/app/api/redis/get/route.js.nft.json +1 -1
  132. package/.next-prod/server/app/api/redis/keys/route.js.nft.json +1 -1
  133. package/.next-prod/server/app/api/redis/set/route.js.nft.json +1 -1
  134. package/.next-prod/server/app/api/review/[id]/comments/route.js.nft.json +1 -1
  135. package/.next-prod/server/app/api/review/[id]/replies/route.js.nft.json +1 -1
  136. package/.next-prod/server/app/api/review/[id]/route.js.nft.json +1 -1
  137. package/.next-prod/server/app/api/review/identify/route.js.nft.json +1 -1
  138. package/.next-prod/server/app/api/review/order/route.js.nft.json +1 -1
  139. package/.next-prod/server/app/api/review/route.js.nft.json +1 -1
  140. package/.next-prod/server/app/api/review/share-info/route.js.nft.json +1 -1
  141. package/.next-prod/server/app/api/review/users/route.js.nft.json +1 -1
  142. package/.next-prod/server/app/api/scheduled-tasks/route.js +1 -1
  143. package/.next-prod/server/app/api/scheduled-tasks/route.js.nft.json +1 -1
  144. package/.next-prod/server/app/api/services/config/route.js.nft.json +1 -1
  145. package/.next-prod/server/app/api/services/scripts/route.js.nft.json +1 -1
  146. package/.next-prod/server/app/api/session/[sessionId]/fork/route.js.nft.json +1 -1
  147. package/.next-prod/server/app/api/session/[sessionId]/history/route.js.nft.json +1 -1
  148. package/.next-prod/server/app/api/session-by-path/route.js +1 -1
  149. package/.next-prod/server/app/api/session-by-path/route.js.nft.json +1 -1
  150. package/.next-prod/server/app/api/sessions/projects/[encodedPath]/route.js.nft.json +1 -1
  151. package/.next-prod/server/app/api/sessions/projects/route.js.nft.json +1 -1
  152. package/.next-prod/server/app/api/sessions/route.js.nft.json +1 -1
  153. package/.next-prod/server/app/api/settings/route.js.nft.json +1 -1
  154. package/.next-prod/server/app/api/skills/[id]/route.js.nft.json +1 -1
  155. package/.next-prod/server/app/api/skills/content/route.js.nft.json +1 -1
  156. package/.next-prod/server/app/api/skills/route.js.nft.json +1 -1
  157. package/.next-prod/server/app/api/terminal/aliases/route.js.nft.json +1 -1
  158. package/.next-prod/server/app/api/terminal/autocomplete/route.js.nft.json +1 -1
  159. package/.next-prod/server/app/api/terminal/bubble-order/route.js.nft.json +1 -1
  160. package/.next-prod/server/app/api/terminal/env/route.js.nft.json +1 -1
  161. package/.next-prod/server/app/api/terminal/history/route.js.nft.json +1 -1
  162. package/.next-prod/server/app/api/version/route.js.nft.json +1 -1
  163. package/.next-prod/server/app/favicon.ico/route.js.nft.json +1 -1
  164. package/.next-prod/server/app/m/page.js +2 -2
  165. package/.next-prod/server/app/m/page.js.nft.json +1 -1
  166. package/.next-prod/server/app/m/page_client-reference-manifest.js +1 -1
  167. package/.next-prod/server/app/manifest.webmanifest/route.js.nft.json +1 -1
  168. package/.next-prod/server/app/page.js +1 -1
  169. package/.next-prod/server/app/page.js.nft.json +1 -1
  170. package/.next-prod/server/app/page_client-reference-manifest.js +1 -1
  171. package/.next-prod/server/app/project/page.js +1 -1
  172. package/.next-prod/server/app/project/page.js.nft.json +1 -1
  173. package/.next-prod/server/app/project/page_client-reference-manifest.js +1 -1
  174. package/.next-prod/server/app/review/[id]/page.js +2 -2
  175. package/.next-prod/server/app/review/[id]/page.js.nft.json +1 -1
  176. package/.next-prod/server/app/review/[id]/page_client-reference-manifest.js +1 -1
  177. package/.next-prod/server/app-paths-manifest.json +3 -3
  178. package/.next-prod/server/chunks/{1035.js → 5897.js} +2 -2
  179. package/.next-prod/server/chunks/7338.js +1 -1
  180. package/.next-prod/server/chunks/9658.js +26 -26
  181. package/.next-prod/server/chunks/9830.js +1 -1
  182. package/.next-prod/server/middleware-build-manifest.js +1 -1
  183. package/.next-prod/server/middleware-react-loadable-manifest.js +1 -1
  184. package/.next-prod/server/pages/404.html +10 -2
  185. package/.next-prod/server/pages/500.html +1 -1
  186. package/.next-prod/server/server-reference-manifest.json +1 -1
  187. package/.next-prod/static/chunks/{2625-bc4748f0cd7db618.js → 1332-5e141e2acf28015a.js} +10 -10
  188. package/.next-prod/static/chunks/{217-92f800c2c438c550.js → 217-656e1fdbc820d9dd.js} +4 -4
  189. package/.next-prod/static/chunks/2502-fb8682bc7a61e04d.js +4 -0
  190. package/.next-prod/static/chunks/{2576-11e41242c7dd98f1.js → 2576-47a83a4d8066d196.js} +11 -11
  191. package/.next-prod/static/chunks/4523.3f277a24fbcfde6d.js +138 -0
  192. package/.next-prod/static/chunks/9304-ddde9008da31b2ce.js +1 -0
  193. package/.next-prod/static/chunks/app/{layout-ac2f1f67df01cc89.js → layout-e9ada9458f95f29c.js} +1 -1
  194. package/.next-prod/static/chunks/app/m/page-06a5ea22c02892d1.js +1 -0
  195. package/.next-prod/static/chunks/app/{page-1f0a6f49656a4c52.js → page-8f349cc054af0d5b.js} +1 -1
  196. package/.next-prod/static/chunks/app/project/{page-1f0a6f49656a4c52.js → page-8f349cc054af0d5b.js} +1 -1
  197. package/.next-prod/static/chunks/app/review/[id]/{page-39fa3a843adf617e.js → page-0c5d03d65758c7dc.js} +1 -1
  198. package/.next-prod/static/chunks/webpack-2c71a79c277961bc.js +1 -0
  199. package/.next-prod/static/css/44a979b47b165c63.css +1 -0
  200. package/.next-prod/trace +13 -13
  201. package/.next-prod/trace-build +1 -1
  202. package/README.md +27 -1
  203. package/README.zh.md +27 -1
  204. package/bin/cock-browser.mjs +12 -9
  205. package/bin/cock.mjs +5 -5
  206. package/bin/postinstall.mjs +6 -6
  207. package/chrome-extension/automation.js +45 -40
  208. package/chrome-extension/background.js +70 -67
  209. package/chrome-extension/content.js +38 -38
  210. package/chrome-extension/disguise.js +4 -4
  211. package/chrome-extension/network-capture.js +16 -16
  212. package/dist/{chunk-VHFWPI2X.mjs → chunk-IQXDXTI7.mjs} +35 -4
  213. package/dist/scheduledTasks.mjs +8 -14
  214. package/dist/wsServer.mjs +9 -31
  215. package/next.config.mjs +4 -2
  216. package/package.json +5 -4
  217. package/public/boot.js +8 -0
  218. package/server.mjs +122 -26
  219. package/.next-prod/server/chunks/7003.js +0 -1
  220. package/.next-prod/static/chunks/471-504b396c8302f4f2.js +0 -4
  221. package/.next-prod/static/chunks/app/m/page-8b1af6f31b60c485.js +0 -1
  222. package/.next-prod/static/chunks/webpack-dbba7fa77ee1faa4.js +0 -1
  223. package/.next-prod/static/css/fa7218e60ff464b5.css +0 -1
  224. /package/.next-prod/static/{WfNQMmytl62j_8KtgCW9_ → cfyYtf1Sz25gcv6tfZqFF}/_buildManifest.js +0 -0
  225. /package/.next-prod/static/{WfNQMmytl62j_8KtgCW9_ → cfyYtf1Sz25gcv6tfZqFF}/_ssgManifest.js +0 -0
@@ -250,6 +250,33 @@ async function getSessionPreview(cwd, sessionId) {
250
250
  searchText
251
251
  };
252
252
  }
253
+ async function getGlobalSessionsSnapshot(limit = 15) {
254
+ const state = await readJsonFile(GLOBAL_STATE_FILE, { sessions: [] });
255
+ for (const s of state.sessions) {
256
+ if (!s.status) {
257
+ const legacy = s;
258
+ s.status = legacy.isLoading ? "loading" : "normal";
259
+ }
260
+ }
261
+ state.sessions.sort((a, b) => b.lastActive - a.lastActive);
262
+ const recent = state.sessions.slice(0, limit);
263
+ return Promise.all(
264
+ recent.map(async (session) => {
265
+ if (session.status === "loading" && session.lastUserMessage) return session;
266
+ try {
267
+ const preview = await getSessionPreview(session.cwd, session.sessionId);
268
+ return {
269
+ ...session,
270
+ lastUserMessage: preview.lastUserMessage ?? session.lastUserMessage,
271
+ firstMessages: preview.firstMessages,
272
+ lastMessages: preview.lastMessages
273
+ };
274
+ } catch {
275
+ return session;
276
+ }
277
+ })
278
+ );
279
+ }
253
280
  function filterCommandTags(text) {
254
281
  let filtered = text.replace(/<command-[^>]*>[\s\S]*?<\/command-[^>]*>/g, "");
255
282
  filtered = filtered.replace(/<local-command-[^>]*>[\s\S]*?<\/local-command-[^>]*>/g, "");
@@ -512,7 +539,7 @@ function fanout(state, ev) {
512
539
  }
513
540
  }
514
541
  }
515
- function startRun(key, cwd, promptText) {
542
+ function startRun(key, cwd, promptText, runId) {
516
543
  const prev = registry.get(key);
517
544
  if (prev?.status === "running") return false;
518
545
  const prevSeq = prev?.seq ?? seqByKey.get(key) ?? 0;
@@ -520,18 +547,22 @@ function startRun(key, cwd, promptText) {
520
547
  if (prev.evictTimer) clearTimeout(prev.evictTimer);
521
548
  for (const k of prev.keys) registry.delete(k);
522
549
  }
550
+ const startedAt = Date.now();
523
551
  registry.set(key, {
524
552
  keys: /* @__PURE__ */ new Set([key]),
525
553
  cwd,
526
554
  status: "running",
527
555
  seq: prevSeq,
528
556
  events: [],
529
- updatedAt: Date.now()
557
+ startedAt,
558
+ updatedAt: startedAt
530
559
  });
531
560
  if (promptText) {
532
561
  appendRun(key, {
533
562
  type: "user",
534
563
  _human: true,
564
+ ...runId ? { _turnId: runId } : {},
565
+ _ts: startedAt,
535
566
  message: { role: "user", content: promptText }
536
567
  });
537
568
  }
@@ -577,7 +608,7 @@ function markRunIdle(key, status = "idle") {
577
608
  function getRunSnapshot(key) {
578
609
  const r = registry.get(key);
579
610
  if (!r) return null;
580
- return { status: r.status, seq: r.seq, events: r.events.slice() };
611
+ return { status: r.status, seq: r.seq, events: r.events.slice(), startedAt: r.startedAt };
581
612
  }
582
613
  function isRunActive(key) {
583
614
  return registry.get(key)?.status === "running";
@@ -616,7 +647,7 @@ function addRunListener(key, cb) {
616
647
  export {
617
648
  updateGlobalState,
618
649
  getSessionTitle,
619
- getSessionPreview,
650
+ getGlobalSessionsSnapshot,
620
651
  startRun,
621
652
  appendRun,
622
653
  rekeyRun,
@@ -10,7 +10,7 @@ import {
10
10
  setRunAbort,
11
11
  startRun,
12
12
  updateGlobalState
13
- } from "./chunk-VHFWPI2X.mjs";
13
+ } from "./chunk-IQXDXTI7.mjs";
14
14
  import {
15
15
  AgentError,
16
16
  AppRuntime,
@@ -1697,7 +1697,7 @@ async function dispatchChat(spec, body, request) {
1697
1697
  let currentKey = sessionId || runId;
1698
1698
  let actualSessionId = sessionId;
1699
1699
  let isClosed = false;
1700
- if (!startRun(currentKey, cwd || "", promptText)) {
1700
+ if (!startRun(currentKey, cwd || "", promptText, runId)) {
1701
1701
  return { ok: false, status: 409, error: "run already active" };
1702
1702
  }
1703
1703
  setRunAbort(currentKey, () => {
@@ -2593,7 +2593,7 @@ import { streamText, stepCountIs } from "ai";
2593
2593
  import { randomUUID as randomUUID3 } from "crypto";
2594
2594
 
2595
2595
  // packages/feature/agent/src/server/engines/ollama/session.ts
2596
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync, mkdirSync as mkdirSync2, appendFileSync } from "fs";
2596
+ import { readFileSync as readFileSync3, existsSync, mkdirSync as mkdirSync2, appendFileSync } from "fs";
2597
2597
  import { join as join3 } from "path";
2598
2598
  var SESSIONS_ROOT = join3(COCKPIT_DIR, "ollama-sessions");
2599
2599
  function getSessionDir(cwd) {
@@ -2606,22 +2606,16 @@ function readSessionMessages(cwd, sessionId) {
2606
2606
  const path2 = getSessionPath(cwd, sessionId);
2607
2607
  if (!existsSync(path2)) return [];
2608
2608
  const lines = readFileSync3(path2, "utf-8").split("\n").filter(Boolean);
2609
- const legacyMessages = [];
2610
2609
  const transcriptEntries = [];
2611
2610
  for (const line of lines) {
2612
2611
  try {
2613
2612
  const obj = JSON.parse(line);
2614
- if (typeof obj.role === "string" && "content" in obj) {
2615
- legacyMessages.push(obj);
2616
- continue;
2617
- }
2618
2613
  if (typeof obj.type === "string") {
2619
2614
  transcriptEntries.push(obj);
2620
2615
  }
2621
2616
  } catch {
2622
2617
  }
2623
2618
  }
2624
- if (transcriptEntries.length === 0) return legacyMessages;
2625
2619
  const toolNameById = /* @__PURE__ */ new Map();
2626
2620
  const toolCallIds = /* @__PURE__ */ new Set();
2627
2621
  const toolResultIds = /* @__PURE__ */ new Set();
@@ -16516,7 +16510,7 @@ function date4(params) {
16516
16510
  config(en_default());
16517
16511
 
16518
16512
  // packages/feature/agent/src/server/engines/ollama/tools.ts
16519
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
16513
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
16520
16514
  import { writeFile, mkdir, readdir, stat, unlink } from "fs/promises";
16521
16515
  import { isAbsolute, join as join4 } from "path";
16522
16516
  import { tmpdir as tmpdir2 } from "os";
@@ -16638,7 +16632,7 @@ function createTools(context) {
16638
16632
  execute: async ({ file_path, content }) => {
16639
16633
  try {
16640
16634
  if (!isAbsolute(file_path)) return "Error: file_path must be an absolute path.";
16641
- writeFileSync4(file_path, content, "utf-8");
16635
+ writeFileSync3(file_path, content, "utf-8");
16642
16636
  return `File written: ${file_path}`;
16643
16637
  } catch (err) {
16644
16638
  return `Error writing file: ${err.message}`;
@@ -16669,7 +16663,7 @@ function createTools(context) {
16669
16663
  return `Error: old_string not found in ${file_path}. The file may have changed.`;
16670
16664
  }
16671
16665
  const updated = replace_all ? content.split(old_string).join(new_string) : content.replace(old_string, new_string);
16672
- writeFileSync4(file_path, updated, "utf-8");
16666
+ writeFileSync3(file_path, updated, "utf-8");
16673
16667
  return `File edited: ${file_path}`;
16674
16668
  } catch (err) {
16675
16669
  return `Error editing file: ${err.message}`;
@@ -17252,7 +17246,7 @@ ${block.think}
17252
17246
  import { spawn as spawn3 } from "child_process";
17253
17247
  import { randomUUID as randomUUID5 } from "crypto";
17254
17248
  import { createInterface as createInterface2 } from "readline";
17255
- import { writeFileSync as writeFileSync5, unlinkSync as unlinkSync2, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "fs";
17249
+ import { writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "fs";
17256
17250
  import { join as join6 } from "path";
17257
17251
  import { tmpdir as tmpdir3 } from "os";
17258
17252
  var MEDIA_EXT = {
@@ -17267,7 +17261,7 @@ function writeImagesToTemp(images) {
17267
17261
  return images.map((img, i) => {
17268
17262
  const ext = MEDIA_EXT[img.media_type] || ".png";
17269
17263
  const filePath = join6(dir, `img-${Date.now()}-${i}${ext}`);
17270
- writeFileSync5(filePath, Buffer.from(img.data, "base64"));
17264
+ writeFileSync4(filePath, Buffer.from(img.data, "base64"));
17271
17265
  return filePath;
17272
17266
  });
17273
17267
  }
package/dist/wsServer.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  addRunListener,
3
- getRunSnapshot,
4
- getSessionPreview
5
- } from "./chunk-VHFWPI2X.mjs";
3
+ getGlobalSessionsSnapshot,
4
+ getRunSnapshot
5
+ } from "./chunk-IQXDXTI7.mjs";
6
6
  import {
7
7
  addExitListener,
8
8
  addOutputListener,
@@ -32,8 +32,7 @@ import {
32
32
  Schedule_exports,
33
33
  Stream_exports,
34
34
  ValidationError,
35
- WSError,
36
- readJsonFile
35
+ WSError
37
36
  } from "./chunk-GFI4IHYM.mjs";
38
37
  import "./chunk-7P6ASYW6.mjs";
39
38
 
@@ -110,34 +109,10 @@ function getDefaultPath() {
110
109
  // src/lib/effect/globalStateHandler.ts
111
110
  var HEARTBEAT_INTERVAL = Schedule_exports.spaced("30 seconds");
112
111
  var sendGlobalState = (conn) => Effect_exports.gen(function* () {
113
- const state = yield* Effect_exports.tryPromise({
114
- try: () => readJsonFile(GLOBAL_STATE_FILE, { sessions: [] }),
112
+ const sessions = yield* Effect_exports.tryPromise({
113
+ try: () => getGlobalSessionsSnapshot(),
115
114
  catch: (cause) => new FSError({ path: GLOBAL_STATE_FILE, op: "read", cause })
116
115
  });
117
- for (const s of state.sessions) {
118
- if (!s.status) {
119
- const legacy = s;
120
- s.status = legacy.isLoading ? "loading" : "normal";
121
- }
122
- }
123
- state.sessions.sort((a, b) => b.lastActive - a.lastActive);
124
- const recent = state.sessions.slice(0, 15);
125
- const sessions = yield* Effect_exports.forEach(
126
- recent,
127
- (session) => session.status === "loading" && session.lastUserMessage ? Effect_exports.succeed(session) : Effect_exports.tryPromise({
128
- try: () => getSessionPreview(session.cwd, session.sessionId),
129
- catch: () => void 0
130
- }).pipe(
131
- Effect_exports.map((preview) => ({
132
- ...session,
133
- lastUserMessage: preview.lastUserMessage ?? session.lastUserMessage,
134
- firstMessages: preview.firstMessages,
135
- lastMessages: preview.lastMessages
136
- })),
137
- Effect_exports.orElseSucceed(() => session)
138
- ),
139
- { concurrency: "unbounded" }
140
- );
141
116
  yield* conn.send({ type: "global-state", data: { sessions } });
142
117
  });
143
118
  var watchStateFile = (trigger) => Effect_exports.acquireRelease(
@@ -1211,6 +1186,9 @@ var handleSessionStream = (conn, sessionId) => Effect_exports.gen(function* () {
1211
1186
  type: "run-snapshot",
1212
1187
  status: snap.status,
1213
1188
  seq: snap.seq,
1189
+ // Turn time-boundary: lets clients cut the in-flight turn's disk image by
1190
+ // message timestamp instead of by prompt text (see useLiveStream).
1191
+ startedAt: snap.startedAt,
1214
1192
  events: snap.events
1215
1193
  });
1216
1194
  } else {
package/next.config.mjs CHANGED
@@ -12,8 +12,9 @@ const nextConfig = {
12
12
  // dev 和 prod 使用不同输出目录,避免 Turbopack 热更新影响 prod
13
13
  distDir: dev ? '.next' : '.next-prod',
14
14
  // Allow loading dev resources (HMR, /_next/*) through tunnels — needed to test
15
- // the mobile /m route on a real phone via ngrok. Dev-only; wildcards cover the
16
- // rotating ngrok hostnames so we don't have to edit this each session.
15
+ // the mobile /m route on a real phone via ngrok / Cloudflare quick tunnels.
16
+ // Dev-only; wildcards cover the rotating tunnel hostnames so we don't have to
17
+ // edit this each session.
17
18
  ...(dev
18
19
  ? {
19
20
  allowedDevOrigins: [
@@ -21,6 +22,7 @@ const nextConfig = {
21
22
  '*.ngrok-free.app',
22
23
  '*.ngrok.app',
23
24
  '*.ngrok.io',
25
+ '*.trycloudflare.com',
24
26
  ],
25
27
  }
26
28
  : {}),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@surething/cockpit",
3
- "version": "1.0.233",
4
- "description": "Claude Code GUI for parallel AI coding drive Claude (default), Codex, DeepSeek, Kimi, or local Ollama in tabs. Multi-project sessions, terminal, browser & DB bubbles, code review, slash modes. Local-first, MIT.",
3
+ "version": "1.0.235",
4
+ "description": "The open-source Claude Code GUI — an IDE-like workbench for parallel AI coding. Drive Claude (default), Codex, DeepSeek, Kimi, or local Ollama in tabs. Terminal, browser & DB bubbles, code review. Self-host on a shared dev box — a seat for every teammate. Local-first, MIT.",
5
5
  "author": "Robert",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -38,9 +38,10 @@
38
38
  "ai-code-review",
39
39
  "agent-sdk",
40
40
  "coding-agent",
41
- "parallel-ai",
41
+ "self-hosted",
42
+ "web-ide",
43
+ "remote-development",
42
44
  "multi-project-ai",
43
- "cockpit",
44
45
  "opencockpit",
45
46
  "cursor-alternative",
46
47
  "aider-alternative",
package/public/boot.js CHANGED
@@ -14,7 +14,15 @@
14
14
  var onMobileRoute = path === '/m' || path.indexOf('/m/') === 0;
15
15
  var forceDesktop = false;
16
16
  try { forceDesktop = !!localStorage.getItem('cockpit-force-desktop'); } catch (_e) {}
17
+ // Never redirect inside an iframe: the desktop shell embeds /project in a
18
+ // frame that is 42px (sidebar) narrower than the window, so a tablet-width
19
+ // top page (e.g. 768px iPad) would otherwise nest the mobile UI inside the
20
+ // desktop shell — and drop the sessionId in the process. The media query is
21
+ // only meaningful for the top-level viewport.
22
+ var isTopWindow = true;
23
+ try { isTopWindow = window.self === window.top; } catch (_e) {}
17
24
  if (
25
+ isTopWindow &&
18
26
  !onMobileRoute &&
19
27
  !forceDesktop &&
20
28
  window.matchMedia &&
package/server.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createServer } from 'http';
2
+ import { createGzip, constants as zlibConstants } from 'zlib';
2
3
  import { exec, execSync } from 'child_process';
3
4
  import { networkInterfaces, homedir } from 'os';
4
5
  import { writeFileSync, mkdirSync, readFileSync, realpathSync } from 'fs';
@@ -53,17 +54,22 @@ async function ensureSingleInstance() {
53
54
  }
54
55
 
55
56
  // ============================================
56
- // 进程生命周期防护
57
+ // Process lifecycle guards
57
58
  //
58
- // 1) 父进程死亡后 stdout/stderr 管道断裂,Next.js 的 uncaughtException handler
59
- // 会尝试 console.log 报错 stdout EPIPE再次触发 handler → CPU 死循环
60
- // 在管道错误升级为 uncaughtException 之前拦截,直接退出。
59
+ // 1) When the parent process dies, the stdout/stderr pipes break. Next.js's
60
+ // uncaughtException handler then tries to console.log the errorwrites
61
+ // to stdout → EPIPE → triggers the handler again → CPU spin loop.
62
+ // Intercept pipe errors before they escalate to uncaughtException and
63
+ // exit immediately.
61
64
  //
62
- // 2) Next.js dev 模式下会在自己的子进程里跑 `next-server` workerturbopack)。
63
- // 如果父进程被异常杀掉(npm reinstall、Ctrl+C npm 包了一层、IDE 杀任务等),
64
- // next-server 子进程不会跟着死,而是会失去父进程后**重新绑定到 next 自带的默认
65
- // 端口 3000**,然后把后续 `npm run dev` 卡死(Next 通过 .next/dev/logs 检测到
66
- // "已有 dev server 在跑"而拒绝再启)。所以父进程退出前必须显式杀掉所有直接子进程。
65
+ // 2) In dev mode Next.js runs a `next-server` worker (turbopack) in its own
66
+ // child process. If the parent is killed abnormally (npm reinstall,
67
+ // Ctrl+C through an npm wrapper, IDE killing the task, etc.), the
68
+ // next-server child doesn't die with it having lost its parent it
69
+ // **re-binds to Next's default port 3000** and then wedges every later
70
+ // `npm run dev` (Next detects "a dev server is already running" via
71
+ // .next/dev/logs and refuses to start). So the parent must explicitly
72
+ // kill all direct children before exiting.
67
73
  // ============================================
68
74
  // Assigned once the server-side bundle is loaded (see app.prepare below). Lets the
69
75
  // synchronous exit hook flush live PTY scrollback to disk so a graceful restart
@@ -75,7 +81,8 @@ function killChildren() {
75
81
  if (_cleanupRan) return;
76
82
  _cleanupRan = true;
77
83
  if (process.platform === 'win32') {
78
- // Windows: wmic 列出子 PID 再逐个 taskkill /F /T,避免 /T 把自己也带走
84
+ // Windows: list child PIDs via wmic, then taskkill /F /T each one —
85
+ // running /T on ourselves would take this process down too
79
86
  try {
80
87
  const out = execSync(`wmic process where (ParentProcessId=${process.pid}) get ProcessId /value`, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
81
88
  const pids = (out.match(/ProcessId=(\d+)/g) || []).map(s => s.split('=')[1]).filter(Boolean);
@@ -85,34 +92,39 @@ function killChildren() {
85
92
  } catch {}
86
93
  return;
87
94
  }
88
- // POSIX: pkill -P 只杀直接子进程,不递归(next-server 等都是直接子进程,足够)
89
- // pkill 在没有匹配到任何进程时返回 1,不当作错误
95
+ // POSIX: pkill -P kills direct children only, no recursion (next-server
96
+ // and friends are all direct children — enough).
97
+ // pkill exits 1 when nothing matches; not treated as an error.
90
98
  try { execSync(`pkill -TERM -P ${process.pid}`, { stdio: 'ignore' }); } catch {}
91
99
  }
92
100
 
93
- // 正常退出路径(包括所有的 process.exit() 调用)—— Node 保证此 handler 同步执行。
94
- // 所有信号/异常路径最终都走 process.exit() 触发 'exit' 这一个点覆盖全部优雅退出。
95
- // 先把活着的 PTY 滚动缓冲同步落盘,再杀子进程(flush 读的是本进程内存,与子进程无关)。
101
+ // Normal exit path (including every process.exit() call) Node guarantees
102
+ // this handler runs synchronously. All signal/exception paths ultimately go
103
+ // through process.exit() 'exit' fires → this single hook covers every
104
+ // graceful shutdown. Flush live PTY scrollback to disk first, then kill the
105
+ // children (the flush reads this process's own memory — independent of them).
96
106
  process.on('exit', () => {
97
107
  try { flushRunningSync?.(); } catch {}
98
108
  killChildren();
99
109
  });
100
110
 
101
- // 信号路径 —— 先杀子进程再退出,并让 shell 看到正确的退出码
111
+ // Signal paths kill children first, then exit with the code the shell expects
102
112
  const cleanupAndExit = (code) => () => { killChildren(); process.exit(code); };
103
113
  process.on('SIGINT', cleanupAndExit(130));
104
114
  process.on('SIGTERM', cleanupAndExit(143));
105
115
  process.on('SIGQUIT', cleanupAndExit(131));
106
116
  process.on('SIGHUP', cleanupAndExit(0));
107
117
 
108
- // 未捕获异常 —— 不能让 Next.js 的默认 handler 再走 console.log 触发 EPIPE 死循环
118
+ // Uncaught exceptions don't let Next.js's default handler console.log its
119
+ // way back into the EPIPE spin loop
109
120
  process.on('uncaughtException', (err) => {
110
121
  try { console.error('uncaughtException:', err); } catch {}
111
122
  killChildren();
112
123
  process.exit(1);
113
124
  });
114
125
 
115
- // stdout/stderr 管道断裂立刻退出(exit handler 会顺手清子进程)
126
+ // Broken stdout/stderr pipe → exit immediately (the exit handler cleans up
127
+ // the children on the way out)
116
128
  process.stdout.on('error', (err) => {
117
129
  if (err.code === 'EPIPE' || err.code === 'ERR_STREAM_DESTROYED') process.exit(0);
118
130
  });
@@ -134,7 +146,7 @@ app.prepare().then(async () => {
134
146
  flushRunningSync = httpApi.flushAllRunningSync || null;
135
147
  const { scheduledTaskManager } = await import(dev ? '@cockpit/feature-agent/server/scheduledTasks' : './dist/scheduledTasks.mjs');
136
148
 
137
- // 初始化定时任务管理器
149
+ // Initialize the scheduled-task manager
138
150
  scheduledTaskManager.setOnTaskFired((task) => {
139
151
  broadcastToGlobalState({ type: 'task-fired', taskId: task.id, cwd: task.cwd, tabId: task.tabId, sessionId: task.sessionId });
140
152
  });
@@ -176,10 +188,92 @@ app.prepare().then(async () => {
176
188
  return false;
177
189
  };
178
190
 
191
+ // ============================================
192
+ // /api/* JSON gzip — Next's built-in compression only runs under
193
+ // `next start`; with this custom server, API JSON goes out uncompressed
194
+ // (fine locally at <10ms, but behind a tunnel like ngrok a 200KB+
195
+ // session-by-path response means seconds of latency). Transparently wrap
196
+ // application/json responses in gzip; SSE / HTML / static assets (which
197
+ // Next already compresses) are untouched.
198
+ // ============================================
199
+ const gzipJsonResponse = (req, res) => {
200
+ if (!/\bgzip\b/i.test(String(req.headers['accept-encoding'] || ''))) return;
201
+ const origWriteHead = res.writeHead.bind(res);
202
+ const origWrite = res.write.bind(res);
203
+ const origEnd = res.end.bind(res);
204
+ let gzip = null;
205
+ let decided = false;
206
+
207
+ // Decide at first output (writeHead/write/end): only compress JSON that
208
+ // isn't already encoded.
209
+ const decide = () => {
210
+ if (decided) return;
211
+ decided = true;
212
+ const ct = String(res.getHeader('content-type') || '');
213
+ if (!ct.includes('application/json') || res.getHeader('content-encoding')) return;
214
+ res.removeHeader('content-length');
215
+ res.setHeader('content-encoding', 'gzip');
216
+ const vary = String(res.getHeader('vary') || '');
217
+ if (!/\baccept-encoding\b/i.test(vary)) {
218
+ res.setHeader('vary', vary ? `${vary}, Accept-Encoding` : 'Accept-Encoding');
219
+ }
220
+ gzip = createGzip({ flush: zlibConstants.Z_SYNC_FLUSH });
221
+ gzip.on('data', (chunk) => origWrite(chunk));
222
+ gzip.on('end', () => origEnd());
223
+ gzip.on('error', () => { try { origEnd(); } catch {} });
224
+ };
225
+
226
+ // writeHead accepts headers in three shapes: object, flat array
227
+ // [k1,v1,k2,v2,...] (what Next uses internally), and nested array
228
+ // [[k,v],...]. Normalize all of them through setHeader before deciding
229
+ // on compression.
230
+ const applyHeaders = (h) => {
231
+ if (!h) return;
232
+ if (Array.isArray(h)) {
233
+ const pairs = Array.isArray(h[0])
234
+ ? h
235
+ : Array.from({ length: h.length >> 1 }, (_, i) => [h[i * 2], h[i * 2 + 1]]);
236
+ for (const [k, v] of pairs) {
237
+ if (k === undefined || v === undefined) continue;
238
+ const key = String(k);
239
+ const prev = res.getHeader(key);
240
+ // Merge duplicate headers (e.g. set-cookie) into an array instead of overwriting
241
+ res.setHeader(key, prev === undefined ? v : [].concat(prev, v));
242
+ }
243
+ } else {
244
+ for (const [k, v] of Object.entries(h)) {
245
+ if (v !== undefined) res.setHeader(k, v);
246
+ }
247
+ }
248
+ };
249
+
250
+ res.writeHead = (status, arg2, arg3) => {
251
+ applyHeaders(typeof arg2 === 'object' ? arg2 : arg3);
252
+ decide();
253
+ return typeof arg2 === 'string' ? origWriteHead(status, arg2) : origWriteHead(status);
254
+ };
255
+ res.write = (chunk, ...args) => {
256
+ decide();
257
+ if (gzip) { gzip.write(chunk); return true; }
258
+ return origWrite(chunk, ...args);
259
+ };
260
+ res.end = (chunk, ...args) => {
261
+ decide();
262
+ if (gzip) {
263
+ if (chunk && typeof chunk !== 'function') gzip.write(chunk);
264
+ gzip.end();
265
+ return res;
266
+ }
267
+ return origEnd(chunk, ...args);
268
+ };
269
+ };
270
+
179
271
  const server = createServer(async (req, res) => {
180
272
  if (applyHttpGate(req, res)) return;
273
+ if (req.url?.startsWith('/api/')) gzipJsonResponse(req, res);
181
274
 
182
- // /api/browser/* 必须在自定义 server 中处理(与 WS 共享 BrowserBridge 内存)
275
+ // /api/browser/* must be handled inside the custom server (it shares
276
+ // BrowserBridge memory with the WS side)
183
277
  if (req.url?.startsWith('/api/browser/') && req.method === 'POST') {
184
278
  const handled = await handleBrowserApi(req, res);
185
279
  if (handled) return;
@@ -207,19 +301,20 @@ app.prepare().then(async () => {
207
301
  }
208
302
  });
209
303
 
210
- // COCKPIT_HOST: 默认 127.0.0.1(本地),云沙盒等场景设为 0.0.0.0
304
+ // COCKPIT_HOST: defaults to 127.0.0.1 (local-only); set 0.0.0.0 for cloud
305
+ // sandboxes and similar environments
211
306
  const host = process.env.COCKPIT_HOST || '127.0.0.1';
212
307
  server.listen(port, host, () => {
213
308
  const url = `http://localhost:${port}`;
214
309
  console.log(`> Ready on ${url}`);
215
310
 
216
- // 写入 server.json CLI 子命令读取端口
311
+ // Write server.json so CLI subcommands can read the port
217
312
  try {
218
313
  mkdirSync(cockpitHome, { recursive: true });
219
314
  writeFileSync(join(cockpitHome, 'server.json'), JSON.stringify({ pid: process.pid, port }, null, 2));
220
315
  } catch {}
221
316
 
222
- // prod 模式自动打开浏览器(--no-open 禁用)
317
+ // Auto-open the browser in prod mode (disable with --no-open)
223
318
  if (!dev && !process.env.COCKPIT_NO_OPEN) {
224
319
  const openProject = process.env.COCKPIT_OPEN_PROJECT;
225
320
  const openUrl = openProject ? `${url}/?cwd=${encodeURIComponent(openProject)}` : url;
@@ -229,8 +324,8 @@ app.prepare().then(async () => {
229
324
  });
230
325
 
231
326
  // ============================================
232
- // Share Server - LAN 分享评审服务
233
- // 路由白名单:仅开放 /review/* 和相关资源
327
+ // Share Server LAN review-sharing service.
328
+ // Route allowlist: only /review/* and its supporting assets are exposed.
234
329
  // ============================================
235
330
  const sharePort = port + 1000; // dev 3456→4456, prod 3457→4457
236
331
 
@@ -260,8 +355,9 @@ app.prepare().then(async () => {
260
355
  // Token gate first (share is also covered). Read the gate BEFORE we inject
261
356
  // x-forwarded-for below, so the injection can't fool the local check.
262
357
  if (applyHttpGate(req, res)) return;
358
+ if (req.url?.startsWith('/api/')) gzipJsonResponse(req, res);
263
359
  if (isShareAllowed(req.url || '')) {
264
- // 注入客户端真实 IP,供 /api/review/identify 使用
360
+ // Inject the client's real IP for /api/review/identify
265
361
  const clientIp = req.socket.remoteAddress || '';
266
362
  req.headers['x-forwarded-for'] = clientIp;
267
363
  handle(req, res);
@@ -1 +0,0 @@
1
- "use strict";exports.id=7003,exports.ids=[7003],exports.modules={7939:(a,b,c)=>{c.d(b,{$A:()=>M,A0:()=>X,An:()=>$,BF:()=>I,Ct:()=>Q,Eu:()=>H,Ex:()=>u,GB:()=>r,JE:()=>Z,JY:()=>L,Ki:()=>C,Kk:()=>R,Kv:()=>j,LN:()=>k,Mx:()=>D,N9:()=>ac,O0:()=>B,Sr:()=>J,Tb:()=>s,Ux:()=>ab,Wd:()=>Y,aO:()=>m,at:()=>y,ec:()=>o,eg:()=>V,h9:()=>x,i4:()=>G,iK:()=>p,im:()=>T,iy:()=>P,jp:()=>t,lT:()=>z,lq:()=>F,ls:()=>K,mT:()=>w,n6:()=>n,oK:()=>O,oZ:()=>S,of:()=>E,po:()=>N,tP:()=>q,th:()=>v,v4:()=>l,vI:()=>W,yw:()=>U});var d=c(21820),e=c(33873),f=c(79748),g=c(29021),h=c(79646);let i=(0,d.homedir)(),j=process.env.COCKPIT_HOME?(0,e.resolve)(process.env.COCKPIT_HOME.replace(/^~(?=$|\/)/,i)):(0,e.join)(i,".cockpit"),k=(0,e.join)(j,"projects"),l=(0,e.join)(j,"state.json"),m=(0,e.join)(j,"pinned-sessions.json"),n=(0,e.join)(j,"push-subscriptions.json"),o=(0,e.join)(j,"note.md"),p=(0,e.join)(j,"scheduled-tasks.json"),q=(0,e.join)(j,"settings.json"),r=(0,e.join)(j,"skills.json"),s=(0,e.join)(j,"review"),t=(0,e.join)(s,"_signal");function u(){try{(0,g.existsSync)(s)||(0,g.mkdirSync)(s,{recursive:!0}),(0,g.writeFileSync)(t,Date.now().toString())}catch{}}let v=(0,e.join)(i,".claude"),w=(0,e.join)(v,"projects"),x=(0,e.join)(i,".claude2"),y=(0,e.join)(x,"projects"),z=(0,e.join)(j,"deepseek"),A=(0,e.join)(z,"projects");function B(a){return a.replace(/[/.]/g,"-")}function C(a){return(0,e.join)(k,B(a))}function D(a){return(0,e.join)(C(a),"session.json")}function E(a){return(0,e.join)(C(a),"recent-files.json")}function F(a){return(0,e.join)(C(a),"expanded-paths.json")}function G(a){return(0,e.join)(C(a),"comments.json")}function H(a){return(0,e.join)(C(a),"services.json")}function I(a){return(0,e.join)(C(a),"note.md")}function J(a,b){return(0,e.join)(C(a),`terminal-history-${b}.jsonl`)}function K(a,b){return(0,e.join)(C(a),`terminal-output-${b}.txt`)}function L(a,b){let c=b?`terminal-env-${b}.json`:"terminal-env-global.json";return(0,e.join)(C(a),c)}function M(){return(0,e.join)(j,"terminal-aliases.json")}function N(){return(0,e.join)(j,"services.json")}function O(a){return(0,e.join)(C(a),"project-settings.json")}function P(a,b){return(0,e.join)(C(a),`terminal-bubble-order-${b}.json`)}function Q(a){return(0,e.join)(s,`${a}.json`)}function R(a,b){return(0,e.join)((0,e.join)(w,B(a)),`${b}.jsonl`)}function S(a,b){return(0,e.join)((0,e.join)(y,B(a)),`${b}.jsonl`)}function T(a,b){return(0,e.join)((0,e.join)(A,B(a)),`${b}.jsonl`)}function U(a,b){return(0,e.join)((0,e.join)(j,"ollama-sessions",B(a)),`${b}.jsonl`)}function V(a){let b=(0,e.join)(i,".codex","sessions");if(!(0,g.existsSync)(b))return null;try{let c=(0,h.execSync)(`find ${JSON.stringify(b)} -name "*${a}.jsonl" -type f 2>/dev/null`,{encoding:"utf8",timeout:3e3}).trim();if(c)return c.split("\n")[0]}catch{}return null}function W(a){let b=(0,e.join)(i,".kimi","sessions");if(!(0,g.existsSync)(b))return null;try{for(let c of(0,g.readdirSync)(b)){let d=(0,e.join)(b,c,a,"context.jsonl");if((0,g.existsSync)(d))return d}}catch{}return null}async function X(a){(0,g.existsSync)(a)||await (0,f.mkdir)(a,{recursive:!0})}async function Y(a){let b=(0,e.join)(a,"..");await X(b)}async function Z(a,b){try{let b=await (0,f.readFile)(a,"utf-8");return JSON.parse(b)}catch{return b}}async function $(a,b){await Y(a),await (0,f.writeFile)(a,JSON.stringify(b,null,2),"utf-8")}let _=globalThis,aa=_.__cockpitFileLocks??(_.__cockpitFileLocks=new Map);function ab(a,b){let c=(aa.get(a)??Promise.resolve()).then(b),d=c.then(()=>{},()=>{});return aa.set(a,d),d.then(()=>{aa.get(a)===d&&aa.delete(a)}),c}function ac(a,b,c){return ab(a,async()=>{let d=await Z(a,b),e=await c(d);return await $(a,e),e})}},38433:(a,b,c)=>{c.d(b,{SG:()=>m,a_:()=>l,hO:()=>k,lG:()=>j});var d=c(51181),e=c.n(d),f=c(7939);let g=null,h="https://github.com/Surething-io/cockpit";async function i(){if(g)return g;let a=await (0,f.Ux)(f.tP,async()=>{let a=await (0,f.JE)(f.tP,{});if(a.push?.publicKey&&a.push?.privateKey){let b=!function(a){if(!a||a.includes("localhost"))return!1;if(a.startsWith("https://"))return!0;let b=/^mailto:[^\s@]+@([^\s@]+)$/.exec(a);return!!b&&b[1].includes(".")}(a.push.subject)?h:a.push.subject;return b!==a.push.subject&&await (0,f.An)(f.tP,{...a,push:{...a.push,subject:b}}),{publicKey:a.push.publicKey,privateKey:a.push.privateKey,subject:b}}let b=e().generateVAPIDKeys(),c={publicKey:b.publicKey,privateKey:b.privateKey,subject:h};return await (0,f.An)(f.tP,{...a,push:c}),c});return e().setVapidDetails(a.subject,a.publicKey,a.privateKey),g=a,a}async function j(){return(await i()).publicKey}async function k(a){await (0,f.Ux)(f.n6,async()=>{let b=await (0,f.JE)(f.n6,{subscriptions:[]});b.subscriptions.some(b=>b.endpoint===a.endpoint)||(b.subscriptions.push(a),await (0,f.An)(f.n6,b))})}async function l(a){await (0,f.Ux)(f.n6,async()=>{let b=await (0,f.JE)(f.n6,{subscriptions:[]}),c=b.subscriptions.filter(b=>b.endpoint!==a);c.length!==b.subscriptions.length&&await (0,f.An)(f.n6,{subscriptions:c})})}async function m(a){try{await i()}catch{return{sent:0,pruned:0}}let b=await (0,f.JE)(f.n6,{subscriptions:[]});if(0===b.subscriptions.length)return{sent:0,pruned:0};let c=JSON.stringify(a),d=[],g=0;return await Promise.all(b.subscriptions.map(async a=>{try{await e().sendNotification(a,c),g++}catch(c){let b=c?.statusCode;if(404===b||410===b)d.push(a.endpoint);else{let d=a.endpoint;try{d=new URL(a.endpoint).host}catch{}console.error(`[push] send failed (${b??"?"}) to ${d}: ${c?.body||c?.message||"unknown"}`)}}})),d.length&&await (0,f.Ux)(f.n6,async()=>{let a=await (0,f.JE)(f.n6,{subscriptions:[]});await (0,f.An)(f.n6,{subscriptions:a.subscriptions.filter(a=>!d.includes(a.endpoint))})}),{sent:g,pruned:d.length}}},72891:(a,b,c)=>{c.a(a,async(a,d)=>{try{c.d(b,{OZ:()=>o,RL:()=>n,k9:()=>p,ok:()=>l});var e=c(37625),f=c(52693),g=c(18412),h=c(89705),i=c(94506),j=c(770),k=a([i]);i=(k.then?(await k)():k)[0];let l=(a,b=200)=>new Response(JSON.stringify(a),{status:b,headers:{"content-type":"application/json"}}),m=a=>{let b=e.od(a);if(f.Ru(b)){let a=b.value,c=(0,j.Sh)(a);return new Response(JSON.stringify({error:(a=>{let b=a.message;if("string"==typeof b&&b.length>0)return b;let c=a.cause;if(c instanceof Error&&c.message.length>0)return c.message;switch(a._tag){case"ValidationError":return`Invalid ${a.field}: ${a.reason}`;case"NotFoundError":return`${a.resource} not found: ${a.id}`;case"PermissionError":return`Permission denied: ${a.action} on ${a.resource}`;case"DBError":return`${a.db} ${a.op} failed`;case"FSError":return`${a.op} ${a.path} failed`;case"WSError":return`${a.proto} ${a.kind} failed`;case"AgentError":return`${a.provider} ${a.kind} failed`;default:return a._tag}})(a),tag:a._tag}),{status:c,headers:{"content-type":"application/json"}})}return console.error("[handler] uncaught defect:\n"+e.j9(a)),new Response(JSON.stringify({error:"Internal Server Error",tag:"InternalError"}),{status:500,headers:{"content-type":"application/json"}})},n=a=>async b=>{let c=await i.s9.runPromiseExit(a(b));return g.YW(c,{onFailure:a=>m(a),onSuccess:a=>a})},o=a=>async(b,c)=>{let d=await c.params,e=await i.s9.runPromiseExit(a(b,d));return g.YW(e,{onFailure:a=>m(a),onSuccess:a=>a})},p=a=>h.$mh({try:()=>a.json(),catch:()=>new j.yI({field:"body",reason:"invalid JSON"})});d()}catch(a){d(a)}})}};