@scrylog/cli 0.3.0 → 0.3.2
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/dist/assets/highlights-eq9cgrbb.scm +604 -0
- package/dist/assets/highlights-ghv9g403.scm +205 -0
- package/dist/assets/highlights-hk7bwhj4.scm +284 -0
- package/dist/assets/highlights-r812a2qc.scm +150 -0
- package/dist/assets/highlights-x6tmsnaa.scm +115 -0
- package/dist/assets/injections-73j83es3.scm +27 -0
- package/dist/assets/parser.worker-xyhf83rf.js +4563 -0
- package/dist/assets/tree-sitter-3jzf13jk.wasm +0 -0
- package/dist/assets/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/assets/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/assets/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/assets/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/assets/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/dist/cli/index.js +499 -0
- package/dist/cli/index.js.map +61 -0
- package/dist/daemon/index.js +670 -0
- package/dist/daemon/index.js.map +1475 -0
- package/dist/hooks/claude.js +6 -0
- package/dist/hooks/claude.js.map +11 -0
- package/dist/hooks/codex.js +10 -0
- package/dist/hooks/codex.js.map +11 -0
- package/dist/mcp/index.js +100 -0
- package/dist/mcp/index.js.map +231 -0
- package/dist/oc/index.js +21 -0
- package/dist/oc/index.js.map +10 -0
- package/package.json +16 -13
- package/src/api/docs/sessions.keys.ts +0 -6
- package/src/api/docs/sessions.ts +0 -82
- package/src/api/docs/system.keys.ts +0 -5
- package/src/api/docs/system.ts +0 -69
- package/src/api/docs/usage.keys.ts +0 -3
- package/src/api/docs/usage.ts +0 -24
- package/src/api/docs/webhooks.keys.ts +0 -5
- package/src/api/docs/webhooks.ts +0 -78
- package/src/api/index.ts +0 -18
- package/src/api/lib/auth.ts +0 -43
- package/src/api/lib/errors.ts +0 -28
- package/src/api/lib/route.ts +0 -18
- package/src/api/models/claude.ts +0 -4
- package/src/api/models/codex.ts +0 -4
- package/src/api/models/opencode.ts +0 -11
- package/src/api/routes/sessions.ts +0 -51
- package/src/api/routes/system.ts +0 -106
- package/src/api/routes/usage.ts +0 -15
- package/src/api/routes/webhooks/claude.ts +0 -149
- package/src/api/routes/webhooks/codex.ts +0 -175
- package/src/api/routes/webhooks/opencode.ts +0 -112
- package/src/api/routes.ts +0 -93
- package/src/api/schemas/common.ts +0 -29
- package/src/api/schemas/sessions.ts +0 -86
- package/src/api/schemas/usage.ts +0 -51
- package/src/api/schemas/webhooks/claude.ts +0 -32
- package/src/api/schemas/webhooks/codex.ts +0 -34
- package/src/api/schemas/webhooks/opencode.ts +0 -29
- package/src/api/services/claude-session.ts +0 -9
- package/src/api/services/codex-session.ts +0 -8
- package/src/api/services/opencode-session.ts +0 -73
- package/src/classifier.ts +0 -206
- package/src/cli/commands.ts +0 -714
- package/src/cli/index.ts +0 -18
- package/src/cli/program.ts +0 -175
- package/src/cli/prompt.ts +0 -44
- package/src/daemon/cli.ts +0 -27
- package/src/daemon/collectors/claude/README.md +0 -108
- package/src/daemon/collectors/claude/collector.ts +0 -423
- package/src/daemon/collectors/claude/discovery.ts +0 -127
- package/src/daemon/collectors/claude/hooks.ts +0 -181
- package/src/daemon/collectors/claude/index.ts +0 -8
- package/src/daemon/collectors/claude/jsonl.ts +0 -85
- package/src/daemon/collectors/claude/transcript.ts +0 -246
- package/src/daemon/collectors/codex/README.md +0 -147
- package/src/daemon/collectors/codex/collector.ts +0 -661
- package/src/daemon/collectors/codex/discovery.ts +0 -104
- package/src/daemon/collectors/codex/hooks.ts +0 -179
- package/src/daemon/collectors/codex/index.ts +0 -7
- package/src/daemon/collectors/codex/jsonl.ts +0 -108
- package/src/daemon/collectors/codex/transcript.ts +0 -299
- package/src/daemon/collectors/git.ts +0 -353
- package/src/daemon/collectors/herdr/collector.ts +0 -128
- package/src/daemon/collectors/herdr/index.ts +0 -2
- package/src/daemon/collectors/herdr/normalization.ts +0 -111
- package/src/daemon/collectors/opencode/README.md +0 -106
- package/src/daemon/collectors/opencode/collector.ts +0 -545
- package/src/daemon/collectors/opencode/discovery.ts +0 -257
- package/src/daemon/collectors/opencode/index.ts +0 -5
- package/src/daemon/collectors/opencode/session-builder.ts +0 -59
- package/src/daemon/collectors/opencode-db.ts +0 -294
- package/src/daemon/collectors/process.ts +0 -201
- package/src/daemon/collectors/tmux/README.md +0 -128
- package/src/daemon/collectors/tmux/collector.ts +0 -492
- package/src/daemon/collectors/tmux/enrichment.ts +0 -242
- package/src/daemon/collectors/tmux/index.ts +0 -4
- package/src/daemon/collectors/tmux/parse.ts +0 -128
- package/src/daemon/collectors/usage.ts +0 -259
- package/src/daemon/index.ts +0 -209
- package/src/daemon/scheduler.ts +0 -86
- package/src/daemon/store.ts +0 -630
- package/src/detect.ts +0 -143
- package/src/hooks/claude.ts +0 -596
- package/src/hooks/codex.ts +0 -517
- package/src/legacy/classifier.ts +0 -215
- package/src/legacy/detect.ts +0 -143
- package/src/mcp/client-server.ts +0 -21
- package/src/mcp/http-handler.ts +0 -126
- package/src/mcp/index.ts +0 -61
- package/src/mcp/resources/config-current.ts +0 -19
- package/src/mcp/resources/index.ts +0 -11
- package/src/mcp/resources/sessions-active.ts +0 -19
- package/src/mcp/resources/sessions-all.ts +0 -19
- package/src/mcp/schemas/sessions.ts +0 -3
- package/src/mcp/server.ts +0 -17
- package/src/mcp/services/api-adapter.ts +0 -48
- package/src/mcp/services/data-source.ts +0 -22
- package/src/mcp/services/store-adapter.ts +0 -40
- package/src/mcp/tools/get-active-sessions.ts +0 -16
- package/src/mcp/tools/get-session-children.ts +0 -24
- package/src/mcp/tools/get-session-messages.ts +0 -24
- package/src/mcp/tools/get-session.ts +0 -23
- package/src/mcp/tools/index.ts +0 -15
- package/src/mcp/tools/list-sessions.ts +0 -16
- package/src/oc/index.ts +0 -302
- package/src/service/manager.ts +0 -284
- package/src/shared/config.ts +0 -511
- package/src/shared/ipc.ts +0 -80
- package/src/shared/logger.ts +0 -248
- package/src/shared/paths.ts +0 -73
- package/src/shared/platform.ts +0 -110
- package/src/shared/session-exclusions.ts +0 -5
- package/src/shared/session-view.ts +0 -302
- package/src/shared/terminal-capabilities.ts +0 -130
- package/src/shared/types.ts +0 -330
- package/src/shared/utils.ts +0 -339
- package/src/shared/version.ts +0 -44
- package/src/tui/App.tsx +0 -240
- package/src/tui/client.ts +0 -163
- package/src/tui/components/BindingsModal.tsx +0 -82
- package/src/tui/components/DashboardHeader.tsx +0 -256
- package/src/tui/components/KeyBar.tsx +0 -67
- package/src/tui/components/SessionDetailModal.tsx +0 -169
- package/src/tui/components/SessionsView.tsx +0 -184
- package/src/tui/components/TabBar.tsx +0 -40
- package/src/tui/components/UsageSummaryPane.tsx +0 -272
- package/src/tui/components/UsageView.tsx +0 -242
- package/src/tui/components/tool-icons.ts +0 -24
- package/src/tui/index.tsx +0 -106
- package/src/tui/keybindings.ts +0 -49
- package/src/tui/model.ts +0 -295
- package/src/tui/state.ts +0 -77
- package/src/tui/theme.ts +0 -110
- package/src/types.ts +0 -113
- package/src/usage/README.md +0 -323
- package/src/usage/api.ts +0 -99
- package/src/usage/client.ts +0 -3
- package/src/usage/clients/anthropic-oauth-usage.ts +0 -258
- package/src/usage/clients/anthropic-web-usage.ts +0 -329
- package/src/usage/clients/claude-cli-probe.ts +0 -513
- package/src/usage/clients/codex-app-server.ts +0 -354
- package/src/usage/clients/codex-oauth-usage.ts +0 -240
- package/src/usage/clients/opencode-go-api.ts +0 -100
- package/src/usage/clients/opencode-go-usage.ts +0 -577
- package/src/usage/errors.ts +0 -33
- package/src/usage/index.ts +0 -20
- package/src/usage/local/opencode-go-local.ts +0 -194
- package/src/usage/mappers.ts +0 -282
- package/src/usage/planner.ts +0 -118
- package/src/usage/providers/claude.ts +0 -233
- package/src/usage/providers/codex.ts +0 -115
- package/src/usage/providers/opencode-go.ts +0 -115
- package/src/usage/resolver.ts +0 -4
- package/src/usage/resolvers/browser-cookies.ts +0 -808
- package/src/usage/resolvers/claude-auth.ts +0 -208
- package/src/usage/resolvers/claude-cli.ts +0 -24
- package/src/usage/resolvers/claude-desktop-auth.ts +0 -170
- package/src/usage/resolvers/claude-web.ts +0 -82
- package/src/usage/resolvers/codex-auth.ts +0 -174
- package/src/usage/resolvers/keychain-coordinator.ts +0 -397
- package/src/usage/resolvers/opencode-go-web.ts +0 -55
- package/src/usage/types.ts +0 -225
- package/src/usage/web-session-cache.ts +0 -37
- /package/{src/tui → dist/cli}/assets/providers/SOURCES.md +0 -0
- /package/{src/tui → dist/cli}/assets/providers/claude-terminal.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/claude-terminal.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/claude.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/copilot.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/copilot.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/openai-terminal.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/openai-terminal.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/openai.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/opencode-terminal.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/opencode-terminal.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/opencode.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/shell.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/shell.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/unknown.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/unknown.svg +0 -0
package/dist/oc/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
var D=Object.create;var{getPrototypeOf:I,defineProperty:Q,getOwnPropertyNames:$}=Object;var V=Object.prototype.hasOwnProperty;function X(q){return this[q]}var h=(q,y,z)=>{var E=$(y);for(let B of E)if(!V.call(q,B)&&B!=="default")Q(q,B,{get:X.bind(y,B),enumerable:!0});if(z){for(let B of E)if(!V.call(z,B)&&B!=="default")Q(z,B,{get:X.bind(y,B),enumerable:!0});return z}},T,U,A=(q,y,z)=>{var E=q!=null&&typeof q==="object";if(E){var B=y?T??=new WeakMap:U??=new WeakMap,G=B.get(q);if(G)return G}z=q!=null?D(I(q)):{};let K=y||!q||!q.__esModule?Q(z,"default",{value:q,enumerable:!0}):z;for(let J of $(q))if(!V.call(K,J))Q(K,J,{get:X.bind(q,J),enumerable:!0});if(E)B.set(q,K);return K};var g=(q,y)=>()=>(y||q((y={exports:{}}).exports,y),y.exports);var k=(q)=>q;function S(q,y){this[q]=k.bind(null,y)}var p=(q,y)=>{for(var z in y)Q(q,z,{get:y[z],enumerable:!0,configurable:!0,set:S.bind(y,z)})};var l=(q,y)=>()=>(q&&(y=q(q=0)),y);var u=(q)=>Promise.all(q),m=import.meta.require;function W(){process.stdout.write(`oc
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
oc [opencode args...]
|
|
7
|
+
|
|
8
|
+
Environment variables:
|
|
9
|
+
OPENCODE_LOCAL_PORT Force a specific port
|
|
10
|
+
OPENCODE_HOST Force a specific host
|
|
11
|
+
|
|
12
|
+
Behavior:
|
|
13
|
+
Auto-discovers running OpenCode instances and attaches when possible.
|
|
14
|
+
Launches a new instance on port 4096 when none is available.
|
|
15
|
+
`)}function F(){let q=Bun.spawnSync(["ps","-ax","-o","pid=,command="],{stdout:"pipe",stderr:"pipe"});if(q.exitCode!==0)return[];let y=Buffer.from(q.stdout).toString("utf8");return M(y,/^\s*(\d+)\s+(.*)$/)}function L(){let q=Bun.spawnSync(["wmic","process","get","ProcessId,CommandLine","/format:csv"],{stdout:"pipe",stderr:"pipe"});if(q.exitCode!==0)return[];let y=Buffer.from(q.stdout).toString("utf8"),z=[];for(let E of y.split(/\r?\n/).filter(Boolean)){let B=E.split(",");if(B.length<3)continue;let G=B.slice(1,-1).join(","),K=B[B.length-1]??"",J=Number.parseInt(K.trim(),10);if(Number.isNaN(J))continue;let N=j(G,J);if(N)z.push(N)}return z}function M(q,y){let z=[];for(let E of q.split(/\r?\n/).filter(Boolean)){let B=E.match(y);if(!B)continue;let G=Number.parseInt(B[1]??"",10),K=B[2]??"",J=j(K,G);if(J)z.push(J)}return z}function j(q,y){let z=/\bopencode\b.*\bserve\b/i.test(q),E=/\bopencode\b/i.test(q)&&/--port[=\s]+([1-9]\d*)/.test(q)&&!/\bserve\b/i.test(q);if(!z&&!E)return null;let B=q.match(/--port[=\s]+(\d+)/),G=q.match(/--hostname[=\s]+(\S+)/),K=B?Number.parseInt(B[1]??"",10):4096,J=G?G[1]??"127.0.0.1":"127.0.0.1";return{url:`http://${J==="0.0.0.0"?"127.0.0.1":J}:${K}`,pid:y}}function f(){let q=process.platform==="win32"?L():F(),y=new Set,z=q.filter((E)=>{if(y.has(E.url))return!1;return y.add(E.url),!0});if(!y.has("http://127.0.0.1:4096"))z.push({url:"http://127.0.0.1:4096"});return z}async function Z(q){try{return(await fetch(`${q}/global/health`,{signal:AbortSignal.timeout(800)})).ok}catch{return!1}}async function H(q){return(await Promise.all(q.map(async(z)=>({candidate:z,alive:await Z(z.url)})))).filter((z)=>z.alive).map((z)=>z.candidate)}async function b(q){let y=[`Found ${q.length} running OpenCode instances:`,...q.map((z,E)=>` [${E+1}] ${z.url}${z.pid!==void 0?` (pid ${z.pid})`:""}`),`Select [1-${q.length}]: `];process.stderr.write(y.join(`
|
|
16
|
+
`));for(let z=0;z<3;z++){let E=await C(),B=Number.parseInt(E.trim(),10);if(!Number.isNaN(B)&&B>=1&&B<=q.length)return q[B-1];if(z<2)process.stderr.write(`Invalid choice. Select [1-${q.length}]: `)}process.stderr.write(`
|
|
17
|
+
Invalid selection. Exiting.
|
|
18
|
+
`),process.exit(1)}function C(){return new Promise((q)=>{let y="";process.stdin.setEncoding("utf8"),process.stdin.resume(),process.stdin.once("data",(z)=>{process.stdin.pause(),y+=z,q(y.split(`
|
|
19
|
+
`)[0]??"")})})}async function O(q){let z=await Bun.spawn(q,{stdio:["inherit","inherit","inherit"]}).exited;process.exit(z??0)}function P(q){return q.includes("--dir")||q.some((y)=>y.startsWith("--dir="))}function w(q){let y=q[0];return y!==void 0&&!y.startsWith("-")}function R(q,y,z){return P(y)?["opencode","attach",q,...y]:["opencode","attach",q,"--dir",z,...y]}function Y(q,y,z,E){let G=["opencode",...w(q)?[...q]:[y,...q]];if(z!==void 0)G.push("--port",z);if(E!==void 0)G.push("--hostname",E);return G}async function v(){let q=process.argv.slice(2);if(q.includes("--help")||q.includes("-h"))W(),process.exit(0);let y=process.cwd(),z=process.env.OPENCODE_LOCAL_PORT,E=process.env.OPENCODE_HOST;if(z!==void 0&&E!==void 0){let J=`http://${E}:${z}`;if(await Z(J))await O(R(J,q,y));else await O(Y(q,y,z,E));return}if(z!==void 0||E!==void 0){let J=z??String(4096),N=E??"127.0.0.1",_=`http://${N}:${J}`;if(await Z(_))await O(R(_,q,y));else await O(Y(q,y,J,N));return}let B=f(),G=await H(B);if(G.length===0){await O(Y(q,y,String(4096)));return}if(G.length===1){await O(R(G[0].url,q,y));return}let K=await b(G);await O(R(K.url,q,y))}if(import.meta.main)await v();export{Y as buildLaunchCommand,R as buildAttachCommand};
|
|
20
|
+
|
|
21
|
+
//# debugId=CE8E69F7FD96D16564756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/oc/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"#!/usr/bin/env bun\n/**\n * oc — OpenCode launcher / attach helper\n *\n * Usage:\n * oc [args...] Auto-discover running instance → attach or launch\n * oc [args...] All extra args are forwarded to the opencode command\n *\n * Environment variables:\n * OPENCODE_LOCAL_PORT Force a specific port (skips discovery)\n * OPENCODE_HOST Force a specific host (skips discovery, default 127.0.0.1)\n *\n * When both env vars are absent, oc discovers running opencode processes via\n * process scanning (ps on Unix, wmic on Windows), probes their health endpoints,\n * and either attaches to a live instance or launches a new one on port 4096.\n *\n * If multiple live instances are found an interactive numbered selector is shown.\n */\n\nconst DEFAULT_PORT = 4096;\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst HEALTH_TIMEOUT_MS = 800;\n\nfunction printHelp() {\n process.stdout.write(`oc\n\nUsage:\n oc [opencode args...]\n\nEnvironment variables:\n OPENCODE_LOCAL_PORT Force a specific port\n OPENCODE_HOST Force a specific host\n\nBehavior:\n Auto-discovers running OpenCode instances and attaches when possible.\n Launches a new instance on port 4096 when none is available.\n`);\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\ninterface Candidate {\n url: string;\n pid?: number;\n}\n\n// ── Process scanning ──────────────────────────────────────────────────────────\n\nfunction scanProcessesUnix(): Candidate[] {\n const proc = Bun.spawnSync([\"ps\", \"-ax\", \"-o\", \"pid=,command=\"], {\n stdout: \"pipe\",\n stderr: \"pipe\",\n });\n if (proc.exitCode !== 0) return [];\n\n const output = Buffer.from(proc.stdout).toString(\"utf8\");\n return parseProcessLines(output, /^\\s*(\\d+)\\s+(.*)$/);\n}\n\nfunction scanProcessesWindows(): Candidate[] {\n const proc = Bun.spawnSync(\n [\"wmic\", \"process\", \"get\", \"ProcessId,CommandLine\", \"/format:csv\"],\n { stdout: \"pipe\", stderr: \"pipe\" },\n );\n if (proc.exitCode !== 0) return [];\n\n const output = Buffer.from(proc.stdout).toString(\"utf8\");\n const candidates: Candidate[] = [];\n\n for (const line of output.split(/\\r?\\n/).filter(Boolean)) {\n // CSV columns: Node,CommandLine,ProcessId\n const cols = line.split(\",\");\n if (cols.length < 3) continue;\n const command = cols.slice(1, -1).join(\",\"); // CommandLine may contain commas\n const pidStr = cols[cols.length - 1] ?? \"\";\n const pid = Number.parseInt(pidStr.trim(), 10);\n if (Number.isNaN(pid)) continue;\n\n const candidate = extractFromCommand(command, pid);\n if (candidate) candidates.push(candidate);\n }\n\n return candidates;\n}\n\nfunction parseProcessLines(\n output: string,\n linePattern: RegExp,\n): Candidate[] {\n const candidates: Candidate[] = [];\n\n for (const line of output.split(/\\r?\\n/).filter(Boolean)) {\n const match = line.match(linePattern);\n if (!match) continue;\n const pid = Number.parseInt(match[1] ?? \"\", 10);\n const command = match[2] ?? \"\";\n const candidate = extractFromCommand(command, pid);\n if (candidate) candidates.push(candidate);\n }\n\n return candidates;\n}\n\nfunction extractFromCommand(command: string, pid: number): Candidate | null {\n const isServe = /\\bopencode\\b.*\\bserve\\b/i.test(command);\n const isTuiWithPort =\n /\\bopencode\\b/i.test(command) &&\n /--port[=\\s]+([1-9]\\d*)/.test(command) &&\n !/\\bserve\\b/i.test(command);\n\n if (!isServe && !isTuiWithPort) return null;\n\n const portMatch = command.match(/--port[=\\s]+(\\d+)/);\n const hostMatch = command.match(/--hostname[=\\s]+(\\S+)/);\n const port = portMatch ? Number.parseInt(portMatch[1] ?? \"\", 10) : DEFAULT_PORT;\n const rawHost = hostMatch ? (hostMatch[1] ?? DEFAULT_HOST) : DEFAULT_HOST;\n // 0.0.0.0 listens on all interfaces — connect via loopback\n const host = rawHost === \"0.0.0.0\" ? \"127.0.0.1\" : rawHost;\n\n return { url: `http://${host}:${port}`, pid };\n}\n\nfunction discoverCandidates(): Candidate[] {\n const raw =\n process.platform === \"win32\" ? scanProcessesWindows() : scanProcessesUnix();\n\n // Deduplicate by URL\n const seen = new Set<string>();\n const deduped = raw.filter((c) => {\n if (seen.has(c.url)) return false;\n seen.add(c.url);\n return true;\n });\n\n // Always include the default endpoint as a fallback\n if (!seen.has(`http://${DEFAULT_HOST}:${DEFAULT_PORT}`)) {\n deduped.push({ url: `http://${DEFAULT_HOST}:${DEFAULT_PORT}` });\n }\n\n return deduped;\n}\n\n// ── Health probe ──────────────────────────────────────────────────────────────\n\nasync function isAlive(url: string): Promise<boolean> {\n try {\n const res = await fetch(`${url}/global/health`, {\n signal: AbortSignal.timeout(HEALTH_TIMEOUT_MS),\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\nasync function filterAlive(candidates: Candidate[]): Promise<Candidate[]> {\n const results = await Promise.all(\n candidates.map(async (c) => ({ candidate: c, alive: await isAlive(c.url) })),\n );\n return results.filter((r) => r.alive).map((r) => r.candidate);\n}\n\n// ── Interactive selector ──────────────────────────────────────────────────────\n\nasync function selectInstance(candidates: Candidate[]): Promise<Candidate> {\n const lines = [\n `Found ${candidates.length} running OpenCode instances:`,\n ...candidates.map(\n (c, i) =>\n ` [${i + 1}] ${c.url}${c.pid !== undefined ? ` (pid ${c.pid})` : \"\"}`,\n ),\n `Select [1-${candidates.length}]: `,\n ];\n\n process.stderr.write(lines.join(\"\\n\"));\n\n // Read a line from stdin\n for (let attempt = 0; attempt < 3; attempt++) {\n const raw = await readLine();\n const n = Number.parseInt(raw.trim(), 10);\n if (!Number.isNaN(n) && n >= 1 && n <= candidates.length) {\n return candidates[n - 1]!;\n }\n if (attempt < 2) {\n process.stderr.write(`Invalid choice. Select [1-${candidates.length}]: `);\n }\n }\n\n process.stderr.write(\"\\nInvalid selection. Exiting.\\n\");\n process.exit(1);\n}\n\nfunction readLine(): Promise<string> {\n return new Promise((resolve) => {\n let buffer = \"\";\n process.stdin.setEncoding(\"utf8\");\n process.stdin.resume();\n process.stdin.once(\"data\", (chunk: string) => {\n process.stdin.pause();\n buffer += chunk;\n resolve(buffer.split(\"\\n\")[0] ?? \"\");\n });\n });\n}\n\n// ── Spawn helpers ─────────────────────────────────────────────────────────────\n\nasync function run(cmd: string[]): Promise<void> {\n const proc = Bun.spawn(cmd, {\n stdio: [\"inherit\", \"inherit\", \"inherit\"],\n });\n const code = await proc.exited;\n process.exit(code ?? 0);\n}\n\nfunction hasDirFlag(args: string[]): boolean {\n return args.includes(\"--dir\") || args.some((arg) => arg.startsWith(\"--dir=\"));\n}\n\nfunction hasProjectArg(args: string[]): boolean {\n const first = args[0];\n return first !== undefined && !first.startsWith(\"-\");\n}\n\nexport function buildAttachCommand(url: string, extraArgs: string[], cwd: string): string[] {\n return hasDirFlag(extraArgs)\n ? [\"opencode\", \"attach\", url, ...extraArgs]\n : [\"opencode\", \"attach\", url, \"--dir\", cwd, ...extraArgs];\n}\n\nexport function buildLaunchCommand(extraArgs: string[], cwd: string, port?: string, host?: string): string[] {\n const args = hasProjectArg(extraArgs) ? [...extraArgs] : [cwd, ...extraArgs];\n const command = [\"opencode\", ...args];\n if (port !== undefined) command.push(\"--port\", port);\n if (host !== undefined) command.push(\"--hostname\", host);\n return command;\n}\n\n// ── Main ──────────────────────────────────────────────────────────────────────\n\nasync function main(): Promise<void> {\n const extraArgs = process.argv.slice(2);\n if (extraArgs.includes(\"--help\") || extraArgs.includes(\"-h\")) {\n printHelp();\n process.exit(0);\n }\n const cwd = process.cwd();\n\n const envPort = process.env[\"OPENCODE_LOCAL_PORT\"];\n const envHost = process.env[\"OPENCODE_HOST\"];\n\n // ── Explicit mode: both env vars set ───────────────────────────────────────\n if (envPort !== undefined && envHost !== undefined) {\n const url = `http://${envHost}:${envPort}`;\n const alive = await isAlive(url);\n if (alive) {\n await run(buildAttachCommand(url, extraArgs, cwd));\n } else {\n await run(buildLaunchCommand(extraArgs, cwd, envPort, envHost));\n }\n return;\n }\n\n // Partial override: only port or only host specified\n if (envPort !== undefined || envHost !== undefined) {\n const port = envPort ?? String(DEFAULT_PORT);\n const host = envHost ?? DEFAULT_HOST;\n const url = `http://${host}:${port}`;\n const alive = await isAlive(url);\n if (alive) {\n await run(buildAttachCommand(url, extraArgs, cwd));\n } else {\n await run(buildLaunchCommand(extraArgs, cwd, port, host));\n }\n return;\n }\n\n // ── Auto-discovery mode ────────────────────────────────────────────────────\n const candidates = discoverCandidates();\n const alive = await filterAlive(candidates);\n\n if (alive.length === 0) {\n // Nothing running — launch on default port\n await run(buildLaunchCommand(extraArgs, cwd, String(DEFAULT_PORT)));\n return;\n }\n\n if (alive.length === 1) {\n await run(buildAttachCommand(alive[0]!.url, extraArgs, cwd));\n return;\n }\n\n // Multiple instances — show selector\n const chosen = await selectInstance(alive);\n await run(buildAttachCommand(chosen.url, extraArgs, cwd));\n}\n\nif (import.meta.main) {\n await main();\n}\n\nexport {};\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "CE8E69F7FD96D16564756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrylog/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Terminal dashboard for monitoring tmux sessions running AI coding agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,15 +31,16 @@
|
|
|
31
31
|
"bun": ">=1.3.0"
|
|
32
32
|
},
|
|
33
33
|
"bin": {
|
|
34
|
-
"scrylog": "
|
|
35
|
-
"scrylogd": "
|
|
36
|
-
"scrylog-mcp": "
|
|
37
|
-
"scrylog-claude-hook": "
|
|
38
|
-
"scrylog-codex-hook": "
|
|
39
|
-
"oc": "
|
|
34
|
+
"scrylog": "dist/cli/index.js",
|
|
35
|
+
"scrylogd": "dist/daemon/index.js",
|
|
36
|
+
"scrylog-mcp": "dist/mcp/index.js",
|
|
37
|
+
"scrylog-claude-hook": "dist/hooks/claude.js",
|
|
38
|
+
"scrylog-codex-hook": "dist/hooks/codex.js",
|
|
39
|
+
"oc": "dist/oc/index.js"
|
|
40
40
|
},
|
|
41
41
|
"files": [
|
|
42
|
-
"
|
|
42
|
+
"dist",
|
|
43
|
+
"src/postinstall.cjs",
|
|
43
44
|
"README.md",
|
|
44
45
|
"LICENSE"
|
|
45
46
|
],
|
|
@@ -52,16 +53,20 @@
|
|
|
52
53
|
"dev": "bun run src/cli/index.ts",
|
|
53
54
|
"dev:daemon": "bun run src/daemon/index.ts --loglevel debug",
|
|
54
55
|
"start": "bun run src/cli/index.ts",
|
|
56
|
+
"build": "bun run build.ts",
|
|
57
|
+
"prepack": "bun run build",
|
|
55
58
|
"postinstall": "bun src/postinstall.cjs",
|
|
56
59
|
"check-types": "tsc --noEmit",
|
|
57
60
|
"test": "bun test",
|
|
58
61
|
"pack:dry-run": "bash ../../scripts/npm-pack.sh \"$PWD\" \"\" --dry-run"
|
|
59
62
|
},
|
|
60
63
|
"dependencies": {
|
|
64
|
+
"@opentui/core": "0.5.11"
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
61
67
|
"@hono/zod-openapi": "^1.3.0",
|
|
62
68
|
"@joeldmtz/prtl-core": "^0.3.0",
|
|
63
69
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
64
|
-
"@opentui/core": "0.5.11",
|
|
65
70
|
"@opentui/react": "0.5.11",
|
|
66
71
|
"@opencode-ai/sdk": "1.4.10",
|
|
67
72
|
"@scalar/hono-api-reference": "^0.10.11",
|
|
@@ -69,12 +74,10 @@
|
|
|
69
74
|
"commander": "15.0.0",
|
|
70
75
|
"hono": "^4.12.15",
|
|
71
76
|
"react": "19.2.7",
|
|
72
|
-
"zod": "^4.3.6"
|
|
73
|
-
},
|
|
74
|
-
"devDependencies": {
|
|
75
77
|
"@types/bun": "^1.3.14",
|
|
76
78
|
"@types/node": "^24.3.0",
|
|
77
79
|
"@types/react": "19.2.17",
|
|
78
|
-
"typescript": "5.9.3"
|
|
80
|
+
"typescript": "5.9.3",
|
|
81
|
+
"zod": "^4.3.6"
|
|
79
82
|
}
|
|
80
83
|
}
|
package/src/api/docs/sessions.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import type { RouteConfig } from "@hono/zod-openapi";
|
|
2
|
-
import { ApiErrorSchema, IncludeUnknownQuerySchema, SessionIdParamSchema } from "../schemas/common";
|
|
3
|
-
import {
|
|
4
|
-
SessionsResponseSchema,
|
|
5
|
-
SessionDetailResponseSchema,
|
|
6
|
-
SessionChildrenResponseSchema,
|
|
7
|
-
SessionMessagesResponseSchema,
|
|
8
|
-
} from "../schemas/sessions";
|
|
9
|
-
import { SessionRouteKey } from "./sessions.keys";
|
|
10
|
-
|
|
11
|
-
export const SessionRoutes = {
|
|
12
|
-
[SessionRouteKey.LIST_SESSIONS]: {
|
|
13
|
-
method: "get",
|
|
14
|
-
path: "/sessions",
|
|
15
|
-
tags: ["Sessions"],
|
|
16
|
-
summary: "List sessions",
|
|
17
|
-
description: "Returns the full session tree with counts.",
|
|
18
|
-
security: [{ BearerAuth: [] }],
|
|
19
|
-
request: { query: IncludeUnknownQuerySchema },
|
|
20
|
-
responses: {
|
|
21
|
-
200: {
|
|
22
|
-
content: { "application/json": { schema: SessionsResponseSchema } },
|
|
23
|
-
description: "Session tree",
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
[SessionRouteKey.SESSION_CHILDREN]: {
|
|
28
|
-
method: "get",
|
|
29
|
-
path: "/sessions/{id}/children",
|
|
30
|
-
tags: ["Sessions"],
|
|
31
|
-
summary: "List session children",
|
|
32
|
-
security: [{ BearerAuth: [] }],
|
|
33
|
-
request: { params: SessionIdParamSchema, query: IncludeUnknownQuerySchema },
|
|
34
|
-
responses: {
|
|
35
|
-
200: {
|
|
36
|
-
content: { "application/json": { schema: SessionChildrenResponseSchema } },
|
|
37
|
-
description: "Child sessions",
|
|
38
|
-
},
|
|
39
|
-
404: {
|
|
40
|
-
content: { "application/json": { schema: ApiErrorSchema } },
|
|
41
|
-
description: "Session not found",
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
[SessionRouteKey.SESSION_MESSAGES]: {
|
|
46
|
-
method: "get",
|
|
47
|
-
path: "/sessions/{id}/messages",
|
|
48
|
-
tags: ["Sessions"],
|
|
49
|
-
summary: "List session messages",
|
|
50
|
-
description: "Message history for a session (placeholder — always returns empty array).",
|
|
51
|
-
security: [{ BearerAuth: [] }],
|
|
52
|
-
request: { params: SessionIdParamSchema },
|
|
53
|
-
responses: {
|
|
54
|
-
200: {
|
|
55
|
-
content: { "application/json": { schema: SessionMessagesResponseSchema } },
|
|
56
|
-
description: "Message list",
|
|
57
|
-
},
|
|
58
|
-
404: {
|
|
59
|
-
content: { "application/json": { schema: ApiErrorSchema } },
|
|
60
|
-
description: "Session not found",
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
[SessionRouteKey.SESSION_DETAIL]: {
|
|
65
|
-
method: "get",
|
|
66
|
-
path: "/sessions/{id}",
|
|
67
|
-
tags: ["Sessions"],
|
|
68
|
-
summary: "Get session",
|
|
69
|
-
security: [{ BearerAuth: [] }],
|
|
70
|
-
request: { params: SessionIdParamSchema },
|
|
71
|
-
responses: {
|
|
72
|
-
200: {
|
|
73
|
-
content: { "application/json": { schema: SessionDetailResponseSchema } },
|
|
74
|
-
description: "Session detail",
|
|
75
|
-
},
|
|
76
|
-
404: {
|
|
77
|
-
content: { "application/json": { schema: ApiErrorSchema } },
|
|
78
|
-
description: "Session not found",
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
} satisfies Record<SessionRouteKey, RouteConfig>;
|
package/src/api/docs/system.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { z } from "@hono/zod-openapi";
|
|
2
|
-
import type { RouteConfig } from "@hono/zod-openapi";
|
|
3
|
-
import { IncludeUnknownQuerySchema } from "../schemas/common";
|
|
4
|
-
import { SystemRouteKey } from "./system.keys";
|
|
5
|
-
|
|
6
|
-
export const SystemRoutes = {
|
|
7
|
-
[SystemRouteKey.HEALTH]: {
|
|
8
|
-
method: "get",
|
|
9
|
-
path: "/health",
|
|
10
|
-
tags: ["System"],
|
|
11
|
-
summary: "Health check",
|
|
12
|
-
responses: {
|
|
13
|
-
200: {
|
|
14
|
-
content: {
|
|
15
|
-
"application/json": {
|
|
16
|
-
schema: z.object({
|
|
17
|
-
ok: z.literal(true),
|
|
18
|
-
version: z.string(),
|
|
19
|
-
startedAt: z.number(),
|
|
20
|
-
refreshedAt: z.number(),
|
|
21
|
-
collectors: z.record(z.string(), z.unknown()),
|
|
22
|
-
}),
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
description: "Daemon is running",
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
[SystemRouteKey.STATUS]: {
|
|
30
|
-
method: "get",
|
|
31
|
-
path: "/status",
|
|
32
|
-
tags: ["System"],
|
|
33
|
-
summary: "Session counts and collector status",
|
|
34
|
-
security: [{ BearerAuth: [] }],
|
|
35
|
-
request: { query: IncludeUnknownQuerySchema },
|
|
36
|
-
responses: {
|
|
37
|
-
200: {
|
|
38
|
-
content: {
|
|
39
|
-
"application/json": {
|
|
40
|
-
schema: z.object({
|
|
41
|
-
counts: z.record(z.string(), z.unknown()),
|
|
42
|
-
collectorStatus: z.record(z.string(), z.unknown()),
|
|
43
|
-
generatedAt: z.number(),
|
|
44
|
-
}),
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
description: "Current status",
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
[SystemRouteKey.CONFIG]: {
|
|
52
|
-
method: "get",
|
|
53
|
-
path: "/config",
|
|
54
|
-
tags: ["System"],
|
|
55
|
-
summary: "Current daemon configuration",
|
|
56
|
-
description: "Bearer token is redacted in the response.",
|
|
57
|
-
security: [{ BearerAuth: [] }],
|
|
58
|
-
responses: {
|
|
59
|
-
200: {
|
|
60
|
-
content: {
|
|
61
|
-
"application/json": {
|
|
62
|
-
schema: z.object({ config: z.record(z.string(), z.unknown()) }),
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
description: "Configuration",
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
} satisfies Record<SystemRouteKey, RouteConfig>;
|
package/src/api/docs/usage.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { z } from "@hono/zod-openapi";
|
|
2
|
-
import type { RouteConfig } from "@hono/zod-openapi";
|
|
3
|
-
import { UsageRouteKey } from "./usage.keys";
|
|
4
|
-
import { UsageResponseSchema } from "../schemas/usage";
|
|
5
|
-
|
|
6
|
-
export const UsageRoutes = {
|
|
7
|
-
[UsageRouteKey.GET_USAGE]: {
|
|
8
|
-
method: "get",
|
|
9
|
-
path: "/usage",
|
|
10
|
-
tags: ["Usage"],
|
|
11
|
-
summary: "Get subscription usage and extra-use credits",
|
|
12
|
-
security: [{ BearerAuth: [] }],
|
|
13
|
-
responses: {
|
|
14
|
-
200: {
|
|
15
|
-
content: {
|
|
16
|
-
"application/json": {
|
|
17
|
-
schema: UsageResponseSchema,
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
description: "Current limits, extra-usage spend, and balances for configured providers",
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
} satisfies Record<UsageRouteKey, RouteConfig>;
|
package/src/api/docs/webhooks.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import type { RouteConfig } from "@hono/zod-openapi";
|
|
2
|
-
import { ApiErrorSchema, WebhookOkSchema } from "../schemas/common";
|
|
3
|
-
import { OpencodeWebhookBodySchema } from "../schemas/webhooks/opencode";
|
|
4
|
-
import { ClaudeWebhookBodySchema } from "../schemas/webhooks/claude";
|
|
5
|
-
import { CodexWebhookBodySchema } from "../schemas/webhooks/codex";
|
|
6
|
-
import { WebhookRouteKey } from "./webhooks.keys";
|
|
7
|
-
|
|
8
|
-
export const WebhookRoutes = {
|
|
9
|
-
[WebhookRouteKey.WEBHOOK_OPENCODE]: {
|
|
10
|
-
method: "post",
|
|
11
|
-
path: "/opencode",
|
|
12
|
-
tags: ["Webhooks"],
|
|
13
|
-
summary: "OpenCode session hook",
|
|
14
|
-
description: "Receives lifecycle events from OpenCode webhooks.",
|
|
15
|
-
request: {
|
|
16
|
-
body: {
|
|
17
|
-
required: true,
|
|
18
|
-
content: { "application/json": { schema: OpencodeWebhookBodySchema } },
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
responses: {
|
|
22
|
-
200: {
|
|
23
|
-
content: { "application/json": { schema: WebhookOkSchema } },
|
|
24
|
-
description: "Event accepted",
|
|
25
|
-
},
|
|
26
|
-
400: {
|
|
27
|
-
content: { "application/json": { schema: ApiErrorSchema } },
|
|
28
|
-
description: "Missing required field",
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
[WebhookRouteKey.WEBHOOK_CLAUDE]: {
|
|
33
|
-
method: "post",
|
|
34
|
-
path: "/claude",
|
|
35
|
-
tags: ["Webhooks"],
|
|
36
|
-
summary: "Claude session hook",
|
|
37
|
-
description: "Receives lifecycle events from the Claude CLI hooks system.",
|
|
38
|
-
request: {
|
|
39
|
-
body: {
|
|
40
|
-
required: true,
|
|
41
|
-
content: { "application/json": { schema: ClaudeWebhookBodySchema } },
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
responses: {
|
|
45
|
-
200: {
|
|
46
|
-
content: { "application/json": { schema: WebhookOkSchema } },
|
|
47
|
-
description: "Event accepted",
|
|
48
|
-
},
|
|
49
|
-
400: {
|
|
50
|
-
content: { "application/json": { schema: ApiErrorSchema } },
|
|
51
|
-
description: "Missing required field",
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
[WebhookRouteKey.WEBHOOK_CODEX]: {
|
|
56
|
-
method: "post",
|
|
57
|
-
path: "/codex",
|
|
58
|
-
tags: ["Webhooks"],
|
|
59
|
-
summary: "Codex session hook",
|
|
60
|
-
description: "Receives lifecycle events from the Codex CLI hooks system.",
|
|
61
|
-
request: {
|
|
62
|
-
body: {
|
|
63
|
-
required: true,
|
|
64
|
-
content: { "application/json": { schema: CodexWebhookBodySchema } },
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
responses: {
|
|
68
|
-
200: {
|
|
69
|
-
content: { "application/json": { schema: WebhookOkSchema } },
|
|
70
|
-
description: "Event accepted",
|
|
71
|
-
},
|
|
72
|
-
400: {
|
|
73
|
-
content: { "application/json": { schema: ApiErrorSchema } },
|
|
74
|
-
description: "Missing required field",
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
} satisfies Record<WebhookRouteKey, RouteConfig>;
|
package/src/api/index.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { ScrylogConfig } from "../shared/config";
|
|
2
|
-
import type { DaemonStore } from "../daemon/store";
|
|
3
|
-
import type { GitCollector } from "../daemon/collectors/git";
|
|
4
|
-
import type { Logger } from "../shared/logger";
|
|
5
|
-
import { createApiApp } from "./routes";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Create the fetch handler for the daemon HTTP API.
|
|
9
|
-
* Used by both the Unix socket and TCP listeners (same handler, two servers).
|
|
10
|
-
*/
|
|
11
|
-
export function createApiHandler(
|
|
12
|
-
store: DaemonStore,
|
|
13
|
-
config: ScrylogConfig,
|
|
14
|
-
logger: Logger,
|
|
15
|
-
gitCollector?: GitCollector,
|
|
16
|
-
): (request: Request) => Response | Promise<Response> {
|
|
17
|
-
return createApiApp(store, config, logger, gitCollector).fetch;
|
|
18
|
-
}
|
package/src/api/lib/auth.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { timingSafeEqual } from "node:crypto";
|
|
2
|
-
import type { MiddlewareHandler } from "hono";
|
|
3
|
-
import { UnauthorizedError } from "./errors";
|
|
4
|
-
|
|
5
|
-
function secureCompare(a: string, b: string): boolean {
|
|
6
|
-
const bufA = Buffer.from(a, "utf-8");
|
|
7
|
-
const bufB = Buffer.from(b, "utf-8");
|
|
8
|
-
if (bufA.length !== bufB.length) return false;
|
|
9
|
-
return timingSafeEqual(bufA, bufB);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function createBearerAuthMiddleware(bearerToken: string | undefined): MiddlewareHandler {
|
|
13
|
-
const PUBLIC_PATHS = [
|
|
14
|
-
"/api/health",
|
|
15
|
-
"/api/docs",
|
|
16
|
-
"/api/openapi.json",
|
|
17
|
-
"/api/llms.txt",
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
return async (c, next) => {
|
|
21
|
-
if (!bearerToken) {
|
|
22
|
-
return next();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const path = c.req.path;
|
|
26
|
-
|
|
27
|
-
if (PUBLIC_PATHS.includes(path)) {
|
|
28
|
-
return next();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (path.startsWith("/api/webhook/")) {
|
|
32
|
-
return next();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const auth = c.req.header("Authorization");
|
|
36
|
-
const expected = `Bearer ${bearerToken}`;
|
|
37
|
-
if (!auth || !secureCompare(auth, expected)) {
|
|
38
|
-
throw new UnauthorizedError();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return next();
|
|
42
|
-
};
|
|
43
|
-
}
|
package/src/api/lib/errors.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export class ApiError extends Error {
|
|
2
|
-
constructor(
|
|
3
|
-
public readonly status: number,
|
|
4
|
-
public readonly code: string,
|
|
5
|
-
message: string,
|
|
6
|
-
) {
|
|
7
|
-
super(message);
|
|
8
|
-
this.name = "ApiError";
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export class NotFoundError extends ApiError {
|
|
13
|
-
constructor(resource: string, id: string) {
|
|
14
|
-
super(404, "NOT_FOUND", `${resource} '${id}' not found`);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class UnauthorizedError extends ApiError {
|
|
19
|
-
constructor() {
|
|
20
|
-
super(401, "UNAUTHORIZED", "Unauthorized");
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class ValidationError extends ApiError {
|
|
25
|
-
constructor(message: string) {
|
|
26
|
-
super(400, "VALIDATION_ERROR", message);
|
|
27
|
-
}
|
|
28
|
-
}
|
package/src/api/lib/route.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { createRoute } from "@hono/zod-openapi";
|
|
2
|
-
import { SystemRoutes } from "../docs/system";
|
|
3
|
-
import { SessionRoutes } from "../docs/sessions";
|
|
4
|
-
import { WebhookRoutes } from "../docs/webhooks";
|
|
5
|
-
import { UsageRoutes } from "../docs/usage";
|
|
6
|
-
|
|
7
|
-
const _registry = {
|
|
8
|
-
...SystemRoutes,
|
|
9
|
-
...SessionRoutes,
|
|
10
|
-
...WebhookRoutes,
|
|
11
|
-
...UsageRoutes,
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export type RouteKey = keyof typeof _registry;
|
|
15
|
-
|
|
16
|
-
export function defineRoute<K extends RouteKey>(key: K) {
|
|
17
|
-
return createRoute(_registry[key] as (typeof _registry)[K]);
|
|
18
|
-
}
|
package/src/api/models/claude.ts
DELETED
package/src/api/models/codex.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { z } from "@hono/zod-openapi";
|
|
2
|
-
import type { OpencodeWebhookBodySchema } from "../schemas/webhooks/opencode";
|
|
3
|
-
import type { SessionState } from "../../shared/types";
|
|
4
|
-
|
|
5
|
-
export type OpencodeWebhookPayload = z.infer<typeof OpencodeWebhookBodySchema>;
|
|
6
|
-
|
|
7
|
-
export interface HookStateResult {
|
|
8
|
-
state: SessionState;
|
|
9
|
-
hasAttention: boolean;
|
|
10
|
-
pendingAttentionCount?: number;
|
|
11
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
2
|
-
import type { ScrylogConfig } from "../../shared/config";
|
|
3
|
-
import type { DaemonStore } from "../../daemon/store";
|
|
4
|
-
import { buildSessionView } from "../../shared/session-view";
|
|
5
|
-
import { NotFoundError } from "../lib/errors";
|
|
6
|
-
import { SessionRouteKey } from "../docs/sessions.keys";
|
|
7
|
-
import { defineRoute } from "../lib/route";
|
|
8
|
-
|
|
9
|
-
export function createSessionsRouter(store: DaemonStore, config: ScrylogConfig): OpenAPIHono {
|
|
10
|
-
const router = new OpenAPIHono();
|
|
11
|
-
|
|
12
|
-
router.openapi(defineRoute(SessionRouteKey.LIST_SESSIONS), (c) => {
|
|
13
|
-
const { includeUnknown } = c.req.valid("query");
|
|
14
|
-
const view = buildSessionView(store.getSessions(), config, Date.now(), { includeUnknown: includeUnknown === "true" });
|
|
15
|
-
return c.json({ sessions: view.sessions, counts: view.counts, generatedAt: store.getState().refreshedAt }, 200);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
// Must be registered before /:id to avoid the wildcard swallowing the path
|
|
19
|
-
router.openapi(defineRoute(SessionRouteKey.SESSION_CHILDREN), (c) => {
|
|
20
|
-
const { id } = c.req.valid("param");
|
|
21
|
-
const { includeUnknown } = c.req.valid("query");
|
|
22
|
-
const parent = store.getSession(id);
|
|
23
|
-
if (!parent) {
|
|
24
|
-
throw new NotFoundError("Session", id);
|
|
25
|
-
}
|
|
26
|
-
const children = buildSessionView(store.getSessions(), config, Date.now(), {
|
|
27
|
-
includeUnknown: includeUnknown === "true",
|
|
28
|
-
}).sessions.find((s) => s.id === id)?.children ?? [];
|
|
29
|
-
return c.json({ sessionId: id, children }, 200);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
router.openapi(defineRoute(SessionRouteKey.SESSION_MESSAGES), (c) => {
|
|
33
|
-
const { id } = c.req.valid("param");
|
|
34
|
-
const session = store.getSession(id);
|
|
35
|
-
if (!session) {
|
|
36
|
-
throw new NotFoundError("Session", id);
|
|
37
|
-
}
|
|
38
|
-
return c.json({ sessionId: id, messages: [] }, 200);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
router.openapi(defineRoute(SessionRouteKey.SESSION_DETAIL), (c) => {
|
|
42
|
-
const { id } = c.req.valid("param");
|
|
43
|
-
const session = store.getSession(id);
|
|
44
|
-
if (!session) {
|
|
45
|
-
throw new NotFoundError("Session", id);
|
|
46
|
-
}
|
|
47
|
-
return c.json({ session: session as unknown as Record<string, unknown> }, 200);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
return router;
|
|
51
|
-
}
|