@paradow/ledge 0.11.0 → 0.12.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-6JONTTMA.js → attach-UWADG6NX.js} +9 -9
- package/dist/cli/{bridge-ZOHP53DV.js → bridge-53ZTDSST.js} +1 -1
- package/dist/cli/chunk-A7YEHYLY.js +105 -0
- package/dist/cli/chunk-DKL4DFNI.js +6 -0
- package/dist/cli/{who-WVYFPCII.js → chunk-FRGNPUDO.js} +3 -3
- package/dist/cli/{chunk-OHKQKXYM.js → chunk-QAEH5TJB.js} +73 -2
- package/dist/cli/{chunk-JOCNCNRP.js → chunk-VY45MACP.js} +394 -338
- package/dist/cli/{chunk-TSA5LD4D.js → chunk-X34LPKG7.js} +1 -1
- package/dist/cli/{chunk-USGAFLXF.js → chunk-XIO4OIHR.js} +1 -1
- package/dist/cli/{chunk-TC3XJUJA.js → chunk-XXFUYME7.js} +9 -1
- package/dist/cli/{chunk-YV7BMEEB.js → chunk-ZEHTNFPV.js} +2 -2
- package/dist/cli/{chunk-47G477C7.js → chunk-ZNRXLDSH.js} +9 -9
- package/dist/cli/{daemon-YY66JWIB.js → daemon-LV7V2VP5.js} +326 -91
- package/dist/cli/{hook-7V2GJUNO.js → hook-OLZ2I4GM.js} +49 -8
- package/dist/cli/index.js +19 -12
- package/dist/cli/{launch-2IQZPPTY.js → launch-ZGICGFVU.js} +5 -5
- package/dist/cli/send-VV25P46X.js +14 -0
- package/dist/cli/{setup-SG43PPOZ.js → setup-TULNBDFB.js} +2 -2
- package/dist/cli/{status-QOXWNLLF.js → status-LLITXNOW.js} +2 -2
- package/dist/cli/{uninstall-SD736TBP.js → uninstall-OFSLCP4T.js} +2 -2
- package/dist/cli/{version-QSIZ47AV.js → version-BGEWDJNC.js} +1 -1
- package/dist/cli/who-K6VK7EXV.js +11 -0
- package/package.json +1 -1
- package/dist/cli/chunk-RVEGELWF.js +0 -6
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
attachEnv
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-TSA5LD4D.js";
|
|
5
|
-
import {
|
|
6
|
-
realTmux
|
|
7
|
-
} from "./chunk-YV7BMEEB.js";
|
|
8
|
-
import "./chunk-YUSY3E47.js";
|
|
3
|
+
} from "./chunk-ZNRXLDSH.js";
|
|
9
4
|
import {
|
|
10
5
|
connect
|
|
11
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-XXFUYME7.js";
|
|
7
|
+
import "./chunk-X34LPKG7.js";
|
|
12
8
|
import {
|
|
13
9
|
PROTOCOL_PORT
|
|
14
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-VY45MACP.js";
|
|
11
|
+
import {
|
|
12
|
+
realTmux
|
|
13
|
+
} from "./chunk-ZEHTNFPV.js";
|
|
14
|
+
import "./chunk-YUSY3E47.js";
|
|
15
15
|
import "./chunk-FGOFRWIB.js";
|
|
16
16
|
|
|
17
17
|
// src/cli/attach.ts
|
|
@@ -47,7 +47,7 @@ async function attach(id, opts = {}) {
|
|
|
47
47
|
if (process.stdout.isTTY) process.stdout.write(s);
|
|
48
48
|
});
|
|
49
49
|
write(`\x1B]0;${title}\x07`);
|
|
50
|
-
return (opts.attach ?? attachInherit)(tmux.attachArgs(tmuxName), attachEnv(process.env));
|
|
50
|
+
return (opts.attach ?? attachInherit)(tmux.attachArgs(tmuxName, { readOnly: opts.readOnly }), attachEnv(process.env));
|
|
51
51
|
}
|
|
52
52
|
export {
|
|
53
53
|
attach
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {
|
|
2
|
+
connect,
|
|
3
|
+
sendMessageAndAwaitAck
|
|
4
|
+
} from "./chunk-XXFUYME7.js";
|
|
5
|
+
import {
|
|
6
|
+
CODEX_HOOK_VAR
|
|
7
|
+
} from "./chunk-X34LPKG7.js";
|
|
8
|
+
|
|
9
|
+
// src/cli/send.ts
|
|
10
|
+
import { randomUUID } from "crypto";
|
|
11
|
+
var USAGE = 'ledge send <nom> "<message>" [--summary <texte>] write to another Ledge session';
|
|
12
|
+
var SEND_MS = 4e3;
|
|
13
|
+
function sendUsage(args) {
|
|
14
|
+
const positional = [];
|
|
15
|
+
let summary;
|
|
16
|
+
for (let i = 0; i < args.length; i++) {
|
|
17
|
+
if (args[i] === "--summary") {
|
|
18
|
+
summary = args[++i];
|
|
19
|
+
if (summary === void 0) return void 0;
|
|
20
|
+
} else positional.push(args[i]);
|
|
21
|
+
}
|
|
22
|
+
const [to, text] = positional;
|
|
23
|
+
if (positional.length !== 2 || !to || !text) return void 0;
|
|
24
|
+
return { to, text, ...summary ? { summary } : {} };
|
|
25
|
+
}
|
|
26
|
+
async function sendOnClient(client, args, from, ms) {
|
|
27
|
+
const parsed = sendUsage(args);
|
|
28
|
+
if (!parsed) return { line: USAGE, code: 1 };
|
|
29
|
+
const { to, text, summary } = parsed;
|
|
30
|
+
let sent = false;
|
|
31
|
+
try {
|
|
32
|
+
const pending = sendMessageAndAwaitAck(client, { type: "session.message", rid: randomUUID(), from, to, text, ...summary ? { summary } : {} }, ms);
|
|
33
|
+
sent = true;
|
|
34
|
+
const ack = await pending;
|
|
35
|
+
if (ack.outcome === "accepted") return { line: `remis \xE0 ${to}`, code: 0 };
|
|
36
|
+
if (ack.outcome === "queued") return { line: `${to} est occup\xE9 : il le recevra d\xE8s qu'il sera libre`, code: 0 };
|
|
37
|
+
return { line: `refus\xE9 : ${ack.reason ?? "raison inconnue"}`, code: 1 };
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return { line: sent ? "pas de r\xE9ponse de Ledge : le message est peut-\xEAtre d\xE9j\xE0 parti" : `refus\xE9 : ${e.message}`, code: 1 };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function sendMessage(args, opts = {}) {
|
|
43
|
+
if (!sendUsage(args)) return { line: USAGE, code: 1 };
|
|
44
|
+
const env = opts.env ?? process.env;
|
|
45
|
+
const from = opts.from ?? (env[CODEX_HOOK_VAR] || env.LEDGE_SESSION);
|
|
46
|
+
if (!from) return { line: "ledge send ne marche que dans une session lanc\xE9e par Ledge", code: 1 };
|
|
47
|
+
const deadline = Date.now() + (opts.timeoutMs ?? SEND_MS);
|
|
48
|
+
const left = () => Math.max(0, deadline - Date.now());
|
|
49
|
+
let client;
|
|
50
|
+
try {
|
|
51
|
+
client = await connect({ port: opts.port, timeoutMs: left() });
|
|
52
|
+
} catch {
|
|
53
|
+
return { line: "daemon not running (start a session with `ledge claude`, or run `ledge daemon`)", code: 1 };
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return await sendOnClient(client, args, from, left());
|
|
57
|
+
} finally {
|
|
58
|
+
await client.close();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function parseLedgeCommand(command) {
|
|
62
|
+
command = command.replace(/[\r\n\s]+$/, "");
|
|
63
|
+
const words = [];
|
|
64
|
+
let word = "";
|
|
65
|
+
let inWord = false;
|
|
66
|
+
let quote;
|
|
67
|
+
for (let i = 0; i < command.length; i++) {
|
|
68
|
+
const c = command[i];
|
|
69
|
+
if (quote === "'") {
|
|
70
|
+
if (c === "'") quote = void 0;
|
|
71
|
+
else word += c;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (quote === '"') {
|
|
75
|
+
if (c === '"') quote = void 0;
|
|
76
|
+
else if (c === "$" || c === "`") return void 0;
|
|
77
|
+
else if (c === "\\" && '"\\$`'.includes(command[i + 1] ?? "")) word += command[++i];
|
|
78
|
+
else word += c;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (c === "\n" || c === "\r") return void 0;
|
|
82
|
+
if (c === " " || c === " ") {
|
|
83
|
+
if (inWord) words.push(word);
|
|
84
|
+
word = "";
|
|
85
|
+
inWord = false;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (";&|<>()$`\\*?[~{".includes(c) || c === "#" && !inWord) return void 0;
|
|
89
|
+
inWord = true;
|
|
90
|
+
if (c === '"' || c === "'") quote = c;
|
|
91
|
+
else word += c;
|
|
92
|
+
}
|
|
93
|
+
if (quote) return void 0;
|
|
94
|
+
if (inWord) words.push(word);
|
|
95
|
+
if (words[0] !== "ledge") return void 0;
|
|
96
|
+
if (words[1] === "who" && words.length === 2) return { cmd: "who" };
|
|
97
|
+
if (words[1] === "send") return { cmd: "send", args: words.slice(2) };
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
sendOnClient,
|
|
103
|
+
sendMessage,
|
|
104
|
+
parseLedgeCommand
|
|
105
|
+
};
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
connect
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XXFUYME7.js";
|
|
4
4
|
import {
|
|
5
5
|
isArchived
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-FGOFRWIB.js";
|
|
6
|
+
} from "./chunk-VY45MACP.js";
|
|
8
7
|
|
|
9
8
|
// src/cli/who.ts
|
|
10
9
|
import { basename } from "path";
|
|
@@ -37,6 +36,7 @@ async function printWho(opts = {}) {
|
|
|
37
36
|
await client.close();
|
|
38
37
|
}
|
|
39
38
|
}
|
|
39
|
+
|
|
40
40
|
export {
|
|
41
41
|
formatWho,
|
|
42
42
|
printWho
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
cleanTopic,
|
|
3
3
|
sessionTitle
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VY45MACP.js";
|
|
5
5
|
|
|
6
6
|
// src/daemon/crossSession.ts
|
|
7
7
|
var FIRST_LINE = /^Another Claude session sent a message:\s*\n/;
|
|
@@ -19,6 +19,28 @@ function crossSession(text) {
|
|
|
19
19
|
}
|
|
20
20
|
return out.length ? out : void 0;
|
|
21
21
|
}
|
|
22
|
+
var LEDGE_CLOSE = "</ledge-message>";
|
|
23
|
+
var LEDGE_BLOCK = /^<ledge-message\b([^>]*)>([\s\S]*?)(?:<\/ledge-message>|$)/;
|
|
24
|
+
var note = (from, to) => `(message de la session ${from}, pas de l'utilisateur ; pour r\xE9pondre : ${to === "codex" ? `ledge send ${from} "\u2026"` : `SendMessage vers ${from}`})`;
|
|
25
|
+
var ESCAPES = /\x1b(?:\[[0-?]*[ -/]*[@-~]|[\]PX^_][^\x07\x1b]*(?:\x07|\x1b\\)|[ -/]*[0-~])/g;
|
|
26
|
+
var CONTROLS = /[\x00-\x1f\x7f-\x9f]/g;
|
|
27
|
+
var typable = (text, newline) => text.replace(ESCAPES, "").replace(/\r\n|\r|\n/g, newline).replace(CONTROLS, " ");
|
|
28
|
+
var ledgeBody = (text) => typable(text.replaceAll("<", "\u2039"), " \u23CE ");
|
|
29
|
+
function formatLedgeMessage(m) {
|
|
30
|
+
const from = typable(m.from, " ").replace(/["<>]/g, "").trim();
|
|
31
|
+
return `<ledge-message from="${from}" tool="${m.tool}">${note(from, m.to)} ${ledgeBody(m.text)}${LEDGE_CLOSE}`;
|
|
32
|
+
}
|
|
33
|
+
function ledgeMessages(text) {
|
|
34
|
+
const m = LEDGE_BLOCK.exec(text.trimStart());
|
|
35
|
+
if (!m) return void 0;
|
|
36
|
+
const attrs = new Map([...m[1].matchAll(ATTR)].map((a) => [a[1], a[2]]));
|
|
37
|
+
const from = attrs.get("from");
|
|
38
|
+
const tool = attrs.get("tool");
|
|
39
|
+
if (!from) return void 0;
|
|
40
|
+
const body = m[2].trimStart();
|
|
41
|
+
const lead = [note(from, "codex"), note(from, "claude")].find((n) => body.startsWith(n));
|
|
42
|
+
return [{ from, ...tool ? { tool } : {}, text: (lead ? body.slice(lead.length) : body).trim() }];
|
|
43
|
+
}
|
|
22
44
|
var NOTICE_HEAD = /^\[Cross-session ([\w -]+?) notice\]/;
|
|
23
45
|
var NOTICE_SPLIT = /\n\s*(?=\[Cross-session [\w -]+? notice\])/;
|
|
24
46
|
var DELIVERY = [
|
|
@@ -338,8 +360,54 @@ async function gitSummary(cwd, exec = execGit) {
|
|
|
338
360
|
return [`# git status --short --branch`, status.stdout.trimEnd(), "", "# git log --oneline -10", log.stdout.trimEnd(), "", "# git diff --stat", diff.stdout.trimEnd() || "(clean)"].join("\n");
|
|
339
361
|
}
|
|
340
362
|
|
|
363
|
+
// src/daemon/peers.ts
|
|
364
|
+
import { readdirSync, readFileSync } from "fs";
|
|
365
|
+
import { homedir } from "os";
|
|
366
|
+
import { basename as basename2, join } from "path";
|
|
367
|
+
var defaultDir = () => join(process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude"), "sessions");
|
|
368
|
+
function parsePeer(path) {
|
|
369
|
+
try {
|
|
370
|
+
const j = JSON.parse(readFileSync(path, "utf8"));
|
|
371
|
+
if (typeof j.pid !== "number" || typeof j.sessionId !== "string" || typeof j.name !== "string" || typeof j.messagingSocketPath !== "string") return void 0;
|
|
372
|
+
return { pid: j.pid, sessionId: j.sessionId, name: j.name, address: `uds:${j.messagingSocketPath}` };
|
|
373
|
+
} catch {
|
|
374
|
+
return void 0;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function readPeers(dir = defaultDir()) {
|
|
378
|
+
let names;
|
|
379
|
+
try {
|
|
380
|
+
names = readdirSync(dir);
|
|
381
|
+
} catch {
|
|
382
|
+
return [];
|
|
383
|
+
}
|
|
384
|
+
return names.filter((n) => n.endsWith(".json")).flatMap((n) => parsePeer(join(dir, n)) ?? []);
|
|
385
|
+
}
|
|
386
|
+
var bareName = (name) => name.replace(/ \[[^\]]*\]$/, "");
|
|
387
|
+
function peerFor(target, peers, sessions, dir = defaultDir()) {
|
|
388
|
+
const name = bareName(target);
|
|
389
|
+
const found = peers.filter((p) => p.name === name || p.address === name);
|
|
390
|
+
if (!found.length && name.startsWith("uds:") && name.endsWith(".sock")) {
|
|
391
|
+
const pid = basename2(name, ".sock");
|
|
392
|
+
const direct = /^\d+$/.test(pid) ? parsePeer(join(dir, `${pid}.json`)) : void 0;
|
|
393
|
+
if (direct) found.push(direct);
|
|
394
|
+
}
|
|
395
|
+
for (const { sessionId: id } of found) {
|
|
396
|
+
const s = sessions.find((s2) => s2.agentSessionId === id || s2.transcriptPath?.endsWith(`/${id}.jsonl`));
|
|
397
|
+
if (s) return s;
|
|
398
|
+
}
|
|
399
|
+
return void 0;
|
|
400
|
+
}
|
|
401
|
+
function ledgePeer(target, peers, sessions) {
|
|
402
|
+
const name = bareName(target);
|
|
403
|
+
return peerFor(target, peers, sessions) ?? sessions.find((s) => s.name === name && s.state !== "exited");
|
|
404
|
+
}
|
|
405
|
+
|
|
341
406
|
export {
|
|
342
407
|
crossSession,
|
|
408
|
+
ledgeBody,
|
|
409
|
+
formatLedgeMessage,
|
|
410
|
+
ledgeMessages,
|
|
343
411
|
crossSessionNotices,
|
|
344
412
|
QUESTION_TOOLS,
|
|
345
413
|
pendingTool,
|
|
@@ -347,5 +415,8 @@ export {
|
|
|
347
415
|
reduce,
|
|
348
416
|
repositoryId,
|
|
349
417
|
gitInfo,
|
|
350
|
-
gitSummary
|
|
418
|
+
gitSummary,
|
|
419
|
+
readPeers,
|
|
420
|
+
bareName,
|
|
421
|
+
ledgePeer
|
|
351
422
|
};
|