sessionpipe-core 0.11.0 → 0.12.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/dist/control.js CHANGED
@@ -1,4 +1,4 @@
1
- // sessionpipe-core 0.11.0 · Apache-2.0 · https://sessionpipe.org
1
+ // sessionpipe-core 0.12.0 · Apache-2.0 · https://sessionpipe.org
2
2
 
3
3
  // src/control/verify.ts
4
4
  var GRANT_MAX_MS = 24 * 36e5;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // sessionpipe-core 0.11.0 · Apache-2.0 · https://sessionpipe.org
1
+ // sessionpipe-core 0.12.0 · Apache-2.0 · https://sessionpipe.org
2
2
  var __defProp = Object.defineProperty;
3
3
  var __export = (target, all) => {
4
4
  for (var name in all)
@@ -645,13 +645,7 @@ var claudeCode = {
645
645
  );
646
646
  break;
647
647
  case "Stop":
648
- events.push(
649
- ev("turn.ended", {
650
- turn_id,
651
- reason: "stop",
652
- background: Array.isArray(s.background_tasks) ? s.background_tasks.length : void 0
653
- })
654
- );
648
+ events.push(ev("turn.ended", { turn_id, reason: "stop", background: backgroundCount(s.background_tasks) }));
655
649
  break;
656
650
  case "StopFailure":
657
651
  events.push(
@@ -674,7 +668,8 @@ var claudeCode = {
674
668
  events.push(
675
669
  ev("subagent.ended", {
676
670
  agent_id: String(s.agent_id ?? ""),
677
- agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
671
+ agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0,
672
+ background: backgroundCount(s.background_tasks, String(s.agent_id ?? ""))
678
673
  })
679
674
  );
680
675
  break;
@@ -749,6 +744,15 @@ var claudeCode = {
749
744
  return rows2;
750
745
  }
751
746
  };
747
+ var WAITER_RE = /(?:^|[\s"'`/\\])sessionpipe(?:\.\w+)?["']?\s+wait(?:\s|$)/;
748
+ function backgroundCount(tasks, ending) {
749
+ if (!Array.isArray(tasks)) return void 0;
750
+ return tasks.filter((t) => {
751
+ const task = t;
752
+ if (ending !== void 0 && task?.id === ending) return false;
753
+ return !(typeof task?.command === "string" && WAITER_RE.test(task.command));
754
+ }).length;
755
+ }
752
756
  var LINE_REFRESH_S = 60;
753
757
  function statusLine(file, want, cmd) {
754
758
  const f = readJsonFile(file);
@@ -1629,7 +1633,7 @@ import path7 from "node:path";
1629
1633
 
1630
1634
  // src/adapters/headless.ts
1631
1635
  import { spawn } from "node:child_process";
1632
- import { accessSync, constants, statSync as statSync3 } from "node:fs";
1636
+ import { accessSync, constants, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
1633
1637
  import path6 from "node:path";
1634
1638
  var JOB_TIMEOUT_MS = 30 * 6e4;
1635
1639
  var OUT_CAP = 1024 * 1024;
@@ -1651,6 +1655,32 @@ function findBinary(o) {
1651
1655
  for (const d of o.dirs) for (const n of o.names) if (isExe(path6.join(d, n))) return path6.join(d, n);
1652
1656
  return null;
1653
1657
  }
1658
+ function shimTarget(shim) {
1659
+ let text = "";
1660
+ try {
1661
+ text = readFileSync4(shim, "utf8");
1662
+ } catch {
1663
+ return null;
1664
+ }
1665
+ const m = /"%(?:~dp0|dp0%)\\([^"]+)"\s+%\*/.exec(text);
1666
+ if (!m) return null;
1667
+ const target = path6.resolve(path6.dirname(shim), m[1].split("\\").join(path6.sep));
1668
+ try {
1669
+ return statSync3(target).isFile() ? target : null;
1670
+ } catch {
1671
+ return null;
1672
+ }
1673
+ }
1674
+ function spawnable(bin, args) {
1675
+ if (!/\.(cmd|bat)$/i.test(bin)) return { file: bin, args };
1676
+ const target = shimTarget(bin);
1677
+ return target ? { file: process.execPath, args: [target, ...args] } : null;
1678
+ }
1679
+ var shimNote = (bin) => `${path6.basename(bin)} is a launcher script that names no program to run; point the harness's override (SESSIONPIPE_CLAUDE, SESSIONPIPE_CODEX, \u2026) at the program itself`;
1680
+ function probe(bin, args) {
1681
+ const s = spawnable(bin, args);
1682
+ return s ? { ...s, shell: false } : { file: [`"${bin}"`, ...args].join(" "), args: [], shell: true };
1683
+ }
1654
1684
  function jobEnv(env = process.env) {
1655
1685
  return { ...env, SESSIONPIPE_CONTROL_JOB: "1" };
1656
1686
  }
@@ -1663,8 +1693,13 @@ function runHeadless(bin, args, o) {
1663
1693
  let timedOut = false;
1664
1694
  let settled = false;
1665
1695
  let child;
1696
+ const c = spawnable(bin, args);
1697
+ if (!c) {
1698
+ resolve({ error: shimNote(bin) });
1699
+ return;
1700
+ }
1666
1701
  try {
1667
- child = spawn(bin, args, {
1702
+ child = spawn(c.file, c.args, {
1668
1703
  cwd: o.cwd,
1669
1704
  env: o.env,
1670
1705
  stdio: ["ignore", "pipe", "pipe"],
@@ -1864,8 +1899,10 @@ function startAcp(o) {
1864
1899
  });
1865
1900
  const label = o.label;
1866
1901
  let child;
1902
+ const c = spawnable(o.bin, o.args);
1903
+ if (!c) return Promise.resolve({ failed: `couldn't start ${label}: ${shimNote(o.bin)}` });
1867
1904
  try {
1868
- child = spawn2(o.bin, o.args, {
1905
+ child = spawn2(c.file, c.args, {
1869
1906
  cwd: o.cwd,
1870
1907
  env: jobEnv(o.env),
1871
1908
  stdio: ["pipe", "pipe", "pipe"],
@@ -1926,15 +1963,15 @@ function startAcp(o) {
1926
1963
  return;
1927
1964
  }
1928
1965
  if (kind !== "agent_message_chunk" || !turn) return;
1929
- const c = obj(u.content);
1930
- if (c?.type !== "text" || !str(c.text)) return;
1966
+ const c2 = obj(u.content);
1967
+ if (c2?.type !== "text" || !str(c2.text)) return;
1931
1968
  const mid = str(u.messageId) || null;
1932
1969
  if (turn.message !== void 0 && mid !== turn.message && turn.text.trim())
1933
1970
  turn.text = `${turn.text.trimEnd()}
1934
1971
 
1935
1972
  `;
1936
1973
  turn.message = mid;
1937
- turn.text += str(c.text);
1974
+ turn.text += str(c2.text);
1938
1975
  o.onText?.(turn.text);
1939
1976
  };
1940
1977
  const onRequest = (id, method, params) => {
@@ -2130,7 +2167,7 @@ import {
2130
2167
  existsSync as existsSync3,
2131
2168
  mkdirSync as mkdirSync2,
2132
2169
  readdirSync as readdirSync3,
2133
- readFileSync as readFileSync4,
2170
+ readFileSync as readFileSync5,
2134
2171
  realpathSync,
2135
2172
  statSync as statSync4,
2136
2173
  unlinkSync as unlinkSync2,
@@ -2259,7 +2296,7 @@ var antigravity = {
2259
2296
  readTranscript(file, fromLine) {
2260
2297
  let text = "";
2261
2298
  try {
2262
- text = readFileSync4(file, "utf8");
2299
+ text = readFileSync5(file, "utf8");
2263
2300
  } catch {
2264
2301
  return { turns: [], line: fromLine };
2265
2302
  }
@@ -2338,7 +2375,7 @@ function agProjects(dir) {
2338
2375
  if (!ID_RE2.test(id)) continue;
2339
2376
  let j;
2340
2377
  try {
2341
- j = JSON.parse(readFileSync4(path8.join(dir, n), "utf8"));
2378
+ j = JSON.parse(readFileSync5(path8.join(dir, n), "utf8"));
2342
2379
  } catch {
2343
2380
  continue;
2344
2381
  }
@@ -2375,7 +2412,7 @@ function antigravityRemoteUrl(transcript, opts) {
2375
2412
  for (const d of dirs) {
2376
2413
  try {
2377
2414
  const m = /installation_uuid:\s*"([0-9a-f-]{36})"/i.exec(
2378
- readFileSync4(path8.join(d, "antigravity_state.pbtxt"), "utf8")
2415
+ readFileSync5(path8.join(d, "antigravity_state.pbtxt"), "utf8")
2379
2416
  );
2380
2417
  if (!m) continue;
2381
2418
  const base = `https://antigravity.google.com/r/${m[1]}-v2`;
@@ -2579,7 +2616,7 @@ __export(claude_code_control_exports, {
2579
2616
  sessionFolder: () => sessionFolder
2580
2617
  });
2581
2618
  import { execFile, execFileSync as execFileSync3 } from "node:child_process";
2582
- import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync5, readSync as readSync2, statSync as statSync5 } from "node:fs";
2619
+ import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync6, readSync as readSync2, statSync as statSync5 } from "node:fs";
2583
2620
  import os5 from "node:os";
2584
2621
  import path10 from "node:path";
2585
2622
 
@@ -2746,11 +2783,12 @@ function parseAuthStatus(stdout) {
2746
2783
  }
2747
2784
  }
2748
2785
  function claudeLogin2(bin, configDir, env = process.env) {
2786
+ const c = probe(bin, ["auth", "status", "--json"]);
2749
2787
  return new Promise((resolve) => {
2750
2788
  execFile(
2751
- bin,
2752
- ["auth", "status", "--json"],
2753
- { encoding: "utf8", timeout: 2e4, env: claudeEnv(configDir, env), maxBuffer: 256 * 1024 },
2789
+ c.file,
2790
+ c.args,
2791
+ { encoding: "utf8", timeout: 2e4, env: claudeEnv(configDir, env), maxBuffer: 256 * 1024, shell: c.shell },
2754
2792
  // `auth status` may exit non-zero when signed out; its JSON still says so.
2755
2793
  (_err, stdout) => resolve(parseAuthStatus(stdout))
2756
2794
  );
@@ -2770,13 +2808,15 @@ function loginHint(configDir, platform = process.platform) {
2770
2808
  }
2771
2809
  function detectCaps(bin) {
2772
2810
  let help = "";
2811
+ const c = probe(bin, ["--help"]);
2773
2812
  try {
2774
- help = execFileSync3(bin, ["--help"], {
2813
+ help = execFileSync3(c.file, c.args, {
2775
2814
  encoding: "utf8",
2776
2815
  timeout: 3e4,
2777
2816
  env: claudeEnv(),
2778
2817
  stdio: ["ignore", "pipe", "pipe"],
2779
- maxBuffer: 4 * 1024 * 1024
2818
+ maxBuffer: 4 * 1024 * 1024,
2819
+ shell: c.shell
2780
2820
  });
2781
2821
  } catch (e) {
2782
2822
  help = String(e?.stdout || "");
@@ -2883,7 +2923,7 @@ function liveProcess(session, configDir) {
2883
2923
  }
2884
2924
  for (const n of names) {
2885
2925
  try {
2886
- const j = JSON.parse(readFileSync5(path10.join(configDir, "sessions", n), "utf8"));
2926
+ const j = JSON.parse(readFileSync6(path10.join(configDir, "sessions", n), "utf8"));
2887
2927
  if (String(j.sessionId) !== session) continue;
2888
2928
  if (pidAlive(Number(j.pid ?? n.slice(0, -5)))) return true;
2889
2929
  } catch {
@@ -2931,7 +2971,7 @@ function parseResult(stdout) {
2931
2971
  var isUuid = (s) => typeof s === "string" && UUID_RE2.test(s);
2932
2972
 
2933
2973
  // src/adapters/codex.ts
2934
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
2974
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
2935
2975
  import path11 from "node:path";
2936
2976
 
2937
2977
  // src/adapters/codex-config.ts
@@ -3088,7 +3128,7 @@ function installNotify(cmd, env) {
3088
3128
  const file = configFile2(env);
3089
3129
  let text = "";
3090
3130
  try {
3091
- text = readFileSync6(file, "utf8");
3131
+ text = readFileSync7(file, "utf8");
3092
3132
  } catch {
3093
3133
  }
3094
3134
  const argv = cmd([NAME3, "notify"]).argv;
@@ -3134,7 +3174,7 @@ function uninstallNotify(env) {
3134
3174
  const file = configFile2(env);
3135
3175
  let text;
3136
3176
  try {
3137
- text = readFileSync6(file, "utf8");
3177
+ text = readFileSync7(file, "utf8");
3138
3178
  } catch {
3139
3179
  return { file, changed: false };
3140
3180
  }
@@ -3206,7 +3246,7 @@ var codex = {
3206
3246
  ];
3207
3247
  let text = "";
3208
3248
  try {
3209
- text = readFileSync6(configFile2(env), "utf8");
3249
+ text = readFileSync7(configFile2(env), "utf8");
3210
3250
  } catch {
3211
3251
  }
3212
3252
  const n = readNotify(text);
@@ -3333,7 +3373,7 @@ var codex = {
3333
3373
  readTranscript(file, fromLine) {
3334
3374
  let text = "";
3335
3375
  try {
3336
- text = readFileSync6(file, "utf8");
3376
+ text = readFileSync7(file, "utf8");
3337
3377
  } catch {
3338
3378
  return { turns: [], line: fromLine };
3339
3379
  }
@@ -3450,7 +3490,7 @@ __export(codex_control_exports, {
3450
3490
  reading: () => reading2
3451
3491
  });
3452
3492
  import { execFile as execFile2 } from "node:child_process";
3453
- import { readFileSync as readFileSync7 } from "node:fs";
3493
+ import { readFileSync as readFileSync8 } from "node:fs";
3454
3494
  import os6 from "node:os";
3455
3495
  import path12 from "node:path";
3456
3496
  var LABEL2 = "Codex";
@@ -3496,7 +3536,7 @@ function codexArgs(o) {
3496
3536
  function codexModels(env = process.env, home = os6.homedir()) {
3497
3537
  try {
3498
3538
  const file = path12.join(env.CODEX_HOME || path12.join(home, ".codex"), "models_cache.json");
3499
- const j = JSON.parse(readFileSync7(file, "utf8"));
3539
+ const j = JSON.parse(readFileSync8(file, "utf8"));
3500
3540
  if (!Array.isArray(j.models)) return [];
3501
3541
  const out = [];
3502
3542
  for (const m of j.models)
@@ -3512,11 +3552,12 @@ var codexEnv = (env = process.env) => jobEnv(env);
3512
3552
  var LOGIN_HINT = "Codex isn't signed in on this machine: run `codex login --device-auth` there.";
3513
3553
  function codexLogin(bin, env = process.env) {
3514
3554
  if (env.CODEX_API_KEY) return Promise.resolve({ loggedIn: true, method: "api key" });
3555
+ const c = probe(bin, ["login", "status"]);
3515
3556
  return new Promise((resolve) => {
3516
3557
  execFile2(
3517
- bin,
3518
- ["login", "status"],
3519
- { encoding: "utf8", timeout: 2e4, env: codexEnv(env), maxBuffer: 256 * 1024 },
3558
+ c.file,
3559
+ c.args,
3560
+ { encoding: "utf8", timeout: 2e4, env: codexEnv(env), maxBuffer: 256 * 1024, shell: c.shell },
3520
3561
  (err) => {
3521
3562
  if (!err) return resolve({ loggedIn: true });
3522
3563
  const code = err.code;
@@ -4021,7 +4062,7 @@ function devinAccount(env = process.env) {
4021
4062
 
4022
4063
  // src/adapters/gemini-cli.ts
4023
4064
  import { execFileSync as execFileSync5 } from "node:child_process";
4024
- import { existsSync as existsSync7, readFileSync as readFileSync8 } from "node:fs";
4065
+ import { existsSync as existsSync7, readFileSync as readFileSync9 } from "node:fs";
4025
4066
  import path14 from "node:path";
4026
4067
  var NAME5 = "gemini-cli";
4027
4068
  var GEMINI_EVENTS = [
@@ -4043,14 +4084,14 @@ function geminiVersion(platform = process.platform, env = process.env) {
4043
4084
  const win = platform === "win32";
4044
4085
  const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [path14.dirname(process.execPath)], env }) : "gemini";
4045
4086
  if (!bin) throw new Error("no gemini on PATH");
4046
- const o = {
4087
+ const c = probe(bin, ["--version"]);
4088
+ return execFileSync5(c.file, c.args, {
4047
4089
  encoding: "utf8",
4048
4090
  timeout: 4e3,
4049
4091
  stdio: ["ignore", "pipe", "ignore"],
4050
- env
4051
- };
4052
- const out = /\.cmd$/i.test(bin) ? execFileSync5(`"${bin}" --version`, { ...o, shell: true }) : execFileSync5(bin, ["--version"], o);
4053
- return out.trim();
4092
+ env,
4093
+ shell: c.shell
4094
+ }).trim();
4054
4095
  }
4055
4096
  function geminiHooksActive(settingsFile) {
4056
4097
  const tools = readJson(settingsFile).tools;
@@ -4175,7 +4216,7 @@ var geminiCli = {
4175
4216
  readTranscript(file, fromLine) {
4176
4217
  let text = "";
4177
4218
  try {
4178
- text = readFileSync8(file, "utf8");
4219
+ text = readFileSync9(file, "utf8");
4179
4220
  } catch {
4180
4221
  return { turns: [], line: fromLine };
4181
4222
  }
@@ -4224,12 +4265,12 @@ function drivableHarnesses(env = process.env) {
4224
4265
  }
4225
4266
 
4226
4267
  // src/config.ts
4227
- import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
4268
+ import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
4228
4269
  import path15 from "node:path";
4229
4270
  var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
4230
4271
  function readConfig(file = configFile()) {
4231
4272
  try {
4232
- const j = JSON.parse(readFileSync9(file, "utf8"));
4273
+ const j = JSON.parse(readFileSync10(file, "utf8"));
4233
4274
  return {
4234
4275
  ...DEFAULT_CONFIG,
4235
4276
  ...j,
@@ -4330,7 +4371,7 @@ import {
4330
4371
  mkdirSync as mkdirSync5,
4331
4372
  openSync as openSync3,
4332
4373
  readdirSync as readdirSync6,
4333
- readFileSync as readFileSync10,
4374
+ readFileSync as readFileSync11,
4334
4375
  readSync as readSync3,
4335
4376
  renameSync,
4336
4377
  statSync as statSync6,
@@ -4360,7 +4401,7 @@ var Outbox = class {
4360
4401
  const f = this.seqFile(ref);
4361
4402
  let n = -1;
4362
4403
  try {
4363
- const t = readFileSync10(f, "utf8").trim();
4404
+ const t = readFileSync11(f, "utf8").trim();
4364
4405
  if (/^\d+$/.test(t)) n = Number(t);
4365
4406
  } catch {
4366
4407
  }
@@ -4372,7 +4413,7 @@ var Outbox = class {
4372
4413
  lastSeqInFile(ref) {
4373
4414
  const f = this.file(ref);
4374
4415
  if (!existsSync9(f)) return -1;
4375
- const lines = readFileSync10(f, "utf8").trim().split("\n");
4416
+ const lines = readFileSync11(f, "utf8").trim().split("\n");
4376
4417
  for (let i = lines.length - 1; i >= 0; i--) {
4377
4418
  try {
4378
4419
  const j = JSON.parse(lines[i]);
@@ -4495,7 +4536,7 @@ var Cursors = class {
4495
4536
  }
4496
4537
  read() {
4497
4538
  try {
4498
- return JSON.parse(readFileSync10(this.file(), "utf8"));
4539
+ return JSON.parse(readFileSync11(this.file(), "utf8"));
4499
4540
  } catch {
4500
4541
  return {};
4501
4542
  }
@@ -4519,7 +4560,7 @@ function takeNamedLock(root, name, staleMs = 5 * 6e4) {
4519
4560
  mkdirSync5(dir, { recursive: true, mode: 448 });
4520
4561
  const f = path16.join(dir, `${name}.lock`);
4521
4562
  try {
4522
- if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync10(f, "utf8")))) unlinkSync3(f);
4563
+ if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync11(f, "utf8")))) unlinkSync3(f);
4523
4564
  else return null;
4524
4565
  } catch {
4525
4566
  }