sessionpipe 0.13.2 → 0.13.3
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 +100 -21
- package/dist/cli.js.map +3 -3
- package/dist/control/cli.d.ts.map +1 -1
- package/dist/control/daemon.d.ts +12 -0
- package/dist/control/daemon.d.ts.map +1 -1
- package/dist/control/pair.d.ts +6 -0
- package/dist/control/pair.d.ts.map +1 -1
- package/dist/control/route.d.ts +11 -3
- package/dist/control/route.d.ts.map +1 -1
- package/dist/hook.js +1 -1
- package/dist/index.js +27 -2
- package/dist/index.js.map +3 -3
- package/dist/worker.js +27 -2
- package/dist/worker.js.map +3 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// sessionpipe 0.13.
|
|
2
|
+
// sessionpipe 0.13.3 · Apache-2.0 · https://sessionpipe.org
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __export = (target2, all) => {
|
|
5
5
|
for (var name in all)
|
|
@@ -1045,6 +1045,14 @@ function claudeAccounts(dirs, home = homeDir()) {
|
|
|
1045
1045
|
}
|
|
1046
1046
|
return out2;
|
|
1047
1047
|
}
|
|
1048
|
+
function claudeAccountDirs(dirs, home = homeDir()) {
|
|
1049
|
+
const out2 = [];
|
|
1050
|
+
for (const d of dirs) {
|
|
1051
|
+
const l = claudeLogin(d, home);
|
|
1052
|
+
if (l) out2.push({ id: l.id, dir: d });
|
|
1053
|
+
}
|
|
1054
|
+
return out2;
|
|
1055
|
+
}
|
|
1048
1056
|
var LINK_RE = /^https:\/\/claude\.ai\/code\/session_[A-Za-z0-9]+$/;
|
|
1049
1057
|
function transcriptLink(records) {
|
|
1050
1058
|
for (let i = records.length - 1; i >= 0; i--) {
|
|
@@ -2740,7 +2748,8 @@ __export(claude_code_control_exports, {
|
|
|
2740
2748
|
relaunchMode: () => relaunchMode,
|
|
2741
2749
|
resolveHome: () => resolveHome,
|
|
2742
2750
|
runClaude: () => runClaude,
|
|
2743
|
-
sessionFolder: () => sessionFolder
|
|
2751
|
+
sessionFolder: () => sessionFolder,
|
|
2752
|
+
versionManagerBins: () => versionManagerBins
|
|
2744
2753
|
});
|
|
2745
2754
|
import { execFile, execFileSync as execFileSync3 } from "node:child_process";
|
|
2746
2755
|
import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync6, readSync as readSync2, statSync as statSync5 } from "node:fs";
|
|
@@ -3298,6 +3307,29 @@ var cmpVersion = (a, b) => {
|
|
|
3298
3307
|
if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
3299
3308
|
return 0;
|
|
3300
3309
|
};
|
|
3310
|
+
function versionManagerBins(env = process.env, home = os5.homedir()) {
|
|
3311
|
+
const out2 = [];
|
|
3312
|
+
const versions = (dir, tail2) => {
|
|
3313
|
+
let names = [];
|
|
3314
|
+
try {
|
|
3315
|
+
names = readdirSync4(dir).filter((n) => /^v?\d+(\.\d+)*$/.test(n));
|
|
3316
|
+
} catch {
|
|
3317
|
+
return;
|
|
3318
|
+
}
|
|
3319
|
+
for (const n of names.sort((a, b) => cmpVersion(a.replace(/^v/, ""), b.replace(/^v/, ""))).reverse())
|
|
3320
|
+
out2.push(path10.join(dir, n, ...tail2));
|
|
3321
|
+
};
|
|
3322
|
+
versions(path10.join(env.NVM_DIR || path10.join(home, ".nvm"), "versions", "node"), ["bin"]);
|
|
3323
|
+
const fnmDirs = env.FNM_DIR ? [env.FNM_DIR] : [
|
|
3324
|
+
path10.join(home, "Library", "Application Support", "fnm"),
|
|
3325
|
+
path10.join(home, ".local", "share", "fnm"),
|
|
3326
|
+
path10.join(home, ".fnm")
|
|
3327
|
+
];
|
|
3328
|
+
for (const d of fnmDirs) versions(path10.join(d, "node-versions"), ["installation", "bin"]);
|
|
3329
|
+
out2.push(path10.join(env.VOLTA_HOME || path10.join(home, ".volta"), "bin"));
|
|
3330
|
+
versions(path10.join(env.ASDF_DATA_DIR || path10.join(home, ".asdf"), "installs", "nodejs"), ["bin"]);
|
|
3331
|
+
return out2;
|
|
3332
|
+
}
|
|
3301
3333
|
function findClaude(env = process.env, home = os5.homedir()) {
|
|
3302
3334
|
if (env.SESSIONPIPE_CLAUDE && isExe(env.SESSIONPIPE_CLAUDE)) return env.SESSIONPIPE_CLAUDE;
|
|
3303
3335
|
const names = process.platform === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
|
|
@@ -3312,6 +3344,7 @@ function findClaude(env = process.env, home = os5.homedir()) {
|
|
|
3312
3344
|
"/usr/local/bin/claude"
|
|
3313
3345
|
])
|
|
3314
3346
|
if (isExe(p)) return p;
|
|
3347
|
+
for (const d of versionManagerBins(env, home)) if (isExe(path10.join(d, "claude"))) return path10.join(d, "claude");
|
|
3315
3348
|
const app = path10.join(home, "Library", "Application Support", "Claude", "claude-code");
|
|
3316
3349
|
let versions = [];
|
|
3317
3350
|
try {
|
|
@@ -6475,7 +6508,7 @@ var writeState = (file, s) => writePrivate(file, {
|
|
|
6475
6508
|
});
|
|
6476
6509
|
|
|
6477
6510
|
// src/control/pair.ts
|
|
6478
|
-
var VERSION = true ? "0.13.
|
|
6511
|
+
var VERSION = true ? "0.13.3" : "0.0.0";
|
|
6479
6512
|
var RENEW_FOR_MS = 60 * 6e4;
|
|
6480
6513
|
async function json(r) {
|
|
6481
6514
|
try {
|
|
@@ -6683,8 +6716,16 @@ var spec = (o) => ({
|
|
|
6683
6716
|
description: DESCRIPTION,
|
|
6684
6717
|
argv: [o.node, o.cli, "control", "run"],
|
|
6685
6718
|
logPath: path22.join(o.logDir, "daemon.log"),
|
|
6686
|
-
...o.home ? { home: o.home } : {}
|
|
6719
|
+
...o.home ? { home: o.home } : {},
|
|
6720
|
+
...o.env ? { env: o.env } : {}
|
|
6687
6721
|
});
|
|
6722
|
+
function serviceEnv(env = process.env) {
|
|
6723
|
+
const out2 = {};
|
|
6724
|
+
if (env.PATH) out2.PATH = env.PATH;
|
|
6725
|
+
const claude = claude_code_control_exports.findClaude(env);
|
|
6726
|
+
if (claude) out2.SESSIONPIPE_CLAUDE = claude;
|
|
6727
|
+
return out2;
|
|
6728
|
+
}
|
|
6688
6729
|
function installService2(o) {
|
|
6689
6730
|
const logDir = controlState(o.env);
|
|
6690
6731
|
mkdirSync7(logDir, { recursive: true, mode: 448 });
|
|
@@ -6695,7 +6736,7 @@ function installService2(o) {
|
|
|
6695
6736
|
} catch {
|
|
6696
6737
|
}
|
|
6697
6738
|
const r = installService({
|
|
6698
|
-
...spec({ node: o.node, cli: o.cli, logDir }),
|
|
6739
|
+
...spec({ node: o.node, cli: o.cli, logDir, env: serviceEnv(o.env) }),
|
|
6699
6740
|
...o.exec ? { exec: o.exec } : {},
|
|
6700
6741
|
...o.platform ? { platform: o.platform } : {}
|
|
6701
6742
|
});
|
|
@@ -7069,7 +7110,7 @@ async function tellDaemon(state2, job, session, cwd, transcript) {
|
|
|
7069
7110
|
300
|
|
7070
7111
|
);
|
|
7071
7112
|
}
|
|
7072
|
-
var VERSION2 = true ? "0.13.
|
|
7113
|
+
var VERSION2 = true ? "0.13.3" : "0.0.0";
|
|
7073
7114
|
function enqueueJob(state2, job) {
|
|
7074
7115
|
const jobs = jobsDir(state2);
|
|
7075
7116
|
mkdirSync11(jobs, { recursive: true, mode: 448 });
|
|
@@ -10026,10 +10067,12 @@ var FITS = {
|
|
|
10026
10067
|
resume: (s) => HEADLESS.has(s.harness) && s.known && !s.live,
|
|
10027
10068
|
fork: (s) => HEADLESS.has(s.harness) && s.known && s.live && s.canFork && !s.copied
|
|
10028
10069
|
};
|
|
10070
|
+
var CLAUDE_NOT_FOUND = "Claude Code not found on this computer: no `claude` on the daemon's PATH or in the usual folders (nvm, fnm, volta, asdf, Homebrew, ~/.local/bin, the desktop app). Run `sessionpipe connect` again from a shell where `claude` works, or set SESSIONPIPE_CLAUDE to its path in the daemon's service file";
|
|
10029
10071
|
function routePrompt(s) {
|
|
10030
10072
|
for (const mode of DELIVERY_LADDER) if (FITS[mode](s)) return { mode };
|
|
10031
10073
|
if (!HEADLESS.has(s.harness)) return { unsupported: `no idle delivery for ${s.harness} yet` };
|
|
10032
10074
|
if (!s.known) return { refused: "no_session" };
|
|
10075
|
+
if (s.harness === "claude-code" && s.installed === false) return { unsupported: CLAUDE_NOT_FOUND };
|
|
10033
10076
|
return {
|
|
10034
10077
|
unsupported: s.harness === "claude-code" ? s.copied ? "the session is open, and so is the copy its messages already go to; a copy is never itself copied" : "the session is open and this Claude Code can't copy it (no --fork-session)" : `the session is open in ${s.harness} right now, and a ${s.harness} session can't be copied: send it again once it has been idle a couple of minutes`
|
|
10035
10078
|
};
|
|
@@ -10068,7 +10111,7 @@ function frameInbox(text, receiver, id) {
|
|
|
10068
10111
|
|
|
10069
10112
|
${text}`;
|
|
10070
10113
|
}
|
|
10071
|
-
var COPY_NOTE = "You are a copy of that session, started
|
|
10114
|
+
var COPY_NOTE = "You are a copy of that session, started because the original is open in a window, and you have its conversation, folder and tools. Do what the message asks the way the original would: edits, commands and publishing all take effect for real. Only this conversation stays here, so the person reads your reply and not the original's window: say what you did, then stop.";
|
|
10072
10115
|
function frameStart(text, receiver) {
|
|
10073
10116
|
return `${text}
|
|
10074
10117
|
|
|
@@ -10834,7 +10877,7 @@ function startNamed(cmd) {
|
|
|
10834
10877
|
return null;
|
|
10835
10878
|
}
|
|
10836
10879
|
}
|
|
10837
|
-
var VERSION3 = true ? "0.13.
|
|
10880
|
+
var VERSION3 = true ? "0.13.3" : "0.0.0";
|
|
10838
10881
|
var MAX_REPLY = 2e4;
|
|
10839
10882
|
var HELLO_EVERY_MS = 10 * 6e4;
|
|
10840
10883
|
var TURN_FALLBACK_MS = 30 * 6e4;
|
|
@@ -10971,6 +11014,8 @@ var ControlDaemon = class {
|
|
|
10971
11014
|
procWaiters = [];
|
|
10972
11015
|
/** configDir → the last login answer and when (a yes is kept 10 minutes, a no 30 s). */
|
|
10973
11016
|
logins = /* @__PURE__ */ new Map();
|
|
11017
|
+
/** account id → the `signed_in` the log last said, so a change is said once. */
|
|
11018
|
+
signedInSaid = /* @__PURE__ */ new Map();
|
|
10974
11019
|
/** Intake paused for an update: the long-polls end and wait for this. */
|
|
10975
11020
|
paused = null;
|
|
10976
11021
|
/** Poll answers whose messages are being read and handed out. */
|
|
@@ -11556,7 +11601,7 @@ ${IN_PLACE_ANSWER}`,
|
|
|
11556
11601
|
// Every ACP agent's turn is acked `api` (§6).
|
|
11557
11602
|
...harnesses.some((h) => acp_control_exports.acpAgent(h)) ? ["api"] : []
|
|
11558
11603
|
];
|
|
11559
|
-
const accounts = this.accounts();
|
|
11604
|
+
const accounts = await this.signedInMarked(this.accounts());
|
|
11560
11605
|
const start = this.startOptions(harnesses);
|
|
11561
11606
|
for (const r of this.cfg.receivers) {
|
|
11562
11607
|
const body = {
|
|
@@ -11908,6 +11953,47 @@ ${IN_PLACE_ANSWER}`,
|
|
|
11908
11953
|
return [];
|
|
11909
11954
|
}
|
|
11910
11955
|
}
|
|
11956
|
+
/** Each Claude Code account's `signed_in` for hello: what `claude auth status` says
|
|
11957
|
+
* for its config dir (the answer a resume would get, cached as `signedIn` caches it),
|
|
11958
|
+
* so a receiver can say "run claude auth login on <machine>" before a message is
|
|
11959
|
+
* sent, not in its failed ack. An account signed in from any of its dirs is signed in;
|
|
11960
|
+
* one the daemon can't ask (no claude, no answer) is left unsaid. A change is logged.
|
|
11961
|
+
* (2026-11-05: sessions in the Claude app sign in through the app, and the command
|
|
11962
|
+
* line the daemon resumes with had no login of its own; the first message found out.) */
|
|
11963
|
+
async signedInMarked(accounts) {
|
|
11964
|
+
const login = this.deps.login;
|
|
11965
|
+
if (!login || !accounts.length || !this.deps.claude()) return accounts;
|
|
11966
|
+
const states = /* @__PURE__ */ new Map();
|
|
11967
|
+
for (const { id, dir } of claudeAccountDirs(this.deps.claudeDirs())) {
|
|
11968
|
+
const was = states.get(id);
|
|
11969
|
+
if (was === true) continue;
|
|
11970
|
+
const is = await this.loginState(dir, () => login(dir));
|
|
11971
|
+
if (is !== null || was === void 0) states.set(id, is);
|
|
11972
|
+
}
|
|
11973
|
+
return accounts.map((a) => {
|
|
11974
|
+
const is = a.harness === "claude-code" ? states.get(a.id) : void 0;
|
|
11975
|
+
if (is === void 0 || is === null) return a;
|
|
11976
|
+
const who2 = a.email ?? a.name ?? a.id;
|
|
11977
|
+
if (this.signedInSaid.get(a.id) !== is) {
|
|
11978
|
+
this.signedInSaid.set(a.id, is);
|
|
11979
|
+
this.deps.log(
|
|
11980
|
+
is ? `claude is signed in as ${who2}` : `claude isn't signed in as ${who2} as the daemon sees it: a message to a session of that account would fail until \`claude auth login\` is run here`
|
|
11981
|
+
);
|
|
11982
|
+
}
|
|
11983
|
+
return { ...a, signed_in: is };
|
|
11984
|
+
});
|
|
11985
|
+
}
|
|
11986
|
+
/** One harness account's login as `signedIn` reads it, with the answer itself. */
|
|
11987
|
+
async loginState(key, ask2) {
|
|
11988
|
+
const now = this.deps.now();
|
|
11989
|
+
const hit = this.logins.get(key);
|
|
11990
|
+
let login = hit && now - hit.at < (hit.login.loggedIn === false ? 3e4 : 6e5) ? hit.login : null;
|
|
11991
|
+
if (!login) {
|
|
11992
|
+
login = await ask2().catch(() => ({ loggedIn: null }));
|
|
11993
|
+
this.logins.set(key, { at: now, login });
|
|
11994
|
+
}
|
|
11995
|
+
return login.loggedIn;
|
|
11996
|
+
}
|
|
11911
11997
|
/** The harnesses this machine can run a message in, for hello: at most
|
|
11912
11998
|
* HELLO_HARNESSES_MAX, the three with their own drivers first. */
|
|
11913
11999
|
harnesses() {
|
|
@@ -12278,6 +12364,7 @@ ${IN_PLACE_ANSWER}`,
|
|
|
12278
12364
|
live,
|
|
12279
12365
|
known: !!t,
|
|
12280
12366
|
canFork: !!claude?.caps.fork,
|
|
12367
|
+
installed: !!claude,
|
|
12281
12368
|
// A message for a session that already has a copy goes to that copy; when the copy
|
|
12282
12369
|
// is live too the person is told, rather than handed a copy of a copy.
|
|
12283
12370
|
copied: !!this.state.copies[ref.id],
|
|
@@ -12441,14 +12528,7 @@ ${IN_PLACE_ANSWER}`;
|
|
|
12441
12528
|
}
|
|
12442
12529
|
/** One harness account's login, asked at most every 10 minutes (30 s after a no). */
|
|
12443
12530
|
async signedIn(key, ask2) {
|
|
12444
|
-
|
|
12445
|
-
const hit = this.logins.get(key);
|
|
12446
|
-
let login = hit && now - hit.at < (hit.login.loggedIn === false ? 3e4 : 6e5) ? hit.login : null;
|
|
12447
|
-
if (!login) {
|
|
12448
|
-
login = await ask2().catch(() => ({ loggedIn: null }));
|
|
12449
|
-
this.logins.set(key, { at: now, login });
|
|
12450
|
-
}
|
|
12451
|
-
return login.loggedIn !== false;
|
|
12531
|
+
return await this.loginState(key, ask2) !== false;
|
|
12452
12532
|
}
|
|
12453
12533
|
/** Null when a Codex or Antigravity run may go ahead; otherwise what to tell the person. */
|
|
12454
12534
|
async drivenProblem(drv) {
|
|
@@ -12562,8 +12642,7 @@ ${IN_PLACE_ANSWER}`;
|
|
|
12562
12642
|
if (acp_control_exports.acpAgent(harness)) return this.resumeAcp(r, id, cmd, place, sent);
|
|
12563
12643
|
if (harness !== "claude-code") return this.resumeDriven(r, id, cmd, place, sent);
|
|
12564
12644
|
const claude = this.deps.claude();
|
|
12565
|
-
if (!claude)
|
|
12566
|
-
return this.finish(r, id, { outcome: "failed", detail: "Claude Code isn't installed on this machine" });
|
|
12645
|
+
if (!claude) return this.finish(r, id, { outcome: "failed", detail: CLAUDE_NOT_FOUND });
|
|
12567
12646
|
const ref = parseSessionRef(cmd.session);
|
|
12568
12647
|
const dir = place.transcript?.configDir;
|
|
12569
12648
|
const home = homeSettings2(dir);
|
|
@@ -12856,8 +12935,7 @@ ${COPY_NOTE}` : "");
|
|
|
12856
12935
|
detail: "that new session's id is already taken"
|
|
12857
12936
|
});
|
|
12858
12937
|
const claude = this.deps.sdk ? null : this.deps.claude();
|
|
12859
|
-
if (!this.deps.sdk && !claude)
|
|
12860
|
-
return this.finish(r, id, { outcome: "failed", detail: "Claude Code isn't installed on this machine" });
|
|
12938
|
+
if (!this.deps.sdk && !claude) return this.finish(r, id, { outcome: "failed", detail: CLAUDE_NOT_FOUND });
|
|
12861
12939
|
const signedOut = await this.loginProblem(this.deps.env?.CLAUDE_CONFIG_DIR);
|
|
12862
12940
|
if (signedOut) return this.finish(r, id, { outcome: "failed", detail: signedOut });
|
|
12863
12941
|
if (claude) {
|
|
@@ -13844,6 +13922,7 @@ async function runDaemon(out2, distDir2) {
|
|
|
13844
13922
|
}
|
|
13845
13923
|
return claude;
|
|
13846
13924
|
};
|
|
13925
|
+
if (!findClaude2()) log2(CLAUDE_NOT_FOUND);
|
|
13847
13926
|
const sdk = await loadSdk({ claudeBin: findClaude2(), env: process.env, log: log2 });
|
|
13848
13927
|
let updater = null;
|
|
13849
13928
|
d = new ControlDaemon(cfg, controlState(), socketPath(stateDir()), {
|