@paradow/ledge 0.4.0 → 0.5.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/cli/{attach-IKK5XQGC.js → attach-CUKFTN63.js} +6 -6
- package/dist/cli/bridge-R6DKTSYF.js +60 -0
- package/dist/cli/{chunk-34DHZSGH.js → chunk-5HVWJTTJ.js} +1 -1
- package/dist/cli/{chunk-XDRYMOVF.js → chunk-HGNMPXXR.js} +40 -14
- package/dist/cli/chunk-IUBEGKBP.js +6 -0
- package/dist/cli/chunk-OLLQBK6N.js +41 -0
- package/dist/cli/{chunk-6ZTCETRG.js → chunk-P2SHPHAN.js} +13 -2
- package/dist/cli/{chunk-ZPR4LCDM.js → chunk-QHDPDZOJ.js} +9 -9
- package/dist/cli/{chunk-5OJZV5MV.js → chunk-SMA4Z3N2.js} +9 -5
- package/dist/cli/{chunk-B73JDPSW.js → chunk-U4A4XKDM.js} +2 -13
- package/dist/cli/{chunk-DMV5VW4Q.js → chunk-XZMLFV6H.js} +2 -4
- package/dist/cli/{chunk-YT4T7DL2.js → chunk-Y4ODVTTN.js} +133 -59
- package/dist/cli/{daemon-2KHF4XUC.js → daemon-6UFCIFQF.js} +667 -113
- package/dist/cli/{hook-QH44D3JC.js → hook-KFSK75QH.js} +62 -20
- package/dist/cli/index.js +14 -8
- package/dist/cli/{launch-CPNM5RX7.js → launch-YKYPT6BU.js} +6 -6
- package/dist/cli/{setup-KUWSQLVG.js → setup-EXA6OG5W.js} +3 -2
- package/dist/cli/{status-E2VZMTAF.js → status-I7FHW6ZM.js} +2 -2
- package/dist/cli/{uninstall-5GHUYN7P.js → uninstall-34TVL5BP.js} +3 -2
- package/dist/cli/{version-J6BW646U.js → version-IWGIZJJV.js} +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-Y74YLRHP.js +0 -6
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
attachEnv
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QHDPDZOJ.js";
|
|
4
|
+
import "./chunk-U4A4XKDM.js";
|
|
4
5
|
import {
|
|
5
6
|
realTmux
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-6ZTCETRG.js";
|
|
7
|
+
} from "./chunk-XZMLFV6H.js";
|
|
8
|
+
import "./chunk-P2SHPHAN.js";
|
|
9
9
|
import {
|
|
10
10
|
connect
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-5HVWJTTJ.js";
|
|
12
12
|
import {
|
|
13
13
|
PROTOCOL_PORT
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-Y4ODVTTN.js";
|
|
15
15
|
|
|
16
16
|
// src/cli/attach.ts
|
|
17
17
|
import { spawn } from "child_process";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_FRAME,
|
|
3
|
+
readFrames
|
|
4
|
+
} from "./chunk-OLLQBK6N.js";
|
|
5
|
+
import {
|
|
6
|
+
PROTOCOL_PORT,
|
|
7
|
+
parseClientMessage,
|
|
8
|
+
parseServerMessage
|
|
9
|
+
} from "./chunk-Y4ODVTTN.js";
|
|
10
|
+
|
|
11
|
+
// src/cli/bridge.ts
|
|
12
|
+
import WebSocket from "ws";
|
|
13
|
+
function bridge(opts = {}) {
|
|
14
|
+
const input = opts.input ?? process.stdin, output = opts.output ?? process.stdout;
|
|
15
|
+
const ws = new WebSocket(`ws://127.0.0.1:${opts.port ?? PROTOCOL_PORT}`, { maxPayload: MAX_FRAME, handshakeTimeout: 1e4 });
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
let settled = false, off = () => {
|
|
18
|
+
};
|
|
19
|
+
const finish = (error) => {
|
|
20
|
+
if (settled) return;
|
|
21
|
+
settled = true;
|
|
22
|
+
off();
|
|
23
|
+
input.pause();
|
|
24
|
+
input.off("end", end);
|
|
25
|
+
input.off("error", fail);
|
|
26
|
+
output.off("error", fail);
|
|
27
|
+
output.off("drain", drain);
|
|
28
|
+
ws.terminate();
|
|
29
|
+
error ? reject(error) : resolve();
|
|
30
|
+
};
|
|
31
|
+
const fail = (e) => finish(e);
|
|
32
|
+
const end = () => ws.close();
|
|
33
|
+
const drain = () => ws.resume();
|
|
34
|
+
input.on("error", fail);
|
|
35
|
+
output.on("error", fail);
|
|
36
|
+
output.on("drain", drain);
|
|
37
|
+
ws.on("open", () => {
|
|
38
|
+
off = readFrames(input, (raw) => {
|
|
39
|
+
const msg = parseClientMessage(raw);
|
|
40
|
+
if (ws.bufferedAmount > MAX_FRAME) throw new Error("Ledge bridge is overloaded");
|
|
41
|
+
ws.send(JSON.stringify(msg));
|
|
42
|
+
}, fail);
|
|
43
|
+
input.on("end", end);
|
|
44
|
+
if (input.readableEnded) end();
|
|
45
|
+
});
|
|
46
|
+
ws.on("message", (data) => {
|
|
47
|
+
try {
|
|
48
|
+
if (!output.write(`${JSON.stringify(parseServerMessage(JSON.parse(String(data))))}
|
|
49
|
+
`)) ws.pause();
|
|
50
|
+
} catch (e) {
|
|
51
|
+
fail(e);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
ws.on("error", fail);
|
|
55
|
+
ws.on("close", (code) => finish(code === 1e3 || code === 1005 ? void 0 : new Error(`Ledge daemon disconnected (${code})`)));
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
bridge
|
|
60
|
+
};
|
|
@@ -1,13 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
run
|
|
3
|
+
} from "./chunk-XZMLFV6H.js";
|
|
1
4
|
import {
|
|
2
5
|
ensureLedgeDir,
|
|
3
6
|
paths
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-P2SHPHAN.js";
|
|
5
8
|
|
|
6
9
|
// src/cli/setup.ts
|
|
7
|
-
import { readFile, writeFile, mkdir, rename, realpath, rm, stat, lstat, readlink, symlink, unlink } from "fs/promises";
|
|
10
|
+
import { readFile as readFile2, writeFile, mkdir, rename, realpath, rm, stat, lstat, readlink, symlink, unlink } from "fs/promises";
|
|
8
11
|
import { execFile } from "child_process";
|
|
9
12
|
import { homedir, userInfo } from "os";
|
|
10
13
|
import { join, dirname } from "path";
|
|
14
|
+
|
|
15
|
+
// src/update/guard.ts
|
|
16
|
+
import { readFile } from "fs/promises";
|
|
17
|
+
var inDaemonService = (cgroup) => cgroup.split("\n").some((l) => l.split("/").includes(SERVICE));
|
|
18
|
+
async function tmuxServerAtRisk(opts = {}) {
|
|
19
|
+
const runTmux = opts.runTmux ?? run;
|
|
20
|
+
const readCgroup = opts.readCgroup ?? ((pid2) => readFile(`/proc/${pid2}/cgroup`, "utf8"));
|
|
21
|
+
const r = await runTmux(["-L", opts.socket ?? "ledge", "list-sessions", "-F", "#{pid}"]);
|
|
22
|
+
if (r.code !== 0) return false;
|
|
23
|
+
const pid = r.stdout.split("\n").map((s) => s.trim()).find(Boolean);
|
|
24
|
+
if (!pid) return false;
|
|
25
|
+
try {
|
|
26
|
+
return inDaemonService(await readCgroup(pid));
|
|
27
|
+
} catch {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/cli/setup.ts
|
|
11
33
|
var CLAUDE_EVENTS = [
|
|
12
34
|
"SessionStart",
|
|
13
35
|
"UserPromptSubmit",
|
|
@@ -91,7 +113,7 @@ function mergeCursorHooks(config, command = "ledge hook cursor") {
|
|
|
91
113
|
async function mergeJsonFile(path, merge, name) {
|
|
92
114
|
let raw = null;
|
|
93
115
|
try {
|
|
94
|
-
raw = await
|
|
116
|
+
raw = await readFile2(path, "utf8");
|
|
95
117
|
} catch (e) {
|
|
96
118
|
if (e.code !== "ENOENT") throw e;
|
|
97
119
|
}
|
|
@@ -134,7 +156,7 @@ WantedBy=default.target
|
|
|
134
156
|
async function writeIfChanged(path, content) {
|
|
135
157
|
let current = null;
|
|
136
158
|
try {
|
|
137
|
-
current = await
|
|
159
|
+
current = await readFile2(path, "utf8");
|
|
138
160
|
} catch (e) {
|
|
139
161
|
if (e.code !== "ENOENT") throw e;
|
|
140
162
|
}
|
|
@@ -148,7 +170,7 @@ async function writeIfChanged(path, content) {
|
|
|
148
170
|
async function stopDetachedDaemon(pidFile) {
|
|
149
171
|
let pid;
|
|
150
172
|
try {
|
|
151
|
-
pid = Number((await
|
|
173
|
+
pid = Number((await readFile2(pidFile, "utf8")).trim());
|
|
152
174
|
} catch {
|
|
153
175
|
return null;
|
|
154
176
|
}
|
|
@@ -177,16 +199,18 @@ async function stopDetachedDaemon(pidFile) {
|
|
|
177
199
|
}
|
|
178
200
|
async function installService(opts = {}) {
|
|
179
201
|
const out = [];
|
|
180
|
-
const
|
|
202
|
+
const run2 = opts.run ?? execRunner;
|
|
181
203
|
const unitDir = opts.unitDir ?? join(homedir(), ".config", "systemd", "user");
|
|
182
204
|
const node = opts.node ?? await realpath(process.execPath);
|
|
183
205
|
const script = opts.script ?? await realpath(process.argv[1]);
|
|
184
206
|
const changed = await writeIfChanged(join(unitDir, SERVICE), systemdUnit({ node, script }));
|
|
185
|
-
const reload = await
|
|
207
|
+
const reload = await run2("systemctl", ["--user", "daemon-reload"]);
|
|
186
208
|
if (reload.code !== 0) throw new Error(`systemd user session unavailable (systemctl --user daemon-reload failed: ${reload.stderr.trim() || reload.code}); the daemon will still be started on demand by ledge claude`);
|
|
187
|
-
const active = (await
|
|
188
|
-
if (active && changed) {
|
|
189
|
-
|
|
209
|
+
const active = (await run2("systemctl", ["--user", "is-active", SERVICE])).code === 0;
|
|
210
|
+
if (active && changed && await (opts.atRisk ?? tmuxServerAtRisk)()) {
|
|
211
|
+
out.push(`${SERVICE} unit updated but not restarted: the tmux server of your agents runs inside the service and would stop with it; close the sessions, then run ledge setup again (or systemctl --user restart ${SERVICE})`);
|
|
212
|
+
} else if (active && changed) {
|
|
213
|
+
const r = await run2("systemctl", ["--user", "restart", SERVICE]);
|
|
190
214
|
if (r.code !== 0) throw new Error(`systemctl --user restart ${SERVICE} failed: ${r.stderr.trim()}`);
|
|
191
215
|
out.push(`${SERVICE} updated and restarted`);
|
|
192
216
|
} else if (active) {
|
|
@@ -194,14 +218,14 @@ async function installService(opts = {}) {
|
|
|
194
218
|
} else {
|
|
195
219
|
const pid = await stopDetachedDaemon(paths().pid);
|
|
196
220
|
if (pid !== null) out.push(`detached daemon (pid ${pid}) stopped, the service takes over`);
|
|
197
|
-
const r = await
|
|
221
|
+
const r = await run2("systemctl", ["--user", "enable", "--now", SERVICE]);
|
|
198
222
|
if (r.code !== 0) throw new Error(`systemctl --user enable --now ${SERVICE} failed: ${r.stderr.trim()}`);
|
|
199
223
|
out.push(`${SERVICE} installed and started (journalctl --user -u ${SERVICE} for its output)`);
|
|
200
224
|
}
|
|
201
225
|
const user = process.env.USER ?? userInfo().username;
|
|
202
|
-
const linger = await
|
|
226
|
+
const linger = await run2("loginctl", ["show-user", user, "-p", "Linger"]);
|
|
203
227
|
if (linger.code === 0 && /^Linger=yes/m.test(linger.stdout)) return out;
|
|
204
|
-
const enable = await
|
|
228
|
+
const enable = await run2("loginctl", ["enable-linger"]);
|
|
205
229
|
if (enable.code === 0) out.push("linger enabled: the daemon starts with WSL, before any shell is open");
|
|
206
230
|
else out.push(`linger not enabled (${enable.stderr.trim() || `exit ${enable.code}`}): the daemon starts with your first WSL shell; run \`sudo loginctl enable-linger ${user}\` to start it at WSL boot`);
|
|
207
231
|
return out;
|
|
@@ -263,5 +287,7 @@ export {
|
|
|
263
287
|
installService,
|
|
264
288
|
isDir,
|
|
265
289
|
relinkBin,
|
|
266
|
-
runSetup
|
|
290
|
+
runSetup,
|
|
291
|
+
inDaemonService,
|
|
292
|
+
tmuxServerAtRisk
|
|
267
293
|
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// src/transport/frames.ts
|
|
2
|
+
var MAX_FRAME = 4 * 1024 * 1024;
|
|
3
|
+
function readFrames(input, frame, fail) {
|
|
4
|
+
let buffer = "", stopped = false;
|
|
5
|
+
input.setEncoding("utf8");
|
|
6
|
+
const data = (chunk) => {
|
|
7
|
+
if (stopped) return;
|
|
8
|
+
buffer += chunk;
|
|
9
|
+
try {
|
|
10
|
+
let i;
|
|
11
|
+
while ((i = buffer.indexOf("\n")) >= 0) {
|
|
12
|
+
const line = buffer.slice(0, i);
|
|
13
|
+
buffer = buffer.slice(i + 1);
|
|
14
|
+
if (Buffer.byteLength(line) > MAX_FRAME) throw new Error("Ledge frame too large");
|
|
15
|
+
if (line.trim()) frame(JSON.parse(line));
|
|
16
|
+
}
|
|
17
|
+
if (Buffer.byteLength(buffer) > MAX_FRAME) throw new Error("Ledge frame too large");
|
|
18
|
+
} catch (e) {
|
|
19
|
+
stopped = true;
|
|
20
|
+
fail(e instanceof Error ? e : new Error(String(e)));
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const end = () => {
|
|
24
|
+
if (!stopped && buffer.trim()) {
|
|
25
|
+
stopped = true;
|
|
26
|
+
fail(new Error("Incomplete Ledge frame"));
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
input.on("data", data);
|
|
30
|
+
input.on("end", end);
|
|
31
|
+
return () => {
|
|
32
|
+
stopped = true;
|
|
33
|
+
input.off("data", data);
|
|
34
|
+
input.off("end", end);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
MAX_FRAME,
|
|
40
|
+
readFrames
|
|
41
|
+
};
|
|
@@ -17,7 +17,7 @@ function ledgeDir() {
|
|
|
17
17
|
}
|
|
18
18
|
function paths() {
|
|
19
19
|
const dir = ledgeDir();
|
|
20
|
-
return { dir, tmuxConf: join(dir, "tmux.conf"), journal: join(dir, "sessions.json"), log: join(dir, "daemon.log"), pid: join(dir, "daemon.pid") };
|
|
20
|
+
return { dir, tmuxConf: join(dir, "tmux.conf"), journal: join(dir, "sessions.json"), log: join(dir, "daemon.log"), pid: join(dir, "daemon.pid"), history: join(dir, "history") };
|
|
21
21
|
}
|
|
22
22
|
async function ensureLedgeDir() {
|
|
23
23
|
const p = paths();
|
|
@@ -32,7 +32,18 @@ async function ensureLedgeDir() {
|
|
|
32
32
|
${TITLE_LINES}`);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// src/log.ts
|
|
36
|
+
import { appendFileSync } from "fs";
|
|
37
|
+
function log(line) {
|
|
38
|
+
try {
|
|
39
|
+
appendFileSync(paths().log, `${(/* @__PURE__ */ new Date()).toISOString()} | ${line}
|
|
40
|
+
`);
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
35
45
|
export {
|
|
36
46
|
paths,
|
|
37
|
-
ensureLedgeDir
|
|
47
|
+
ensureLedgeDir,
|
|
48
|
+
log
|
|
38
49
|
};
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
realTmux
|
|
3
|
-
} from "./chunk-DMV5VW4Q.js";
|
|
4
1
|
import {
|
|
5
2
|
exitFile,
|
|
6
|
-
log,
|
|
7
3
|
spawnSession
|
|
8
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-U4A4XKDM.js";
|
|
5
|
+
import {
|
|
6
|
+
realTmux
|
|
7
|
+
} from "./chunk-XZMLFV6H.js";
|
|
9
8
|
import {
|
|
10
|
-
ensureLedgeDir
|
|
11
|
-
|
|
9
|
+
ensureLedgeDir,
|
|
10
|
+
log
|
|
11
|
+
} from "./chunk-P2SHPHAN.js";
|
|
12
12
|
import {
|
|
13
13
|
connect
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-5HVWJTTJ.js";
|
|
15
15
|
import {
|
|
16
16
|
PROTOCOL_PORT
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-Y4ODVTTN.js";
|
|
18
18
|
|
|
19
19
|
// src/cli/launch.ts
|
|
20
20
|
import { spawn } from "child_process";
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
cleanTopic,
|
|
3
3
|
sessionTitle
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Y4ODVTTN.js";
|
|
5
5
|
|
|
6
6
|
// src/daemon/state.ts
|
|
7
7
|
var HOOK_PRIORITY_MS = 2e3;
|
|
8
8
|
var hooksReportPrompts = (s) => s.tool === "claude";
|
|
9
9
|
var QUESTION_TOOLS = /* @__PURE__ */ new Set(["AskUserQuestion", "request_user_input"]);
|
|
10
10
|
var DIFF_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "apply_patch"]);
|
|
11
|
+
var details = (ev) => ev.optionDetails?.some(Boolean) ? { optionDetails: ev.optionDetails } : {};
|
|
11
12
|
function pendingFor(ev) {
|
|
12
13
|
if (ev.name === "PermissionRequest") {
|
|
13
14
|
const diff = !!ev.tool && DIFF_TOOLS.has(ev.tool);
|
|
14
15
|
return { kind: diff ? "diff" : "permission", text: ev.text ?? "", tool: ev.tool, source: "hook" };
|
|
15
16
|
}
|
|
16
17
|
if (ev.name === "PreToolUse" && ev.tool && QUESTION_TOOLS.has(ev.tool)) {
|
|
17
|
-
return { kind: "question", text: ev.text ?? "", options: ev.options, source: "hook", ...ev.tool !== "AskUserQuestion" ? { tool: ev.tool } : {} };
|
|
18
|
+
return { kind: "question", text: ev.text ?? "", options: ev.options, ...details(ev), source: "hook", ...ev.tool !== "AskUserQuestion" ? { tool: ev.tool } : {} };
|
|
18
19
|
}
|
|
19
20
|
if (ev.name === "PreToolUse" && ev.tool === "ExitPlanMode") {
|
|
20
21
|
return { kind: "plan", text: ev.text ?? "", source: "hook" };
|
|
@@ -23,7 +24,7 @@ function pendingFor(ev) {
|
|
|
23
24
|
return { kind: "permission", text: ev.text ?? "", source: "hook" };
|
|
24
25
|
}
|
|
25
26
|
if (ev.name === "PanePrompt") {
|
|
26
|
-
return { kind: ev.kind ?? "permission", text: ev.text ?? "", options: ev.options, source: "pane" };
|
|
27
|
+
return { kind: ev.kind ?? "permission", text: ev.text ?? "", options: ev.options, ...details(ev), source: "pane" };
|
|
27
28
|
}
|
|
28
29
|
return void 0;
|
|
29
30
|
}
|
|
@@ -34,7 +35,10 @@ function mergePending(current, incoming) {
|
|
|
34
35
|
if (current.helperId !== incoming.helperId) return current;
|
|
35
36
|
if (current.source === "hook" && (current.kind === "plan" || current.kind === "question") && incoming.source === "hook") return current;
|
|
36
37
|
const kind = incoming.kind;
|
|
37
|
-
|
|
38
|
+
const options = incoming.options ?? current.options;
|
|
39
|
+
const optionDetails = incoming.options ? incoming.optionDetails : current.optionDetails;
|
|
40
|
+
const { optionDetails: _old, ...rest } = { ...current, ...incoming };
|
|
41
|
+
return { ...rest, kind, text: incoming.text || current.text, tool: incoming.tool ?? current.tool, options, ...optionDetails ? { optionDetails } : {} };
|
|
38
42
|
}
|
|
39
43
|
function pendingTool(p) {
|
|
40
44
|
if (p.kind === "question") return p.tool ?? "AskUserQuestion";
|
|
@@ -114,7 +118,7 @@ function withoutReply(s) {
|
|
|
114
118
|
return rest;
|
|
115
119
|
}
|
|
116
120
|
function samePending(a, b) {
|
|
117
|
-
return a.kind === b.kind && a.text === b.text && a.tool === b.tool && a.source === b.source && JSON.stringify(a.options) === JSON.stringify(b.options);
|
|
121
|
+
return a.kind === b.kind && a.text === b.text && a.tool === b.tool && a.source === b.source && JSON.stringify(a.options) === JSON.stringify(b.options) && JSON.stringify(a.optionDetails) === JSON.stringify(b.optionDetails);
|
|
118
122
|
}
|
|
119
123
|
function endCursorHelpers(s, ev) {
|
|
120
124
|
if (s.tool !== "cursor" || ev.source !== "hook" || ev.helperId || !s.helpers.some((h) => h.state === "working")) return s;
|
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
paths
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-P2SHPHAN.js";
|
|
4
4
|
import {
|
|
5
5
|
pickName
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
|
|
8
|
-
// src/log.ts
|
|
9
|
-
import { appendFileSync } from "fs";
|
|
10
|
-
function log(line) {
|
|
11
|
-
try {
|
|
12
|
-
appendFileSync(paths().log, `${(/* @__PURE__ */ new Date()).toISOString()} | ${line}
|
|
13
|
-
`);
|
|
14
|
-
} catch {
|
|
15
|
-
}
|
|
16
|
-
}
|
|
6
|
+
} from "./chunk-Y4ODVTTN.js";
|
|
17
7
|
|
|
18
8
|
// src/spawn.ts
|
|
19
9
|
import { basename, join } from "path";
|
|
@@ -50,7 +40,6 @@ async function spawnSession(opts) {
|
|
|
50
40
|
}
|
|
51
41
|
|
|
52
42
|
export {
|
|
53
|
-
log,
|
|
54
43
|
CODEX_ENV_VAR,
|
|
55
44
|
CODEX_HOOK_VAR,
|
|
56
45
|
exitFile,
|