@surething/cockpit 1.0.230 → 1.0.231
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 +2 -2
- package/.next-prod/build-manifest.json +2 -2
- package/.next-prod/prerender-manifest.json +3 -3
- 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 +2 -2
- package/.next-prod/server/app/_not-found.rsc +2 -2
- package/.next-prod/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next-prod/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_index.segment.rsc +2 -2
- 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 +1 -1
- 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.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 +3 -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.nft.json +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.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.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/projects/[encodedPath]/route.js +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.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.nft.json +1 -1
- package/.next-prod/server/app/page_client-reference-manifest.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.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 +2 -2
- package/.next-prod/server/chunks/3633.js +1 -1
- package/.next-prod/server/chunks/9658.js +2 -2
- package/.next-prod/server/chunks/9830.js +1 -0
- package/.next-prod/server/middleware-build-manifest.js +1 -1
- package/.next-prod/server/pages/404.html +2 -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/{217-7dafa608b7290bdc.js → 217-a25b84ad39cf73ed.js} +1 -1
- package/.next-prod/static/chunks/2576-439565bf051dc1ba.js +25 -0
- package/.next-prod/trace +13 -13
- package/.next-prod/trace-build +1 -1
- package/bin/cock.mjs +10 -0
- package/dist/auth.mjs +92 -0
- package/dist/{chunk-XW5CKMXI.mjs → chunk-VHFWPI2X.mjs} +64 -3
- package/dist/scheduledTasks.mjs +1 -1
- package/dist/wsServer.mjs +1 -1
- package/package.json +1 -1
- package/server.mjs +48 -0
- package/.next-prod/static/chunks/2576-55f4835e4d7e27a8.js +0 -25
- /package/.next-prod/static/{9qXUVBkdZdaWcpPpaLd3c → eirTPmw6oXFp3wKgUVcXn}/_buildManifest.js +0 -0
- /package/.next-prod/static/{9qXUVBkdZdaWcpPpaLd3c → eirTPmw6oXFp3wKgUVcXn}/_ssgManifest.js +0 -0
package/.next-prod/trace-build
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"name":"run-webpack","duration":
|
|
1
|
+
[{"name":"run-webpack","duration":19913822,"timestamp":394761478,"id":14,"parentId":1,"tags":{},"startTime":1782573830076,"traceId":"19b1732556652e31"},{"name":"run-typescript","duration":18180157,"timestamp":414679089,"id":1603,"parentId":1,"tags":{},"startTime":1782573849994,"traceId":"19b1732556652e31"},{"name":"static-check","duration":1122581,"timestamp":432893720,"id":1606,"parentId":1,"tags":{},"startTime":1782573868209,"traceId":"19b1732556652e31"},{"name":"static-generation","duration":6167280,"timestamp":434127573,"id":1878,"parentId":1,"tags":{},"startTime":1782573869442,"traceId":"19b1732556652e31"},{"name":"collect-build-traces","duration":19577375,"timestamp":434016895,"id":1875,"parentId":1,"tags":{},"startTime":1782573869332,"traceId":"19b1732556652e31"},{"name":"telemetry-flush","duration":60,"timestamp":453598132,"id":1887,"parentId":1,"tags":{},"startTime":1782573888913,"traceId":"19b1732556652e31"},{"name":"next-build","duration":58977558,"timestamp":394620647,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1782573829936,"traceId":"19b1732556652e31"}]
|
package/bin/cock.mjs
CHANGED
|
@@ -27,6 +27,7 @@ Commands:
|
|
|
27
27
|
|
|
28
28
|
Options:
|
|
29
29
|
--port <port> Set server port (default: 3457)
|
|
30
|
+
--token <token> Require this token for remote access (local stays open)
|
|
30
31
|
--no-open Don't open browser after start
|
|
31
32
|
-v, --version Show version
|
|
32
33
|
-h, --help Show this help
|
|
@@ -57,6 +58,15 @@ if (portIdx !== -1 && process.argv[portIdx + 1]) {
|
|
|
57
58
|
process.argv.splice(portIdx, 2);
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
// Parse --token argument → enable shared-token auth (server.mjs reads
|
|
62
|
+
// COCKPIT_TOKEN). Off entirely when omitted. Local (loopback) access stays
|
|
63
|
+
// exempt; remote access must present the token.
|
|
64
|
+
const tokenIdx = process.argv.indexOf('--token');
|
|
65
|
+
if (tokenIdx !== -1 && process.argv[tokenIdx + 1] && !process.argv[tokenIdx + 1].startsWith('-')) {
|
|
66
|
+
process.env.COCKPIT_TOKEN = process.argv[tokenIdx + 1];
|
|
67
|
+
process.argv.splice(tokenIdx, 2);
|
|
68
|
+
}
|
|
69
|
+
|
|
60
70
|
// Default prod port
|
|
61
71
|
if (!process.env.COCKPIT_PORT) {
|
|
62
72
|
process.env.COCKPIT_PORT = '3457';
|
package/dist/auth.mjs
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import "./chunk-7P6ASYW6.mjs";
|
|
2
|
+
|
|
3
|
+
// src/lib/auth.ts
|
|
4
|
+
import { timingSafeEqual } from "crypto";
|
|
5
|
+
var COOKIE_NAME = "cockpit_token";
|
|
6
|
+
var QUERY_KEY = "token";
|
|
7
|
+
var COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
|
|
8
|
+
function tokenEnabled() {
|
|
9
|
+
return Boolean(process.env.COCKPIT_TOKEN);
|
|
10
|
+
}
|
|
11
|
+
function configuredToken() {
|
|
12
|
+
return process.env.COCKPIT_TOKEN || "";
|
|
13
|
+
}
|
|
14
|
+
function safeEqual(a, b) {
|
|
15
|
+
const ba = Buffer.from(a);
|
|
16
|
+
const bb = Buffer.from(b);
|
|
17
|
+
if (ba.length !== bb.length) return false;
|
|
18
|
+
return timingSafeEqual(ba, bb);
|
|
19
|
+
}
|
|
20
|
+
function isLoopbackAddr(addr) {
|
|
21
|
+
if (!addr) return false;
|
|
22
|
+
return addr === "::1" || addr === "::ffff:127.0.0.1" || addr.startsWith("127.");
|
|
23
|
+
}
|
|
24
|
+
function parseCookies(header) {
|
|
25
|
+
const out = {};
|
|
26
|
+
if (!header) return out;
|
|
27
|
+
for (const part of header.split(";")) {
|
|
28
|
+
const i = part.indexOf("=");
|
|
29
|
+
if (i < 0) continue;
|
|
30
|
+
const k = part.slice(0, i).trim();
|
|
31
|
+
if (!k) continue;
|
|
32
|
+
out[k] = decodeURIComponent(part.slice(i + 1).trim());
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
function tokenFromQuery(url) {
|
|
37
|
+
try {
|
|
38
|
+
return new URL(url, "http://localhost").searchParams.get(QUERY_KEY) ?? void 0;
|
|
39
|
+
} catch {
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function stripTokenParam(url) {
|
|
44
|
+
try {
|
|
45
|
+
const u = new URL(url, "http://localhost");
|
|
46
|
+
u.searchParams.delete(QUERY_KEY);
|
|
47
|
+
const qs = u.searchParams.toString();
|
|
48
|
+
return u.pathname + (qs ? `?${qs}` : "") + (u.hash || "");
|
|
49
|
+
} catch {
|
|
50
|
+
return "/";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function makeCookie(token, secure) {
|
|
54
|
+
const attrs = [
|
|
55
|
+
`${COOKIE_NAME}=${encodeURIComponent(token)}`,
|
|
56
|
+
"Path=/",
|
|
57
|
+
"HttpOnly",
|
|
58
|
+
"SameSite=Lax",
|
|
59
|
+
`Max-Age=${COOKIE_MAX_AGE}`
|
|
60
|
+
];
|
|
61
|
+
if (secure) attrs.push("Secure");
|
|
62
|
+
return attrs.join("; ");
|
|
63
|
+
}
|
|
64
|
+
function checkAccess(input) {
|
|
65
|
+
if (!tokenEnabled()) return { action: "pass" };
|
|
66
|
+
if (!input.forwarded && isLoopbackAddr(input.remoteAddr)) {
|
|
67
|
+
return { action: "pass" };
|
|
68
|
+
}
|
|
69
|
+
const want = configuredToken();
|
|
70
|
+
const cookieTok = parseCookies(input.cookieHeader)[COOKIE_NAME];
|
|
71
|
+
if (cookieTok && safeEqual(cookieTok, want)) return { action: "pass" };
|
|
72
|
+
const auth = input.authHeader;
|
|
73
|
+
const bearer = auth && auth.startsWith("Bearer ") ? auth.slice(7).trim() : void 0;
|
|
74
|
+
if (bearer && safeEqual(bearer, want)) return { action: "pass" };
|
|
75
|
+
const queryTok = tokenFromQuery(input.url);
|
|
76
|
+
if (queryTok && safeEqual(queryTok, want)) {
|
|
77
|
+
if (input.isWs) return { action: "pass" };
|
|
78
|
+
return {
|
|
79
|
+
action: "redirect",
|
|
80
|
+
location: stripTokenParam(input.url),
|
|
81
|
+
setCookie: makeCookie(want, input.isHttps)
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return { action: "deny" };
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
COOKIE_NAME,
|
|
88
|
+
checkAccess,
|
|
89
|
+
isLoopbackAddr,
|
|
90
|
+
makeCookie,
|
|
91
|
+
tokenEnabled
|
|
92
|
+
};
|
|
@@ -217,16 +217,37 @@ async function getLastUserMessage(cwd, sessionId) {
|
|
|
217
217
|
var SUMMARY_THRESHOLD = 10;
|
|
218
218
|
var SUMMARY_HEAD = 5;
|
|
219
219
|
var SUMMARY_TAIL = 5;
|
|
220
|
+
async function getSessionContent(cwd, sessionId) {
|
|
221
|
+
const claudePath = getClaudeSessionPath(cwd, sessionId);
|
|
222
|
+
if (existsSync(claudePath)) return getClaudeStyleContent(claudePath);
|
|
223
|
+
const claude2Path = getClaude2SessionPath(cwd, sessionId);
|
|
224
|
+
if (existsSync(claude2Path)) return getClaudeStyleContent(claude2Path);
|
|
225
|
+
const ollamaPath = getOllamaSessionPath(cwd, sessionId);
|
|
226
|
+
if (existsSync(ollamaPath)) return getClaudeStyleContent(ollamaPath);
|
|
227
|
+
const codexPath = findCodexSessionPath(sessionId);
|
|
228
|
+
if (codexPath && existsSync(codexPath)) {
|
|
229
|
+
return { summary: "", messages: await getCodexUserMessages(codexPath) };
|
|
230
|
+
}
|
|
231
|
+
const kimiPath = findKimiSessionPath(sessionId);
|
|
232
|
+
if (kimiPath && existsSync(kimiPath)) {
|
|
233
|
+
return { summary: "", messages: await getKimiUserMessages(kimiPath) };
|
|
234
|
+
}
|
|
235
|
+
return { summary: "", messages: [] };
|
|
236
|
+
}
|
|
220
237
|
async function getSessionPreview(cwd, sessionId) {
|
|
221
|
-
const messages = await
|
|
238
|
+
const { summary, messages } = await getSessionContent(cwd, sessionId);
|
|
239
|
+
const title = generateTitle(summary, messages);
|
|
222
240
|
const lastUserMessage = messages[messages.length - 1];
|
|
241
|
+
const searchText = [summary, ...messages].join("\n").toLowerCase();
|
|
223
242
|
if (messages.length <= SUMMARY_THRESHOLD) {
|
|
224
|
-
return { lastUserMessage, firstMessages: messages.map((m) => truncate(m)), lastMessages: [] };
|
|
243
|
+
return { title, lastUserMessage, firstMessages: messages.map((m) => truncate(m)), lastMessages: [], searchText };
|
|
225
244
|
}
|
|
226
245
|
return {
|
|
246
|
+
title,
|
|
227
247
|
lastUserMessage,
|
|
228
248
|
firstMessages: messages.slice(0, SUMMARY_HEAD).map((m) => truncate(m)),
|
|
229
|
-
lastMessages: messages.slice(-SUMMARY_TAIL).map((m) => truncate(m))
|
|
249
|
+
lastMessages: messages.slice(-SUMMARY_TAIL).map((m) => truncate(m)),
|
|
250
|
+
searchText
|
|
230
251
|
};
|
|
231
252
|
}
|
|
232
253
|
function filterCommandTags(text) {
|
|
@@ -308,6 +329,46 @@ async function getClaudeStyleUserMessages(filePath) {
|
|
|
308
329
|
}
|
|
309
330
|
return messages;
|
|
310
331
|
}
|
|
332
|
+
async function getClaudeStyleContent(filePath) {
|
|
333
|
+
let summary = "";
|
|
334
|
+
const messages = [];
|
|
335
|
+
try {
|
|
336
|
+
const fileStream = createReadStream(filePath);
|
|
337
|
+
const rl = createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
338
|
+
for await (const line of rl) {
|
|
339
|
+
if (!line.trim()) continue;
|
|
340
|
+
try {
|
|
341
|
+
const entry = JSON.parse(line);
|
|
342
|
+
if (entry.type === "summary" && entry.summary) {
|
|
343
|
+
summary = entry.summary;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
if (entry.type !== "user") continue;
|
|
347
|
+
const message = entry.message;
|
|
348
|
+
if (!message?.content) continue;
|
|
349
|
+
let text = "";
|
|
350
|
+
if (typeof message.content === "string") {
|
|
351
|
+
text = message.content;
|
|
352
|
+
} else if (Array.isArray(message.content)) {
|
|
353
|
+
for (const block of message.content) {
|
|
354
|
+
if (block.type === "text" && block.text) {
|
|
355
|
+
text = block.text;
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (!text) continue;
|
|
361
|
+
const filtered = filterCommandTags(text);
|
|
362
|
+
if (filtered && isValidUserMessage(filtered)) {
|
|
363
|
+
messages.push(filtered);
|
|
364
|
+
}
|
|
365
|
+
} catch {
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
} catch {
|
|
369
|
+
}
|
|
370
|
+
return { summary, messages };
|
|
371
|
+
}
|
|
311
372
|
async function getCodexUserMessages(filePath) {
|
|
312
373
|
const messages = [];
|
|
313
374
|
try {
|
package/dist/scheduledTasks.mjs
CHANGED
package/dist/wsServer.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@surething/cockpit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.231",
|
|
4
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",
|
package/server.mjs
CHANGED
|
@@ -128,6 +128,7 @@ app.prepare().then(async () => {
|
|
|
128
128
|
const upgradeHandler = app.getUpgradeHandler();
|
|
129
129
|
// v2 P8: HTTP intercepts (handleTerminalApi / handleBrowserApi) moved to src/lib/httpApi.ts
|
|
130
130
|
const { handleUpgrade, broadcastToGlobalState } = await import(dev ? './src/lib/wsServer.ts' : './dist/wsServer.mjs');
|
|
131
|
+
const auth = await import(dev ? './src/lib/auth.ts' : './dist/auth.mjs');
|
|
131
132
|
const httpApi = await import(dev ? './src/lib/httpApi.ts' : './dist/httpApi.mjs');
|
|
132
133
|
const { handleBrowserApi, handleTerminalApi, handleConnectionApi } = httpApi;
|
|
133
134
|
flushRunningSync = httpApi.flushAllRunningSync || null;
|
|
@@ -139,7 +140,45 @@ app.prepare().then(async () => {
|
|
|
139
140
|
});
|
|
140
141
|
await scheduledTaskManager.init();
|
|
141
142
|
|
|
143
|
+
// ============================================
|
|
144
|
+
// Token gate — opt-in via `cockpit --token <value>` (COCKPIT_TOKEN).
|
|
145
|
+
// Off by default (open). Local callers (loopback peer + no forwarding header)
|
|
146
|
+
// are exempt, so the CLI / /cg curls / self-probe never need a token.
|
|
147
|
+
// ============================================
|
|
148
|
+
const gateInput = (req, isWs) => ({
|
|
149
|
+
url: req.url || '',
|
|
150
|
+
remoteAddr: req.socket?.remoteAddress,
|
|
151
|
+
cookieHeader: req.headers?.cookie,
|
|
152
|
+
authHeader: req.headers?.authorization,
|
|
153
|
+
forwarded:
|
|
154
|
+
req.headers?.['x-forwarded-for'] ||
|
|
155
|
+
req.headers?.['x-real-ip'] ||
|
|
156
|
+
req.headers?.['forwarded'],
|
|
157
|
+
isWs,
|
|
158
|
+
isHttps:
|
|
159
|
+
String(req.headers?.['x-forwarded-proto'] || '').split(',')[0].trim() === 'https',
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Apply the gate to an HTTP request. Returns true if it wrote a response
|
|
163
|
+
// (blocked / redirected) and the caller should stop.
|
|
164
|
+
const applyHttpGate = (req, res) => {
|
|
165
|
+
const decision = auth.checkAccess(gateInput(req, false));
|
|
166
|
+
if (decision.action === 'redirect') {
|
|
167
|
+
res.writeHead(302, { Location: decision.location, 'Set-Cookie': decision.setCookie });
|
|
168
|
+
res.end();
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
if (decision.action === 'deny') {
|
|
172
|
+
res.writeHead(401, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
173
|
+
res.end('401 Unauthorized - append ?token=<token> to the URL to authenticate\n');
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
};
|
|
178
|
+
|
|
142
179
|
const server = createServer(async (req, res) => {
|
|
180
|
+
if (applyHttpGate(req, res)) return;
|
|
181
|
+
|
|
143
182
|
// /api/browser/* 必须在自定义 server 中处理(与 WS 共享 BrowserBridge 内存)
|
|
144
183
|
if (req.url?.startsWith('/api/browser/') && req.method === 'POST') {
|
|
145
184
|
const handled = await handleBrowserApi(req, res);
|
|
@@ -157,6 +196,12 @@ app.prepare().then(async () => {
|
|
|
157
196
|
});
|
|
158
197
|
|
|
159
198
|
server.on('upgrade', (req, socket, head) => {
|
|
199
|
+
// Cookie / ?token ride the same-origin upgrade → gate WS too.
|
|
200
|
+
if (auth.checkAccess(gateInput(req, true)).action !== 'pass') {
|
|
201
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
202
|
+
socket.destroy();
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
160
205
|
if (!handleUpgrade(req, socket, head)) {
|
|
161
206
|
upgradeHandler(req, socket, head);
|
|
162
207
|
}
|
|
@@ -212,6 +257,9 @@ app.prepare().then(async () => {
|
|
|
212
257
|
}
|
|
213
258
|
|
|
214
259
|
const shareServer = createServer((req, res) => {
|
|
260
|
+
// Token gate first (share is also covered). Read the gate BEFORE we inject
|
|
261
|
+
// x-forwarded-for below, so the injection can't fool the local check.
|
|
262
|
+
if (applyHttpGate(req, res)) return;
|
|
215
263
|
if (isShareAllowed(req.url || '')) {
|
|
216
264
|
// 注入客户端真实 IP,供 /api/review/identify 使用
|
|
217
265
|
const clientIp = req.socket.remoteAddress || '';
|