@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.
- package/.next-prod/BUILD_ID +1 -1
- package/.next-prod/app-path-routes-manifest.json +3 -3
- package/.next-prod/build-manifest.json +3 -3
- package/.next-prod/prerender-manifest.json +3 -3
- package/.next-prod/react-loadable-manifest.json +7 -0
- package/.next-prod/server/app/_global-error/page.js.nft.json +1 -1
- package/.next-prod/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/_global-error.html +1 -1
- package/.next-prod/server/app/_global-error.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found/page.js.nft.json +1 -1
- package/.next-prod/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/_not-found.html +10 -2
- package/.next-prod/server/app/_not-found.rsc +29 -20
- package/.next-prod/server/app/_not-found.segments/_full.segment.rsc +29 -20
- package/.next-prod/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_index.segment.rsc +12 -4
- package/.next-prod/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next-prod/server/app/api/agent/test/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/bash/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/codex/route.js +1 -1
- package/.next-prod/server/app/api/chat/codex/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/deepseek/route.js +1 -1
- package/.next-prod/server/app/api/chat/deepseek/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/kimi/route.js +1 -1
- package/.next-prod/server/app/api/chat/kimi/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/ollama/route.js +1 -1
- package/.next-prod/server/app/api/chat/ollama/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/pty-input/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/route.js +1 -1
- package/.next-prod/server/app/api/chat/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/stop/route.js +1 -1
- package/.next-prod/server/app/api/chat/stop/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/claude-stats/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/commands/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/comments/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/columns/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/export/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/query/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/schemas/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/dev/spans/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/extension/version/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/file/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/blame/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/clipboard/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/copy/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/delete/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/expanded/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/index/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/init/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/paste/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/read/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/readdir/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/recent/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/save/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/search/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/stat/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/text/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/branch-diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/branches/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/commit-diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/commits/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/current-branch/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/discard/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/stage/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/status/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/unstage/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/worktree/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/global-state/route.js +1 -1
- package/.next-prod/server/app/api/global-state/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/health/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/jupyter/load/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/jupyter/save/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/jupyter/shutdown/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/definition/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/hover/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/references/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/status/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/warmup/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/columns/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/export/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/query/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/schemas/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/query/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/schema/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/note/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/ollama/models/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/ollama/start/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/open-cursor/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/open-vscode/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/pick-folder/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/pinned-sessions/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/project-settings/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/project-state/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/affected/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/callees/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/callers/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/coedit/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/context/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/file/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/file-functions/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/impact/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/related/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/risk/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/search/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projects/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/push/public-key/route.js +1 -1
- package/.next-prod/server/app/api/push/public-key/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/push/subscribe/route.js +1 -1
- package/.next-prod/server/app/api/push/subscribe/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/push/unsubscribe/route.js +1 -1
- package/.next-prod/server/app/api/push/unsubscribe/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/command/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/delete/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/get/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/keys/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/set/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/[id]/comments/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/[id]/replies/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/[id]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/identify/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/order/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/share-info/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/users/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/scheduled-tasks/route.js +1 -1
- package/.next-prod/server/app/api/scheduled-tasks/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/services/config/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/services/scripts/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/session/[sessionId]/fork/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/session/[sessionId]/history/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/session-by-path/route.js +1 -1
- package/.next-prod/server/app/api/session-by-path/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/projects/[encodedPath]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/projects/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/settings/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/skills/[id]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/skills/content/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/skills/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/aliases/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/autocomplete/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/bubble-order/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/env/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/history/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/version/route.js.nft.json +1 -1
- package/.next-prod/server/app/favicon.ico/route.js.nft.json +1 -1
- package/.next-prod/server/app/m/page.js +2 -2
- package/.next-prod/server/app/m/page.js.nft.json +1 -1
- package/.next-prod/server/app/m/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/manifest.webmanifest/route.js.nft.json +1 -1
- package/.next-prod/server/app/page.js +1 -1
- package/.next-prod/server/app/page.js.nft.json +1 -1
- package/.next-prod/server/app/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/project/page.js +1 -1
- package/.next-prod/server/app/project/page.js.nft.json +1 -1
- package/.next-prod/server/app/project/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/review/[id]/page.js +2 -2
- package/.next-prod/server/app/review/[id]/page.js.nft.json +1 -1
- package/.next-prod/server/app/review/[id]/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app-paths-manifest.json +3 -3
- package/.next-prod/server/chunks/{1035.js → 5897.js} +2 -2
- package/.next-prod/server/chunks/7338.js +1 -1
- package/.next-prod/server/chunks/9658.js +26 -26
- package/.next-prod/server/chunks/9830.js +1 -1
- package/.next-prod/server/middleware-build-manifest.js +1 -1
- package/.next-prod/server/middleware-react-loadable-manifest.js +1 -1
- package/.next-prod/server/pages/404.html +10 -2
- package/.next-prod/server/pages/500.html +1 -1
- package/.next-prod/server/server-reference-manifest.json +1 -1
- package/.next-prod/static/chunks/{2625-bc4748f0cd7db618.js → 1332-5e141e2acf28015a.js} +10 -10
- package/.next-prod/static/chunks/{217-92f800c2c438c550.js → 217-656e1fdbc820d9dd.js} +4 -4
- package/.next-prod/static/chunks/2502-fb8682bc7a61e04d.js +4 -0
- package/.next-prod/static/chunks/{2576-11e41242c7dd98f1.js → 2576-47a83a4d8066d196.js} +11 -11
- package/.next-prod/static/chunks/4523.3f277a24fbcfde6d.js +138 -0
- package/.next-prod/static/chunks/9304-ddde9008da31b2ce.js +1 -0
- package/.next-prod/static/chunks/app/{layout-ac2f1f67df01cc89.js → layout-e9ada9458f95f29c.js} +1 -1
- package/.next-prod/static/chunks/app/m/page-06a5ea22c02892d1.js +1 -0
- package/.next-prod/static/chunks/app/{page-1f0a6f49656a4c52.js → page-8f349cc054af0d5b.js} +1 -1
- package/.next-prod/static/chunks/app/project/{page-1f0a6f49656a4c52.js → page-8f349cc054af0d5b.js} +1 -1
- package/.next-prod/static/chunks/app/review/[id]/{page-39fa3a843adf617e.js → page-0c5d03d65758c7dc.js} +1 -1
- package/.next-prod/static/chunks/webpack-2c71a79c277961bc.js +1 -0
- package/.next-prod/static/css/44a979b47b165c63.css +1 -0
- package/.next-prod/trace +13 -13
- package/.next-prod/trace-build +1 -1
- package/README.md +27 -1
- package/README.zh.md +27 -1
- package/bin/cock-browser.mjs +12 -9
- package/bin/cock.mjs +5 -5
- package/bin/postinstall.mjs +6 -6
- package/chrome-extension/automation.js +45 -40
- package/chrome-extension/background.js +70 -67
- package/chrome-extension/content.js +38 -38
- package/chrome-extension/disguise.js +4 -4
- package/chrome-extension/network-capture.js +16 -16
- package/dist/{chunk-VHFWPI2X.mjs → chunk-IQXDXTI7.mjs} +35 -4
- package/dist/scheduledTasks.mjs +8 -14
- package/dist/wsServer.mjs +9 -31
- package/next.config.mjs +4 -2
- package/package.json +5 -4
- package/public/boot.js +8 -0
- package/server.mjs +122 -26
- package/.next-prod/server/chunks/7003.js +0 -1
- package/.next-prod/static/chunks/471-504b396c8302f4f2.js +0 -4
- package/.next-prod/static/chunks/app/m/page-8b1af6f31b60c485.js +0 -1
- package/.next-prod/static/chunks/webpack-dbba7fa77ee1faa4.js +0 -1
- package/.next-prod/static/css/fa7218e60ff464b5.css +0 -1
- /package/.next-prod/static/{WfNQMmytl62j_8KtgCW9_ → cfyYtf1Sz25gcv6tfZqFF}/_buildManifest.js +0 -0
- /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
|
-
|
|
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
|
-
|
|
650
|
+
getGlobalSessionsSnapshot,
|
|
620
651
|
startRun,
|
|
621
652
|
appendRun,
|
|
622
653
|
rekeyRun,
|
package/dist/scheduledTasks.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
setRunAbort,
|
|
11
11
|
startRun,
|
|
12
12
|
updateGlobalState
|
|
13
|
-
} from "./chunk-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
} from "./chunk-
|
|
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
|
|
114
|
-
try: () =>
|
|
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
|
|
16
|
-
// rotating
|
|
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.
|
|
4
|
-
"description": "Claude Code GUI for parallel AI coding
|
|
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
|
-
"
|
|
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)
|
|
59
|
-
//
|
|
60
|
-
//
|
|
59
|
+
// 1) When the parent process dies, the stdout/stderr pipes break. Next.js's
|
|
60
|
+
// uncaughtException handler then tries to console.log the error → writes
|
|
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
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
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:
|
|
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
|
|
89
|
-
//
|
|
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
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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/*
|
|
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:
|
|
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
|
-
//
|
|
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
|
|
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
|
|
233
|
-
//
|
|
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
|
-
//
|
|
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)}})}};
|