@paradow/ledge 0.9.0 → 0.9.1
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-HPGWEUKX.js → attach-AZFKVVKL.js} +7 -6
- package/dist/cli/{bridge-2CVD7BMI.js → bridge-XHPCIYE4.js} +9 -3
- package/dist/cli/{chunk-N3MPPCON.js → chunk-65XGYBHL.js} +15 -6
- package/dist/cli/{chunk-L6IGH4LF.js → chunk-FGOFRWIB.js} +2 -13
- package/dist/cli/{chunk-JVELEEIO.js → chunk-I4C5U6AI.js} +9 -2
- package/dist/cli/{chunk-XPWGZIYL.js → chunk-IWJAJAPK.js} +14 -4
- package/dist/cli/{chunk-WM4MAV46.js → chunk-KDYAHPYN.js} +136 -1
- package/dist/cli/{chunk-564TJJZ6.js → chunk-LYTRXJAQ.js} +33 -4
- package/dist/cli/{chunk-Q5CLZPYJ.js → chunk-Q7RCYJ4X.js} +2 -2
- package/dist/cli/{chunk-3TNPEAMR.js → chunk-TO4XO2ML.js} +4 -2
- package/dist/cli/chunk-YUSY3E47.js +17 -0
- package/dist/cli/chunk-YXXXSRQX.js +6 -0
- package/dist/cli/{daemon-SGTQRTHV.js → daemon-NDXT3ZDF.js} +113 -30
- package/dist/cli/{hook-WQYL4YFH.js → hook-5HXY2UGM.js} +6 -5
- package/dist/cli/index.js +10 -10
- package/dist/cli/{launch-I4T5MFWF.js → launch-HEJKGMYS.js} +7 -6
- package/dist/cli/{setup-RMPIUCPH.js → setup-7AEH5DC3.js} +4 -3
- package/dist/cli/{status-GGXF4DSQ.js → status-PDNC6IC2.js} +3 -2
- package/dist/cli/{uninstall-GI4WT3UQ.js → uninstall-7U7ENKOS.js} +4 -3
- package/dist/cli/{version-XFBINYKQ.js → version-AQN6JNH7.js} +1 -1
- package/dist/cli/{who-EJWN7HM6.js → who-LIMQJMNI.js} +3 -2
- package/package.json +1 -1
- package/dist/cli/chunk-LWPKX7SB.js +0 -6
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
attachEnv
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-65XGYBHL.js";
|
|
4
|
+
import "./chunk-LYTRXJAQ.js";
|
|
5
5
|
import {
|
|
6
6
|
realTmux
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-TO4XO2ML.js";
|
|
8
|
+
import "./chunk-YUSY3E47.js";
|
|
9
9
|
import {
|
|
10
10
|
connect
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-IWJAJAPK.js";
|
|
12
12
|
import {
|
|
13
13
|
PROTOCOL_PORT
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-KDYAHPYN.js";
|
|
15
|
+
import "./chunk-FGOFRWIB.js";
|
|
15
16
|
|
|
16
17
|
// src/cli/attach.ts
|
|
17
18
|
import { spawn } from "child_process";
|
|
@@ -5,14 +5,20 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
PROTOCOL_PORT,
|
|
7
7
|
parseClientMessage,
|
|
8
|
-
parseServerMessage
|
|
9
|
-
|
|
8
|
+
parseServerMessage,
|
|
9
|
+
readDaemonToken
|
|
10
|
+
} from "./chunk-KDYAHPYN.js";
|
|
11
|
+
import "./chunk-FGOFRWIB.js";
|
|
10
12
|
|
|
11
13
|
// src/cli/bridge.ts
|
|
12
14
|
import WebSocket from "ws";
|
|
15
|
+
function bearerHeaders() {
|
|
16
|
+
const token = readDaemonToken();
|
|
17
|
+
return token ? { authorization: `Bearer ${token}` } : {};
|
|
18
|
+
}
|
|
13
19
|
function bridge(opts = {}) {
|
|
14
20
|
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 });
|
|
21
|
+
const ws = new WebSocket(`ws://127.0.0.1:${opts.port ?? PROTOCOL_PORT}`, { maxPayload: MAX_FRAME, handshakeTimeout: 1e4, headers: bearerHeaders() });
|
|
16
22
|
return new Promise((resolve, reject) => {
|
|
17
23
|
let settled = false, off = () => {
|
|
18
24
|
};
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
exitFile,
|
|
3
3
|
spawnSession
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-LYTRXJAQ.js";
|
|
5
5
|
import {
|
|
6
6
|
realTmux
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-TO4XO2ML.js";
|
|
8
8
|
import {
|
|
9
|
-
ensureLedgeDir,
|
|
10
9
|
log
|
|
11
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-YUSY3E47.js";
|
|
12
11
|
import {
|
|
12
|
+
DaemonRefusedError,
|
|
13
13
|
connect
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-IWJAJAPK.js";
|
|
15
15
|
import {
|
|
16
16
|
PROTOCOL_PORT
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-KDYAHPYN.js";
|
|
18
|
+
import {
|
|
19
|
+
ensureLedgeDir
|
|
20
|
+
} from "./chunk-FGOFRWIB.js";
|
|
18
21
|
|
|
19
22
|
// src/cli/launch.ts
|
|
20
23
|
import { spawn } from "child_process";
|
|
@@ -59,6 +62,7 @@ async function probe(port, timeoutMs) {
|
|
|
59
62
|
try {
|
|
60
63
|
client = await connect({ port, timeoutMs });
|
|
61
64
|
} catch (e) {
|
|
65
|
+
if (e instanceof DaemonRefusedError) return { client: null, state: "unauthorized" };
|
|
62
66
|
if (e.message === "connect timeout") return { client: null, state: "timeout" };
|
|
63
67
|
return { client: null, state: await portOpen(port) ? "foreign" : "refused" };
|
|
64
68
|
}
|
|
@@ -80,6 +84,10 @@ async function ensureDaemon(opts = {}) {
|
|
|
80
84
|
log(`port ${port} is open but does not speak the Ledge protocol`);
|
|
81
85
|
return { client: null, reason: "foreign" };
|
|
82
86
|
}
|
|
87
|
+
if (first.state === "unauthorized") {
|
|
88
|
+
log(`the daemon on port ${port} refused this client's token`);
|
|
89
|
+
return { client: null, reason: "unauthorized" };
|
|
90
|
+
}
|
|
83
91
|
if (first.state === "timeout") {
|
|
84
92
|
log(`port ${port} accepted a connection but did not answer within ${timeoutMs} ms`);
|
|
85
93
|
return { client: null, reason: "unresponsive" };
|
|
@@ -142,6 +150,7 @@ async function readExitCode(id) {
|
|
|
142
150
|
}
|
|
143
151
|
var UNTRACKED = {
|
|
144
152
|
foreign: (port) => `ledge: port ${port} is used by another program, session not tracked`,
|
|
153
|
+
unauthorized: () => "ledge: the daemon refused this client's token (missing or changed), session not tracked; restart the daemon: systemctl --user restart ledge-daemon",
|
|
145
154
|
unresponsive: () => "ledge: daemon not responding, session not tracked",
|
|
146
155
|
unreachable: () => "ledge: daemon not running, session not tracked"
|
|
147
156
|
};
|
|
@@ -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"), history: join(dir, "history"), machines: join(dir, "machines.json"), projects: join(dir, "projects.json"), island: join(dir, "island.json") };
|
|
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"), machines: join(dir, "machines.json"), projects: join(dir, "projects.json"), island: join(dir, "island.json"), token: join(dir, "token") };
|
|
21
21
|
}
|
|
22
22
|
async function ensureLedgeDir() {
|
|
23
23
|
const p = paths();
|
|
@@ -32,18 +32,7 @@ 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
|
-
|
|
45
35
|
export {
|
|
46
36
|
paths,
|
|
47
|
-
ensureLedgeDir
|
|
48
|
-
log
|
|
37
|
+
ensureLedgeDir
|
|
49
38
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
cleanTopic,
|
|
3
3
|
sessionTitle
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KDYAHPYN.js";
|
|
5
5
|
|
|
6
6
|
// src/daemon/crossSession.ts
|
|
7
7
|
var FIRST_LINE = /^Another Claude session sent a message:\s*\n/;
|
|
@@ -101,6 +101,11 @@ function pendingTool(p) {
|
|
|
101
101
|
if (p.kind === "plan") return "ExitPlanMode";
|
|
102
102
|
return p.tool;
|
|
103
103
|
}
|
|
104
|
+
function answeredBy(p, ev) {
|
|
105
|
+
if (p.source !== "hook" || ev.name !== "PostToolUse" && ev.name !== "PostToolUseFailure") return false;
|
|
106
|
+
const tool = pendingTool(p);
|
|
107
|
+
return tool !== void 0 && ev.tool === tool && ev.helperId === p.helperId;
|
|
108
|
+
}
|
|
104
109
|
function clearedByToolRun(p, tool) {
|
|
105
110
|
const t = pendingTool(p);
|
|
106
111
|
return p.source === "pane" || t === void 0 || t === tool;
|
|
@@ -277,7 +282,7 @@ function reduceEvent(s, ev) {
|
|
|
277
282
|
if (ev.name === "UserPromptSubmit" && ev.text && !s.topic && !notice2 && !crossSession(ev.text)) s = withTopic(s, ev.text, "prompt");
|
|
278
283
|
const incoming = pendingFor(ev);
|
|
279
284
|
if (incoming) {
|
|
280
|
-
if (ev.name === "Notification" && s.pending
|
|
285
|
+
if (ev.name === "Notification" && s.pending) return { ...s, lastHookAt: at, lastActivityAt: at };
|
|
281
286
|
const pending = mergePending(s.pending, incoming);
|
|
282
287
|
const state = pending.kind === "question" ? "waiting_input" : "waiting_permission";
|
|
283
288
|
return { ...s, state, pending, lastHookAt: at, lastActivityAt: at };
|
|
@@ -337,6 +342,8 @@ export {
|
|
|
337
342
|
crossSession,
|
|
338
343
|
crossSessionNotices,
|
|
339
344
|
QUESTION_TOOLS,
|
|
345
|
+
pendingTool,
|
|
346
|
+
answeredBy,
|
|
340
347
|
reduce,
|
|
341
348
|
repositoryId,
|
|
342
349
|
gitInfo,
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PROTOCOL_PORT,
|
|
3
|
-
parseServerMessage
|
|
4
|
-
|
|
3
|
+
parseServerMessage,
|
|
4
|
+
readDaemonToken
|
|
5
|
+
} from "./chunk-KDYAHPYN.js";
|
|
5
6
|
|
|
6
7
|
// src/client.ts
|
|
7
8
|
import WebSocket from "ws";
|
|
9
|
+
var DaemonRefusedError = class extends Error {
|
|
10
|
+
};
|
|
8
11
|
function connect(opts = {}) {
|
|
9
12
|
const port = opts.port ?? PROTOCOL_PORT;
|
|
10
13
|
const host = opts.host ?? "127.0.0.1";
|
|
11
|
-
|
|
14
|
+
let token;
|
|
15
|
+
try {
|
|
16
|
+
token = opts.token === void 0 ? readDaemonToken() : opts.token;
|
|
17
|
+
} catch {
|
|
18
|
+
token = null;
|
|
19
|
+
}
|
|
20
|
+
const ws = new WebSocket(`ws://${host}:${port}`, token ? { headers: { authorization: `Bearer ${token}` } } : {});
|
|
12
21
|
const queue = [];
|
|
13
22
|
const MAX_QUEUED = 100;
|
|
14
23
|
const waiters = [];
|
|
@@ -66,12 +75,13 @@ function connect(opts = {}) {
|
|
|
66
75
|
if (!settled) {
|
|
67
76
|
settled = true;
|
|
68
77
|
clearTimeout(t);
|
|
69
|
-
reject(e);
|
|
78
|
+
reject(/Unexpected server response: 401/.test(e.message) ? new DaemonRefusedError("daemon refused the token") : e);
|
|
70
79
|
}
|
|
71
80
|
});
|
|
72
81
|
});
|
|
73
82
|
}
|
|
74
83
|
|
|
75
84
|
export {
|
|
85
|
+
DaemonRefusedError,
|
|
76
86
|
connect
|
|
77
87
|
};
|
|
@@ -1,3 +1,126 @@
|
|
|
1
|
+
import {
|
|
2
|
+
paths
|
|
3
|
+
} from "./chunk-FGOFRWIB.js";
|
|
4
|
+
|
|
5
|
+
// src/auth.ts
|
|
6
|
+
import { createHash, randomBytes, timingSafeEqual } from "crypto";
|
|
7
|
+
import { chmod, link, mkdir, readFile, unlink, writeFile } from "fs/promises";
|
|
8
|
+
import { readFileSync } from "fs";
|
|
9
|
+
import { dirname } from "path";
|
|
10
|
+
var TOKEN_RE = /^[0-9a-f]{64}$/;
|
|
11
|
+
var DAEMON_PROTOCOL_PREFIX = "ledge.";
|
|
12
|
+
function isDaemonToken(value) {
|
|
13
|
+
return TOKEN_RE.test(value);
|
|
14
|
+
}
|
|
15
|
+
function tokenEquals(presented, expected) {
|
|
16
|
+
const a = createHash("sha256").update(presented.slice(0, 256), "utf8").digest();
|
|
17
|
+
const b = createHash("sha256").update(expected, "utf8").digest();
|
|
18
|
+
return timingSafeEqual(a, b);
|
|
19
|
+
}
|
|
20
|
+
function hostAllowed(hostHeader) {
|
|
21
|
+
const raw = Array.isArray(hostHeader) ? hostHeader[0] : hostHeader;
|
|
22
|
+
if (!raw || raw.length > 255) return false;
|
|
23
|
+
const host = raw.trim().toLowerCase();
|
|
24
|
+
if (!host || host.includes(" ") || host.endsWith(".")) return false;
|
|
25
|
+
let name;
|
|
26
|
+
if (host.startsWith("[")) {
|
|
27
|
+
const end = host.indexOf("]");
|
|
28
|
+
if (end < 0) return false;
|
|
29
|
+
name = host.slice(1, end);
|
|
30
|
+
const rest = host.slice(end + 1);
|
|
31
|
+
if (rest !== "" && !/^:\d{1,5}$/.test(rest)) return false;
|
|
32
|
+
} else {
|
|
33
|
+
const colon = host.lastIndexOf(":");
|
|
34
|
+
if (colon >= 0) {
|
|
35
|
+
if (!/^\d{1,5}$/.test(host.slice(colon + 1))) return false;
|
|
36
|
+
name = host.slice(0, colon);
|
|
37
|
+
} else name = host;
|
|
38
|
+
}
|
|
39
|
+
return name === "localhost" || name === "127.0.0.1" || name === "::1";
|
|
40
|
+
}
|
|
41
|
+
function headerValue(value, max) {
|
|
42
|
+
const raw = Array.isArray(value) ? value.join(",") : value;
|
|
43
|
+
return !raw || raw.length > max ? "" : raw;
|
|
44
|
+
}
|
|
45
|
+
function bearerToken(authorization) {
|
|
46
|
+
if (!authorization || authorization.length > 512) return "";
|
|
47
|
+
return /^Bearer[ \t]+(\S+)$/i.exec(authorization)?.[1] ?? "";
|
|
48
|
+
}
|
|
49
|
+
function protocolToken(header) {
|
|
50
|
+
const raw = headerValue(header, 512);
|
|
51
|
+
if (!raw) return "";
|
|
52
|
+
for (const part of raw.split(",").slice(0, 8)) {
|
|
53
|
+
const protocol = part.trim();
|
|
54
|
+
if (protocol.startsWith(DAEMON_PROTOCOL_PREFIX)) return protocol.slice(DAEMON_PROTOCOL_PREFIX.length);
|
|
55
|
+
}
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
function requestAuthorized(headers, expected) {
|
|
59
|
+
const bearerOk = tokenEquals(bearerToken(typeof headers.authorization === "string" ? headers.authorization : void 0), expected);
|
|
60
|
+
const presented = protocolToken(headers["sec-websocket-protocol"]);
|
|
61
|
+
return bearerOk || presented !== "" && tokenEquals(presented, expected);
|
|
62
|
+
}
|
|
63
|
+
function selectDaemonProtocol(protocols, expected) {
|
|
64
|
+
for (const protocol of protocols) {
|
|
65
|
+
if (protocol.startsWith(DAEMON_PROTOCOL_PREFIX) && tokenEquals(protocol.slice(DAEMON_PROTOCOL_PREFIX.length), expected)) return protocol;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
async function readTokenFile(file) {
|
|
70
|
+
try {
|
|
71
|
+
const raw = (await readFile(file, "utf8")).trim();
|
|
72
|
+
return isDaemonToken(raw) ? raw : void 0;
|
|
73
|
+
} catch (e) {
|
|
74
|
+
if (e.code === "ENOENT") return void 0;
|
|
75
|
+
throw e;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function lockTokenFile(file) {
|
|
79
|
+
await chmod(file, 384);
|
|
80
|
+
}
|
|
81
|
+
async function ensureDaemonToken() {
|
|
82
|
+
const file = paths().token;
|
|
83
|
+
await mkdir(dirname(file), { recursive: true });
|
|
84
|
+
const current = await readTokenFile(file);
|
|
85
|
+
if (current) {
|
|
86
|
+
await lockTokenFile(file);
|
|
87
|
+
return current;
|
|
88
|
+
}
|
|
89
|
+
const token = randomBytes(32).toString("hex");
|
|
90
|
+
const tmp = `${file}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
|
|
91
|
+
await writeFile(tmp, `${token}
|
|
92
|
+
`, { mode: 384, flag: "wx" });
|
|
93
|
+
await chmod(tmp, 384);
|
|
94
|
+
try {
|
|
95
|
+
await link(tmp, file);
|
|
96
|
+
} catch (e) {
|
|
97
|
+
await unlink(tmp).catch(() => {
|
|
98
|
+
});
|
|
99
|
+
if (e.code !== "EEXIST") throw e;
|
|
100
|
+
const raced = await readTokenFile(file);
|
|
101
|
+
if (raced) {
|
|
102
|
+
await lockTokenFile(file);
|
|
103
|
+
return raced;
|
|
104
|
+
}
|
|
105
|
+
await writeFile(file, `${token}
|
|
106
|
+
`, { mode: 384 });
|
|
107
|
+
}
|
|
108
|
+
await unlink(tmp).catch(() => {
|
|
109
|
+
});
|
|
110
|
+
await lockTokenFile(file);
|
|
111
|
+
const stored = await readTokenFile(file);
|
|
112
|
+
if (!stored) throw new Error("daemon token was not stored");
|
|
113
|
+
return stored;
|
|
114
|
+
}
|
|
115
|
+
function readDaemonToken() {
|
|
116
|
+
try {
|
|
117
|
+
const raw = readFileSync(paths().token, "utf8").trim();
|
|
118
|
+
return isDaemonToken(raw) ? raw : void 0;
|
|
119
|
+
} catch {
|
|
120
|
+
return void 0;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
1
124
|
// ../protocol/src/session.ts
|
|
2
125
|
import { z } from "zod";
|
|
3
126
|
var ToolSchema = z.enum(["claude", "codex", "cursor"]);
|
|
@@ -524,7 +647,14 @@ var ClientMessageSchema = z13.discriminatedUnion("type", [
|
|
|
524
647
|
/** Copied into the `folder.listing` that answers; the fleet relays only that listing, from the machine asked. */
|
|
525
648
|
requestId: z13.string().min(1).max(100).optional()
|
|
526
649
|
}),
|
|
527
|
-
z13.object({
|
|
650
|
+
z13.object({
|
|
651
|
+
type: z13.literal("folder.create"),
|
|
652
|
+
parent: z13.string().min(1),
|
|
653
|
+
name: z13.string().min(1),
|
|
654
|
+
/** As for `folder.list`: routing by the fleet, and the request its `folder.listing` answers. */
|
|
655
|
+
machineId: z13.string().optional(),
|
|
656
|
+
requestId: z13.string().min(1).max(100).optional()
|
|
657
|
+
}),
|
|
528
658
|
z13.object({ type: z13.literal("session.reply"), id: z13.string(), text: z13.string().min(1) }),
|
|
529
659
|
/** The chat card's instruction (spec 2026-10-12 relay §4): sent now if the session is free, else queued until it is. */
|
|
530
660
|
z13.object({ type: z13.literal("session.relay"), qid: z13.string().min(1) }),
|
|
@@ -720,6 +850,11 @@ function sessionTitle(s) {
|
|
|
720
850
|
}
|
|
721
851
|
|
|
722
852
|
export {
|
|
853
|
+
hostAllowed,
|
|
854
|
+
requestAuthorized,
|
|
855
|
+
selectDaemonProtocol,
|
|
856
|
+
ensureDaemonToken,
|
|
857
|
+
readDaemonToken,
|
|
723
858
|
SessionSchema,
|
|
724
859
|
isArchived,
|
|
725
860
|
MAX_PROPOSALS,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
paths
|
|
3
|
-
} from "./chunk-L6IGH4LF.js";
|
|
4
1
|
import {
|
|
5
2
|
pickName
|
|
6
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KDYAHPYN.js";
|
|
4
|
+
import {
|
|
5
|
+
paths
|
|
6
|
+
} from "./chunk-FGOFRWIB.js";
|
|
7
7
|
|
|
8
8
|
// src/daemon/transcript.ts
|
|
9
9
|
import { closeSync, fstatSync, openSync, readSync } from "fs";
|
|
@@ -95,6 +95,33 @@ function claudePlan(path) {
|
|
|
95
95
|
}
|
|
96
96
|
return plan?.text;
|
|
97
97
|
}
|
|
98
|
+
function claudeCalls(path) {
|
|
99
|
+
const calls = [];
|
|
100
|
+
for (const line of tailLines(path)) {
|
|
101
|
+
let e;
|
|
102
|
+
try {
|
|
103
|
+
e = JSON.parse(line);
|
|
104
|
+
} catch {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (e.isSidechain === true || !Array.isArray(e.message?.content)) continue;
|
|
108
|
+
for (const b of e.message.content) {
|
|
109
|
+
if (e.type === "assistant" && b?.type === "tool_use" && typeof b.id === "string" && typeof b.name === "string") calls.push({ id: b.id, name: b.name, answered: false });
|
|
110
|
+
else if (e.type === "user" && b?.type === "tool_result") {
|
|
111
|
+
const c = calls.find((c2) => c2.id === b.tool_use_id);
|
|
112
|
+
if (c) c.answered = true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return calls;
|
|
117
|
+
}
|
|
118
|
+
function claudeOpenCall(path, tool) {
|
|
119
|
+
const last = claudeCalls(path).filter((c) => c.name === tool).at(-1);
|
|
120
|
+
return last && !last.answered ? last.id : void 0;
|
|
121
|
+
}
|
|
122
|
+
function claudeToolAnswered(path, id) {
|
|
123
|
+
return claudeCalls(path).some((c) => c.id === id && c.answered);
|
|
124
|
+
}
|
|
98
125
|
|
|
99
126
|
// src/spawn.ts
|
|
100
127
|
import { basename, join as join2 } from "path";
|
|
@@ -145,6 +172,8 @@ export {
|
|
|
145
172
|
agentMessages,
|
|
146
173
|
codexPlan,
|
|
147
174
|
claudePlan,
|
|
175
|
+
claudeOpenCall,
|
|
176
|
+
claudeToolAnswered,
|
|
148
177
|
CODEX_ENV_VAR,
|
|
149
178
|
CODEX_HOOK_VAR,
|
|
150
179
|
exitFile,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
run
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-TO4XO2ML.js";
|
|
4
4
|
import {
|
|
5
5
|
ensureLedgeDir,
|
|
6
6
|
paths
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-FGOFRWIB.js";
|
|
8
8
|
|
|
9
9
|
// src/cli/setup.ts
|
|
10
10
|
import { readFile as readFile2, writeFile, mkdir, rename, realpath, rm, stat, lstat, readlink, symlink, unlink } from "fs/promises";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
paths
|
|
3
|
+
} from "./chunk-FGOFRWIB.js";
|
|
4
|
+
|
|
5
|
+
// src/log.ts
|
|
6
|
+
import { appendFileSync } from "fs";
|
|
7
|
+
function log(line) {
|
|
8
|
+
try {
|
|
9
|
+
appendFileSync(paths().log, `${(/* @__PURE__ */ new Date()).toISOString()} | ${line}
|
|
10
|
+
`);
|
|
11
|
+
} catch {
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
log
|
|
17
|
+
};
|
|
@@ -4,35 +4,37 @@ import {
|
|
|
4
4
|
} from "./chunk-OLLQBK6N.js";
|
|
5
5
|
import {
|
|
6
6
|
QUESTION_TOOLS,
|
|
7
|
+
answeredBy,
|
|
7
8
|
crossSession,
|
|
8
9
|
crossSessionNotices,
|
|
9
10
|
gitInfo,
|
|
10
11
|
gitSummary,
|
|
12
|
+
pendingTool,
|
|
11
13
|
reduce,
|
|
12
14
|
repositoryId
|
|
13
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-I4C5U6AI.js";
|
|
14
16
|
import {
|
|
15
17
|
agentMessages,
|
|
18
|
+
claudeOpenCall,
|
|
16
19
|
claudePlan,
|
|
20
|
+
claudeToolAnswered,
|
|
17
21
|
spawnSession
|
|
18
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-LYTRXJAQ.js";
|
|
19
23
|
import {
|
|
20
24
|
LEDGE_VERSION
|
|
21
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-YXXXSRQX.js";
|
|
22
26
|
import {
|
|
23
27
|
SERVICE,
|
|
24
28
|
inDaemonService,
|
|
25
29
|
tmuxServerAtRisk
|
|
26
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-Q7RCYJ4X.js";
|
|
27
31
|
import {
|
|
28
32
|
realTmux,
|
|
29
33
|
runFile
|
|
30
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-TO4XO2ML.js";
|
|
31
35
|
import {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
paths
|
|
35
|
-
} from "./chunk-L6IGH4LF.js";
|
|
36
|
+
log
|
|
37
|
+
} from "./chunk-YUSY3E47.js";
|
|
36
38
|
import {
|
|
37
39
|
DEFAULT_PROJECTS,
|
|
38
40
|
DOCUMENT_MAX_BYTES,
|
|
@@ -46,7 +48,9 @@ import {
|
|
|
46
48
|
RouteResultSchema,
|
|
47
49
|
SessionSchema,
|
|
48
50
|
SshAliasSchema,
|
|
51
|
+
ensureDaemonToken,
|
|
49
52
|
extractProposal,
|
|
53
|
+
hostAllowed,
|
|
50
54
|
isArchived,
|
|
51
55
|
offers,
|
|
52
56
|
parseClientMessage,
|
|
@@ -57,8 +61,14 @@ import {
|
|
|
57
61
|
pendingKey,
|
|
58
62
|
pickName,
|
|
59
63
|
projectOf,
|
|
60
|
-
proposalRows
|
|
61
|
-
|
|
64
|
+
proposalRows,
|
|
65
|
+
requestAuthorized,
|
|
66
|
+
selectDaemonProtocol
|
|
67
|
+
} from "./chunk-KDYAHPYN.js";
|
|
68
|
+
import {
|
|
69
|
+
ensureLedgeDir,
|
|
70
|
+
paths
|
|
71
|
+
} from "./chunk-FGOFRWIB.js";
|
|
62
72
|
|
|
63
73
|
// src/daemon/index.ts
|
|
64
74
|
import { writeFile as writeFile6, rm as rm5 } from "fs/promises";
|
|
@@ -583,6 +593,7 @@ var History = class {
|
|
|
583
593
|
};
|
|
584
594
|
|
|
585
595
|
// src/daemon/server.ts
|
|
596
|
+
import { createServer } from "http";
|
|
586
597
|
import { homedir as homedir5 } from "os";
|
|
587
598
|
import { basename as basename3 } from "path";
|
|
588
599
|
import { WebSocketServer, WebSocket as WebSocket2 } from "ws";
|
|
@@ -637,6 +648,9 @@ function dialogMismatch(shown, pending, width) {
|
|
|
637
648
|
if (pending.tool !== void 0 && COMMAND_TOOLS.has(pending.tool)) return shown.command ? commandMismatch(shown.command, pending.text, width) : UNCHECKED;
|
|
638
649
|
return null;
|
|
639
650
|
}
|
|
651
|
+
function bottomLines(pane, n = 5) {
|
|
652
|
+
return pane.split("\n").filter((l) => l.trim() !== "").slice(-n).join("\n");
|
|
653
|
+
}
|
|
640
654
|
var flatten = (text) => text.replace(/\s*[\r\n]+\s*/g, " ").trim();
|
|
641
655
|
|
|
642
656
|
// src/adapters/claude.ts
|
|
@@ -766,7 +780,7 @@ function promptStillVisible(lastLines2) {
|
|
|
766
780
|
}
|
|
767
781
|
var squash = (t) => t.replace(/\s+/g, " ").trim();
|
|
768
782
|
function promptGone(pane, pending) {
|
|
769
|
-
if (pending.kind !== "question" || !squash(pending.text)) return !promptStillVisible(pane
|
|
783
|
+
if (pending.kind !== "question" || !squash(pending.text)) return !promptStillVisible(bottomLines(pane));
|
|
770
784
|
const m = detectPrompt(pane);
|
|
771
785
|
if (!m || m.kind !== "question") return true;
|
|
772
786
|
const shown = squash(m.text);
|
|
@@ -2859,6 +2873,25 @@ var ALLOWED_ORIGINS = [
|
|
|
2859
2873
|
function originAllowed(origin) {
|
|
2860
2874
|
return origin === void 0 || ALLOWED_ORIGINS.includes(origin);
|
|
2861
2875
|
}
|
|
2876
|
+
function requestPath(url) {
|
|
2877
|
+
const raw = url ?? "/";
|
|
2878
|
+
const q = raw.indexOf("?");
|
|
2879
|
+
return q === -1 ? raw : raw.slice(0, q);
|
|
2880
|
+
}
|
|
2881
|
+
function refusal(req, token) {
|
|
2882
|
+
const path = requestPath(req.url);
|
|
2883
|
+
if (!(path === "/" || path.startsWith("/ssh/") && SshAliasSchema.safeParse(path.slice(5)).success)) return "path";
|
|
2884
|
+
if (!originAllowed(req.headers.origin)) return "origin";
|
|
2885
|
+
if (!hostAllowed(req.headers.host)) return "host";
|
|
2886
|
+
if (!requestAuthorized(req.headers, token)) return "auth";
|
|
2887
|
+
return null;
|
|
2888
|
+
}
|
|
2889
|
+
function unauthorized(req, res) {
|
|
2890
|
+
req.resume();
|
|
2891
|
+
const body = "Unauthorized\n";
|
|
2892
|
+
res.writeHead(401, { "Content-Type": "text/plain; charset=utf-8", "Content-Length": Buffer.byteLength(body), "Cache-Control": "no-store", Connection: "close" });
|
|
2893
|
+
res.end(body);
|
|
2894
|
+
}
|
|
2862
2895
|
function logDroppedRequest(id, ev, before, after) {
|
|
2863
2896
|
if (ev.name !== "PermissionRequest" || !after || after.pending !== before?.pending) return;
|
|
2864
2897
|
const held = before?.pending;
|
|
@@ -2874,22 +2907,36 @@ async function startServer(opts) {
|
|
|
2874
2907
|
const agentEnv = opts.agentEnv ?? (async () => ({ ...process.env, ...await loginShellEnv() }));
|
|
2875
2908
|
const chat = opts.chat ?? createChat({ store, tmux, home: paths().dir, userHome: opts.home, env: agentEnv, bin: opts.claudeBin, now, projects: () => projects, forge: createForge({ env: agentEnv }) });
|
|
2876
2909
|
const router = createRouter({ env: agentEnv });
|
|
2910
|
+
const token = await ensureDaemonToken();
|
|
2911
|
+
const httpServer = createServer(unauthorized);
|
|
2912
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
2913
|
+
httpServer.on("connection", (socket) => {
|
|
2914
|
+
sockets.add(socket);
|
|
2915
|
+
socket.on("close", () => sockets.delete(socket));
|
|
2916
|
+
});
|
|
2877
2917
|
const wss = new WebSocketServer({
|
|
2878
|
-
|
|
2879
|
-
port: opts.port ?? PROTOCOL_PORT,
|
|
2918
|
+
server: httpServer,
|
|
2880
2919
|
maxPayload: MAX_FRAME,
|
|
2881
2920
|
verifyClient: ({ req }) => {
|
|
2882
|
-
const
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2921
|
+
const why = refusal(req, token);
|
|
2922
|
+
if (why) {
|
|
2923
|
+
const origin = String(req.headers.origin ?? "none").replace(/[\r\n]/g, "").slice(0, 120);
|
|
2924
|
+
log(`refused a connection (${why}) from origin ${origin}`);
|
|
2925
|
+
}
|
|
2926
|
+
return why === null;
|
|
2927
|
+
},
|
|
2928
|
+
handleProtocols: (protocols) => selectDaemonProtocol(protocols, token)
|
|
2887
2929
|
});
|
|
2888
2930
|
await new Promise((res, rej) => {
|
|
2889
|
-
|
|
2890
|
-
wss.once("error",
|
|
2931
|
+
const onError = (e) => rej(e);
|
|
2932
|
+
wss.once("error", onError);
|
|
2933
|
+
httpServer.listen(opts.port ?? PROTOCOL_PORT, opts.host ?? "127.0.0.1", () => {
|
|
2934
|
+
wss.off("error", onError);
|
|
2935
|
+
res();
|
|
2936
|
+
});
|
|
2891
2937
|
});
|
|
2892
|
-
|
|
2938
|
+
wss.on("error", (e) => log(`daemon server error: ${e.message}`));
|
|
2939
|
+
const port = httpServer.address().port;
|
|
2893
2940
|
const machinesFile = opts.machinesFile ?? paths().machines;
|
|
2894
2941
|
let machines = readMachines(machinesFile).map((m) => ({ ...m }));
|
|
2895
2942
|
const machinesMsg = () => ({ type: "machines", machines });
|
|
@@ -2971,6 +3018,16 @@ async function startServer(opts) {
|
|
|
2971
3018
|
}
|
|
2972
3019
|
}
|
|
2973
3020
|
const inFlight = /* @__PURE__ */ new Set();
|
|
3021
|
+
const CONFIRM_MS = opts.confirmMs ?? 3e3;
|
|
3022
|
+
const awaited = /* @__PURE__ */ new Map();
|
|
3023
|
+
function settleByHook(id, ev) {
|
|
3024
|
+
const a = awaited.get(id);
|
|
3025
|
+
const cur = store.get(id)?.pending;
|
|
3026
|
+
if (!a || a.settled || !answeredBy(a.pending, ev) || !cur || pendingKey(cur) !== pendingKey(a.pending)) return;
|
|
3027
|
+
a.settled = true;
|
|
3028
|
+
a.byHook = true;
|
|
3029
|
+
store.apply(id, { name: "Decided", at: now(), source: "daemon", ...a.decision ? { decision: a.decision } : {} });
|
|
3030
|
+
}
|
|
2974
3031
|
async function injectAndVerify(id, planFor, requirePrompt, decision) {
|
|
2975
3032
|
const s = store.get(id);
|
|
2976
3033
|
if (!s) throw new Error(`unknown session ${id}`);
|
|
@@ -2986,7 +3043,7 @@ async function startServer(opts) {
|
|
|
2986
3043
|
let before = null;
|
|
2987
3044
|
if (requirePrompt) {
|
|
2988
3045
|
before = s.tmuxSession ? await tmux.capture(s.tmuxSession, DETECT_LINES) : null;
|
|
2989
|
-
if (before === null || !adapter.promptStillVisible(before
|
|
3046
|
+
if (before === null || !adapter.promptStillVisible(bottomLines(before))) throw new Error(NO_PROMPT);
|
|
2990
3047
|
if (!stillTheSame()) throw new Error(STALE);
|
|
2991
3048
|
if (decided) {
|
|
2992
3049
|
const shown = adapter.detectPrompt(before) ?? adapter.detectPrompt(await tmux.capture(s.tmuxSession, CHECK_LINES) ?? "");
|
|
@@ -2995,21 +3052,41 @@ async function startServer(opts) {
|
|
|
2995
3052
|
}
|
|
2996
3053
|
}
|
|
2997
3054
|
const plan = typeof planFor === "function" ? planFor(before ?? "") : planFor;
|
|
2998
|
-
const
|
|
3055
|
+
const stillShown = (p) => decided && adapter.promptGone ? !adapter.promptGone(p, decided) : adapter.promptStillVisible(bottomLines(p));
|
|
3056
|
+
const tool = s.tool === "claude" && decided?.source === "hook" ? pendingTool(decided) : void 0;
|
|
3057
|
+
const answer = tool ? { pending: decided, ...decision ? { decision } : {}, settled: false, byHook: false } : void 0;
|
|
3058
|
+
const transcript = tool && !decided.helperId && s.transcriptPath ? s.transcriptPath : void 0;
|
|
3059
|
+
const call = transcript ? claudeOpenCall(transcript, tool) : void 0;
|
|
3060
|
+
const promptGone3 = async () => {
|
|
3061
|
+
if (!answer) return verifyGone(() => tmux.capture(s.tmuxSession, DETECT_LINES), stillShown, opts.verify);
|
|
3062
|
+
const interval = opts.verify?.intervalMs ?? 200;
|
|
3063
|
+
for (const end = Date.now() + CONFIRM_MS; ; ) {
|
|
3064
|
+
if (answer.byHook || call && claudeToolAnswered(transcript, call)) return true;
|
|
3065
|
+
const pane = await tmux.capture(s.tmuxSession, DETECT_LINES);
|
|
3066
|
+
if (answer.byHook || pane !== null && !stillShown(pane)) return true;
|
|
3067
|
+
if (Date.now() >= end) return false;
|
|
3068
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
3069
|
+
}
|
|
3070
|
+
};
|
|
3071
|
+
if (answer) awaited.set(id, answer);
|
|
2999
3072
|
try {
|
|
3000
3073
|
await runPlan(s, plan);
|
|
3001
3074
|
} catch (e) {
|
|
3002
3075
|
if (e instanceof NoteNotTyped) {
|
|
3003
3076
|
if (await promptGone3()) {
|
|
3004
|
-
if (
|
|
3077
|
+
if (answer) answer.settled = true;
|
|
3078
|
+
if (!answer?.byHook && stillTheSame()) store.apply(id, { name: "Decided", at: now(), source: "daemon", ...decision ? { decision } : {} });
|
|
3005
3079
|
throw e;
|
|
3006
3080
|
}
|
|
3007
3081
|
e = new Error(NOT_TAKEN);
|
|
3008
3082
|
}
|
|
3083
|
+
if (answer?.byHook) return true;
|
|
3009
3084
|
store.apply(id, { name: "InjectFailed", at: now(), source: "daemon", text: e.message });
|
|
3010
3085
|
throw e;
|
|
3011
3086
|
}
|
|
3012
3087
|
const gone = await promptGone3();
|
|
3088
|
+
if (answer) answer.settled = true;
|
|
3089
|
+
if (answer?.byHook) return true;
|
|
3013
3090
|
if (!stillTheSame()) return false;
|
|
3014
3091
|
if (gone) {
|
|
3015
3092
|
store.apply(id, { name: "Decided", at: now(), source: "daemon", ...decision ? { decision } : {} });
|
|
@@ -3019,6 +3096,7 @@ async function startServer(opts) {
|
|
|
3019
3096
|
throw new Error(NOT_TAKEN);
|
|
3020
3097
|
} finally {
|
|
3021
3098
|
inFlight.delete(id);
|
|
3099
|
+
awaited.delete(id);
|
|
3022
3100
|
}
|
|
3023
3101
|
}
|
|
3024
3102
|
const READ_ONLY = "session started without ledge, read only";
|
|
@@ -3176,7 +3254,7 @@ async function startServer(opts) {
|
|
|
3176
3254
|
case "folder.list":
|
|
3177
3255
|
return send(ws, await listFolder(msg.path, opts.home, msg.requestId));
|
|
3178
3256
|
case "folder.create": {
|
|
3179
|
-
const listing = await createFolder(msg.parent, msg.name, opts.home);
|
|
3257
|
+
const listing = await createFolder(msg.parent, msg.name, opts.home, msg.requestId);
|
|
3180
3258
|
if (!listing.error) log(`created folder ${listing.path}`);
|
|
3181
3259
|
return send(ws, listing);
|
|
3182
3260
|
}
|
|
@@ -3203,7 +3281,10 @@ async function startServer(opts) {
|
|
|
3203
3281
|
const base = { id: msg.id, name: pickName(taken), tool: msg.agent ?? "claude", cwd, project: basename3(cwd) || "unknown", title: "untracked session", tmuxSession: null, state: "working", helpers: [], startedAt: msg.event.at, lastActivityAt: msg.event.at, source: "hook_only" };
|
|
3204
3282
|
store.upsert(base);
|
|
3205
3283
|
store.apply(msg.id, msg.event);
|
|
3206
|
-
} else
|
|
3284
|
+
} else {
|
|
3285
|
+
settleByHook(msg.id, ev);
|
|
3286
|
+
store.apply(msg.id, msg.event);
|
|
3287
|
+
}
|
|
3207
3288
|
logDroppedRequest(msg.id, msg.event, before, store.get(msg.id));
|
|
3208
3289
|
return;
|
|
3209
3290
|
}
|
|
@@ -3336,14 +3417,15 @@ async function startServer(opts) {
|
|
|
3336
3417
|
}
|
|
3337
3418
|
wss.on("connection", (ws, req) => {
|
|
3338
3419
|
ws.on("error", (e) => log(`socket error: ${e.message}`));
|
|
3339
|
-
|
|
3420
|
+
const path = requestPath(req.url);
|
|
3421
|
+
if (path.startsWith("/ssh/")) {
|
|
3340
3422
|
if (remoteClients.size >= 20) {
|
|
3341
3423
|
ws.close(1013, "Too many remote connections");
|
|
3342
3424
|
return;
|
|
3343
3425
|
}
|
|
3344
3426
|
remoteClients.add(ws);
|
|
3345
3427
|
ws.once("close", () => remoteClients.delete(ws));
|
|
3346
|
-
const parsed = SshAliasSchema.safeParse(
|
|
3428
|
+
const parsed = SshAliasSchema.safeParse(path.slice(5));
|
|
3347
3429
|
if (!parsed.success) {
|
|
3348
3430
|
ws.close(1008, "Invalid SSH alias");
|
|
3349
3431
|
remoteClients.delete(ws);
|
|
@@ -3388,7 +3470,8 @@ async function startServer(opts) {
|
|
|
3388
3470
|
for (const u of unsubscribeHistory) u();
|
|
3389
3471
|
relay.close();
|
|
3390
3472
|
for (const c of wss.clients) c.terminate();
|
|
3391
|
-
|
|
3473
|
+
for (const socket of sockets) socket.destroy();
|
|
3474
|
+
wss.close(() => httpServer.close(() => res()));
|
|
3392
3475
|
})
|
|
3393
3476
|
};
|
|
3394
3477
|
}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
QUESTION_TOOLS,
|
|
3
3
|
gitInfo
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-I4C5U6AI.js";
|
|
5
5
|
import {
|
|
6
6
|
CODEX_ENV_VAR,
|
|
7
7
|
CODEX_HOOK_VAR,
|
|
8
8
|
codexPlan
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-LYTRXJAQ.js";
|
|
10
10
|
import {
|
|
11
11
|
log
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-YUSY3E47.js";
|
|
13
13
|
import {
|
|
14
14
|
connect
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
} from "./chunk-IWJAJAPK.js";
|
|
16
|
+
import "./chunk-KDYAHPYN.js";
|
|
17
|
+
import "./chunk-FGOFRWIB.js";
|
|
17
18
|
|
|
18
19
|
// src/cli/hook.ts
|
|
19
20
|
import { readFileSync, openSync, fstatSync, readSync, closeSync } from "fs";
|
package/dist/cli/index.js
CHANGED
|
@@ -17,7 +17,7 @@ async function hookMain(args) {
|
|
|
17
17
|
try {
|
|
18
18
|
const chunks = [];
|
|
19
19
|
for await (const c of process.stdin) chunks.push(c);
|
|
20
|
-
const { runHook } = await import("./hook-
|
|
20
|
+
const { runHook } = await import("./hook-5HXY2UGM.js");
|
|
21
21
|
const out = await runHook(event, Buffer.concat(chunks).toString("utf8"), { tool });
|
|
22
22
|
if (out) process.stdout.write(`${out}
|
|
23
23
|
`);
|
|
@@ -29,7 +29,7 @@ async function main(argv) {
|
|
|
29
29
|
const [cmd, ...rest] = argv;
|
|
30
30
|
switch (cmd) {
|
|
31
31
|
case "daemon": {
|
|
32
|
-
const { startDaemon } = await import("./daemon-
|
|
32
|
+
const { startDaemon } = await import("./daemon-NDXT3ZDF.js");
|
|
33
33
|
await startDaemon();
|
|
34
34
|
await new Promise(() => {
|
|
35
35
|
});
|
|
@@ -37,44 +37,44 @@ async function main(argv) {
|
|
|
37
37
|
}
|
|
38
38
|
case "--version":
|
|
39
39
|
case "version": {
|
|
40
|
-
const { LEDGE_VERSION } = await import("./version-
|
|
40
|
+
const { LEDGE_VERSION } = await import("./version-AQN6JNH7.js");
|
|
41
41
|
console.log(LEDGE_VERSION);
|
|
42
42
|
return 0;
|
|
43
43
|
}
|
|
44
44
|
case "setup": {
|
|
45
|
-
const { runSetup } = await import("./setup-
|
|
45
|
+
const { runSetup } = await import("./setup-7AEH5DC3.js");
|
|
46
46
|
for (const l of await runSetup()) console.log(l);
|
|
47
47
|
return 0;
|
|
48
48
|
}
|
|
49
49
|
case "uninstall": {
|
|
50
|
-
const { runUninstall } = await import("./uninstall-
|
|
50
|
+
const { runUninstall } = await import("./uninstall-7U7ENKOS.js");
|
|
51
51
|
const r = await runUninstall({ purge: rest.includes("--purge") });
|
|
52
52
|
for (const l of r.lines) console.log(l);
|
|
53
53
|
return r.ok ? 0 : 1;
|
|
54
54
|
}
|
|
55
55
|
case "status": {
|
|
56
|
-
const { printStatus } = await import("./status-
|
|
56
|
+
const { printStatus } = await import("./status-PDNC6IC2.js");
|
|
57
57
|
console.log(await printStatus());
|
|
58
58
|
return 0;
|
|
59
59
|
}
|
|
60
60
|
case "who": {
|
|
61
|
-
const { printWho } = await import("./who-
|
|
61
|
+
const { printWho } = await import("./who-LIMQJMNI.js");
|
|
62
62
|
console.log(await printWho());
|
|
63
63
|
return 0;
|
|
64
64
|
}
|
|
65
65
|
case "bridge": {
|
|
66
|
-
const { bridge } = await import("./bridge-
|
|
66
|
+
const { bridge } = await import("./bridge-XHPCIYE4.js");
|
|
67
67
|
await bridge();
|
|
68
68
|
return 0;
|
|
69
69
|
}
|
|
70
70
|
case "claude":
|
|
71
71
|
case "codex":
|
|
72
72
|
case "cursor": {
|
|
73
|
-
const { launch } = await import("./launch-
|
|
73
|
+
const { launch } = await import("./launch-HEJKGMYS.js");
|
|
74
74
|
return launch(cmd, rest);
|
|
75
75
|
}
|
|
76
76
|
case "attach": {
|
|
77
|
-
const { attach } = await import("./attach-
|
|
77
|
+
const { attach } = await import("./attach-AZFKVVKL.js");
|
|
78
78
|
return attach(rest[0] ?? "");
|
|
79
79
|
}
|
|
80
80
|
default:
|
|
@@ -5,15 +5,16 @@ import {
|
|
|
5
5
|
ensureDaemon,
|
|
6
6
|
launch,
|
|
7
7
|
parseLaunchArgs
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-65XGYBHL.js";
|
|
9
9
|
import {
|
|
10
10
|
agentEnv,
|
|
11
11
|
exitFile
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-LYTRXJAQ.js";
|
|
13
|
+
import "./chunk-TO4XO2ML.js";
|
|
14
|
+
import "./chunk-YUSY3E47.js";
|
|
15
|
+
import "./chunk-IWJAJAPK.js";
|
|
16
|
+
import "./chunk-KDYAHPYN.js";
|
|
17
|
+
import "./chunk-FGOFRWIB.js";
|
|
17
18
|
export {
|
|
18
19
|
DaemonError,
|
|
19
20
|
agentEnv,
|
|
@@ -13,9 +13,10 @@ import {
|
|
|
13
13
|
runSetup,
|
|
14
14
|
stopDetachedDaemon,
|
|
15
15
|
systemdUnit
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-Q7RCYJ4X.js";
|
|
17
|
+
import "./chunk-TO4XO2ML.js";
|
|
18
|
+
import "./chunk-YUSY3E47.js";
|
|
19
|
+
import "./chunk-FGOFRWIB.js";
|
|
19
20
|
export {
|
|
20
21
|
CLAUDE_EVENTS,
|
|
21
22
|
CODEX_EVENTS,
|
|
@@ -2,11 +2,12 @@ import {
|
|
|
2
2
|
SERVICE,
|
|
3
3
|
execRunner,
|
|
4
4
|
stopDetachedDaemon
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-Q7RCYJ4X.js";
|
|
6
|
+
import "./chunk-TO4XO2ML.js";
|
|
7
|
+
import "./chunk-YUSY3E47.js";
|
|
7
8
|
import {
|
|
8
9
|
paths
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-FGOFRWIB.js";
|
|
10
11
|
|
|
11
12
|
// src/cli/uninstall.ts
|
|
12
13
|
import { readFile, writeFile, rename, rm, lstat, realpath } from "fs/promises";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
connect
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-IWJAJAPK.js";
|
|
4
4
|
import {
|
|
5
5
|
isArchived
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-KDYAHPYN.js";
|
|
7
|
+
import "./chunk-FGOFRWIB.js";
|
|
7
8
|
|
|
8
9
|
// src/cli/who.ts
|
|
9
10
|
import { basename } from "path";
|
package/package.json
CHANGED