@surething/cockpit 1.0.217 → 1.0.218

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 (63) 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 +2 -2
  4. package/.next-prod/prerender-manifest.json +3 -3
  5. package/.next-prod/server/app/_global-error/page_client-reference-manifest.js +1 -1
  6. package/.next-prod/server/app/_global-error.html +1 -1
  7. package/.next-prod/server/app/_global-error.rsc +1 -1
  8. package/.next-prod/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/.next-prod/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  10. package/.next-prod/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  11. package/.next-prod/server/app/_global-error.segments/_head.segment.rsc +1 -1
  12. package/.next-prod/server/app/_global-error.segments/_index.segment.rsc +1 -1
  13. package/.next-prod/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next-prod/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/.next-prod/server/app/_not-found.html +1 -1
  16. package/.next-prod/server/app/_not-found.rsc +3 -3
  17. package/.next-prod/server/app/_not-found.segments/_full.segment.rsc +3 -3
  18. package/.next-prod/server/app/_not-found.segments/_head.segment.rsc +1 -1
  19. package/.next-prod/server/app/_not-found.segments/_index.segment.rsc +3 -3
  20. package/.next-prod/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  21. package/.next-prod/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  22. package/.next-prod/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  23. package/.next-prod/server/app/api/chat/deepseek/route.js +1 -1
  24. package/.next-prod/server/app/api/chat/route.js +1 -1
  25. package/.next-prod/server/app/api/extension/version/route.js.nft.json +1 -1
  26. package/.next-prod/server/app/api/projectGraph/file-functions/route.js +1 -1
  27. package/.next-prod/server/app/api/scheduled-tasks/route.js +1 -1
  28. package/.next-prod/server/app/page_client-reference-manifest.js +1 -1
  29. package/.next-prod/server/app/project/page_client-reference-manifest.js +1 -1
  30. package/.next-prod/server/app/review/[id]/page_client-reference-manifest.js +1 -1
  31. package/.next-prod/server/app-paths-manifest.json +3 -3
  32. package/.next-prod/server/chunks/2939.js +1 -1
  33. package/.next-prod/server/chunks/8916.js +1 -1
  34. package/.next-prod/server/chunks/9658.js +5 -5
  35. package/.next-prod/server/middleware-build-manifest.js +1 -1
  36. package/.next-prod/server/pages/404.html +1 -1
  37. package/.next-prod/server/pages/500.html +1 -1
  38. package/.next-prod/server/server-reference-manifest.json +1 -1
  39. package/.next-prod/static/chunks/6345-2637497e8b101740.js +14 -0
  40. package/.next-prod/static/chunks/{6917-0a22d7764ca45244.js → 6917-ed0e9c62a123d529.js} +2 -2
  41. package/.next-prod/static/chunks/app/{layout-8e3a54b794cb35b6.js → layout-1659a95e6c4a6bb5.js} +1 -1
  42. package/.next-prod/static/chunks/app/{page-3ab0a0f28cbdc8e2.js → page-afcbd897b4c3600f.js} +1 -1
  43. package/.next-prod/static/chunks/app/project/{page-3ab0a0f28cbdc8e2.js → page-afcbd897b4c3600f.js} +1 -1
  44. package/.next-prod/static/css/f4a773117ca8af75.css +1 -0
  45. package/.next-prod/trace +13 -13
  46. package/.next-prod/trace-build +1 -1
  47. package/README.md +5 -5
  48. package/README.zh.md +5 -5
  49. package/bin/cock-browser.messages.mjs +176 -0
  50. package/bin/cock-browser.mjs +290 -18
  51. package/chrome-extension/automation.js +684 -32
  52. package/chrome-extension/manifest.json +1 -1
  53. package/chrome-extension/messages.js +45 -0
  54. package/dist/{chunk-W6G6X3FP.mjs → chunk-WOM47O75.mjs} +49 -1
  55. package/dist/httpApi.mjs +66 -6
  56. package/dist/scheduledTasks.mjs +6 -1
  57. package/dist/{server-ZBUZ24TC.mjs → server-SNB4H35J.mjs} +5 -1
  58. package/dist/wsServer.mjs +5 -2
  59. package/package.json +3 -5
  60. package/.next-prod/static/chunks/6345-d477b8d5c682b1fb.js +0 -14
  61. package/.next-prod/static/css/fc2730c2dbe4866e.css +0 -1
  62. /package/.next-prod/static/{7pu1LXbRRLfg05VN3u39s → bOkuiIr_nWzG5GjPLNqdN}/_buildManifest.js +0 -0
  63. /package/.next-prod/static/{7pu1LXbRRLfg05VN3u39s → bOkuiIr_nWzG5GjPLNqdN}/_ssgManifest.js +0 -0
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "web_accessible_resources": [
32
32
  {
33
- "resources": ["disguise.js", "automation.js", "network-capture.js"],
33
+ "resources": ["disguise.js", "automation.js", "network-capture.js", "messages.js"],
34
34
  "matches": ["<all_urls>"]
35
35
  }
36
36
  ],
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Extension-side error templates.
3
+ *
4
+ * Used by automation.js. Generated strings are returned to the CLI as
5
+ * `data.error` and printed verbatim — so they MUST be self-contained and
6
+ * include actionable next steps with generic examples (no case-specific
7
+ * selectors or API paths).
8
+ *
9
+ * The CLI side has its own copy in bin/cock-browser.messages.mjs; this file
10
+ * stays in sync manually (only ~3 templates here, drift risk is low).
11
+ */
12
+
13
+ const EXAMPLES = Object.freeze({
14
+ selectorSubmit: 'button[type="submit"]',
15
+ selectorAriaSave: 'button[aria-label="Save"]',
16
+ textSignIn: 'Sign in',
17
+ textSave: 'Save',
18
+ });
19
+
20
+ function staleRefMsg(ref, currentEpoch, kind) {
21
+ return `Element ref "${ref}" is stale (current snapshot v=${currentEpoch}; kind: ${kind}).
22
+ Refs are valid only until the next snapshot / re-render / route change.
23
+ Fix one of:
24
+ 1. Re-run \`snapshot\` to get fresh refs (look for v=${currentEpoch} in the banner).
25
+ 2. Use a CSS selector or visible text directly:
26
+ cockpit browser <id> click --text "${EXAMPLES.textSignIn}"
27
+ cockpit browser <id> click --selector '${EXAMPLES.selectorSubmit}'
28
+ 3. Drop to evaluate:
29
+ cockpit browser <id> evaluate "(() => document.querySelector('${EXAMPLES.selectorAriaSave}').click())()"`;
30
+ }
31
+
32
+ function unknownActionMsg(action, suggestions) {
33
+ const hint = suggestions && suggestions.length
34
+ ? `\n Did you mean: ${suggestions.join(', ')}?`
35
+ : '';
36
+ return `Unknown action "${action}".${hint}
37
+ Run: cockpit browser --help-all`;
38
+ }
39
+
40
+ // Expose on globalThis so automation.js (loaded as module) can use them
41
+ // without an explicit import path that differs by build mode.
42
+ if (typeof window !== 'undefined') {
43
+ window.__cockpitBrowserMessages = { staleRefMsg, unknownActionMsg, EXAMPLES };
44
+ }
45
+ export { staleRefMsg, unknownActionMsg, EXAMPLES };
@@ -88,6 +88,7 @@ function createPendingRequest(reqId, timeout) {
88
88
  return new Promise((resolve, reject) => {
89
89
  const timer = setTimeout(() => {
90
90
  pendingRequests.delete(reqId);
91
+ recordCommandResolved(reqId, false);
91
92
  reject(new Error(`Timeout after ${timeout}ms`));
92
93
  }, timeout);
93
94
  pendingRequests.set(reqId, { resolve, reject, timer });
@@ -95,15 +96,20 @@ function createPendingRequest(reqId, timeout) {
95
96
  }
96
97
  function resolvePendingRequest(reqId, ok2, data, error) {
97
98
  const pending = pendingRequests.get(reqId);
98
- if (!pending) return;
99
+ if (!pending) {
100
+ recordCommandResolved(reqId, ok2);
101
+ return;
102
+ }
99
103
  clearTimeout(pending.timer);
100
104
  pendingRequests.delete(reqId);
105
+ recordCommandResolved(reqId, ok2);
101
106
  if (ok2) {
102
107
  pending.resolve(data);
103
108
  } else {
104
109
  pending.reject(new Error(error || "Browser command failed"));
105
110
  }
106
111
  }
112
+ var reqIdMeta = globalThis.__cockpitBrowserReqMeta ?? (globalThis.__cockpitBrowserReqMeta = /* @__PURE__ */ new Map());
107
113
  function sendCommandToBrowser(shortId, reqId, action, params) {
108
114
  const entry = registry.get(shortId);
109
115
  if (!entry || !entry.ws || entry.ws.readyState !== WebSocket.OPEN) {
@@ -115,8 +121,48 @@ function sendCommandToBrowser(shortId, reqId, action, params) {
115
121
  action,
116
122
  params
117
123
  }));
124
+ reqIdMeta.set(reqId, { shortId, action });
118
125
  return true;
119
126
  }
127
+ function recordCommandResolved(reqId, ok2) {
128
+ const meta = reqIdMeta.get(reqId);
129
+ if (!meta) return;
130
+ reqIdMeta.delete(reqId);
131
+ if (ok2) {
132
+ const entry = registry.get(meta.shortId);
133
+ if (entry) {
134
+ entry.lastSuccessTs = Date.now();
135
+ entry.lastSuccessAction = meta.action;
136
+ entry.lastSeen = Date.now();
137
+ }
138
+ }
139
+ }
140
+ function getBrowserHealth(shortId) {
141
+ const entry = registry.get(shortId);
142
+ if (!entry) {
143
+ return {
144
+ found: false,
145
+ ws: "unknown",
146
+ lastSeenMs: null,
147
+ lastSuccessMs: null,
148
+ lastSuccessAction: null,
149
+ pendingCommands: 0
150
+ };
151
+ }
152
+ const now = Date.now();
153
+ let pending = 0;
154
+ for (const reqId of pendingRequests.keys()) {
155
+ if (reqIdMeta.get(reqId)?.shortId === shortId) pending += 1;
156
+ }
157
+ return {
158
+ found: true,
159
+ ws: entry.ws && entry.ws.readyState === WebSocket.OPEN ? "open" : "closed",
160
+ lastSeenMs: entry.lastSeen ? now - entry.lastSeen : null,
161
+ lastSuccessMs: entry.lastSuccessTs ? now - entry.lastSuccessTs : null,
162
+ lastSuccessAction: entry.lastSuccessAction ?? null,
163
+ pendingCommands: pending
164
+ };
165
+ }
120
166
 
121
167
  // packages/feature/console/src/server/plugins/jupyter/JupyterKernelManager.ts
122
168
  import { spawn } from "child_process";
@@ -1121,6 +1167,8 @@ export {
1121
1167
  createPendingRequest,
1122
1168
  resolvePendingRequest,
1123
1169
  sendCommandToBrowser,
1170
+ recordCommandResolved,
1171
+ getBrowserHealth,
1124
1172
  kernelManager,
1125
1173
  registerTerminal,
1126
1174
  finalizeTerminal,
package/dist/httpApi.mjs CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  addOutputListener,
4
4
  createPendingRequest,
5
5
  getBrowserByShortId,
6
+ getBrowserHealth,
6
7
  getFirstAvailableLine,
7
8
  getRunningCommand,
8
9
  getTerminalByShortId,
@@ -20,7 +21,7 @@ import {
20
21
  unregisterBrowser,
21
22
  unregisterTerminal,
22
23
  writeStdinToCommand
23
- } from "./chunk-W6G6X3FP.mjs";
24
+ } from "./chunk-WOM47O75.mjs";
24
25
  import {
25
26
  getTerminalHistoryPath
26
27
  } from "./chunk-GCYLMG43.mjs";
@@ -391,36 +392,95 @@ async function handleBrowserApi(req, res) {
391
392
  sendJson(200, { ok: true });
392
393
  return true;
393
394
  }
395
+ if (action === "health" && !cmdParams?.deep) {
396
+ if (!id) {
397
+ sendJson(400, { ok: false, error: "Missing browser id" });
398
+ return true;
399
+ }
400
+ sendJson(200, { ok: true, data: getBrowserHealth(id) });
401
+ return true;
402
+ }
394
403
  if (!id) {
395
404
  sendJson(400, { ok: false, error: "Missing browser id" });
396
405
  return true;
397
406
  }
398
407
  const browser = getBrowserByShortId(id);
399
408
  if (!browser) {
400
- sendJson(404, { ok: false, error: `Browser "${id}" not found` });
409
+ const all = listBrowsers().map((b) => b.shortId);
410
+ const suggestions = fuzzyTopK(id, all, 3);
411
+ const hint = suggestions.length ? `
412
+ Did you mean: ${suggestions.join(", ")}?` : all.length ? `
413
+ Available: ${all.join(", ")}` : "\n No browsers currently connected. Open a browser bubble in the cockpit console.";
414
+ sendJson(404, {
415
+ ok: false,
416
+ error: `Browser "${id}" not found.${hint}
417
+ Run: cockpit browser list`
418
+ });
401
419
  return true;
402
420
  }
403
421
  if (!browser.ws || browser.ws.readyState !== WebSocket.OPEN) {
404
422
  sendJson(503, {
405
423
  ok: false,
406
- error: `Browser "${id}" is disconnected`
424
+ error: `Browser "${id}" is disconnected (WS closed).
425
+ Recover:
426
+ 1. Refresh the browser tab to re-register the extension.
427
+ 2. Run \`cockpit browser list\` to confirm the shortId.
428
+ 3. If the bubble is gone, re-open it from the cockpit console panel.`
407
429
  });
408
430
  return true;
409
431
  }
432
+ const wsAction = action === "health" ? "health_deep" : action;
410
433
  const reqId = `r-${randomUUID().slice(0, 8)}`;
411
- const sent = sendCommandToBrowser(id, reqId, action, cmdParams);
434
+ const sent = sendCommandToBrowser(id, reqId, wsAction, cmdParams);
412
435
  if (!sent) {
413
- sendJson(503, { ok: false, error: "Failed to send command" });
436
+ sendJson(503, { ok: false, error: "Failed to send command (WS write failed)" });
414
437
  return true;
415
438
  }
416
439
  try {
417
440
  const data = await createPendingRequest(reqId, timeout);
418
441
  sendJson(200, { ok: true, data });
419
442
  } catch (err) {
420
- sendJson(504, { ok: false, error: err.message });
443
+ const msg = err.message;
444
+ if (/timeout/i.test(msg)) {
445
+ const h = getBrowserHealth(id);
446
+ sendJson(504, {
447
+ ok: false,
448
+ error: `${msg} (action "${action}").
449
+ Bridge state: ws=${h.ws} pending=${h.pendingCommands}` + (h.lastSuccessMs !== null ? ` lastSuccess=${Math.round(h.lastSuccessMs / 1e3)}s ago (${h.lastSuccessAction})` : "") + `
450
+ Diagnose:
451
+ cockpit browser ${id} health # cheap server-side probe
452
+ cockpit browser ${id} wait --extension-ready (Phase 2)
453
+ If the bridge state shows fresh activity, the page itself is blocked.
454
+ Consider a service-level test if driven by an async LLM/agent flow.`
455
+ });
456
+ } else {
457
+ sendJson(504, { ok: false, error: msg });
458
+ }
421
459
  }
422
460
  return true;
423
461
  }
462
+ function fuzzyTopK(input, candidates, k) {
463
+ if (!input || !candidates.length) return [];
464
+ const inputLow = input.toLowerCase();
465
+ const bag = bagCounts(inputLow);
466
+ const scored = candidates.map((c) => {
467
+ const cl = c.toLowerCase();
468
+ const cBag = bagCounts(cl);
469
+ let inter = 0;
470
+ for (const [ch, n] of bag) inter += Math.min(n, cBag.get(ch) ?? 0);
471
+ const ratio = inter / Math.max(inputLow.length, cl.length);
472
+ const startsWith = cl.startsWith(inputLow) ? 0.5 : 0;
473
+ const contains = cl.includes(inputLow) ? 0.3 : 0;
474
+ return { c, score: ratio + startsWith + contains };
475
+ });
476
+ scored.sort((a, b) => b.score - a.score);
477
+ return scored.slice(0, k).filter((s) => s.score >= 0.6).map((s) => s.c);
478
+ }
479
+ function bagCounts(s) {
480
+ const m = /* @__PURE__ */ new Map();
481
+ for (const ch of s) m.set(ch, (m.get(ch) ?? 0) + 1);
482
+ return m;
483
+ }
424
484
  async function handleConnectionApi(req, res) {
425
485
  const { pathname } = parse(req.url || "", true);
426
486
  const match = pathname?.match(/^\/api\/connection\/([a-z]+)$/);
@@ -75,7 +75,12 @@ var sendChatMessageEff = (task) => {
75
75
  "WebFetch",
76
76
  "WebSearch",
77
77
  "Task",
78
- "TodoWrite",
78
+ // Task management — claude-agent-sdk@0.3.142 replaced TodoWrite
79
+ // with per-task TaskCreate/Update/Get/List events.
80
+ "TaskCreate",
81
+ "TaskUpdate",
82
+ "TaskGet",
83
+ "TaskList",
79
84
  "mcp__*"
80
85
  ],
81
86
  permissionMode: "bypassPermissions",
@@ -11,6 +11,7 @@ import {
11
11
  findSafeStart,
12
12
  getAllProjectCwds,
13
13
  getBrowserByShortId,
14
+ getBrowserHealth,
14
15
  getFirstAvailableLine,
15
16
  getLinesView,
16
17
  getRegistrySize,
@@ -29,6 +30,7 @@ import {
29
30
  readHead,
30
31
  readSince,
31
32
  readTail,
33
+ recordCommandResolved,
32
34
  registerBrowser,
33
35
  registerCommand,
34
36
  registerTerminal,
@@ -39,7 +41,7 @@ import {
39
41
  unregisterTerminal,
40
42
  updateBrowserWs,
41
43
  writeStdinToCommand
42
- } from "./chunk-W6G6X3FP.mjs";
44
+ } from "./chunk-WOM47O75.mjs";
43
45
  import {
44
46
  createNeo4jDriver,
45
47
  runCypherWithDriver,
@@ -60,6 +62,7 @@ export {
60
62
  findSafeStart,
61
63
  getAllProjectCwds,
62
64
  getBrowserByShortId,
65
+ getBrowserHealth,
63
66
  getFirstAvailableLine,
64
67
  getLinesView,
65
68
  getRegistrySize,
@@ -78,6 +81,7 @@ export {
78
81
  readHead,
79
82
  readSince,
80
83
  readTail,
84
+ recordCommandResolved,
81
85
  registerBrowser,
82
86
  registerCommand,
83
87
  registerTerminal,
package/dist/wsServer.mjs CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  resolvePendingRequest,
17
17
  unregisterBrowser,
18
18
  writeStdinToCommand
19
- } from "./chunk-W6G6X3FP.mjs";
19
+ } from "./chunk-WOM47O75.mjs";
20
20
  import {
21
21
  AppError,
22
22
  Effect_exports,
@@ -683,7 +683,7 @@ var handleJupyter = (conn, bubbleId, cwd) => Effect_exports.gen(function* () {
683
683
  Effect_exports.annotateLogs("cwd", cwd)
684
684
  );
685
685
  const { kernelManager } = yield* Effect_exports.tryPromise({
686
- try: () => import("./server-ZBUZ24TC.mjs"),
686
+ try: () => import("./server-SNB4H35J.mjs"),
687
687
  catch: (cause) => new AppError({
688
688
  message: "failed to import kernel manager",
689
689
  cause
@@ -1165,6 +1165,8 @@ var runTerminalHandler = (ws, projectCwd) => {
1165
1165
  // packages/feature/explorer/src/server/codeMap/projectGraph/codeIndex.ts
1166
1166
  import { promises as fs4 } from "fs";
1167
1167
  import path6 from "path";
1168
+ import { exec as exec2 } from "child_process";
1169
+ import { promisify as promisify2 } from "util";
1168
1170
 
1169
1171
  // packages/feature/explorer/src/server/codeMap/projectGraph/buildGraph.ts
1170
1172
  import { exec } from "child_process";
@@ -3872,6 +3874,7 @@ function registerWatcherSubscriber(fn) {
3872
3874
  }
3873
3875
  }
3874
3876
  }
3877
+ var execAsync2 = promisify2(exec2);
3875
3878
 
3876
3879
  // src/lib/codeIndexSync.ts
3877
3880
  var wired = false;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@surething/cockpit",
3
- "version": "1.0.217",
4
- "description": "Claude Code GUI for parallel AI coding — drive Claude (default), OpenAI 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.218",
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.",
5
5
  "author": "Robert",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -19,7 +19,6 @@
19
19
  "claude-code-client",
20
20
  "claude-agent-sdk",
21
21
  "anthropic",
22
- "openai-codex",
23
22
  "codex",
24
23
  "deepseek",
25
24
  "deepseek-gui",
@@ -93,7 +92,7 @@
93
92
  },
94
93
  "dependencies": {
95
94
  "@ai-sdk/openai": "^3.0.52",
96
- "@anthropic-ai/claude-agent-sdk": "^0.2.112",
95
+ "@anthropic-ai/claude-agent-sdk": "^0.3.158",
97
96
  "@dagrejs/dagre": "^3.0.0",
98
97
  "@tanstack/react-virtual": "^3.13.18",
99
98
  "@tiptap/extension-link": "^3.19.0",
@@ -168,7 +167,6 @@
168
167
  "typescript": "5.9.3"
169
168
  },
170
169
  "overrides": {
171
- "@anthropic-ai/sdk": "^0.91.1",
172
170
  "postcss": "^8.5.10"
173
171
  }
174
172
  }