@paradow/ledge 0.15.0 → 0.15.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/README.md +2 -2
- package/dist/cli/chunk-LKQ5DDQ4.js +6 -0
- package/dist/cli/{chunk-CU2E3W4L.js → chunk-QCEVJMXI.js} +3 -2
- package/dist/cli/{chunk-DXVHFVJI.js → chunk-ZANOMSLW.js} +20 -10
- package/dist/cli/{daemon-NIEJZDEC.js → daemon-TBYCUHC3.js} +3 -3
- package/dist/cli/{hook-BGIM76EW.js → hook-PRPEB3T7.js} +1 -1
- package/dist/cli/index.js +5 -5
- package/dist/cli/{setup-2GROTY6B.js → setup-LWYZEVNE.js} +7 -5
- package/dist/cli/{uninstall-JCX7QXU2.js → uninstall-TFC4LALT.js} +4 -3
- package/dist/cli/{version-X45HSUBB.js → version-5BLH6OA6.js} +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-EHHYRQWY.js +0 -6
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ pnpm --filter @paradow/ledge build
|
|
|
30
30
|
chmod +x packages/ledge/dist/cli/index.js
|
|
31
31
|
mkdir -p ~/.local/bin
|
|
32
32
|
ln -sf "$PWD/packages/ledge/dist/cli/index.js" ~/.local/bin/ledge # ~/.local/bin must be on the PATH Claude Code uses for hooks
|
|
33
|
-
ledge setup
|
|
33
|
+
ledge setup # the link becomes a script that runs the CLI with this node (a session Ledge launches may have no node on its PATH: nvm)
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
The daemon is spawned from the built CLI (`dist/cli/index.js`), so rebuild after changes (`pnpm --filter @paradow/ledge build`).
|
|
@@ -43,7 +43,7 @@ ledge claude --name max # pick the bot's name
|
|
|
43
43
|
ledge status # list bots and what they wait for
|
|
44
44
|
ledge attach <id> # put this terminal on a running session (what the overlay opens a tab with)
|
|
45
45
|
ledge daemon # run the daemon in the foreground (normally run by the systemd service)
|
|
46
|
-
ledge uninstall # undo ledge setup: hooks, service and
|
|
46
|
+
ledge uninstall # undo ledge setup: hooks, service and ~/.local/bin/ledge (--purge: ~/.ledge too)
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
`ledge setup` installs `ledge-daemon.service` as a systemd user unit, starts it and enables linger, so the daemon runs from WSL boot and restarts after a crash. Rerun it after a node upgrade or a repository move: the unit is rewritten and the service restarted. Useful commands:
|
|
@@ -356,9 +356,10 @@ function reduceEvent(s, ev) {
|
|
|
356
356
|
case "PanePrompt": {
|
|
357
357
|
const incoming = pendingFor(ev);
|
|
358
358
|
const moved = s.pending?.source === "hook" && s.pending.kind === "question" && incoming.kind === "question" && !sameQuestion(incoming.text, s.pending.text);
|
|
359
|
-
|
|
359
|
+
const bare = s.pending?.source === "hook" && s.pending.kind === "permission" && !s.pending.tool;
|
|
360
|
+
if (s.pending?.source === "hook" && !moved && !bare) return s;
|
|
360
361
|
if (!moved && hooksReportPrompts(s) && lastHookAt && Date.parse(at) - Date.parse(lastHookAt) < HOOK_PRIORITY_MS) return s;
|
|
361
|
-
let pending = moved ? { ...incoming, ...s.pending.helperId ? { helperId: s.pending.helperId } : {} } : mergePending(s.pending, incoming);
|
|
362
|
+
let pending = moved ? { ...incoming, ...s.pending.helperId ? { helperId: s.pending.helperId } : {} } : bare ? incoming : mergePending(s.pending, incoming);
|
|
362
363
|
if (pending.kind === "plan" && !pending.body && s.tool === "codex" && s.lastReply) pending = { ...pending, body: s.lastReply.text };
|
|
363
364
|
if (s.pending && samePending(s.pending, pending)) return s;
|
|
364
365
|
const state = pending.kind === "question" ? "waiting_input" : "waiting_permission";
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-R5462SQM.js";
|
|
8
8
|
|
|
9
9
|
// src/cli/setup.ts
|
|
10
|
-
import { readFile as readFile2, writeFile, mkdir, rename, realpath, rm, stat, lstat, readlink
|
|
10
|
+
import { readFile as readFile2, writeFile, mkdir, rename, realpath, rm, stat, lstat, readlink } from "fs/promises";
|
|
11
11
|
import { execFile } from "child_process";
|
|
12
12
|
import { homedir, userInfo } from "os";
|
|
13
13
|
import { join, dirname } from "path";
|
|
@@ -237,14 +237,23 @@ async function isDir(path) {
|
|
|
237
237
|
return false;
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
|
-
|
|
240
|
+
var BIN_MARK = "#!/bin/sh\n# Written by `ledge setup`";
|
|
241
|
+
var shQuote = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
242
|
+
async function writeBin(opts) {
|
|
241
243
|
const st = await lstat(opts.link).catch(() => null);
|
|
242
|
-
if (!st
|
|
243
|
-
const
|
|
244
|
-
if (
|
|
245
|
-
await
|
|
246
|
-
|
|
247
|
-
|
|
244
|
+
if (!st) return null;
|
|
245
|
+
const was = st.isSymbolicLink() ? await readlink(opts.link) : null;
|
|
246
|
+
if (was !== null && !was.endsWith("/dist/cli/index.js")) return null;
|
|
247
|
+
const current = was === null && st.isFile() ? await readFile2(opts.link, "utf8").catch(() => "") : "";
|
|
248
|
+
if (was === null && !current.startsWith(BIN_MARK)) return null;
|
|
249
|
+
const script = `${BIN_MARK}: the hooks run \`ledge\` wherever node is not on the PATH.
|
|
250
|
+
exec ${shQuote(opts.node)} ${shQuote(opts.target)} "$@"
|
|
251
|
+
`;
|
|
252
|
+
if (current === script) return null;
|
|
253
|
+
const tmp = `${opts.link}.tmp`;
|
|
254
|
+
await writeFile(tmp, script, { mode: 493 });
|
|
255
|
+
await rename(tmp, opts.link);
|
|
256
|
+
return `${opts.link} runs ${opts.target} with ${opts.node}${was !== null ? ` (was a link to ${was})` : ""}`;
|
|
248
257
|
}
|
|
249
258
|
async function runSetup(opts = {}) {
|
|
250
259
|
const out = [];
|
|
@@ -263,7 +272,7 @@ async function runSetup(opts = {}) {
|
|
|
263
272
|
else if (await mergeJsonFile(join(cursorDir, "hooks.json"), mergeCursorHooks, "hooks.json")) out.push(`Cursor Agent hooks added in ${join(cursorDir, "hooks.json")}`);
|
|
264
273
|
else out.push("Cursor Agent hooks already present");
|
|
265
274
|
if ((opts.service ?? true) && !process.argv[1]?.endsWith(".ts")) {
|
|
266
|
-
const line = await
|
|
275
|
+
const line = await writeBin({ link: join(homedir(), ".local", "bin", "ledge"), node: process.execPath, target: await realpath(process.argv[1]) });
|
|
267
276
|
if (line) out.push(line);
|
|
268
277
|
}
|
|
269
278
|
if (opts.service ?? true) {
|
|
@@ -286,7 +295,8 @@ export {
|
|
|
286
295
|
stopDetachedDaemon,
|
|
287
296
|
installService,
|
|
288
297
|
isDir,
|
|
289
|
-
|
|
298
|
+
BIN_MARK,
|
|
299
|
+
writeBin,
|
|
290
300
|
runSetup,
|
|
291
301
|
inDaemonService,
|
|
292
302
|
tmuxServerAtRisk
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
reduce,
|
|
28
28
|
repositoryId,
|
|
29
29
|
shownCommand
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-QCEVJMXI.js";
|
|
31
31
|
import {
|
|
32
32
|
PARENT_AGENT_VARS,
|
|
33
33
|
agentEnv,
|
|
@@ -99,12 +99,12 @@ import {
|
|
|
99
99
|
} from "./chunk-2LQITRTR.js";
|
|
100
100
|
import {
|
|
101
101
|
LEDGE_VERSION
|
|
102
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-LKQ5DDQ4.js";
|
|
103
103
|
import {
|
|
104
104
|
SERVICE,
|
|
105
105
|
inDaemonService,
|
|
106
106
|
tmuxServerAtRisk
|
|
107
|
-
} from "./chunk-
|
|
107
|
+
} from "./chunk-ZANOMSLW.js";
|
|
108
108
|
import {
|
|
109
109
|
realTmux,
|
|
110
110
|
runFile
|
package/dist/cli/index.js
CHANGED
|
@@ -18,7 +18,7 @@ async function hookMain(args) {
|
|
|
18
18
|
try {
|
|
19
19
|
const chunks = [];
|
|
20
20
|
for await (const c of process.stdin) chunks.push(c);
|
|
21
|
-
const { runHook } = await import("./hook-
|
|
21
|
+
const { runHook } = await import("./hook-PRPEB3T7.js");
|
|
22
22
|
const out = await runHook(event, Buffer.concat(chunks).toString("utf8"), { tool });
|
|
23
23
|
if (out) process.stdout.write(`${out}
|
|
24
24
|
`);
|
|
@@ -30,7 +30,7 @@ async function main(argv) {
|
|
|
30
30
|
const [cmd, ...rest] = argv;
|
|
31
31
|
switch (cmd) {
|
|
32
32
|
case "daemon": {
|
|
33
|
-
const { startDaemon } = await import("./daemon-
|
|
33
|
+
const { startDaemon } = await import("./daemon-TBYCUHC3.js");
|
|
34
34
|
await startDaemon();
|
|
35
35
|
await new Promise(() => {
|
|
36
36
|
});
|
|
@@ -38,17 +38,17 @@ async function main(argv) {
|
|
|
38
38
|
}
|
|
39
39
|
case "--version":
|
|
40
40
|
case "version": {
|
|
41
|
-
const { LEDGE_VERSION } = await import("./version-
|
|
41
|
+
const { LEDGE_VERSION } = await import("./version-5BLH6OA6.js");
|
|
42
42
|
console.log(LEDGE_VERSION);
|
|
43
43
|
return 0;
|
|
44
44
|
}
|
|
45
45
|
case "setup": {
|
|
46
|
-
const { runSetup } = await import("./setup-
|
|
46
|
+
const { runSetup } = await import("./setup-LWYZEVNE.js");
|
|
47
47
|
for (const l of await runSetup()) console.log(l);
|
|
48
48
|
return 0;
|
|
49
49
|
}
|
|
50
50
|
case "uninstall": {
|
|
51
|
-
const { runUninstall } = await import("./uninstall-
|
|
51
|
+
const { runUninstall } = await import("./uninstall-TFC4LALT.js");
|
|
52
52
|
const r = await runUninstall({ purge: rest.includes("--purge") });
|
|
53
53
|
for (const l of r.lines) console.log(l);
|
|
54
54
|
return r.ok ? 0 : 1;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
BIN_MARK,
|
|
2
3
|
CLAUDE_EVENTS,
|
|
3
4
|
CODEX_EVENTS,
|
|
4
5
|
CURSOR_EVENTS,
|
|
@@ -9,15 +10,16 @@ import {
|
|
|
9
10
|
mergeClaudeHooks,
|
|
10
11
|
mergeCodexHooks,
|
|
11
12
|
mergeCursorHooks,
|
|
12
|
-
relinkBin,
|
|
13
13
|
runSetup,
|
|
14
14
|
stopDetachedDaemon,
|
|
15
|
-
systemdUnit
|
|
16
|
-
|
|
15
|
+
systemdUnit,
|
|
16
|
+
writeBin
|
|
17
|
+
} from "./chunk-ZANOMSLW.js";
|
|
17
18
|
import "./chunk-2H5FJH4Q.js";
|
|
18
19
|
import "./chunk-BNKPXSUY.js";
|
|
19
20
|
import "./chunk-R5462SQM.js";
|
|
20
21
|
export {
|
|
22
|
+
BIN_MARK,
|
|
21
23
|
CLAUDE_EVENTS,
|
|
22
24
|
CODEX_EVENTS,
|
|
23
25
|
CURSOR_EVENTS,
|
|
@@ -28,8 +30,8 @@ export {
|
|
|
28
30
|
mergeClaudeHooks,
|
|
29
31
|
mergeCodexHooks,
|
|
30
32
|
mergeCursorHooks,
|
|
31
|
-
relinkBin,
|
|
32
33
|
runSetup,
|
|
33
34
|
stopDetachedDaemon,
|
|
34
|
-
systemdUnit
|
|
35
|
+
systemdUnit,
|
|
36
|
+
writeBin
|
|
35
37
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
+
BIN_MARK,
|
|
2
3
|
SERVICE,
|
|
3
4
|
execRunner,
|
|
4
5
|
stopDetachedDaemon
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZANOMSLW.js";
|
|
6
7
|
import "./chunk-2H5FJH4Q.js";
|
|
7
8
|
import "./chunk-BNKPXSUY.js";
|
|
8
9
|
import {
|
|
@@ -96,8 +97,8 @@ async function runningSessions(run, socket) {
|
|
|
96
97
|
}
|
|
97
98
|
async function removeLink(link, script) {
|
|
98
99
|
try {
|
|
99
|
-
|
|
100
|
-
if (await realpath(link) !== await realpath(script)) return false;
|
|
100
|
+
const st = await lstat(link);
|
|
101
|
+
if (st.isFile() ? !(await readFile(link, "utf8")).startsWith(BIN_MARK) : !st.isSymbolicLink() || await realpath(link) !== await realpath(script)) return false;
|
|
101
102
|
} catch {
|
|
102
103
|
return false;
|
|
103
104
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paradow/ledge",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "Follow and answer your WSL agent sessions (Claude Code, Codex, Cursor Agent) from the Ledge island on Windows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": { "ledge": "dist/cli/index.js" },
|