@surething/cockpit 1.0.230 → 1.0.232
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 +1 -1
- 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 +3 -3
- package/.next-prod/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next-prod/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_index.segment.rsc +3 -3
- 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.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 +2 -2
- 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 +2 -2
- 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 +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 +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 +1 -1
- package/.next-prod/server/chunks/3633.js +1 -1
- package/.next-prod/server/chunks/4257.js +3 -3
- package/.next-prod/server/chunks/8916.js +1 -1
- package/.next-prod/server/chunks/9658.js +15 -5
- 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-4f8250a204080159.js +15 -0
- package/.next-prod/static/chunks/2576-6420f6bad0b8ffb3.js +25 -0
- package/.next-prod/static/chunks/6784-8a99d051f03bb100.js +0 -0
- package/.next-prod/static/chunks/{9428-5c287b255a9831e6.js → 9428-ffc16bc24c6e0da1.js} +7 -7
- package/.next-prod/static/chunks/app/{layout-03c738d41e660f72.js → layout-ac2f1f67df01cc89.js} +1 -1
- package/.next-prod/static/chunks/app/page-1f0a6f49656a4c52.js +1 -0
- package/.next-prod/static/chunks/app/project/page-1f0a6f49656a4c52.js +1 -0
- package/.next-prod/static/css/2a854655f4108c3a.css +1 -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 +47 -25
- package/dist/wsServer.mjs +1 -1
- package/package.json +1 -1
- package/server.mjs +48 -0
- package/.next-prod/static/chunks/217-7dafa608b7290bdc.js +0 -5
- package/.next-prod/static/chunks/2576-55f4835e4d7e27a8.js +0 -25
- package/.next-prod/static/chunks/6784-b644510a0e060aac.js +0 -0
- package/.next-prod/static/chunks/app/page-677439ad5fd63732.js +0 -1
- package/.next-prod/static/chunks/app/project/page-677439ad5fd63732.js +0 -1
- package/.next-prod/static/css/ee516fc73c03aa88.css +0 -1
- /package/.next-prod/static/{9qXUVBkdZdaWcpPpaLd3c → Z3Em1C_kM7x4RjcH7E7v2}/_buildManifest.js +0 -0
- /package/.next-prod/static/{9qXUVBkdZdaWcpPpaLd3c → Z3Em1C_kM7x4RjcH7E7v2}/_ssgManifest.js +0 -0
package/.next-prod/trace-build
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"name":"run-webpack","duration":
|
|
1
|
+
[{"name":"run-webpack","duration":21093023,"timestamp":158697893,"id":14,"parentId":1,"tags":{},"startTime":1782900947547,"traceId":"dcd58a4bd7b49062"},{"name":"run-typescript","duration":18807400,"timestamp":179794924,"id":1603,"parentId":1,"tags":{},"startTime":1782900968644,"traceId":"dcd58a4bd7b49062"},{"name":"static-check","duration":1213527,"timestamp":198644615,"id":1606,"parentId":1,"tags":{},"startTime":1782900987494,"traceId":"dcd58a4bd7b49062"},{"name":"static-generation","duration":6335613,"timestamp":200113230,"id":1878,"parentId":1,"tags":{},"startTime":1782900988963,"traceId":"dcd58a4bd7b49062"},{"name":"collect-build-traces","duration":21023731,"timestamp":199858733,"id":1875,"parentId":1,"tags":{},"startTime":1782900988708,"traceId":"dcd58a4bd7b49062"},{"name":"telemetry-flush","duration":92,"timestamp":220886988,"id":1887,"parentId":1,"tags":{},"startTime":1782901009736,"traceId":"dcd58a4bd7b49062"},{"name":"next-build","duration":62368455,"timestamp":158518639,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1782900947368,"traceId":"dcd58a4bd7b49062"}]
|
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
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
setRunAbort,
|
|
11
11
|
startRun,
|
|
12
12
|
updateGlobalState
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-VHFWPI2X.mjs";
|
|
14
14
|
import {
|
|
15
15
|
AgentError,
|
|
16
16
|
AppRuntime,
|
|
@@ -1400,41 +1400,63 @@ async function runSdkLoop(ctx, buildOptions) {
|
|
|
1400
1400
|
}
|
|
1401
1401
|
}
|
|
1402
1402
|
if (ctx.prompt) content.push({ type: "text", text: ctx.prompt });
|
|
1403
|
-
const
|
|
1404
|
-
const
|
|
1405
|
-
let
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
type: "user",
|
|
1410
|
-
message: { role: "user", content },
|
|
1411
|
-
parent_tool_use_id: null,
|
|
1412
|
-
session_id: ctx.sessionId || `session-${ctx.currentKey()}`
|
|
1413
|
-
};
|
|
1414
|
-
})();
|
|
1415
|
-
response = query({ prompt: messages, options: baseOptions });
|
|
1416
|
-
} else {
|
|
1417
|
-
response = query({ prompt: ctx.prompt, options: baseOptions });
|
|
1403
|
+
const firstContent = hasImages ? content : ctx.prompt ?? "";
|
|
1404
|
+
const pendingTasks = /* @__PURE__ */ new Set();
|
|
1405
|
+
let closeInput = () => {
|
|
1406
|
+
};
|
|
1407
|
+
if (!ctx.signal.aborted) {
|
|
1408
|
+
ctx.signal.addEventListener("abort", () => closeInput(), { once: true });
|
|
1418
1409
|
}
|
|
1419
|
-
let currentResponse = response;
|
|
1420
1410
|
for (let attempt = 0; attempt <= MAX_COMPACTION_RETRIES; attempt++) {
|
|
1411
|
+
let gateClosed = false;
|
|
1412
|
+
const gateOpen = new Promise((resolve) => {
|
|
1413
|
+
closeInput = () => {
|
|
1414
|
+
if (!gateClosed) {
|
|
1415
|
+
gateClosed = true;
|
|
1416
|
+
resolve();
|
|
1417
|
+
}
|
|
1418
|
+
};
|
|
1419
|
+
});
|
|
1420
|
+
if (ctx.signal.aborted) closeInput();
|
|
1421
|
+
const isRetry = attempt > 0;
|
|
1422
|
+
const firstYield = {
|
|
1423
|
+
type: "user",
|
|
1424
|
+
// A compaction retry resumes the same session with 'continue'; the first attempt sends the
|
|
1425
|
+
// user turn (text ∪ images).
|
|
1426
|
+
message: { role: "user", content: isRetry ? "continue" : firstContent },
|
|
1427
|
+
parent_tool_use_id: null,
|
|
1428
|
+
session_id: ctx.sessionId || `session-${ctx.currentKey()}`
|
|
1429
|
+
};
|
|
1430
|
+
const input = (async function* () {
|
|
1431
|
+
yield firstYield;
|
|
1432
|
+
await gateOpen;
|
|
1433
|
+
})();
|
|
1434
|
+
const attemptAbort = follow(ctx);
|
|
1435
|
+
const resume = isRetry ? ctx.currentKey() : ctx.sessionId;
|
|
1436
|
+
const options = isRetry ? { ...buildOptions(attemptAbort, resume), resume } : buildOptions(attemptAbort, ctx.sessionId);
|
|
1437
|
+
const response = query({ prompt: input, options });
|
|
1421
1438
|
let receivedResult = false;
|
|
1422
|
-
for await (const message of
|
|
1423
|
-
if (ctx.signal.aborted)
|
|
1439
|
+
for await (const message of response) {
|
|
1440
|
+
if (ctx.signal.aborted) {
|
|
1441
|
+
closeInput();
|
|
1442
|
+
break;
|
|
1443
|
+
}
|
|
1424
1444
|
const msg = message;
|
|
1425
1445
|
if (msg.type === "system" && msg.subtype === "init" && msg.session_id) {
|
|
1426
1446
|
ctx.rekey(msg.session_id);
|
|
1427
1447
|
}
|
|
1448
|
+
if (msg.type === "system" && msg.subtype === "task_started" && msg.task_id) {
|
|
1449
|
+
pendingTasks.add(msg.task_id);
|
|
1450
|
+
}
|
|
1451
|
+
if (msg.type === "system" && msg.subtype === "task_notification" && msg.task_id) {
|
|
1452
|
+
pendingTasks.delete(msg.task_id);
|
|
1453
|
+
}
|
|
1428
1454
|
if (msg.type === "result") receivedResult = true;
|
|
1429
1455
|
ctx.emit(message);
|
|
1456
|
+
if (msg.type === "result" && pendingTasks.size === 0) closeInput();
|
|
1430
1457
|
}
|
|
1458
|
+
closeInput();
|
|
1431
1459
|
if (receivedResult || ctx.signal.aborted) break;
|
|
1432
|
-
const retryAbort = follow(ctx);
|
|
1433
|
-
const resume = ctx.currentKey();
|
|
1434
|
-
currentResponse = query({
|
|
1435
|
-
prompt: "continue",
|
|
1436
|
-
options: { ...buildOptions(retryAbort, resume), resume }
|
|
1437
|
-
});
|
|
1438
1460
|
}
|
|
1439
1461
|
}
|
|
1440
1462
|
|
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.232",
|
|
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 || '';
|