@paradow/ledge 0.28.0 → 0.29.0-snapshot.41297455946.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 +8 -0
- package/dist/cli/{action-NQRSZEC5.js → action-MCR4BCSE.js} +5 -5
- package/dist/cli/ask-L3PSQZ2K.js +17 -0
- package/dist/cli/{attach-KXOSFUHO.js → attach-R7FT7LFH.js} +9 -14
- package/dist/cli/brain-BJTBN2ZK.js +20 -0
- package/dist/cli/{bridge-XCTXVWKH.js → bridge-YBHXFEFX.js} +2 -2
- package/dist/cli/{chunk-VKRFHDRK.js → chunk-2X56ZRK7.js} +1122 -1164
- package/dist/cli/{chunk-3NQNWGDX.js → chunk-6EW2FD2U.js} +3 -5
- package/dist/cli/chunk-DJMFCYF4.js +60 -0
- package/dist/cli/chunk-DLKJOK3B.js +6 -0
- package/dist/cli/{chunk-VMGACWNP.js → chunk-FWLT3SAN.js} +1 -1
- package/dist/cli/{chunk-6IYPRUQG.js → chunk-G3ST5WKR.js} +1 -1
- package/dist/cli/{chunk-ZI47U5CQ.js → chunk-GY35ZR5I.js} +102 -72
- package/dist/cli/{chunk-NLL2EQJ2.js → chunk-KBSXP63H.js} +22 -7
- package/dist/cli/chunk-LOXLGCGN.js +57 -0
- package/dist/cli/{chunk-LG3XFIPV.js → chunk-O4MUYFPH.js} +6 -7
- package/dist/cli/{chunk-JLUHY5AB.js → chunk-OR2NHT5O.js} +4 -4
- package/dist/cli/chunk-S6BZV7AY.js +57 -0
- package/dist/cli/{chunk-CR3LGTZQ.js → chunk-TCQ5PYIF.js} +3 -3
- package/dist/cli/{chunk-RYZ6FHYT.js → chunk-UMQGRTA4.js} +17 -39
- package/dist/cli/{chunk-3CF4FQWG.js → chunk-VQR5ME75.js} +4 -3
- package/dist/cli/{chunk-HOOHS3MX.js → chunk-W724IVDC.js} +1 -1
- package/dist/cli/{chunk-K7UEKSB2.js → chunk-WYRQZA4G.js} +2 -2
- package/dist/cli/chunk-XTDE2URL.js +365 -0
- package/dist/cli/daemon-ICC6LBCO.js +16562 -0
- package/dist/cli/{decide-OSSTI3FE.js → decide-JSY46QZC.js} +14 -16
- package/dist/cli/{handover-2G7OEWIG.js → handover-5RUSRVAD.js} +5 -5
- package/dist/cli/{hook-XY4E5H7O.js → hook-QQP3ENGA.js} +87 -81
- package/dist/cli/index.js +40 -28
- package/dist/cli/launch-RTBGUJSC.js +28 -0
- package/dist/cli/{send-RUHGKLMA.js → send-BIIH6SQW.js} +5 -5
- package/dist/cli/{setup-K42IVMHS.js → setup-OLVRJLJE.js} +4 -4
- package/dist/cli/{status-4Z2VJFID.js → status-Z3I33KZJ.js} +3 -3
- package/dist/cli/{tui-4IRYQXYU.js → tui-AIXCETOB.js} +6036 -3629
- package/dist/cli/{uninstall-4IMMR55T.js → uninstall-X65CWHIJ.js} +4 -4
- package/dist/cli/{version-L2Y7MRH6.js → version-QMHFVP3N.js} +1 -1
- package/dist/cli/who-C2VMEM35.js +12 -0
- package/package.json +21 -6
- package/dist/cli/chunk-CIQAFCX3.js +0 -118
- package/dist/cli/chunk-JHFKBKSO.js +0 -159
- package/dist/cli/chunk-OYDTU6AP.js +0 -9482
- package/dist/cli/chunk-QSRB3F5Y.js +0 -198
- package/dist/cli/chunk-UME2F7VM.js +0 -6
- package/dist/cli/daemon-ZUHJKLSL.js +0 -2322
- package/dist/cli/launch-7WOARLZM.js +0 -35
- package/dist/cli/task-2GESYTOL.js +0 -19
- package/dist/cli/team-MTSKNMAE.js +0 -205
- package/dist/cli/who-PSKHASMK.js +0 -12
|
@@ -1,31 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from "./chunk-OYDTU6AP.js";
|
|
2
|
+
realTmux
|
|
3
|
+
} from "./chunk-KBSXP63H.js";
|
|
5
4
|
import {
|
|
6
|
-
|
|
7
|
-
} from "./chunk-
|
|
5
|
+
log
|
|
6
|
+
} from "./chunk-G3ST5WKR.js";
|
|
8
7
|
import {
|
|
9
8
|
exitFile,
|
|
10
9
|
spawnSession
|
|
11
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-O4MUYFPH.js";
|
|
12
11
|
import {
|
|
13
12
|
DaemonRefusedError,
|
|
14
13
|
connect
|
|
15
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-FWLT3SAN.js";
|
|
16
15
|
import {
|
|
17
|
-
PROTOCOL_PORT
|
|
18
|
-
|
|
19
|
-
} from "./chunk-VKRFHDRK.js";
|
|
20
|
-
import {
|
|
21
|
-
realTmux
|
|
22
|
-
} from "./chunk-NLL2EQJ2.js";
|
|
23
|
-
import {
|
|
24
|
-
log
|
|
25
|
-
} from "./chunk-6IYPRUQG.js";
|
|
16
|
+
PROTOCOL_PORT
|
|
17
|
+
} from "./chunk-2X56ZRK7.js";
|
|
26
18
|
import {
|
|
27
19
|
ensureLedgeDir
|
|
28
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-W724IVDC.js";
|
|
29
21
|
|
|
30
22
|
// src/cli/launch.ts
|
|
31
23
|
import { spawn } from "child_process";
|
|
@@ -36,30 +28,19 @@ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
36
28
|
function parseLaunchArgs(argv) {
|
|
37
29
|
const args = [];
|
|
38
30
|
let name;
|
|
39
|
-
let role;
|
|
40
31
|
for (let i = 0; i < argv.length; i++) {
|
|
41
32
|
const a = argv[i];
|
|
42
33
|
if (a === "--name") {
|
|
43
34
|
name = argv[++i];
|
|
44
35
|
continue;
|
|
45
36
|
}
|
|
46
|
-
if (a === "--role") {
|
|
47
|
-
role = argv[++i];
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
37
|
if (a === "--") {
|
|
51
38
|
args.push(...argv.slice(i + 1));
|
|
52
39
|
break;
|
|
53
40
|
}
|
|
54
41
|
args.push(a);
|
|
55
42
|
}
|
|
56
|
-
return { name,
|
|
57
|
-
}
|
|
58
|
-
function roleArgs(tool, role, args) {
|
|
59
|
-
const own = role.tool === void 0 || role.tool === tool;
|
|
60
|
-
const hasModel = args.some((a) => a === "--model" || a === "-m" || a.startsWith("--model="));
|
|
61
|
-
const hasEffort = args.some((a) => a === "--effort" || a.startsWith("--effort=") || a.includes("model_reasoning_effort"));
|
|
62
|
-
return [...own && role.model && !hasModel ? MODEL_ARGS[tool](role.model) : [], ...own && role.effort && !hasEffort ? EFFORT_ARGS[tool]?.(role.effort) ?? [] : [], ...autonomyArgs(tool, role.autonomy)];
|
|
43
|
+
return { name, args };
|
|
63
44
|
}
|
|
64
45
|
function portOpen(port) {
|
|
65
46
|
return new Promise((res) => {
|
|
@@ -137,8 +118,7 @@ async function createSession(opts) {
|
|
|
137
118
|
ids.push(s.id);
|
|
138
119
|
}
|
|
139
120
|
}
|
|
140
|
-
const {
|
|
141
|
-
const session = await spawnSession({ ...rest, ...role ? { args: [...roleArgs(opts.tool, role, opts.args), ...opts.args], role: { id: role.id, label: role.label }, ...role.autonomy ? { autonomy: role.autonomy } : {} } : {}, taken: { names, ids } });
|
|
121
|
+
const session = await spawnSession({ ...opts, taken: { names, ids } });
|
|
142
122
|
try {
|
|
143
123
|
opts.client?.send({ type: "session.register", session });
|
|
144
124
|
} catch (e) {
|
|
@@ -175,21 +155,20 @@ var UNTRACKED = {
|
|
|
175
155
|
unreachable: () => "ledge: daemon not running, session not tracked"
|
|
176
156
|
};
|
|
177
157
|
async function launch(tool, argv, opts = {}) {
|
|
158
|
+
if (argv.some((a) => a === "--role" || a.startsWith("--role="))) {
|
|
159
|
+
console.error("--role a \xE9t\xE9 retir\xE9 (les \xE9quipes et r\xF4les de Ledge n'existent plus)");
|
|
160
|
+
return 1;
|
|
161
|
+
}
|
|
178
162
|
await ensureLedgeDir();
|
|
179
|
-
const { name,
|
|
163
|
+
const { name, args } = parseLaunchArgs(argv);
|
|
180
164
|
const port = opts.port ?? PROTOCOL_PORT;
|
|
181
165
|
const socket = opts.tmuxSocket ?? TMUX_SOCKET;
|
|
182
166
|
const tmux = realTmux(socket);
|
|
183
167
|
const cwd = opts.cwd ?? process.cwd();
|
|
184
|
-
const role = roleId ? findRole(roleId, cwd) : void 0;
|
|
185
|
-
if (roleId && !role) {
|
|
186
|
-
console.error(`ledge: r\xF4le inconnu : ${roleId} (aucun fichier .ledge/roles/${roleId}.md ni ~/.ledge/roles/${roleId}.md)`);
|
|
187
|
-
return 1;
|
|
188
|
-
}
|
|
189
168
|
const daemon = await ensureDaemon({ port, spawnDaemon: opts.spawnDaemon, retryDelayMs: opts.retryDelayMs });
|
|
190
169
|
let client = daemon.client;
|
|
191
170
|
if (!daemon.client) console.error(UNTRACKED[daemon.reason](port));
|
|
192
|
-
const create = (c) => createSession({ tool, args, command: opts.command, recordExit: true, name,
|
|
171
|
+
const create = (c) => createSession({ tool, args, command: opts.command, recordExit: true, name, cwd, tmux, client: c });
|
|
193
172
|
let session;
|
|
194
173
|
try {
|
|
195
174
|
session = await create(client);
|
|
@@ -223,7 +202,6 @@ async function launch(tool, argv, opts = {}) {
|
|
|
223
202
|
|
|
224
203
|
export {
|
|
225
204
|
parseLaunchArgs,
|
|
226
|
-
roleArgs,
|
|
227
205
|
ensureDaemon,
|
|
228
206
|
DaemonError,
|
|
229
207
|
createSession,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CODEX_HOOK_VAR
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-O4MUYFPH.js";
|
|
4
4
|
import {
|
|
5
5
|
connect,
|
|
6
6
|
sendMessageAndAwaitAck
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-FWLT3SAN.js";
|
|
8
8
|
|
|
9
9
|
// src/cli/send.ts
|
|
10
10
|
import { randomUUID } from "crypto";
|
|
@@ -130,8 +130,9 @@ function parseLedgeCommand(command) {
|
|
|
130
130
|
if (words[1] === "who" && words.length === 2) return { cmd: "who" };
|
|
131
131
|
if (words[1] === "send") return { cmd: "send", args: words.slice(2) };
|
|
132
132
|
if (words[1] === "read") return { cmd: "read", args: words.slice(2) };
|
|
133
|
-
if (words[1] === "
|
|
133
|
+
if (words[1] === "ask") return { cmd: "ask", args: words.slice(2) };
|
|
134
134
|
if (words[1] === "handover") return { cmd: "handover", args: words.slice(2) };
|
|
135
|
+
if (words[1] === "brain") return { cmd: "brain", args: words.slice(2) };
|
|
135
136
|
return void 0;
|
|
136
137
|
}
|
|
137
138
|
|
|
@@ -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"), mail: join(dir, "mail.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"), mail: join(dir, "mail.json"), asks: join(dir, "asks.json"), machines: join(dir, "machines.json"), projects: join(dir, "projects.json"), island: join(dir, "island.json"), token: join(dir, "token"), mobile: join(dir, "mobile.json"), mobileSession: join(dir, "mobile-session.json"), mobileKey: join(dir, "mobile-key"), devices: join(dir, "devices.json"), revocations: join(dir, "revocations.json"), attachments: join(dir, "attachments"), openrig: join(dir, "openrig.json"), ledgeThread: join(dir, "ledge-thread.jsonl"), rigs: join(dir, "rigs"), rigLaunches: join(dir, "rig-launches.json"), mandate: join(dir, "mandat.md"), memoryDir: join(dir, "memoire"), rules: join(dir, "memoire", "rules.json"), decisions: join(dir, "memoire", "decisions.jsonl"), brainSettings: join(dir, "brain.json"), brainDir: join(dir, "brain") };
|
|
21
21
|
}
|
|
22
22
|
async function ensureLedgeDir() {
|
|
23
23
|
const p = paths();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
run
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KBSXP63H.js";
|
|
4
4
|
import {
|
|
5
5
|
ensureLedgeDir,
|
|
6
6
|
paths
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-W724IVDC.js";
|
|
8
8
|
|
|
9
9
|
// src/cli/setup.ts
|
|
10
10
|
import { readFile as readFile2, writeFile, mkdir, rename, realpath, rm, stat, lstat, readlink } from "fs/promises";
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PROJECT_COLORS,
|
|
3
|
+
RIG_NAME,
|
|
4
|
+
parseServerMessage
|
|
5
|
+
} from "./chunk-2X56ZRK7.js";
|
|
6
|
+
|
|
7
|
+
// src/imageInfo.ts
|
|
8
|
+
var ok = (mime, width, height) => width > 0 && height > 0 ? { mime, width, height } : null;
|
|
9
|
+
function jpeg(b) {
|
|
10
|
+
let i = 2;
|
|
11
|
+
while (i + 4 <= b.length) {
|
|
12
|
+
if (b[i] !== 255) {
|
|
13
|
+
i++;
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const m = b[i + 1];
|
|
17
|
+
if (m === 255) {
|
|
18
|
+
i++;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (m === 216 || m === 1 || m >= 208 && m <= 215) {
|
|
22
|
+
i += 2;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const len = b.readUInt16BE(i + 2);
|
|
26
|
+
if (m >= 192 && m <= 207 && m !== 196 && m !== 200 && m !== 204) return i + 9 <= b.length ? ok("image/jpeg", b.readUInt16BE(i + 7), b.readUInt16BE(i + 5)) : null;
|
|
27
|
+
i += 2 + len;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function webp(b) {
|
|
32
|
+
const kind = b.toString("latin1", 12, 16);
|
|
33
|
+
if (kind === "VP8X") return b.length >= 30 ? ok("image/webp", b.readUIntLE(24, 3) + 1, b.readUIntLE(27, 3) + 1) : null;
|
|
34
|
+
if (kind === "VP8L") {
|
|
35
|
+
if (b.length < 25 || b[20] !== 47) return null;
|
|
36
|
+
const bits = b.readUInt32LE(21);
|
|
37
|
+
return ok("image/webp", (bits & 16383) + 1, (bits >>> 14 & 16383) + 1);
|
|
38
|
+
}
|
|
39
|
+
if (kind === "VP8 ") return b.length >= 30 && b[23] === 157 && b[24] === 1 && b[25] === 42 ? ok("image/webp", b.readUInt16LE(26) & 16383, b.readUInt16LE(28) & 16383) : null;
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
function imageInfo(b) {
|
|
43
|
+
try {
|
|
44
|
+
if (b.length >= 24 && b.readUInt32BE(0) === 2303741511 && b.readUInt32BE(4) === 218765834 && b.toString("latin1", 12, 16) === "IHDR") return ok("image/png", b.readUInt32BE(16), b.readUInt32BE(20));
|
|
45
|
+
if (b.length >= 10 && (b.toString("latin1", 0, 6) === "GIF87a" || b.toString("latin1", 0, 6) === "GIF89a")) return ok("image/gif", b.readUInt16LE(6), b.readUInt16LE(8));
|
|
46
|
+
if (b.length >= 4 && b[0] === 255 && b[1] === 216) return jpeg(b);
|
|
47
|
+
if (b.length >= 16 && b.toString("latin1", 0, 4) === "RIFF" && b.toString("latin1", 8, 12) === "WEBP") return webp(b);
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ../ui/src/rigs/model.ts
|
|
54
|
+
var rigKey = (r) => `${r.machine}/${r.id}`;
|
|
55
|
+
var COLOR_NAMES = Object.keys(PROJECT_COLORS);
|
|
56
|
+
function rigColor(r) {
|
|
57
|
+
let sum = 0;
|
|
58
|
+
for (let i = 0; i < r.name.length; i++) sum += r.name.charCodeAt(i);
|
|
59
|
+
return COLOR_NAMES[sum % COLOR_NAMES.length];
|
|
60
|
+
}
|
|
61
|
+
function kernelSplit(rig) {
|
|
62
|
+
const hidden = [], shown = [];
|
|
63
|
+
for (const s of rig.seats) (s.name.startsWith("advisor") || s.runtime === "terminal" ? hidden : shown).push(s);
|
|
64
|
+
return { shown, hidden };
|
|
65
|
+
}
|
|
66
|
+
var workingCount = (rig) => rig.seats.filter((s) => s.state === "working").length;
|
|
67
|
+
function rigPlace(rig, projects) {
|
|
68
|
+
if (rig.projectId) return { kind: "project", name: projects?.projects.find((p) => p.id === rig.projectId)?.name ?? rig.projectId };
|
|
69
|
+
if (rig.offProject) return { kind: "off", cwd: rig.cwd };
|
|
70
|
+
return { kind: "none" };
|
|
71
|
+
}
|
|
72
|
+
function isStandalone(s, rigs) {
|
|
73
|
+
if (s.brain) return false;
|
|
74
|
+
const r = s.rig;
|
|
75
|
+
if (!r) return true;
|
|
76
|
+
return !rigs.some((x) => x.machine === r.machine && x.seats.some((seat) => seat.sessionId === s.id && seat.state !== "stopped"));
|
|
77
|
+
}
|
|
78
|
+
var MENTION_RIG = /* @__PURE__ */ new Set(["running", "partial"]);
|
|
79
|
+
var MENTION_DOWN = /* @__PURE__ */ new Set(["stopped", "crashed", "attention"]);
|
|
80
|
+
function seatMentions(rigs, query) {
|
|
81
|
+
const live = rigs.filter((r) => r.machine === "local" && MENTION_RIG.has(r.lifecycle)).flatMap((rig) => rig.seats.filter((s) => !MENTION_DOWN.has(s.state)).map((seat) => ({ rig, seat })));
|
|
82
|
+
const holders = /* @__PURE__ */ new Map();
|
|
83
|
+
for (const { seat } of live) holders.set(seat.address, (holders.get(seat.address) ?? 0) + 1);
|
|
84
|
+
const q = query.toLowerCase();
|
|
85
|
+
return live.filter(({ rig, seat }) => !rig.kernel && seat.address.replace(/^@/, "").toLowerCase().startsWith(q)).sort((a, b) => a.seat.address.localeCompare(b.seat.address) || a.rig.name.localeCompare(b.rig.name)).map(({ rig, seat }) => {
|
|
86
|
+
const shared = (holders.get(seat.address) ?? 0) > 1;
|
|
87
|
+
const insert = shared ? `${seat.address}@${rig.name}` : seat.address;
|
|
88
|
+
return { key: `${rigKey(rig)}/${seat.ref}`, label: shared ? insert : `${seat.address} \xB7 ${rig.name}`, insert };
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function ledgeLine(escalations, rigs, said) {
|
|
92
|
+
const open = escalations.filter((e) => e.intent === "decision" && (e.state === "pending" || e.state === "grace")).sort((a, b) => a.createdAt < b.createdAt ? -1 : a.createdAt > b.createdAt ? 1 : 0);
|
|
93
|
+
const rigOf = (e) => {
|
|
94
|
+
const name = e?.rigId ? rigs.find((r) => r.machine === e.machine && r.id === e.rigId)?.name : void 0;
|
|
95
|
+
return name ? { rig: name } : {};
|
|
96
|
+
};
|
|
97
|
+
const pending = open.filter((e) => e.state === "pending");
|
|
98
|
+
if (pending.length) return { preview: { kind: "decision", count: pending.length, ...rigOf(pending[0]) }, at: pending[0].createdAt, unread: open.length };
|
|
99
|
+
const grace = open[0];
|
|
100
|
+
if (grace) return { preview: { kind: "grace", ...rigOf(grace) }, at: grace.createdAt, unread: open.length };
|
|
101
|
+
if (said) return { preview: { kind: "said", text: said.text }, at: said.at, unread: 0 };
|
|
102
|
+
return { preview: { kind: "calm", rigs: rigs.filter((r) => !r.kernel).length }, unread: 0 };
|
|
103
|
+
}
|
|
104
|
+
function shortCwd(cwd) {
|
|
105
|
+
const m = /^(\/home\/[^/]+|\/root)(?=\/|$)/.exec(cwd);
|
|
106
|
+
return m ? `~${cwd.slice(m[0].length)}` : cwd;
|
|
107
|
+
}
|
|
108
|
+
var RUNTIME_LABELS = { "claude-code": "Claude Code", codex: "Codex" };
|
|
109
|
+
var runtimeLabel = (runtime) => RUNTIME_LABELS[runtime] ?? runtime;
|
|
110
|
+
|
|
111
|
+
// ../fleet/src/token.ts
|
|
112
|
+
import { useSyncExternalStore } from "react";
|
|
113
|
+
var DAEMON_PROTOCOL_PREFIX = "ledge.";
|
|
114
|
+
var READ_TIMEOUT_MS = 1e4;
|
|
115
|
+
function createTokenSource(read) {
|
|
116
|
+
let cached = null, missing = false, inflight = null;
|
|
117
|
+
const fns = /* @__PURE__ */ new Set();
|
|
118
|
+
const setMissing = (m) => {
|
|
119
|
+
if (m !== missing) {
|
|
120
|
+
missing = m;
|
|
121
|
+
fns.forEach((f) => f(m));
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const load = async () => {
|
|
125
|
+
let r, timer;
|
|
126
|
+
const unavailable = { kind: "unavailable" };
|
|
127
|
+
try {
|
|
128
|
+
const got = await Promise.race([read(), new Promise((res) => {
|
|
129
|
+
timer = setTimeout(() => res(unavailable), READ_TIMEOUT_MS);
|
|
130
|
+
})]);
|
|
131
|
+
r = got?.kind === "token" && typeof got.token === "string" || got?.kind === "absent" ? got : unavailable;
|
|
132
|
+
} catch {
|
|
133
|
+
r = unavailable;
|
|
134
|
+
} finally {
|
|
135
|
+
clearTimeout(timer);
|
|
136
|
+
}
|
|
137
|
+
if (r.kind === "token") cached = r.token;
|
|
138
|
+
else if (r.kind === "absent") cached = null;
|
|
139
|
+
setMissing(r.kind === "absent");
|
|
140
|
+
return r;
|
|
141
|
+
};
|
|
142
|
+
return {
|
|
143
|
+
get(fresh) {
|
|
144
|
+
if (!fresh && cached !== null) return Promise.resolve({ kind: "token", token: cached });
|
|
145
|
+
inflight ??= load().finally(() => {
|
|
146
|
+
inflight = null;
|
|
147
|
+
});
|
|
148
|
+
return inflight;
|
|
149
|
+
},
|
|
150
|
+
missing: () => missing,
|
|
151
|
+
onMissing(fn) {
|
|
152
|
+
fns.add(fn);
|
|
153
|
+
return () => {
|
|
154
|
+
fns.delete(fn);
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ../fleet/src/client.ts
|
|
161
|
+
function createDaemonClient(opts) {
|
|
162
|
+
const Ws = opts.WebSocketImpl ?? globalThis.WebSocket;
|
|
163
|
+
const min = opts.minBackoffMs ?? 2e3, max = opts.maxBackoffMs ?? 3e4, token = opts.token;
|
|
164
|
+
let ws = null, status = "connecting", backoff = min, timer = null, closed = false;
|
|
165
|
+
let failed = false;
|
|
166
|
+
let attempt = 0;
|
|
167
|
+
const msgFns = /* @__PURE__ */ new Set(), statusFns = /* @__PURE__ */ new Set();
|
|
168
|
+
const setStatus = (s) => {
|
|
169
|
+
if (s !== status) {
|
|
170
|
+
status = s;
|
|
171
|
+
statusFns.forEach((f) => f(s));
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const schedule = () => {
|
|
175
|
+
if (closed) return;
|
|
176
|
+
timer = setTimeout(open, backoff);
|
|
177
|
+
backoff = Math.min(max, backoff * 2);
|
|
178
|
+
};
|
|
179
|
+
function open() {
|
|
180
|
+
if (closed) return;
|
|
181
|
+
setStatus(status === "open" ? "connecting" : status);
|
|
182
|
+
if (!token) {
|
|
183
|
+
dial(new Ws(opts.url));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const mine = ++attempt;
|
|
187
|
+
void token.get(failed).then((r) => {
|
|
188
|
+
if (closed || mine !== attempt) return;
|
|
189
|
+
if (r.kind === "token") {
|
|
190
|
+
try {
|
|
191
|
+
dial(new Ws(opts.url, [DAEMON_PROTOCOL_PREFIX + r.token]));
|
|
192
|
+
return;
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
failed = true;
|
|
197
|
+
setStatus("unreachable");
|
|
198
|
+
schedule();
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
function dial(sock) {
|
|
202
|
+
ws = sock;
|
|
203
|
+
let opened = false;
|
|
204
|
+
sock.onopen = () => {
|
|
205
|
+
if (ws !== sock) return;
|
|
206
|
+
opened = true;
|
|
207
|
+
setStatus("open");
|
|
208
|
+
};
|
|
209
|
+
sock.onmessage = (ev) => {
|
|
210
|
+
if (ws !== sock) return;
|
|
211
|
+
try {
|
|
212
|
+
const m = parseServerMessage(JSON.parse(String(ev.data)));
|
|
213
|
+
if (m.type === "snapshot") {
|
|
214
|
+
backoff = min;
|
|
215
|
+
failed = false;
|
|
216
|
+
}
|
|
217
|
+
msgFns.forEach((f) => f(m));
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
sock.onerror = () => {
|
|
222
|
+
};
|
|
223
|
+
sock.onclose = () => {
|
|
224
|
+
if (ws !== sock) return;
|
|
225
|
+
ws = null;
|
|
226
|
+
if (!opened) failed = true;
|
|
227
|
+
setStatus("unreachable");
|
|
228
|
+
schedule();
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
// An explicit connect (a « Reconnect ») says « connecting » at once, even after a drop: the automatic retries stay « unreachable ».
|
|
233
|
+
connect: () => {
|
|
234
|
+
closed = false;
|
|
235
|
+
if (timer) {
|
|
236
|
+
clearTimeout(timer);
|
|
237
|
+
timer = null;
|
|
238
|
+
}
|
|
239
|
+
const old = ws;
|
|
240
|
+
ws = null;
|
|
241
|
+
old?.close();
|
|
242
|
+
setStatus("connecting");
|
|
243
|
+
open();
|
|
244
|
+
},
|
|
245
|
+
close: () => {
|
|
246
|
+
closed = true;
|
|
247
|
+
attempt++;
|
|
248
|
+
if (timer) clearTimeout(timer);
|
|
249
|
+
const old = ws;
|
|
250
|
+
ws = null;
|
|
251
|
+
old?.close();
|
|
252
|
+
setStatus("connecting");
|
|
253
|
+
},
|
|
254
|
+
send: (m) => {
|
|
255
|
+
if (ws && ws.readyState === 1) ws.send(JSON.stringify(m));
|
|
256
|
+
},
|
|
257
|
+
status: () => status,
|
|
258
|
+
onMessage: (f) => {
|
|
259
|
+
msgFns.add(f);
|
|
260
|
+
return () => msgFns.delete(f);
|
|
261
|
+
},
|
|
262
|
+
onStatus: (f) => {
|
|
263
|
+
statusFns.add(f);
|
|
264
|
+
return () => statusFns.delete(f);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ../ui/src/launch/rigModel.ts
|
|
270
|
+
var STOP = /* @__PURE__ */ new Set(["le", "la", "les", "de", "des", "du", "un", "une", "et", "\xE0", "au", "pour", "sur", "apr\xE8s", "avant"]);
|
|
271
|
+
var LONG_WORDS = ["jours", "dur\xE9e", "produit", "livrer", "plusieurs"];
|
|
272
|
+
var LONG_GOAL = 160;
|
|
273
|
+
var fold = (s) => s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "");
|
|
274
|
+
function slugOfGoal(goal) {
|
|
275
|
+
const words = goal.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((w) => w && !STOP.has(w)).map(fold).map((w) => w.replace(/[^a-z0-9]/g, "")).filter(Boolean);
|
|
276
|
+
const slug = words.slice(-2).join("-").slice(0, 40).replace(/-+$/, "");
|
|
277
|
+
return RIG_NAME.test(slug) ? slug : "rig";
|
|
278
|
+
}
|
|
279
|
+
function rigFormDefaults(p) {
|
|
280
|
+
const goal = p.wanted.goal ?? "";
|
|
281
|
+
const spec = p.wanted.spec ?? null;
|
|
282
|
+
const machine = p.machines.find((m) => m.id === p.wanted.machine)?.id ?? p.machines.find((m) => m.id === "local")?.id ?? p.machines[0]?.id ?? "local";
|
|
283
|
+
const project = p.projects.find((x) => x.id === p.wanted.project);
|
|
284
|
+
if (!project) return { goal, spec, machine, place: p.wanted.off ? { kind: "off", name: p.wanted.off, nameTouched: true } : { kind: "off", name: slugOfGoal(goal), nameTouched: false } };
|
|
285
|
+
const repo = project.repos.find((r) => r.machine === machine)?.path ?? null;
|
|
286
|
+
return { goal, spec, machine, place: { kind: "project", projectId: project.id, repo, branch: spec ? `feat/${spec}` : "", copy: true } };
|
|
287
|
+
}
|
|
288
|
+
function withGoal(f, goal) {
|
|
289
|
+
const place = f.place.kind === "off" && !f.place.nameTouched ? { ...f.place, name: slugOfGoal(goal) } : f.place;
|
|
290
|
+
return { ...f, goal, place };
|
|
291
|
+
}
|
|
292
|
+
function recommendSpec(goal, specs) {
|
|
293
|
+
const g = goal.toLowerCase();
|
|
294
|
+
const long = goal.length > LONG_GOAL || LONG_WORDS.some((w) => g.includes(w));
|
|
295
|
+
const spec = long ? "factory" : "starter";
|
|
296
|
+
return specs.some((s) => s.name === spec || s.id === spec) ? { spec, why: long ? "long" : "bounded" } : null;
|
|
297
|
+
}
|
|
298
|
+
function missingOf(f) {
|
|
299
|
+
const out = [];
|
|
300
|
+
if (!f.goal.trim()) out.push("goal");
|
|
301
|
+
if (!f.spec) out.push("spec");
|
|
302
|
+
if (f.place.kind === "project") {
|
|
303
|
+
if (!f.place.repo) out.push("repo");
|
|
304
|
+
const b = f.place.branch.trim();
|
|
305
|
+
if (!b || b.startsWith("-")) out.push("branch");
|
|
306
|
+
} else if (!RIG_NAME.test(f.place.name)) out.push("name");
|
|
307
|
+
return out;
|
|
308
|
+
}
|
|
309
|
+
function whereOf(f) {
|
|
310
|
+
const missing = missingOf(f);
|
|
311
|
+
if (f.place.kind === "project") {
|
|
312
|
+
if (missing.includes("repo") || missing.includes("branch") || !f.place.repo) return null;
|
|
313
|
+
return { kind: "project", projectId: f.place.projectId, repo: f.place.repo, branch: f.place.branch.trim(), copy: f.place.copy };
|
|
314
|
+
}
|
|
315
|
+
return missing.includes("name") ? null : { kind: "off-project", name: f.place.name };
|
|
316
|
+
}
|
|
317
|
+
var planRequired = (f) => f.place.kind === "project" && !f.place.copy;
|
|
318
|
+
var primaryAction = (f) => planRequired(f) ? "plan" : "launch";
|
|
319
|
+
function providers(seats) {
|
|
320
|
+
const counts = /* @__PURE__ */ new Map();
|
|
321
|
+
for (const s of seats) counts.set(s.runtime, (counts.get(s.runtime) ?? 0) + 1);
|
|
322
|
+
return [...counts].map(([runtime, count]) => ({ runtime, count }));
|
|
323
|
+
}
|
|
324
|
+
function launchability(m, status) {
|
|
325
|
+
if (m.status !== "open") return { ok: false, why: "offline" };
|
|
326
|
+
if (!status) return { ok: false, why: "unreachable" };
|
|
327
|
+
switch (status.state) {
|
|
328
|
+
case "running":
|
|
329
|
+
case "stopped":
|
|
330
|
+
case "starting":
|
|
331
|
+
return { ok: true };
|
|
332
|
+
case "absent":
|
|
333
|
+
return status.install ? { ok: false, why: "absent", install: status.install } : { ok: false, why: "absent" };
|
|
334
|
+
case "unsupported":
|
|
335
|
+
return { ok: false, why: "unsupported" };
|
|
336
|
+
default:
|
|
337
|
+
return { ok: false, why: "unreachable" };
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export {
|
|
342
|
+
imageInfo,
|
|
343
|
+
rigKey,
|
|
344
|
+
rigColor,
|
|
345
|
+
kernelSplit,
|
|
346
|
+
workingCount,
|
|
347
|
+
rigPlace,
|
|
348
|
+
isStandalone,
|
|
349
|
+
seatMentions,
|
|
350
|
+
ledgeLine,
|
|
351
|
+
shortCwd,
|
|
352
|
+
runtimeLabel,
|
|
353
|
+
DAEMON_PROTOCOL_PREFIX,
|
|
354
|
+
createTokenSource,
|
|
355
|
+
createDaemonClient,
|
|
356
|
+
slugOfGoal,
|
|
357
|
+
rigFormDefaults,
|
|
358
|
+
withGoal,
|
|
359
|
+
recommendSpec,
|
|
360
|
+
missingOf,
|
|
361
|
+
whereOf,
|
|
362
|
+
primaryAction,
|
|
363
|
+
providers,
|
|
364
|
+
launchability
|
|
365
|
+
};
|