sessionpipe 0.2.0 → 0.3.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 +1 -1
- package/dist/cli.js +52 -18
- package/dist/cli.js.map +3 -3
- package/dist/control/cli.d.ts.map +1 -1
- package/dist/control/pair.d.ts +8 -2
- package/dist/control/pair.d.ts.map +1 -1
- package/dist/hook.js +1 -1
- package/dist/index.js +1 -1
- package/dist/worker.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ enroll at this terminal; the receiver can queue a message but can never forge on
|
|
|
33
33
|
([spec](https://sessionpipe.org/protocol/control/)).
|
|
34
34
|
|
|
35
35
|
```sh
|
|
36
|
-
sessionpipe control pair
|
|
36
|
+
sessionpipe control pair spacesheep.dev # one link: approve with your passkey; installs the daemon (this folder by default)
|
|
37
37
|
sessionpipe control status # receivers, parked waiters, sessions mid-turn, open permission prompts
|
|
38
38
|
sessionpipe control keys [remove <id>]
|
|
39
39
|
sessionpipe control off # tell the receiver, forget its token and keys
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// sessionpipe 0.
|
|
2
|
+
// sessionpipe 0.3.0 · Apache-2.0 · https://sessionpipe.org
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __export = (target, all) => {
|
|
5
5
|
for (var name in all)
|
|
@@ -3404,7 +3404,7 @@ var {
|
|
|
3404
3404
|
recentlyWritten: recentlyWritten2,
|
|
3405
3405
|
sessionFolder: sessionFolder2
|
|
3406
3406
|
} = claude_code_control_exports;
|
|
3407
|
-
var VERSION = true ? "0.
|
|
3407
|
+
var VERSION = true ? "0.3.0" : "0.0.0";
|
|
3408
3408
|
var MAX_REPLY = 2e4;
|
|
3409
3409
|
var HELLO_EVERY_MS = 10 * 6e4;
|
|
3410
3410
|
var TURN_FALLBACK_MS = 30 * 6e4;
|
|
@@ -4222,10 +4222,11 @@ function ask(sock, req, timeoutMs) {
|
|
|
4222
4222
|
|
|
4223
4223
|
// src/control/pair.ts
|
|
4224
4224
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
4225
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
4225
4226
|
import { existsSync as existsSync10, mkdirSync as mkdirSync9, unlinkSync as unlinkSync5, writeFileSync as writeFileSync7 } from "node:fs";
|
|
4226
4227
|
import os4 from "node:os";
|
|
4227
4228
|
import path15 from "node:path";
|
|
4228
|
-
var VERSION2 = true ? "0.
|
|
4229
|
+
var VERSION2 = true ? "0.3.0" : "0.0.0";
|
|
4229
4230
|
async function json(r) {
|
|
4230
4231
|
try {
|
|
4231
4232
|
return await r.json();
|
|
@@ -4240,23 +4241,34 @@ async function discover(url, f) {
|
|
|
4240
4241
|
const wk = await r.json();
|
|
4241
4242
|
if (!wk.capabilities?.includes("control") || !wk.endpoints?.control || !wk.control?.signing?.rp_id)
|
|
4242
4243
|
throw new Error(`${base} doesn't serve control yet (its well-known file lists no control.signing)`);
|
|
4243
|
-
const
|
|
4244
|
-
return {
|
|
4244
|
+
const abs = (ep) => /^https?:\/\//.test(ep) ? ep : base + ep;
|
|
4245
|
+
return {
|
|
4246
|
+
control: abs(wk.endpoints.control),
|
|
4247
|
+
rpId: wk.control.signing.rp_id,
|
|
4248
|
+
open: wk.control.open_pairing === true,
|
|
4249
|
+
events: wk.endpoints.events ? abs(wk.endpoints.events) : null
|
|
4250
|
+
};
|
|
4245
4251
|
}
|
|
4246
4252
|
async function pair(o) {
|
|
4247
4253
|
const f = o.fetch ?? fetch;
|
|
4248
4254
|
const sleep2 = o.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
4249
|
-
const { control, rpId } = await discover(o.url, f);
|
|
4255
|
+
const { control, rpId, open } = await discover(o.url, f);
|
|
4256
|
+
if (!o.token && !open)
|
|
4257
|
+
throw new Error(
|
|
4258
|
+
`${o.url} needs a key to pair: add it as a sink first (\`sessionpipe sink add ${o.url} --token \u2026\`) or pass --token.`
|
|
4259
|
+
);
|
|
4260
|
+
const pollKey = o.token ? null : randomBytes2(32).toString("base64url");
|
|
4250
4261
|
const cfg = readControl(o.env) ?? { name: o.name, folders: [], mode: o.mode, receivers: [] };
|
|
4251
4262
|
cfg.name = o.name || cfg.name;
|
|
4252
4263
|
cfg.mode = o.mode;
|
|
4253
4264
|
cfg.folders = [.../* @__PURE__ */ new Set([...cfg.folders, ...o.folders.map((d) => path15.resolve(d))])];
|
|
4254
4265
|
const existing = cfg.receivers.find((r) => r.control === control);
|
|
4255
|
-
const auth = { authorization: `Bearer ${o.token}`, "content-type": "application/json" };
|
|
4266
|
+
const auth = { authorization: `Bearer ${o.token ?? pollKey}`, "content-type": "application/json" };
|
|
4256
4267
|
const started = await f(`${control}/pair`, {
|
|
4257
4268
|
method: "POST",
|
|
4258
|
-
headers: auth,
|
|
4269
|
+
headers: o.token ? auth : { "content-type": "application/json" },
|
|
4259
4270
|
body: JSON.stringify({
|
|
4271
|
+
...pollKey ? { poll_key: pollKey } : {},
|
|
4260
4272
|
...existing ? { machine: existing.machine } : {},
|
|
4261
4273
|
name: cfg.name,
|
|
4262
4274
|
harnesses: ["claude-code"],
|
|
@@ -4274,9 +4286,16 @@ async function pair(o) {
|
|
|
4274
4286
|
const machine = String(s.machine);
|
|
4275
4287
|
const code = String(s.code);
|
|
4276
4288
|
o.out("");
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4289
|
+
const digits = String(s.check);
|
|
4290
|
+
const spaced = /^\d{6}$/.test(digits) ? `${digits.slice(0, 3)} ${digits.slice(3)}` : digits;
|
|
4291
|
+
if (pollKey) {
|
|
4292
|
+
o.out(` Open ${String(s.url)} and enter: ${spaced}`);
|
|
4293
|
+
o.out(" then approve with your passkey (Touch ID, Face ID, a security key).");
|
|
4294
|
+
} else {
|
|
4295
|
+
o.out(" Open this link and confirm with your passkey (Touch ID, Face ID, a security key):");
|
|
4296
|
+
o.out(` ${String(s.url)}`);
|
|
4297
|
+
o.out(` The page shows these digits too: ${digits}`);
|
|
4298
|
+
}
|
|
4280
4299
|
o.out("");
|
|
4281
4300
|
const until = Date.parse(String(s.expires_at)) || Date.now() + 10 * 6e4;
|
|
4282
4301
|
for (; ; ) {
|
|
@@ -4307,6 +4326,9 @@ async function pair(o) {
|
|
|
4307
4326
|
if (!existing) cfg.receivers.push(rec);
|
|
4308
4327
|
writeControl(cfg, o.env);
|
|
4309
4328
|
o.out(` \u2713 Paired with ${rec.url} as ${machine} \xB7 ${rec.keys.length} key(s) trusted on this machine`);
|
|
4329
|
+
if (typeof p.account === "string")
|
|
4330
|
+
o.out(` Approved by ${p.account}. If that isn't you, run \`sessionpipe control off\` now.`);
|
|
4331
|
+
if (typeof p.sink_token === "string" && o.addSink) o.addSink(p.sink_token);
|
|
4310
4332
|
return cfg;
|
|
4311
4333
|
}
|
|
4312
4334
|
}
|
|
@@ -4545,15 +4567,16 @@ async function controlMain(argv, out2, distDir2) {
|
|
|
4545
4567
|
const sub = argv[1];
|
|
4546
4568
|
switch (sub) {
|
|
4547
4569
|
case "pair": {
|
|
4548
|
-
const
|
|
4549
|
-
if (!
|
|
4570
|
+
const raw = argv[2];
|
|
4571
|
+
if (!raw || raw.startsWith("-")) return out2(`usage:
|
|
4550
4572
|
${CONTROL_HELP}`);
|
|
4573
|
+
const url = (/^https?:\/\//.test(raw) ? raw : `https://${raw}`).replace(/\/$/, "");
|
|
4551
4574
|
const cfg = readConfig();
|
|
4552
|
-
const sink2 = cfg.sinks.find((s) => s.url.replace(/\/$/, "") === url
|
|
4553
|
-
const token = flag2("--token") ?? sink2?.token;
|
|
4554
|
-
if (
|
|
4575
|
+
const sink2 = cfg.sinks.find((s) => s.url.replace(/\/$/, "") === url);
|
|
4576
|
+
const token = flag2("--token") ?? sink2?.token ?? null;
|
|
4577
|
+
if (token !== null && !/^[\x21-\x7e]+$/.test(token))
|
|
4555
4578
|
return out2(
|
|
4556
|
-
|
|
4579
|
+
" That token has a character a key never has (a placeholder like ss_\u2026 pasted as is?). Leave --token out to pair with a link instead."
|
|
4557
4580
|
);
|
|
4558
4581
|
const folders = flags("--folder");
|
|
4559
4582
|
const existing = readControl();
|
|
@@ -4561,6 +4584,17 @@ ${CONTROL_HELP}`);
|
|
|
4561
4584
|
await pair({
|
|
4562
4585
|
url,
|
|
4563
4586
|
token,
|
|
4587
|
+
// The key the receiver handed over at pairing becomes this machine's sink for it,
|
|
4588
|
+
// state only (tier 0), unless one is configured already.
|
|
4589
|
+
addSink: (t) => {
|
|
4590
|
+
const now = readConfig();
|
|
4591
|
+
if (now.sinks.some((x) => x.url.replace(/\/$/, "") === url)) return;
|
|
4592
|
+
now.sinks.push({ name: new URL(url).host, url, tier: 0, pii: false, control: false, token: t });
|
|
4593
|
+
writeConfig(now);
|
|
4594
|
+
out2(
|
|
4595
|
+
` \u2713 Sink ${new URL(url).host} added (tier 0: session state, no conversation text). \`sessionpipe sink add ${url} --tier 2\` sends transcripts too.`
|
|
4596
|
+
);
|
|
4597
|
+
},
|
|
4564
4598
|
folders,
|
|
4565
4599
|
mode: flag2("--mode") === "auto" ? "auto" : existing?.mode ?? "safe",
|
|
4566
4600
|
name: flag2("--name") ?? existing?.name ?? cfg.machine ?? os5.hostname(),
|
|
@@ -4779,7 +4813,7 @@ async function tellDaemon(state2, job, session, transcript) {
|
|
|
4779
4813
|
300
|
|
4780
4814
|
);
|
|
4781
4815
|
}
|
|
4782
|
-
var VERSION3 = true ? "0.
|
|
4816
|
+
var VERSION3 = true ? "0.3.0" : "0.0.0";
|
|
4783
4817
|
function enqueueJob(state2, job) {
|
|
4784
4818
|
const jobs = jobsDir(state2);
|
|
4785
4819
|
mkdirSync10(jobs, { recursive: true, mode: 448 });
|