sessionpipe 0.1.0 → 0.1.1
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/cli.js +417 -140
- package/dist/cli.js.map +3 -3
- package/dist/hook.js +5 -5
- package/dist/hook.js.map +2 -2
- package/dist/index.js +283 -96
- package/dist/index.js.map +3 -3
- package/dist/run.d.ts +13 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/worker.js +315 -102
- package/dist/worker.js.map +4 -4
- package/package.json +1 -1
package/dist/hook.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// sessionpipe 0.1.
|
|
2
|
+
// sessionpipe 0.1.1 · Apache-2.0 · https://sessionpipe.org
|
|
3
3
|
|
|
4
4
|
// src/hook.ts
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
@@ -46,7 +46,7 @@ try {
|
|
|
46
46
|
const id = sessionIdOf(stdin);
|
|
47
47
|
if (!id || !beatDue(id)) finish(false);
|
|
48
48
|
}
|
|
49
|
-
mkdirSync(path.join(state, "jobs"), { recursive: true });
|
|
49
|
+
mkdirSync(path.join(state, "jobs"), { recursive: true, mode: 448 });
|
|
50
50
|
const job = {
|
|
51
51
|
harness,
|
|
52
52
|
event,
|
|
@@ -64,7 +64,7 @@ try {
|
|
|
64
64
|
at: Date.now()
|
|
65
65
|
};
|
|
66
66
|
const jobFile = path.join(state, "jobs", `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
67
|
-
writeFileSync(jobFile, JSON.stringify(job));
|
|
67
|
+
writeFileSync(jobFile, JSON.stringify(job), { mode: 384 });
|
|
68
68
|
if (!env.SESSIONPIPE_NO_WORKER) {
|
|
69
69
|
const worker = path.join(path.dirname(fileURLToPath(import.meta.url)), "worker.js");
|
|
70
70
|
const p = spawn(process.execPath, [worker, jobFile], { detached: true, stdio: "ignore", windowsHide: true, env });
|
|
@@ -111,8 +111,8 @@ function beatDue(id) {
|
|
|
111
111
|
} catch {
|
|
112
112
|
}
|
|
113
113
|
try {
|
|
114
|
-
mkdirSync(path.dirname(stamp), { recursive: true });
|
|
115
|
-
writeFileSync(stamp, "");
|
|
114
|
+
mkdirSync(path.dirname(stamp), { recursive: true, mode: 448 });
|
|
115
|
+
writeFileSync(stamp, "", { mode: 384 });
|
|
116
116
|
} catch {
|
|
117
117
|
}
|
|
118
118
|
return true;
|
package/dist/hook.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/hook.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env node\n// SPDX-License-Identifier: Apache-2.0\n// The hook path. A harness runs `node hook.js <harness> <event>`; this reads stdin,\n// writes one job file, spawns the worker detached, prints what the harness needs\n// ({} for Antigravity) and exits 0 \u2014 in milliseconds, with nothing but node:\n// builtins imported (the build checks that). No network, no schema library, no\n// config parsing beyond one small file read for the throttle.\nimport { spawn } from \"node:child_process\";\nimport { appendFileSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst t0 = process.hrtime.bigint();\nconst [harness = \"\", event = \"\", ...rest] = process.argv.slice(2);\nconst env = process.env;\n\nfunction stateDir(): string {\n if (env.SESSIONPIPE_STATE) return path.resolve(env.SESSIONPIPE_STATE);\n const home = os.homedir();\n if (process.platform === \"win32\")\n return path.join(env.LOCALAPPDATA || path.join(home, \"AppData\", \"Local\"), \"sessionpipe\");\n return path.join(env.XDG_STATE_HOME || path.join(home, \".local\", \"state\"), \"sessionpipe\");\n}\nfunction configFile(): string {\n if (env.SESSIONPIPE_CONFIG) return path.resolve(env.SESSIONPIPE_CONFIG);\n const home = os.homedir();\n if (process.platform === \"win32\")\n return path.join(env.APPDATA || path.join(home, \"AppData\", \"Roaming\"), \"sessionpipe\", \"config.json\");\n return path.join(env.XDG_CONFIG_HOME || path.join(home, \".config\"), \"sessionpipe\", \"config.json\");\n}\n\n// Antigravity parses stdout as the hook's answer; `{}` is \"carry on\" for every event.\nif (harness === \"antigravity\") {\n try {\n writeFileSync(1, \"{}\\n\");\n } catch {}\n}\n\nconst state = stateDir();\ntry {\n if (!harness || !event) throw new Error(\"usage: hook <harness> <event>\");\n let stdin = \"\";\n if (harness !== \"codex\" || event !== \"notify\") {\n try {\n const buf = readFileSync(0);\n stdin = buf.subarray(0, 1024 * 1024).toString(\"utf8\");\n } catch {}\n }\n // Tool events are the bulk of what a harness fires. When no sink takes tier \u2265 1\n // they are only a \"still working\" signal: one heartbeat a minute per session.\n const TOOL = /^(PreToolUse|PostToolUse|PostToolUseFailure|BeforeTool|AfterTool|PostInvocation)$/;\n if (TOOL.test(event) && !anySinkAbove(0)) {\n const id = sessionIdOf(stdin);\n if (!id || !beatDue(id)) finish(false);\n }\n mkdirSync(path.join(state, \"jobs\"), { recursive: true });\n const job = {\n harness,\n event,\n argv: [harness, event, ...rest],\n stdin,\n cwd: process.cwd(),\n env: pick([\n \"CLAUDE_CODE_HOST_SESSION_ID\",\n \"CLAUDE_CONFIG_DIR\",\n \"CLAUDE_PROJECT_DIR\",\n \"CODEX_HOME\",\n \"GEMINI_CLI_HOME\",\n ]),\n ppid: process.ppid,\n at: Date.now(),\n };\n const jobFile = path.join(state, \"jobs\", `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);\n writeFileSync(jobFile, JSON.stringify(job));\n if (!env.SESSIONPIPE_NO_WORKER) {\n const worker = path.join(path.dirname(fileURLToPath(import.meta.url)), \"worker.js\");\n const p = spawn(process.execPath, [worker, jobFile], { detached: true, stdio: \"ignore\", windowsHide: true, env });\n p.unref();\n }\n finish(true);\n} catch {\n finish(false); // the hook must be invisible to the harness\n}\n\nfunction finish(spawned: boolean): never {\n const ms = Number(process.hrtime.bigint() - t0) / 1e6;\n try {\n mkdirSync(state, { recursive: true });\n appendFileSync(\n path.join(state, \"timing.jsonl\"),\n `${JSON.stringify({ at: Date.now(), harness, event, ms: Math.round(ms * 10) / 10, spawned })}\\n`,\n );\n } catch {}\n process.exit(0);\n}\nfunction pick(names: string[]): Record<string, string> {\n const out: Record<string, string> = {};\n for (const n of names) if (env[n] !== undefined) out[n] = env[n] as string;\n return out;\n}\nfunction anySinkAbove(tier: number): boolean {\n try {\n const c = JSON.parse(readFileSync(configFile(), \"utf8\")) as { sinks?: { tier?: number; paused?: string }[] };\n return (c.sinks ?? []).some((s) => Number(s.tier) > tier && !s.paused);\n } catch {\n return false;\n }\n}\nfunction sessionIdOf(stdin: string): string {\n const m = /\"(?:session_id|sessionId|conversationId)\"\\s*:\\s*\"([^\"]{1,128})\"/.exec(stdin);\n return m?.[1] ?? \"\";\n}\n/** One heartbeat per minute per session: the stamp file's mtime is the clock. */\nfunction beatDue(id: string): boolean {\n const stamp = path.join(state, \"beats\", `${harness}-${id.replace(/[^A-Za-z0-9_-]/g, \"_\")}`);\n try {\n if (Date.now() - statSync(stamp).mtimeMs < 60_000) return false;\n } catch {}\n try {\n mkdirSync(path.dirname(stamp), { recursive: true });\n writeFileSync(stamp, \"\");\n } catch {}\n return true;\n}\n"],
|
|
5
|
-
"mappings": ";;;;AAOA,SAAS,aAAa;AACtB,SAAS,gBAAgB,WAAW,cAAc,UAAU,qBAAqB;AACjF,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAE9B,IAAM,KAAK,QAAQ,OAAO,OAAO;AACjC,IAAM,CAAC,UAAU,IAAI,QAAQ,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC;AAChE,IAAM,MAAM,QAAQ;AAEpB,SAAS,WAAmB;AAC1B,MAAI,IAAI,kBAAmB,QAAO,KAAK,QAAQ,IAAI,iBAAiB;AACpE,QAAM,OAAO,GAAG,QAAQ;AACxB,MAAI,QAAQ,aAAa;AACvB,WAAO,KAAK,KAAK,IAAI,gBAAgB,KAAK,KAAK,MAAM,WAAW,OAAO,GAAG,aAAa;AACzF,SAAO,KAAK,KAAK,IAAI,kBAAkB,KAAK,KAAK,MAAM,UAAU,OAAO,GAAG,aAAa;AAC1F;AACA,SAAS,aAAqB;AAC5B,MAAI,IAAI,mBAAoB,QAAO,KAAK,QAAQ,IAAI,kBAAkB;AACtE,QAAM,OAAO,GAAG,QAAQ;AACxB,MAAI,QAAQ,aAAa;AACvB,WAAO,KAAK,KAAK,IAAI,WAAW,KAAK,KAAK,MAAM,WAAW,SAAS,GAAG,eAAe,aAAa;AACrG,SAAO,KAAK,KAAK,IAAI,mBAAmB,KAAK,KAAK,MAAM,SAAS,GAAG,eAAe,aAAa;AAClG;AAGA,IAAI,YAAY,eAAe;AAC7B,MAAI;AACF,kBAAc,GAAG,MAAM;AAAA,EACzB,QAAQ;AAAA,EAAC;AACX;AAEA,IAAM,QAAQ,SAAS;AACvB,IAAI;AACF,MAAI,CAAC,WAAW,CAAC,MAAO,OAAM,IAAI,MAAM,+BAA+B;AACvE,MAAI,QAAQ;AACZ,MAAI,YAAY,WAAW,UAAU,UAAU;AAC7C,QAAI;AACF,YAAM,MAAM,aAAa,CAAC;AAC1B,cAAQ,IAAI,SAAS,GAAG,OAAO,IAAI,EAAE,SAAS,MAAM;AAAA,IACtD,QAAQ;AAAA,IAAC;AAAA,EACX;AAGA,QAAM,OAAO;AACb,MAAI,KAAK,KAAK,KAAK,KAAK,CAAC,aAAa,CAAC,GAAG;AACxC,UAAM,KAAK,YAAY,KAAK;AAC5B,QAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAG,QAAO,KAAK;AAAA,EACvC;AACA,YAAU,KAAK,KAAK,OAAO,MAAM,GAAG,EAAE,WAAW,
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n// SPDX-License-Identifier: Apache-2.0\n// The hook path. A harness runs `node hook.js <harness> <event>`; this reads stdin,\n// writes one job file, spawns the worker detached, prints what the harness needs\n// ({} for Antigravity) and exits 0 \u2014 in milliseconds, with nothing but node:\n// builtins imported (the build checks that). No network, no schema library, no\n// config parsing beyond one small file read for the throttle.\nimport { spawn } from \"node:child_process\";\nimport { appendFileSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst t0 = process.hrtime.bigint();\nconst [harness = \"\", event = \"\", ...rest] = process.argv.slice(2);\nconst env = process.env;\n\nfunction stateDir(): string {\n if (env.SESSIONPIPE_STATE) return path.resolve(env.SESSIONPIPE_STATE);\n const home = os.homedir();\n if (process.platform === \"win32\")\n return path.join(env.LOCALAPPDATA || path.join(home, \"AppData\", \"Local\"), \"sessionpipe\");\n return path.join(env.XDG_STATE_HOME || path.join(home, \".local\", \"state\"), \"sessionpipe\");\n}\nfunction configFile(): string {\n if (env.SESSIONPIPE_CONFIG) return path.resolve(env.SESSIONPIPE_CONFIG);\n const home = os.homedir();\n if (process.platform === \"win32\")\n return path.join(env.APPDATA || path.join(home, \"AppData\", \"Roaming\"), \"sessionpipe\", \"config.json\");\n return path.join(env.XDG_CONFIG_HOME || path.join(home, \".config\"), \"sessionpipe\", \"config.json\");\n}\n\n// Antigravity parses stdout as the hook's answer; `{}` is \"carry on\" for every event.\nif (harness === \"antigravity\") {\n try {\n writeFileSync(1, \"{}\\n\");\n } catch {}\n}\n\nconst state = stateDir();\ntry {\n if (!harness || !event) throw new Error(\"usage: hook <harness> <event>\");\n let stdin = \"\";\n if (harness !== \"codex\" || event !== \"notify\") {\n try {\n const buf = readFileSync(0);\n stdin = buf.subarray(0, 1024 * 1024).toString(\"utf8\");\n } catch {}\n }\n // Tool events are the bulk of what a harness fires. When no sink takes tier \u2265 1\n // they are only a \"still working\" signal: one heartbeat a minute per session.\n const TOOL = /^(PreToolUse|PostToolUse|PostToolUseFailure|BeforeTool|AfterTool|PostInvocation)$/;\n if (TOOL.test(event) && !anySinkAbove(0)) {\n const id = sessionIdOf(stdin);\n if (!id || !beatDue(id)) finish(false);\n }\n mkdirSync(path.join(state, \"jobs\"), { recursive: true, mode: 0o700 });\n const job = {\n harness,\n event,\n argv: [harness, event, ...rest],\n stdin,\n cwd: process.cwd(),\n env: pick([\n \"CLAUDE_CODE_HOST_SESSION_ID\",\n \"CLAUDE_CONFIG_DIR\",\n \"CLAUDE_PROJECT_DIR\",\n \"CODEX_HOME\",\n \"GEMINI_CLI_HOME\",\n ]),\n ppid: process.ppid,\n at: Date.now(),\n };\n const jobFile = path.join(state, \"jobs\", `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);\n writeFileSync(jobFile, JSON.stringify(job), { mode: 0o600 });\n if (!env.SESSIONPIPE_NO_WORKER) {\n const worker = path.join(path.dirname(fileURLToPath(import.meta.url)), \"worker.js\");\n const p = spawn(process.execPath, [worker, jobFile], { detached: true, stdio: \"ignore\", windowsHide: true, env });\n p.unref();\n }\n finish(true);\n} catch {\n finish(false); // the hook must be invisible to the harness\n}\n\nfunction finish(spawned: boolean): never {\n const ms = Number(process.hrtime.bigint() - t0) / 1e6;\n try {\n mkdirSync(state, { recursive: true });\n appendFileSync(\n path.join(state, \"timing.jsonl\"),\n `${JSON.stringify({ at: Date.now(), harness, event, ms: Math.round(ms * 10) / 10, spawned })}\\n`,\n );\n } catch {}\n process.exit(0);\n}\nfunction pick(names: string[]): Record<string, string> {\n const out: Record<string, string> = {};\n for (const n of names) if (env[n] !== undefined) out[n] = env[n] as string;\n return out;\n}\nfunction anySinkAbove(tier: number): boolean {\n try {\n const c = JSON.parse(readFileSync(configFile(), \"utf8\")) as { sinks?: { tier?: number; paused?: string }[] };\n return (c.sinks ?? []).some((s) => Number(s.tier) > tier && !s.paused);\n } catch {\n return false;\n }\n}\nfunction sessionIdOf(stdin: string): string {\n const m = /\"(?:session_id|sessionId|conversationId)\"\\s*:\\s*\"([^\"]{1,128})\"/.exec(stdin);\n return m?.[1] ?? \"\";\n}\n/** One heartbeat per minute per session: the stamp file's mtime is the clock. */\nfunction beatDue(id: string): boolean {\n const stamp = path.join(state, \"beats\", `${harness}-${id.replace(/[^A-Za-z0-9_-]/g, \"_\")}`);\n try {\n if (Date.now() - statSync(stamp).mtimeMs < 60_000) return false;\n } catch {}\n try {\n mkdirSync(path.dirname(stamp), { recursive: true, mode: 0o700 });\n writeFileSync(stamp, \"\", { mode: 0o600 });\n } catch {}\n return true;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|