sessionpipe 0.1.1 → 0.2.0
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/README.md +35 -2
- package/dist/cli.js +2425 -219
- package/dist/cli.js.map +4 -4
- package/dist/control/cli.d.ts +4 -0
- package/dist/control/cli.d.ts.map +1 -0
- package/dist/control/daemon.d.ts +172 -0
- package/dist/control/daemon.d.ts.map +1 -0
- package/dist/control/local.d.ts +63 -0
- package/dist/control/local.d.ts.map +1 -0
- package/dist/control/pair.d.ts +50 -0
- package/dist/control/pair.d.ts.map +1 -0
- package/dist/control/reply.d.ts +19 -0
- package/dist/control/reply.d.ts.map +1 -0
- package/dist/control/route.d.ts +41 -0
- package/dist/control/route.d.ts.map +1 -0
- package/dist/control/sdk.d.ts +7 -0
- package/dist/control/sdk.d.ts.map +1 -0
- package/dist/control/store.d.ts +76 -0
- package/dist/control/store.d.ts.map +1 -0
- package/dist/control/wait.d.ts +8 -0
- package/dist/control/wait.d.ts.map +1 -0
- package/dist/hook.js +114 -19
- package/dist/hook.js.map +4 -4
- package/dist/http-sink.d.ts +4 -0
- package/dist/http-sink.d.ts.map +1 -1
- package/dist/index.js +92 -28
- package/dist/index.js.map +4 -4
- package/dist/run.d.ts.map +1 -1
- package/dist/worker.js +87 -23
- package/dist/worker.js.map +4 -4
- package/package.json +1 -1
package/dist/run.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAeA,OAAO,EAGL,KAAK,KAAK,EACV,KAAK,UAAU,EAMf,MAAM,EAIN,KAAK,IAAI,EACT,KAAK,UAAU,EAOhB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAeA,OAAO,EAGL,KAAK,KAAK,EACV,KAAK,UAAU,EAMf,MAAM,EAIN,KAAK,IAAI,EACT,KAAK,UAAU,EAOhB,MAAM,mBAAmB,CAAC;AAqB3B,QAAA,MAAM,OAAO,QAAkF,CAAC;AAEhG,MAAM,WAAW,GAAG;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,CAM1D;AAED;;;2BAG2B;AAC3B,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,SAAO,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAuB9F;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CASrD;AAED,yFAAyF;AACzF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAkBpD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,CAWtD;AAED,mFAAmF;AACnF,wBAAsB,MAAM,CAC1B,GAAG,EAAE,GAAG,EACR,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1C,OAAO,CAAC;IAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC,CA6GjE;AAKD;;;;;4DAK4D;AAC5D,wBAAsB,KAAK,CACzB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,UAAU,EAAE,EACzB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3C,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAmEjC;AAaD,OAAO,EAAE,OAAO,EAAE,CAAC;AACnB,eAAO,MAAM,WAAW,QAAO,MAAkC,CAAC;AAClE,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,KAAG,MAAkC,CAAC;AAC3E,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,KAAG,OAAqC,CAAC"}
|
package/dist/worker.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// sessionpipe 0.
|
|
2
|
+
// sessionpipe 0.2.0 · Apache-2.0 · https://sessionpipe.org
|
|
3
3
|
|
|
4
4
|
// src/worker.ts
|
|
5
5
|
import { readFileSync as readFileSync9, unlinkSync as unlinkSync5 } from "node:fs";
|
|
@@ -282,7 +282,7 @@ function entryFor(event, harness, cmd, o) {
|
|
|
282
282
|
const matcher = o.matcherFor(event);
|
|
283
283
|
return {
|
|
284
284
|
...matcher !== void 0 ? { matcher } : {},
|
|
285
|
-
hooks: [{ type: "command", command: cmd([harness, event]).command, timeout: o.timeout }]
|
|
285
|
+
hooks: [{ type: "command", command: cmd([harness, event]).command, timeout: o.timeoutFor?.(event) ?? o.timeout }]
|
|
286
286
|
};
|
|
287
287
|
}
|
|
288
288
|
function installClaudeShaped(file, harness, events, cmd, o) {
|
|
@@ -706,7 +706,13 @@ var CLAUDE_EVENTS = [
|
|
|
706
706
|
];
|
|
707
707
|
var LEAN_SKIP = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUseFailure"]);
|
|
708
708
|
var eventsFor = (lean) => lean ? CLAUDE_EVENTS.filter((e) => !LEAN_SKIP.has(e)) : [...CLAUDE_EVENTS];
|
|
709
|
-
var OPTS = {
|
|
709
|
+
var OPTS = {
|
|
710
|
+
matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0,
|
|
711
|
+
timeout: 5,
|
|
712
|
+
// The hook waits up to 125 s for a verified control answer, and only when control
|
|
713
|
+
// is paired on this machine; everywhere else it exits in milliseconds as before.
|
|
714
|
+
timeoutFor: (e) => e === "PermissionRequest" ? 130 : void 0
|
|
715
|
+
};
|
|
710
716
|
var DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;
|
|
711
717
|
function claudeDirs(env = process.env, home = HOME) {
|
|
712
718
|
const out = [];
|
|
@@ -1150,9 +1156,9 @@ function claudeFacts(file, sessionId, hints, state) {
|
|
|
1150
1156
|
}
|
|
1151
1157
|
const name = live?.name ? { title: live.name, source: live.name_source } : typeof mem.name === "string" ? { title: mem.name, source: mem.name_source } : null;
|
|
1152
1158
|
const pick = name && name.source === "custom" && name || custom && { title: custom, source: "custom" } || name || ai && { title: ai, source: "harness" };
|
|
1153
|
-
let
|
|
1154
|
-
if (!
|
|
1155
|
-
|
|
1159
|
+
let ask2 = typeof mem.first_ask === "string" ? mem.first_ask : firstAsk(H);
|
|
1160
|
+
if (!ask2 && size > 64 * 1024 && !mem.first_ask_none) {
|
|
1161
|
+
ask2 = scanLines(file, (l) => {
|
|
1156
1162
|
try {
|
|
1157
1163
|
const t = askText(JSON.parse(l));
|
|
1158
1164
|
return t ? appTitle(t) : null;
|
|
@@ -1160,17 +1166,17 @@ function claudeFacts(file, sessionId, hints, state) {
|
|
|
1160
1166
|
return null;
|
|
1161
1167
|
}
|
|
1162
1168
|
});
|
|
1163
|
-
if (!
|
|
1169
|
+
if (!ask2 && size > 16 * 1024 * 1024) state.save(NAME2, id, { first_ask_none: true });
|
|
1164
1170
|
}
|
|
1165
|
-
if (
|
|
1166
|
-
out.first_ask =
|
|
1167
|
-
if (!mem.first_ask) state.save(NAME2, id, { first_ask:
|
|
1171
|
+
if (ask2) {
|
|
1172
|
+
out.first_ask = ask2;
|
|
1173
|
+
if (!mem.first_ask) state.save(NAME2, id, { first_ask: ask2 });
|
|
1168
1174
|
}
|
|
1169
1175
|
if (pick) {
|
|
1170
1176
|
out.title = String(pick.title).slice(0, 120);
|
|
1171
1177
|
out.title_source = pick.source;
|
|
1172
|
-
} else if (
|
|
1173
|
-
out.title =
|
|
1178
|
+
} else if (ask2) {
|
|
1179
|
+
out.title = ask2;
|
|
1174
1180
|
out.title_source = "first-ask";
|
|
1175
1181
|
}
|
|
1176
1182
|
const acct = claudeAccount(dir);
|
|
@@ -2482,12 +2488,54 @@ import {
|
|
|
2482
2488
|
unlinkSync as unlinkSync4,
|
|
2483
2489
|
writeFileSync as writeFileSync6
|
|
2484
2490
|
} from "node:fs";
|
|
2491
|
+
import os3 from "node:os";
|
|
2492
|
+
import path12 from "node:path";
|
|
2493
|
+
|
|
2494
|
+
// src/control/local.ts
|
|
2495
|
+
import net from "node:net";
|
|
2485
2496
|
import os2 from "node:os";
|
|
2486
2497
|
import path11 from "node:path";
|
|
2498
|
+
function socketPath(state, env = process.env) {
|
|
2499
|
+
if (env.SESSIONPIPE_SOCKET) return env.SESSIONPIPE_SOCKET;
|
|
2500
|
+
if (process.platform === "win32") return `\\\\.\\pipe\\sessionpipe-control-${os2.userInfo().username}`;
|
|
2501
|
+
return path11.join(state, "control.sock");
|
|
2502
|
+
}
|
|
2503
|
+
function ask(sock, req, timeoutMs) {
|
|
2504
|
+
return new Promise((resolve) => {
|
|
2505
|
+
let buf = "";
|
|
2506
|
+
let done = false;
|
|
2507
|
+
const s = net.connect(sock);
|
|
2508
|
+
const finish = (r) => {
|
|
2509
|
+
if (done) return;
|
|
2510
|
+
done = true;
|
|
2511
|
+
clearTimeout(t);
|
|
2512
|
+
s.destroy();
|
|
2513
|
+
resolve(r);
|
|
2514
|
+
};
|
|
2515
|
+
const t = setTimeout(() => finish(null), timeoutMs);
|
|
2516
|
+
s.on("connect", () => s.write(`${JSON.stringify(req)}
|
|
2517
|
+
`));
|
|
2518
|
+
s.on("data", (d) => {
|
|
2519
|
+
buf += d.toString("utf8");
|
|
2520
|
+
const i = buf.indexOf("\n");
|
|
2521
|
+
if (i < 0) return;
|
|
2522
|
+
try {
|
|
2523
|
+
finish(JSON.parse(buf.slice(0, i)));
|
|
2524
|
+
} catch {
|
|
2525
|
+
finish(null);
|
|
2526
|
+
}
|
|
2527
|
+
});
|
|
2528
|
+
s.on("error", () => finish(null));
|
|
2529
|
+
s.on("close", () => finish(null));
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2487
2532
|
|
|
2488
2533
|
// src/http-sink.ts
|
|
2489
|
-
import {
|
|
2534
|
+
import { createHash as createHash3, createHmac } from "node:crypto";
|
|
2490
2535
|
var TIMEOUT_MS = 15e3;
|
|
2536
|
+
function webhookId(events) {
|
|
2537
|
+
return `msg_${createHash3("sha256").update(events.map((e) => e.id).join(",")).digest("base64url").slice(0, 32)}`;
|
|
2538
|
+
}
|
|
2491
2539
|
var HttpSink = class {
|
|
2492
2540
|
constructor(cfg, tier) {
|
|
2493
2541
|
this.cfg = cfg;
|
|
@@ -2510,7 +2558,7 @@ var HttpSink = class {
|
|
|
2510
2558
|
const headers = { "content-type": "application/json", "sessionpipe-protocol": "1" };
|
|
2511
2559
|
if (this.cfg.token) headers.authorization = `Bearer ${this.cfg.token}`;
|
|
2512
2560
|
if (this.cfg.secret?.startsWith("whsec_")) {
|
|
2513
|
-
const id =
|
|
2561
|
+
const id = webhookId(events);
|
|
2514
2562
|
const ts = Math.floor(Date.now() / 1e3);
|
|
2515
2563
|
const key = Buffer.from(this.cfg.secret.slice(6), "base64");
|
|
2516
2564
|
const sig = createHmac("sha256", key).update(`${id}.${ts}.${body}`).digest("base64");
|
|
@@ -2571,10 +2619,25 @@ var HttpSink = class {
|
|
|
2571
2619
|
};
|
|
2572
2620
|
|
|
2573
2621
|
// src/run.ts
|
|
2622
|
+
async function tellDaemon(state, job2, session, transcript) {
|
|
2623
|
+
const sock = socketPath(state);
|
|
2624
|
+
if (process.platform !== "win32" && !existsSync8(sock)) return;
|
|
2625
|
+
await ask(
|
|
2626
|
+
sock,
|
|
2627
|
+
{
|
|
2628
|
+
op: "event",
|
|
2629
|
+
session: `claude-code:${session}`,
|
|
2630
|
+
event: job2.event,
|
|
2631
|
+
cwd: job2.cwd,
|
|
2632
|
+
...transcript ? { transcript } : {}
|
|
2633
|
+
},
|
|
2634
|
+
300
|
|
2635
|
+
);
|
|
2636
|
+
}
|
|
2574
2637
|
function enqueueJob(state, job2) {
|
|
2575
2638
|
const jobs = jobsDir(state);
|
|
2576
2639
|
mkdirSync7(jobs, { recursive: true, mode: 448 });
|
|
2577
|
-
const f =
|
|
2640
|
+
const f = path12.join(jobs, `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
2578
2641
|
writeFileSync6(f, JSON.stringify(job2), { mode: 384 });
|
|
2579
2642
|
return f;
|
|
2580
2643
|
}
|
|
@@ -2588,7 +2651,7 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20) {
|
|
|
2588
2651
|
}
|
|
2589
2652
|
let ran = 0;
|
|
2590
2653
|
for (const n of names.sort().slice(0, limit)) {
|
|
2591
|
-
const f =
|
|
2654
|
+
const f = path12.join(jobs, n);
|
|
2592
2655
|
let job2 = null;
|
|
2593
2656
|
try {
|
|
2594
2657
|
if (Date.now() - statSync5(f).mtimeMs < olderThanMs) continue;
|
|
@@ -2606,7 +2669,7 @@ async function sweepJobs(state, olderThanMs = 3e3, limit = 20) {
|
|
|
2606
2669
|
function log(state, line) {
|
|
2607
2670
|
try {
|
|
2608
2671
|
mkdirSync7(state, { recursive: true, mode: 448 });
|
|
2609
|
-
const f =
|
|
2672
|
+
const f = path12.join(state, "log");
|
|
2610
2673
|
try {
|
|
2611
2674
|
if (statSync5(f).size > 1024 * 1024) renameSync2(f, `${f}.1`);
|
|
2612
2675
|
} catch {
|
|
@@ -2617,7 +2680,7 @@ function log(state, line) {
|
|
|
2617
2680
|
}
|
|
2618
2681
|
}
|
|
2619
2682
|
function factsState(state) {
|
|
2620
|
-
const file = (h, s) =>
|
|
2683
|
+
const file = (h, s) => path12.join(state, "facts", safeId(h), `${safeId(s)}.json`);
|
|
2621
2684
|
return {
|
|
2622
2685
|
get(h, s) {
|
|
2623
2686
|
try {
|
|
@@ -2630,7 +2693,7 @@ function factsState(state) {
|
|
|
2630
2693
|
const cur = this.get(h, s);
|
|
2631
2694
|
const next = { ...cur, ...patch };
|
|
2632
2695
|
if (JSON.stringify(next) === JSON.stringify(cur)) return;
|
|
2633
|
-
mkdirSync7(
|
|
2696
|
+
mkdirSync7(path12.dirname(file(h, s)), { recursive: true, mode: 448 });
|
|
2634
2697
|
writeFileSync6(file(h, s), JSON.stringify(next), { mode: 384 });
|
|
2635
2698
|
}
|
|
2636
2699
|
};
|
|
@@ -2642,7 +2705,7 @@ function buildSinks(sinks) {
|
|
|
2642
2705
|
const tier = Math.min(s.tier, s.max_tier ?? 3);
|
|
2643
2706
|
if (s.url === "stdout") out.push(new StdoutSink(s.name, tier, !!s.pii));
|
|
2644
2707
|
else if (s.url.startsWith("file:"))
|
|
2645
|
-
out.push(new FileSink(s.name, s.url.slice(5).replace(/^~(?=\/|$)/,
|
|
2708
|
+
out.push(new FileSink(s.name, s.url.slice(5).replace(/^~(?=\/|$)/, os3.homedir()), tier, !!s.pii));
|
|
2646
2709
|
else if (/^https?:\/\//.test(s.url)) out.push(new HttpSink(s, tier));
|
|
2647
2710
|
}
|
|
2648
2711
|
return out;
|
|
@@ -2654,6 +2717,7 @@ async function runJob(job2, opts = {}) {
|
|
|
2654
2717
|
if (!adapter) return { events: [], delivered: {} };
|
|
2655
2718
|
const r = adapter.fromHook({ argv: job2.argv, stdin: job2.stdin, env: { ...process.env, ...job2.env }, cwd: job2.cwd });
|
|
2656
2719
|
if (!r || !r.session.id) return { events: [], delivered: {} };
|
|
2720
|
+
if (job2.harness === "claude-code") await tellDaemon(state, job2, r.session.id, r.transcript);
|
|
2657
2721
|
const outbox = new Outbox(state);
|
|
2658
2722
|
const ref = { harness: job2.harness, session: r.session.id };
|
|
2659
2723
|
let release = takeLock(state, ref);
|
|
@@ -2688,7 +2752,7 @@ async function runJob(job2, opts = {}) {
|
|
|
2688
2752
|
...learned.branch ? { branch: learned.branch } : {},
|
|
2689
2753
|
id: r.session.id,
|
|
2690
2754
|
seq: 0,
|
|
2691
|
-
machine: machineName(cfg,
|
|
2755
|
+
machine: machineName(cfg, os3.hostname()),
|
|
2692
2756
|
...cwdRaw ? { cwd: tilde(cwdRaw) } : {},
|
|
2693
2757
|
...r.session.model ? { model: r.session.model } : {}
|
|
2694
2758
|
};
|
|
@@ -2791,7 +2855,7 @@ async function flush(state, sinkConfigs, outbox, first) {
|
|
|
2791
2855
|
const { events, offset: next } = outbox.read(ref, offset, room);
|
|
2792
2856
|
if (!events.length) break;
|
|
2793
2857
|
for (const e of events) {
|
|
2794
|
-
const f = filterEvent(e, sink.tier, sink.pii, { home:
|
|
2858
|
+
const f = filterEvent(e, sink.tier, sink.pii, { home: os3.homedir() });
|
|
2795
2859
|
if (f) batch.push(f);
|
|
2796
2860
|
}
|
|
2797
2861
|
offset = next;
|
|
@@ -2823,7 +2887,7 @@ function applySinkVerdict(name, res) {
|
|
|
2823
2887
|
} catch {
|
|
2824
2888
|
}
|
|
2825
2889
|
}
|
|
2826
|
-
var jobsDir = (state) =>
|
|
2890
|
+
var jobsDir = (state) => path12.join(state, "jobs");
|
|
2827
2891
|
|
|
2828
2892
|
// src/worker.ts
|
|
2829
2893
|
var jobFile = process.argv[2];
|