sessionpipe-core 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/dist/index.js CHANGED
@@ -1,4 +1,9 @@
1
- // sessionpipe-core 0.1.1 · Apache-2.0 · https://sessionpipe.org
1
+ // sessionpipe-core 0.2.0 · Apache-2.0 · https://sessionpipe.org
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, { get: all[name], enumerable: true });
6
+ };
2
7
 
3
8
  // src/adapters/antigravity.ts
4
9
  import {
@@ -278,7 +283,7 @@ function entryFor(event, harness, cmd, o) {
278
283
  const matcher = o.matcherFor(event);
279
284
  return {
280
285
  ...matcher !== void 0 ? { matcher } : {},
281
- hooks: [{ type: "command", command: cmd([harness, event]).command, timeout: o.timeout }]
286
+ hooks: [{ type: "command", command: cmd([harness, event]).command, timeout: o.timeoutFor?.(event) ?? o.timeout }]
282
287
  };
283
288
  }
284
289
  function installClaudeShaped(file, harness, events, cmd, o) {
@@ -702,7 +707,13 @@ var CLAUDE_EVENTS = [
702
707
  ];
703
708
  var LEAN_SKIP = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUseFailure"]);
704
709
  var eventsFor = (lean) => lean ? CLAUDE_EVENTS.filter((e) => !LEAN_SKIP.has(e)) : [...CLAUDE_EVENTS];
705
- var OPTS = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
710
+ var OPTS = {
711
+ matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0,
712
+ timeout: 5,
713
+ // The hook waits up to 125 s for a verified control answer, and only when control
714
+ // is paired on this machine; everywhere else it exits in milliseconds as before.
715
+ timeoutFor: (e) => e === "PermissionRequest" ? 130 : void 0
716
+ };
706
717
  var DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;
707
718
  function claudeDirs(env = process.env, home = HOME) {
708
719
  const out = [];
@@ -1843,17 +1854,366 @@ function prune4(o) {
1843
1854
  return o;
1844
1855
  }
1845
1856
 
1857
+ // src/adapters/claude-code-control.ts
1858
+ var claude_code_control_exports = {};
1859
+ __export(claude_code_control_exports, {
1860
+ JOB_TIMEOUT_MS: () => JOB_TIMEOUT_MS,
1861
+ claudeEnv: () => claudeEnv,
1862
+ detectCaps: () => detectCaps,
1863
+ findClaude: () => findClaude,
1864
+ findTranscript: () => findTranscript,
1865
+ isUuid: () => isUuid,
1866
+ liveProcess: () => liveProcess,
1867
+ modeFlags: () => modeFlags,
1868
+ parseCaps: () => parseCaps,
1869
+ parseResult: () => parseResult,
1870
+ recentlyWritten: () => recentlyWritten,
1871
+ runClaude: () => runClaude,
1872
+ sessionFolder: () => sessionFolder
1873
+ });
1874
+ import { execFileSync as execFileSync4, spawn } from "node:child_process";
1875
+ import { accessSync, closeSync as closeSync2, constants, openSync as openSync2, readdirSync as readdirSync5, readFileSync as readFileSync6, readSync as readSync2, statSync as statSync4 } from "node:fs";
1876
+ import os2 from "node:os";
1877
+ import path8 from "node:path";
1878
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1879
+ var JOB_TIMEOUT_MS = 30 * 6e4;
1880
+ var OUT_CAP = 1024 * 1024;
1881
+ var LIVE_WRITE_MS = 9e4;
1882
+ function isExe(p) {
1883
+ try {
1884
+ if (!statSync4(p).isFile()) return false;
1885
+ accessSync(p, constants.X_OK);
1886
+ return true;
1887
+ } catch {
1888
+ return false;
1889
+ }
1890
+ }
1891
+ var cmpVersion = (a, b) => {
1892
+ const pa = a.split(".").map(Number);
1893
+ const pb = b.split(".").map(Number);
1894
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++)
1895
+ if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);
1896
+ return 0;
1897
+ };
1898
+ function findClaude(env = process.env, home = os2.homedir()) {
1899
+ if (env.SESSIONPIPE_CLAUDE && isExe(env.SESSIONPIPE_CLAUDE)) return env.SESSIONPIPE_CLAUDE;
1900
+ const names = process.platform === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
1901
+ for (const d of String(env.PATH || "").split(path8.delimiter)) {
1902
+ if (!d || !path8.isAbsolute(d)) continue;
1903
+ for (const n of names) if (isExe(path8.join(d, n))) return path8.join(d, n);
1904
+ }
1905
+ for (const p of [
1906
+ path8.join(home, ".claude", "local", "claude"),
1907
+ path8.join(home, ".local", "bin", "claude"),
1908
+ "/opt/homebrew/bin/claude",
1909
+ "/usr/local/bin/claude"
1910
+ ])
1911
+ if (isExe(p)) return p;
1912
+ const app = path8.join(home, "Library", "Application Support", "Claude", "claude-code");
1913
+ let versions = [];
1914
+ try {
1915
+ versions = readdirSync5(app).filter((n) => /^\d+(\.\d+)*$/.test(n)).sort(cmpVersion).reverse();
1916
+ } catch {
1917
+ }
1918
+ for (const v of versions) {
1919
+ const p = path8.join(app, v, "claude.app", "Contents", "MacOS", "claude");
1920
+ if (isExe(p)) return p;
1921
+ }
1922
+ return null;
1923
+ }
1924
+ function optionBlock(help, flag) {
1925
+ const lines = help.split("\n");
1926
+ const re = new RegExp(`^\\s{0,4}(?:-\\w, )?${flag.replace(/-/g, "\\-")}(?![\\w-])`);
1927
+ const start = lines.findIndex((l) => re.test(l));
1928
+ if (start < 0) return null;
1929
+ const out = [lines[start]];
1930
+ for (let i = start + 1; i < lines.length && lines[i]?.trim() && !/^\s{0,4}-/.test(lines[i]); i++)
1931
+ out.push(lines[i]);
1932
+ return out.join(" ");
1933
+ }
1934
+ function parseCaps(help) {
1935
+ const pm = optionBlock(help, "--permission-mode");
1936
+ const pp = optionBlock(help, "--permission-prompts");
1937
+ return {
1938
+ hasMode: !!pm,
1939
+ modes: pm ? [...pm.matchAll(/"([A-Za-z]+)"/g)].map((m) => m[1]) : [],
1940
+ promptsNone: !!pp && /"none"/.test(pp),
1941
+ fork: !!optionBlock(help, "--fork-session")
1942
+ };
1943
+ }
1944
+ function modeFlags(mode, caps) {
1945
+ if (mode === "auto") {
1946
+ if (!caps.modes.includes("auto"))
1947
+ return { error: "this machine is in auto mode, but its Claude Code has no auto permission mode" };
1948
+ return { args: ["--permission-mode", "auto"] };
1949
+ }
1950
+ if (caps.modes.includes("dontAsk")) return { args: ["--permission-mode", "dontAsk"] };
1951
+ const args = [];
1952
+ if (caps.hasMode)
1953
+ args.push(
1954
+ "--permission-mode",
1955
+ caps.modes.includes("manual") && !caps.modes.includes("default") ? "manual" : "default"
1956
+ );
1957
+ if (caps.promptsNone) args.push("--permission-prompts", "none");
1958
+ return { args };
1959
+ }
1960
+ function claudeEnv(configDir, env = process.env) {
1961
+ const e = { ...env };
1962
+ for (const k of [
1963
+ "CLAUDECODE",
1964
+ "CLAUDE_CODE_SESSION_ID",
1965
+ "CLAUDE_CODE_HOST_SESSION_ID",
1966
+ "CLAUDE_CODE_ENTRYPOINT",
1967
+ "CLAUDE_CODE_SESSION_ATTENDED",
1968
+ "CLAUDE_CODE_SSE_PORT"
1969
+ ])
1970
+ delete e[k];
1971
+ e.SESSIONPIPE_CONTROL_JOB = "1";
1972
+ if (configDir !== void 0) {
1973
+ if (path8.resolve(configDir) === path8.resolve(os2.homedir(), ".claude")) delete e.CLAUDE_CONFIG_DIR;
1974
+ else e.CLAUDE_CONFIG_DIR = configDir;
1975
+ }
1976
+ return e;
1977
+ }
1978
+ function detectCaps(bin) {
1979
+ let help = "";
1980
+ try {
1981
+ help = execFileSync4(bin, ["--help"], {
1982
+ encoding: "utf8",
1983
+ timeout: 3e4,
1984
+ env: claudeEnv(),
1985
+ stdio: ["ignore", "pipe", "pipe"],
1986
+ maxBuffer: 4 * 1024 * 1024
1987
+ });
1988
+ } catch (e) {
1989
+ help = String(e?.stdout || "");
1990
+ }
1991
+ return parseCaps(help);
1992
+ }
1993
+ function findTranscript(session, dirs) {
1994
+ if (!UUID_RE.test(session)) return null;
1995
+ for (const dir of dirs) {
1996
+ let projects = [];
1997
+ try {
1998
+ projects = readdirSync5(path8.join(dir, "projects"), { withFileTypes: true });
1999
+ } catch {
2000
+ continue;
2001
+ }
2002
+ for (const p of projects) {
2003
+ if (!p.isDirectory()) continue;
2004
+ const file = path8.join(dir, "projects", p.name, `${session}.jsonl`);
2005
+ try {
2006
+ if (statSync4(file).isFile()) return { file, configDir: dir, project: p.name };
2007
+ } catch {
2008
+ }
2009
+ }
2010
+ }
2011
+ return null;
2012
+ }
2013
+ var projectName = (cwd) => cwd.replace(/[^a-zA-Z0-9]/g, "-");
2014
+ function sessionFolder(t, cap2 = 8 * 1024 * 1024) {
2015
+ let first = null;
2016
+ let count = 0;
2017
+ let fd;
2018
+ const look = (line) => {
2019
+ if (line.indexOf('"cwd"') < 0) return null;
2020
+ let j;
2021
+ try {
2022
+ j = JSON.parse(line.toString("utf8"));
2023
+ } catch {
2024
+ return null;
2025
+ }
2026
+ if (!j || typeof j.cwd !== "string" || !path8.isAbsolute(j.cwd)) return null;
2027
+ if (projectName(j.cwd) === t.project) return j.cwd;
2028
+ first ??= j.cwd;
2029
+ count++;
2030
+ return null;
2031
+ };
2032
+ try {
2033
+ fd = openSync2(t.file, "r");
2034
+ const buf = Buffer.alloc(256 * 1024);
2035
+ let pos = 0;
2036
+ let rest = Buffer.alloc(0);
2037
+ let skipping = false;
2038
+ while (pos < cap2 && count < 50) {
2039
+ const n = readSync2(fd, buf, 0, buf.length, pos);
2040
+ if (!n) {
2041
+ if (!skipping && rest.length) {
2042
+ const hit = look(rest);
2043
+ if (hit) return hit;
2044
+ }
2045
+ break;
2046
+ }
2047
+ pos += n;
2048
+ let chunk = rest.length ? Buffer.concat([rest, buf.subarray(0, n)]) : buf.subarray(0, n);
2049
+ let nl = chunk.indexOf(10);
2050
+ while (nl >= 0) {
2051
+ const line = chunk.subarray(0, nl);
2052
+ chunk = chunk.subarray(nl + 1);
2053
+ if (skipping) skipping = false;
2054
+ else {
2055
+ const hit = look(line);
2056
+ if (hit) return hit;
2057
+ }
2058
+ nl = chunk.indexOf(10);
2059
+ }
2060
+ if (chunk.length > 2 * 1024 * 1024) {
2061
+ skipping = true;
2062
+ rest = Buffer.alloc(0);
2063
+ } else rest = skipping ? Buffer.alloc(0) : Buffer.from(chunk);
2064
+ }
2065
+ } catch {
2066
+ } finally {
2067
+ if (fd !== void 0)
2068
+ try {
2069
+ closeSync2(fd);
2070
+ } catch {
2071
+ }
2072
+ }
2073
+ return first;
2074
+ }
2075
+ function pidAlive(pid) {
2076
+ if (!Number.isSafeInteger(pid) || pid <= 1) return false;
2077
+ try {
2078
+ process.kill(pid, 0);
2079
+ return true;
2080
+ } catch (e) {
2081
+ return e.code === "EPERM";
2082
+ }
2083
+ }
2084
+ function liveProcess(session, configDir) {
2085
+ let names = [];
2086
+ try {
2087
+ names = readdirSync5(path8.join(configDir, "sessions")).filter((n) => /^\d+\.json$/.test(n));
2088
+ } catch {
2089
+ return false;
2090
+ }
2091
+ for (const n of names) {
2092
+ try {
2093
+ const j = JSON.parse(readFileSync6(path8.join(configDir, "sessions", n), "utf8"));
2094
+ if (String(j.sessionId) !== session) continue;
2095
+ if (pidAlive(Number(j.pid ?? n.slice(0, -5)))) return true;
2096
+ } catch {
2097
+ }
2098
+ }
2099
+ return false;
2100
+ }
2101
+ function recentlyWritten(file, ourLastEnd, now = Date.now()) {
2102
+ let m;
2103
+ try {
2104
+ m = statSync4(file).mtimeMs;
2105
+ } catch {
2106
+ return false;
2107
+ }
2108
+ if (now - m > LIVE_WRITE_MS) return false;
2109
+ return !(ourLastEnd && m <= ourLastEnd + 2e3);
2110
+ }
2111
+ function runClaude(bin, args, o) {
2112
+ return new Promise((resolve) => {
2113
+ let out = [];
2114
+ let outLen = 0;
2115
+ let tooBig = false;
2116
+ let errTail = "";
2117
+ let timedOut = false;
2118
+ let settled = false;
2119
+ let child;
2120
+ try {
2121
+ child = spawn(bin, args, {
2122
+ cwd: o.cwd,
2123
+ env: o.env,
2124
+ stdio: ["ignore", "pipe", "pipe"],
2125
+ detached: process.platform !== "win32",
2126
+ windowsHide: true
2127
+ });
2128
+ } catch (e) {
2129
+ resolve({ error: e.message });
2130
+ return;
2131
+ }
2132
+ const kill = (sig) => {
2133
+ try {
2134
+ if (process.platform !== "win32" && child.pid) process.kill(-child.pid, sig);
2135
+ else child.kill(sig);
2136
+ } catch {
2137
+ try {
2138
+ child.kill(sig);
2139
+ } catch {
2140
+ }
2141
+ }
2142
+ };
2143
+ child.stdout?.on("data", (b) => {
2144
+ if (tooBig) return;
2145
+ if (outLen + b.length > OUT_CAP) {
2146
+ tooBig = true;
2147
+ out = [];
2148
+ return;
2149
+ }
2150
+ out.push(b);
2151
+ outLen += b.length;
2152
+ });
2153
+ child.stderr?.on("data", (b) => {
2154
+ errTail = (errTail + b.toString("utf8")).slice(-4e3);
2155
+ });
2156
+ const timer = setTimeout(() => {
2157
+ timedOut = true;
2158
+ kill("SIGTERM");
2159
+ setTimeout(() => kill("SIGKILL"), 1e4).unref();
2160
+ }, o.timeoutMs ?? JOB_TIMEOUT_MS);
2161
+ const result = (code, signal) => ({
2162
+ code,
2163
+ signal,
2164
+ stdout: tooBig ? null : Buffer.concat(out).toString("utf8"),
2165
+ tooBig,
2166
+ stderr: errTail,
2167
+ timedOut
2168
+ });
2169
+ const finish = (r) => {
2170
+ if (settled) return;
2171
+ settled = true;
2172
+ clearTimeout(timer);
2173
+ resolve(r);
2174
+ };
2175
+ child.on("error", (e) => finish({ error: e.message }));
2176
+ child.on("exit", (code, signal) => setTimeout(() => finish(result(code, signal)), 2e3).unref());
2177
+ child.on("close", (code, signal) => finish(result(code, signal)));
2178
+ });
2179
+ }
2180
+ function parseResult(stdout) {
2181
+ const s = String(stdout || "").trim();
2182
+ if (!s) return null;
2183
+ const pick = (j) => {
2184
+ if (Array.isArray(j)) {
2185
+ for (let i = j.length - 1; i >= 0; i--) if (j[i]?.type === "result") return j[i];
2186
+ return null;
2187
+ }
2188
+ return j && typeof j === "object" ? j : null;
2189
+ };
2190
+ try {
2191
+ return pick(JSON.parse(s));
2192
+ } catch {
2193
+ }
2194
+ const lines = s.split("\n");
2195
+ for (let i = lines.length - 1; i >= 0; i--) {
2196
+ try {
2197
+ const j = pick(JSON.parse(lines[i]));
2198
+ if (j && (j.type === "result" || "result" in j)) return j;
2199
+ } catch {
2200
+ }
2201
+ }
2202
+ return null;
2203
+ }
2204
+ var isUuid = (s) => typeof s === "string" && UUID_RE.test(s);
2205
+
1846
2206
  // src/adapters/index.ts
1847
2207
  var ADAPTERS = [claudeCode, codex, geminiCli, antigravity];
1848
2208
  var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
1849
2209
 
1850
2210
  // src/config.ts
1851
- import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
1852
- import path8 from "node:path";
2211
+ import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
2212
+ import path9 from "node:path";
1853
2213
  var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
1854
2214
  function readConfig(file = configFile()) {
1855
2215
  try {
1856
- const j = JSON.parse(readFileSync6(file, "utf8"));
2216
+ const j = JSON.parse(readFileSync7(file, "utf8"));
1857
2217
  return {
1858
2218
  ...DEFAULT_CONFIG,
1859
2219
  ...j,
@@ -1865,7 +2225,7 @@ function readConfig(file = configFile()) {
1865
2225
  }
1866
2226
  }
1867
2227
  function writeConfig(c, file = configFile()) {
1868
- mkdirSync4(path8.dirname(file), { recursive: true, mode: 448 });
2228
+ mkdirSync4(path9.dirname(file), { recursive: true, mode: 448 });
1869
2229
  writeFileSync4(file, `${JSON.stringify(c, null, 2)}
1870
2230
  `, { mode: 384 });
1871
2231
  if (process.platform !== "win32" && existsSync6(file)) {
@@ -1934,32 +2294,32 @@ function makeEvent(a, ctx) {
1934
2294
  // src/outbox.ts
1935
2295
  import {
1936
2296
  appendFileSync,
1937
- closeSync as closeSync2,
2297
+ closeSync as closeSync3,
1938
2298
  existsSync as existsSync7,
1939
2299
  mkdirSync as mkdirSync5,
1940
- openSync as openSync2,
1941
- readdirSync as readdirSync5,
1942
- readFileSync as readFileSync7,
1943
- readSync as readSync2,
2300
+ openSync as openSync3,
2301
+ readdirSync as readdirSync6,
2302
+ readFileSync as readFileSync8,
2303
+ readSync as readSync3,
1944
2304
  renameSync,
1945
- statSync as statSync4,
2305
+ statSync as statSync5,
1946
2306
  unlinkSync as unlinkSync3,
1947
2307
  writeFileSync as writeFileSync5
1948
2308
  } from "node:fs";
1949
- import path9 from "node:path";
2309
+ import path10 from "node:path";
1950
2310
  var Outbox = class {
1951
2311
  constructor(root) {
1952
2312
  this.root = root;
1953
2313
  }
1954
2314
  root;
1955
2315
  dir(harness) {
1956
- return path9.join(this.root, "outbox", safeId(harness));
2316
+ return path10.join(this.root, "outbox", safeId(harness));
1957
2317
  }
1958
2318
  file(ref) {
1959
- return path9.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
2319
+ return path10.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
1960
2320
  }
1961
2321
  seqFile(ref) {
1962
- return path9.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
2322
+ return path10.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
1963
2323
  }
1964
2324
  /** The next seq for a session, persisted (survives a hibernating machine and a
1965
2325
  * restart; an in-memory counter would restart at 0 and collide). */
@@ -1967,18 +2327,18 @@ var Outbox = class {
1967
2327
  const f = this.seqFile(ref);
1968
2328
  let n = 0;
1969
2329
  try {
1970
- n = Number(readFileSync7(f, "utf8")) || 0;
2330
+ n = Number(readFileSync8(f, "utf8")) || 0;
1971
2331
  } catch {
1972
2332
  n = this.lastSeqInFile(ref) + 1;
1973
2333
  }
1974
- mkdirSync5(path9.dirname(f), { recursive: true, mode: 448 });
2334
+ mkdirSync5(path10.dirname(f), { recursive: true, mode: 448 });
1975
2335
  writeFileSync5(f, String(n + 1), { mode: 384 });
1976
2336
  return n;
1977
2337
  }
1978
2338
  lastSeqInFile(ref) {
1979
2339
  const f = this.file(ref);
1980
2340
  if (!existsSync7(f)) return -1;
1981
- const lines = readFileSync7(f, "utf8").trim().split("\n");
2341
+ const lines = readFileSync8(f, "utf8").trim().split("\n");
1982
2342
  for (let i = lines.length - 1; i >= 0; i--) {
1983
2343
  try {
1984
2344
  const j = JSON.parse(lines[i] ?? "");
@@ -1997,19 +2357,19 @@ var Outbox = class {
1997
2357
  /** Every session file, newest first. */
1998
2358
  sessions() {
1999
2359
  const out = [];
2000
- const base = path9.join(this.root, "outbox");
2360
+ const base = path10.join(this.root, "outbox");
2001
2361
  if (!existsSync7(base)) return [];
2002
- for (const h of readdirSync5(base)) {
2003
- const d = path9.join(base, h);
2362
+ for (const h of readdirSync6(base)) {
2363
+ const d = path10.join(base, h);
2004
2364
  let names = [];
2005
2365
  try {
2006
- names = readdirSync5(d).filter((n) => n.endsWith(".jsonl"));
2366
+ names = readdirSync6(d).filter((n) => n.endsWith(".jsonl"));
2007
2367
  } catch {
2008
2368
  continue;
2009
2369
  }
2010
2370
  for (const n of names) {
2011
2371
  try {
2012
- out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync4(path9.join(d, n)).mtimeMs });
2372
+ out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync5(path10.join(d, n)).mtimeMs });
2013
2373
  } catch {
2014
2374
  }
2015
2375
  }
@@ -2021,12 +2381,12 @@ var Outbox = class {
2021
2381
  read(ref, from, maxEvents = 500) {
2022
2382
  const f = this.file(ref);
2023
2383
  if (!existsSync7(f)) return { events: [], offset: from };
2024
- const size = statSync4(f).size;
2384
+ const size = statSync5(f).size;
2025
2385
  if (from >= size) return { events: [], offset: from };
2026
- const fd = openSync2(f, "r");
2386
+ const fd = openSync3(f, "r");
2027
2387
  try {
2028
2388
  const buf = Buffer.alloc(Math.min(size - from, 8 * 1024 * 1024));
2029
- const n = readSync2(fd, buf, 0, buf.length, from);
2389
+ const n = readSync3(fd, buf, 0, buf.length, from);
2030
2390
  const text = buf.subarray(0, n).toString("utf8");
2031
2391
  const events = [];
2032
2392
  let offset = from;
@@ -2045,7 +2405,7 @@ var Outbox = class {
2045
2405
  }
2046
2406
  return { events, offset };
2047
2407
  } finally {
2048
- closeSync2(fd);
2408
+ closeSync3(fd);
2049
2409
  }
2050
2410
  }
2051
2411
  /** Files older than `days` are pruned; the cursor files with them. */
@@ -2055,7 +2415,7 @@ var Outbox = class {
2055
2415
  for (const ref of this.sessions()) {
2056
2416
  const f = this.file(ref);
2057
2417
  try {
2058
- if (statSync4(f).mtimeMs < cutoff) {
2418
+ if (statSync5(f).mtimeMs < cutoff) {
2059
2419
  unlinkSync3(f);
2060
2420
  try {
2061
2421
  unlinkSync3(this.seqFile(ref));
@@ -2085,18 +2445,18 @@ var Cursors = class {
2085
2445
  root;
2086
2446
  sink;
2087
2447
  file() {
2088
- return path9.join(this.root, "cursors", `${safeId(this.sink)}.json`);
2448
+ return path10.join(this.root, "cursors", `${safeId(this.sink)}.json`);
2089
2449
  }
2090
2450
  read() {
2091
2451
  try {
2092
- return JSON.parse(readFileSync7(this.file(), "utf8"));
2452
+ return JSON.parse(readFileSync8(this.file(), "utf8"));
2093
2453
  } catch {
2094
2454
  return {};
2095
2455
  }
2096
2456
  }
2097
2457
  write(c) {
2098
2458
  const f = this.file();
2099
- mkdirSync5(path9.dirname(f), { recursive: true, mode: 448 });
2459
+ mkdirSync5(path10.dirname(f), { recursive: true, mode: 448 });
2100
2460
  const tmp = `${f}.${process.pid}.tmp`;
2101
2461
  writeFileSync5(tmp, JSON.stringify(c), { mode: 384 });
2102
2462
  renameSync(tmp, f);
@@ -2106,11 +2466,11 @@ var Cursors = class {
2106
2466
  }
2107
2467
  };
2108
2468
  function takeLock(root, ref, staleMs = 5 * 6e4) {
2109
- const dir = path9.join(root, "locks");
2469
+ const dir = path10.join(root, "locks");
2110
2470
  mkdirSync5(dir, { recursive: true, mode: 448 });
2111
- const f = path9.join(dir, `${safeId(ref.harness)}-${safeId(ref.session)}.lock`);
2471
+ const f = path10.join(dir, `${safeId(ref.harness)}-${safeId(ref.session)}.lock`);
2112
2472
  try {
2113
- if (Date.now() - statSync4(f).mtimeMs > staleMs) unlinkSync3(f);
2473
+ if (Date.now() - statSync5(f).mtimeMs > staleMs) unlinkSync3(f);
2114
2474
  else return null;
2115
2475
  } catch {
2116
2476
  }
@@ -2427,7 +2787,7 @@ function filterEvent(event, tier, pii, opts = {}) {
2427
2787
 
2428
2788
  // src/sinks/file.ts
2429
2789
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
2430
- import path10 from "node:path";
2790
+ import path11 from "node:path";
2431
2791
  var FileSink = class {
2432
2792
  constructor(name, file, tier, pii) {
2433
2793
  this.name = name;
@@ -2441,7 +2801,7 @@ var FileSink = class {
2441
2801
  pii;
2442
2802
  async send(events) {
2443
2803
  try {
2444
- mkdirSync6(path10.dirname(this.file), { recursive: true });
2804
+ mkdirSync6(path11.dirname(this.file), { recursive: true });
2445
2805
  appendFileSync2(this.file, `${events.map((e) => JSON.stringify(e)).join("\n")}
2446
2806
  `);
2447
2807
  return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
@@ -2488,6 +2848,8 @@ export {
2488
2848
  adapterByName,
2489
2849
  antigravity,
2490
2850
  claudeCode,
2851
+ claude_code_control_exports as claudeControl,
2852
+ claudeDirs,
2491
2853
  codex,
2492
2854
  configFile,
2493
2855
  filterEvent,