@paradow/ledge 0.34.0 → 0.36.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/index.js +40782 -39
- package/dist/cli/reduceWorker.js +2216 -0
- package/package.json +4 -1
- package/dist/cli/action-K42H5BDA.js +0 -27
- package/dist/cli/attach-KEE4A5YP.js +0 -87
- package/dist/cli/bridge-X327AZY4.js +0 -70
- package/dist/cli/chunk-25RVT5MR.js +0 -623
- package/dist/cli/chunk-3OJCWJAA.js +0 -144
- package/dist/cli/chunk-4OYVM4MT.js +0 -49
- package/dist/cli/chunk-4TCSADFR.js +0 -252
- package/dist/cli/chunk-53ISXX2R.js +0 -39
- package/dist/cli/chunk-54KF5RBY.js +0 -17
- package/dist/cli/chunk-7NHQGP6J.js +0 -95
- package/dist/cli/chunk-G7XPEVCG.js +0 -118
- package/dist/cli/chunk-IWY7DSSO.js +0 -2476
- package/dist/cli/chunk-KPDTYX7F.js +0 -60
- package/dist/cli/chunk-OLLQBK6N.js +0 -41
- package/dist/cli/chunk-RPH773TY.js +0 -6
- package/dist/cli/chunk-RX5CJ7YH.js +0 -43
- package/dist/cli/chunk-T547FLQN.js +0 -128
- package/dist/cli/chunk-U67V476Y.js +0 -35
- package/dist/cli/chunk-UJ2WBGLW.js +0 -305
- package/dist/cli/chunk-ULOMKEB6.js +0 -85
- package/dist/cli/chunk-V4XKKL6K.js +0 -205
- package/dist/cli/daemon-RO43S3S5.js +0 -11449
- package/dist/cli/decide-KZFYL3HY.js +0 -59
- package/dist/cli/handover-BWNMI2RA.js +0 -17
- package/dist/cli/hook-ZLWXI5EQ.js +0 -473
- package/dist/cli/launch-GCX7TXOH.js +0 -28
- package/dist/cli/send-SXZAPVRH.js +0 -19
- package/dist/cli/setup-TJ32FMVJ.js +0 -38
- package/dist/cli/status-YDVRES4E.js +0 -28
- package/dist/cli/task-TUSOVJK7.js +0 -19
- package/dist/cli/tui-TCVIRZCQ.js +0 -19348
- package/dist/cli/uninstall-QO7YMSLA.js +0 -162
- package/dist/cli/version-BAYSYWFP.js +0 -7
- package/dist/cli/who-6AZYSP7T.js +0 -12
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CODEX_HOOK_VAR
|
|
3
|
-
} from "./chunk-4TCSADFR.js";
|
|
4
|
-
import {
|
|
5
|
-
connect
|
|
6
|
-
} from "./chunk-7NHQGP6J.js";
|
|
7
|
-
import {
|
|
8
|
-
SESSION_KEY_VAR,
|
|
9
|
-
actionById
|
|
10
|
-
} from "./chunk-IWY7DSSO.js";
|
|
11
|
-
|
|
12
|
-
// src/cli/action.ts
|
|
13
|
-
import { randomUUID } from "crypto";
|
|
14
|
-
var CLOSE_USAGE = "ledge close <session>\u2026 [--json] fermer proprement des sessions (leur conversation reste reprenable)";
|
|
15
|
-
var DAEMON_DOWN = "daemon not running (start a session with `ledge claude`, or run `ledge daemon`)";
|
|
16
|
-
var RUN_MS = 6e4;
|
|
17
|
-
function actionArgs(id, args) {
|
|
18
|
-
const parsed = actionById(id).args.safeParse(args);
|
|
19
|
-
return parsed.success ? args : void 0;
|
|
20
|
-
}
|
|
21
|
-
function callerOf(env) {
|
|
22
|
-
const by = env[CODEX_HOOK_VAR] || env.LEDGE_SESSION;
|
|
23
|
-
const key = env[SESSION_KEY_VAR];
|
|
24
|
-
return by ? { by, ...key ? { key } : {} } : void 0;
|
|
25
|
-
}
|
|
26
|
-
async function runOnClient(client, action, args, opts = {}) {
|
|
27
|
-
const rid = randomUUID();
|
|
28
|
-
const by = opts.by;
|
|
29
|
-
client.send({ type: "action.run", rid, action, args, ...by ? { by: by.by, ...by.key ? { key: by.key } : {} } : {}, ...opts.confirmed ? { confirmed: true } : {} });
|
|
30
|
-
const deadline = Date.now() + (opts.ms ?? RUN_MS);
|
|
31
|
-
for (; ; ) {
|
|
32
|
-
const { type: _t, rid: got, ...r } = await client.next("action.result", Math.max(0, deadline - Date.now()));
|
|
33
|
-
if (got === rid) return r;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
var plain = (s) => s.replace(/\*\*/g, "");
|
|
37
|
-
function resultLine(r, json = false) {
|
|
38
|
-
const ok = r.reason === void 0;
|
|
39
|
-
if (json) return { line: JSON.stringify({ ok, ...r }), code: ok ? 0 : 1 };
|
|
40
|
-
if (ok) return { line: `fait : ${plain(r.row?.summary ?? r.action)}`, code: 0 };
|
|
41
|
-
if (r.code === "confirm_required") return { line: `${plain(r.row?.summary ?? r.action)} : irr\xE9versible ; relance avec --yes pour confirmer`, code: 1 };
|
|
42
|
-
return { line: `${r.code === "failed" ? "\xE9chec" : "refus\xE9"} : ${r.reason}`, code: 1 };
|
|
43
|
-
}
|
|
44
|
-
function closeArgs(args) {
|
|
45
|
-
const json = args.includes("--json");
|
|
46
|
-
const sessions = args.filter((a) => a !== "--json");
|
|
47
|
-
if (!sessions.length || sessions.some((s) => s.startsWith("--") || !actionArgs("session.close", { session: s }))) return void 0;
|
|
48
|
-
return { sessions, ...json ? { json: true } : {} };
|
|
49
|
-
}
|
|
50
|
-
async function runClose(args, opts = {}) {
|
|
51
|
-
const parsed = closeArgs(args);
|
|
52
|
-
if (!parsed) return { line: CLOSE_USAGE, code: args.length ? 1 : 0 };
|
|
53
|
-
let client;
|
|
54
|
-
try {
|
|
55
|
-
client = await connect({ port: opts.port, timeoutMs: 1500 });
|
|
56
|
-
} catch {
|
|
57
|
-
return { line: DAEMON_DOWN, code: 1 };
|
|
58
|
-
}
|
|
59
|
-
const by = callerOf(opts.env ?? process.env);
|
|
60
|
-
const lines = [];
|
|
61
|
-
try {
|
|
62
|
-
for (const session of parsed.sessions) {
|
|
63
|
-
const r = resultLine(await runOnClient(client, "session.close", { session }, { by }), parsed.json);
|
|
64
|
-
lines.push(r.line);
|
|
65
|
-
if (r.code) return { line: lines.join("\n"), code: 1 };
|
|
66
|
-
}
|
|
67
|
-
return { line: lines.join("\n"), code: 0 };
|
|
68
|
-
} catch (e) {
|
|
69
|
-
return { line: [...lines, `pas de r\xE9ponse de Ledge : ${e.message}`].join("\n"), code: 1 };
|
|
70
|
-
} finally {
|
|
71
|
-
await client.close();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export {
|
|
76
|
-
CLOSE_USAGE,
|
|
77
|
-
DAEMON_DOWN,
|
|
78
|
-
RUN_MS,
|
|
79
|
-
actionArgs,
|
|
80
|
-
callerOf,
|
|
81
|
-
runOnClient,
|
|
82
|
-
resultLine,
|
|
83
|
-
closeArgs,
|
|
84
|
-
runClose
|
|
85
|
-
};
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
exitFile,
|
|
3
|
-
spawnSession
|
|
4
|
-
} from "./chunk-4TCSADFR.js";
|
|
5
|
-
import {
|
|
6
|
-
DaemonRefusedError,
|
|
7
|
-
connect
|
|
8
|
-
} from "./chunk-7NHQGP6J.js";
|
|
9
|
-
import {
|
|
10
|
-
PROTOCOL_PORT,
|
|
11
|
-
namesInUse
|
|
12
|
-
} from "./chunk-IWY7DSSO.js";
|
|
13
|
-
import {
|
|
14
|
-
realTmux
|
|
15
|
-
} from "./chunk-T547FLQN.js";
|
|
16
|
-
import {
|
|
17
|
-
log
|
|
18
|
-
} from "./chunk-54KF5RBY.js";
|
|
19
|
-
import {
|
|
20
|
-
ensureLedgeDir
|
|
21
|
-
} from "./chunk-53ISXX2R.js";
|
|
22
|
-
|
|
23
|
-
// src/cli/launch.ts
|
|
24
|
-
import { spawn } from "child_process";
|
|
25
|
-
import { readFile, rm } from "fs/promises";
|
|
26
|
-
import { createConnection } from "net";
|
|
27
|
-
var TMUX_SOCKET = "ledge";
|
|
28
|
-
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
29
|
-
function parseLaunchArgs(argv) {
|
|
30
|
-
const args = [];
|
|
31
|
-
let name;
|
|
32
|
-
for (let i = 0; i < argv.length; i++) {
|
|
33
|
-
const a = argv[i];
|
|
34
|
-
if (a === "--name") {
|
|
35
|
-
name = argv[++i];
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (a === "--") {
|
|
39
|
-
args.push(...argv.slice(i + 1));
|
|
40
|
-
break;
|
|
41
|
-
}
|
|
42
|
-
args.push(a);
|
|
43
|
-
}
|
|
44
|
-
return { name, args };
|
|
45
|
-
}
|
|
46
|
-
function portOpen(port) {
|
|
47
|
-
return new Promise((res) => {
|
|
48
|
-
const s = createConnection({ host: "127.0.0.1", port });
|
|
49
|
-
s.once("connect", () => {
|
|
50
|
-
s.end();
|
|
51
|
-
res(true);
|
|
52
|
-
});
|
|
53
|
-
s.once("error", () => res(false));
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
function spawnDetachedDaemon() {
|
|
57
|
-
const child = spawn(process.execPath, [process.argv[1], "daemon"], { detached: true, stdio: "ignore" });
|
|
58
|
-
child.unref();
|
|
59
|
-
}
|
|
60
|
-
var closeQuietly = (c) => Promise.race([c.close(), sleep(500)]);
|
|
61
|
-
async function probe(port, timeoutMs) {
|
|
62
|
-
let client;
|
|
63
|
-
try {
|
|
64
|
-
client = await connect({ port, timeoutMs });
|
|
65
|
-
} catch (e) {
|
|
66
|
-
if (e instanceof DaemonRefusedError) return { client: null, state: "unauthorized" };
|
|
67
|
-
if (e.message === "connect timeout") return { client: null, state: "timeout" };
|
|
68
|
-
return { client: null, state: await portOpen(port) ? "foreign" : "refused" };
|
|
69
|
-
}
|
|
70
|
-
try {
|
|
71
|
-
await client.next("snapshot", timeoutMs);
|
|
72
|
-
return { client };
|
|
73
|
-
} catch {
|
|
74
|
-
void closeQuietly(client);
|
|
75
|
-
return { client: null, state: "foreign" };
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
async function ensureDaemon(opts = {}) {
|
|
79
|
-
await ensureLedgeDir();
|
|
80
|
-
const port = opts.port ?? PROTOCOL_PORT;
|
|
81
|
-
const timeoutMs = opts.timeoutMs ?? 2e3;
|
|
82
|
-
const first = await probe(port, timeoutMs);
|
|
83
|
-
if (first.client) return { client: first.client };
|
|
84
|
-
if (first.state === "foreign") {
|
|
85
|
-
log(`port ${port} is open but does not speak the Ledge protocol`);
|
|
86
|
-
return { client: null, reason: "foreign" };
|
|
87
|
-
}
|
|
88
|
-
if (first.state === "unauthorized") {
|
|
89
|
-
log(`the daemon on port ${port} refused this client's token`);
|
|
90
|
-
return { client: null, reason: "unauthorized" };
|
|
91
|
-
}
|
|
92
|
-
if (first.state === "timeout") {
|
|
93
|
-
log(`port ${port} accepted a connection but did not answer within ${timeoutMs} ms`);
|
|
94
|
-
return { client: null, reason: "unresponsive" };
|
|
95
|
-
}
|
|
96
|
-
(opts.spawnDaemon ?? spawnDetachedDaemon)();
|
|
97
|
-
for (let i = 0; i < 3; i++) {
|
|
98
|
-
await sleep(opts.retryDelayMs ?? 500);
|
|
99
|
-
const r = await probe(port, timeoutMs);
|
|
100
|
-
if (r.client) return { client: r.client };
|
|
101
|
-
}
|
|
102
|
-
return { client: null, reason: "unreachable" };
|
|
103
|
-
}
|
|
104
|
-
var DaemonError = class extends Error {
|
|
105
|
-
};
|
|
106
|
-
async function createSession(opts) {
|
|
107
|
-
const names = [];
|
|
108
|
-
const ids = [];
|
|
109
|
-
if (opts.client) {
|
|
110
|
-
let snap;
|
|
111
|
-
try {
|
|
112
|
-
opts.client.send({ type: "snapshot.request" });
|
|
113
|
-
snap = await opts.client.next("snapshot");
|
|
114
|
-
} catch (e) {
|
|
115
|
-
throw new DaemonError(`daemon did not answer: ${e.message}`);
|
|
116
|
-
}
|
|
117
|
-
names.push(...namesInUse(snap.sessions));
|
|
118
|
-
for (const s of snap.sessions) ids.push(s.id);
|
|
119
|
-
}
|
|
120
|
-
const session = await spawnSession({ ...opts, taken: { names, ids } });
|
|
121
|
-
try {
|
|
122
|
-
opts.client?.send({ type: "session.register", session });
|
|
123
|
-
} catch (e) {
|
|
124
|
-
log(`register failed for ${session.id}: ${e.message}`);
|
|
125
|
-
}
|
|
126
|
-
return session;
|
|
127
|
-
}
|
|
128
|
-
function attachEnv(env) {
|
|
129
|
-
const { TMUX: _ignored, ...rest } = env;
|
|
130
|
-
return rest;
|
|
131
|
-
}
|
|
132
|
-
function attachInherit(args, env) {
|
|
133
|
-
return new Promise((resolve) => {
|
|
134
|
-
const child = spawn("tmux", args, { stdio: "inherit", env });
|
|
135
|
-
child.on("error", () => resolve(1));
|
|
136
|
-
child.on("exit", (c) => resolve(c ?? 1));
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
async function readExitCode(id) {
|
|
140
|
-
const file = exitFile(id);
|
|
141
|
-
let raw = null;
|
|
142
|
-
try {
|
|
143
|
-
raw = await readFile(file, "utf8");
|
|
144
|
-
} catch {
|
|
145
|
-
}
|
|
146
|
-
await rm(file, { force: true });
|
|
147
|
-
const t = raw?.trim();
|
|
148
|
-
return t && /^\d+$/.test(t) ? Number(t) : null;
|
|
149
|
-
}
|
|
150
|
-
var UNTRACKED = {
|
|
151
|
-
foreign: (port) => `ledge: port ${port} is used by another program, session not tracked`,
|
|
152
|
-
unauthorized: () => "ledge: the daemon refused this client's token (missing or changed), session not tracked; restart the daemon: systemctl --user restart ledge-daemon",
|
|
153
|
-
unresponsive: () => "ledge: daemon not responding, session not tracked",
|
|
154
|
-
unreachable: () => "ledge: daemon not running, session not tracked"
|
|
155
|
-
};
|
|
156
|
-
async function launch(tool, argv, opts = {}) {
|
|
157
|
-
await ensureLedgeDir();
|
|
158
|
-
const { name, args } = parseLaunchArgs(argv);
|
|
159
|
-
const port = opts.port ?? PROTOCOL_PORT;
|
|
160
|
-
const socket = opts.tmuxSocket ?? TMUX_SOCKET;
|
|
161
|
-
const tmux = realTmux(socket);
|
|
162
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
163
|
-
const daemon = await ensureDaemon({ port, spawnDaemon: opts.spawnDaemon, retryDelayMs: opts.retryDelayMs });
|
|
164
|
-
let client = daemon.client;
|
|
165
|
-
if (!daemon.client) console.error(UNTRACKED[daemon.reason](port));
|
|
166
|
-
const create = (c) => createSession({ tool, args, command: opts.command, recordExit: true, name, cwd, tmux, client: c });
|
|
167
|
-
let session;
|
|
168
|
-
try {
|
|
169
|
-
session = await create(client);
|
|
170
|
-
} catch (e) {
|
|
171
|
-
if (!(e instanceof DaemonError)) throw e;
|
|
172
|
-
log(e.message);
|
|
173
|
-
if (client) void closeQuietly(client);
|
|
174
|
-
client = null;
|
|
175
|
-
console.error(UNTRACKED.unresponsive(port));
|
|
176
|
-
session = await create(null);
|
|
177
|
-
}
|
|
178
|
-
if (client) await closeQuietly(client);
|
|
179
|
-
const tmuxName = session.tmuxSession;
|
|
180
|
-
if (process.stdout.isTTY) process.stdout.write(`\x1B]0;${session.title}\x07`);
|
|
181
|
-
const attachCode = await (opts.attach ?? attachInherit)(tmux.attachArgs(tmuxName), attachEnv(process.env));
|
|
182
|
-
if (await tmux.hasSession(tmuxName)) {
|
|
183
|
-
if (attachCode !== 0) console.error(`ledge: could not attach; ${session.name} keeps running in tmux session ${tmuxName} (tmux -L ${socket} attach -t ${tmuxName})`);
|
|
184
|
-
return attachCode;
|
|
185
|
-
}
|
|
186
|
-
const exitCode = await readExitCode(session.id);
|
|
187
|
-
if (client) {
|
|
188
|
-
try {
|
|
189
|
-
const c = await connect({ port, timeoutMs: 800 });
|
|
190
|
-
c.send({ type: "session.unregister", id: session.id, exitCode });
|
|
191
|
-
await closeQuietly(c);
|
|
192
|
-
} catch {
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return exitCode ?? 1;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export {
|
|
199
|
-
parseLaunchArgs,
|
|
200
|
-
ensureDaemon,
|
|
201
|
-
DaemonError,
|
|
202
|
-
createSession,
|
|
203
|
-
attachEnv,
|
|
204
|
-
launch
|
|
205
|
-
};
|