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/control.js +352 -0
- package/dist/control.js.map +7 -0
- package/dist/index.js +400 -38
- package/dist/index.js.map +4 -4
- package/dist/schema/v1.d.ts +338 -53
- package/dist/schema/v1.d.ts.map +1 -1
- package/dist/schema.js +149 -24
- package/dist/schema.js.map +2 -2
- package/dist/src/adapters/claude-code-control.d.ts +70 -0
- package/dist/src/adapters/claude-code-control.d.ts.map +1 -0
- package/dist/src/adapters/claude-code.d.ts.map +1 -1
- package/dist/src/adapters/claude-shaped.d.ts +3 -0
- package/dist/src/adapters/claude-shaped.d.ts.map +1 -1
- package/dist/src/adapters/index.d.ts +2 -0
- package/dist/src/adapters/index.d.ts.map +1 -1
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/control/index.d.ts +5 -0
- package/dist/src/control/index.d.ts.map +1 -0
- package/dist/src/control/verify.d.ts +171 -0
- package/dist/src/control/verify.d.ts.map +1 -0
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
// sessionpipe-core 0.
|
|
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 = {
|
|
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
|
|
1852
|
-
import
|
|
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(
|
|
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(
|
|
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
|
|
2297
|
+
closeSync as closeSync3,
|
|
1938
2298
|
existsSync as existsSync7,
|
|
1939
2299
|
mkdirSync as mkdirSync5,
|
|
1940
|
-
openSync as
|
|
1941
|
-
readdirSync as
|
|
1942
|
-
readFileSync as
|
|
1943
|
-
readSync as
|
|
2300
|
+
openSync as openSync3,
|
|
2301
|
+
readdirSync as readdirSync6,
|
|
2302
|
+
readFileSync as readFileSync8,
|
|
2303
|
+
readSync as readSync3,
|
|
1944
2304
|
renameSync,
|
|
1945
|
-
statSync as
|
|
2305
|
+
statSync as statSync5,
|
|
1946
2306
|
unlinkSync as unlinkSync3,
|
|
1947
2307
|
writeFileSync as writeFileSync5
|
|
1948
2308
|
} from "node:fs";
|
|
1949
|
-
import
|
|
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
|
|
2316
|
+
return path10.join(this.root, "outbox", safeId(harness));
|
|
1957
2317
|
}
|
|
1958
2318
|
file(ref) {
|
|
1959
|
-
return
|
|
2319
|
+
return path10.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
|
|
1960
2320
|
}
|
|
1961
2321
|
seqFile(ref) {
|
|
1962
|
-
return
|
|
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(
|
|
2330
|
+
n = Number(readFileSync8(f, "utf8")) || 0;
|
|
1971
2331
|
} catch {
|
|
1972
2332
|
n = this.lastSeqInFile(ref) + 1;
|
|
1973
2333
|
}
|
|
1974
|
-
mkdirSync5(
|
|
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 =
|
|
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 =
|
|
2360
|
+
const base = path10.join(this.root, "outbox");
|
|
2001
2361
|
if (!existsSync7(base)) return [];
|
|
2002
|
-
for (const h of
|
|
2003
|
-
const d =
|
|
2362
|
+
for (const h of readdirSync6(base)) {
|
|
2363
|
+
const d = path10.join(base, h);
|
|
2004
2364
|
let names = [];
|
|
2005
2365
|
try {
|
|
2006
|
-
names =
|
|
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:
|
|
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 =
|
|
2384
|
+
const size = statSync5(f).size;
|
|
2025
2385
|
if (from >= size) return { events: [], offset: from };
|
|
2026
|
-
const fd =
|
|
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 =
|
|
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
|
-
|
|
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 (
|
|
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
|
|
2448
|
+
return path10.join(this.root, "cursors", `${safeId(this.sink)}.json`);
|
|
2089
2449
|
}
|
|
2090
2450
|
read() {
|
|
2091
2451
|
try {
|
|
2092
|
-
return JSON.parse(
|
|
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(
|
|
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 =
|
|
2469
|
+
const dir = path10.join(root, "locks");
|
|
2110
2470
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
2111
|
-
const f =
|
|
2471
|
+
const f = path10.join(dir, `${safeId(ref.harness)}-${safeId(ref.session)}.lock`);
|
|
2112
2472
|
try {
|
|
2113
|
-
if (Date.now() -
|
|
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
|
|
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(
|
|
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,
|