@paradow/ledge 0.40.0 → 0.41.0-snapshot.41719405620.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/index.js +976 -462
- package/dist/cli/reduceWorker.js +1 -1
- package/package.json +24 -6
package/dist/cli/index.js
CHANGED
|
@@ -116,6 +116,12 @@ function backendOf(s) {
|
|
|
116
116
|
function isArchived(s) {
|
|
117
117
|
return s.archivedAt !== void 0;
|
|
118
118
|
}
|
|
119
|
+
function isOmpSeat(s) {
|
|
120
|
+
return s.source === "rig" && s.tool === "omp" && s.backend === void 0;
|
|
121
|
+
}
|
|
122
|
+
function isStoppedOmpSeat(s) {
|
|
123
|
+
return isOmpSeat(s) && s.state === "exited" && !isArchived(s);
|
|
124
|
+
}
|
|
119
125
|
var ToolSchema, OmpApprovalSchema, BackendSchema, SessionStateSchema, AskQuestionSchema, PendingSchema, ReplySchema, HelperSchema, TopicSourceSchema, SessionSchema;
|
|
120
126
|
var init_session = __esm({
|
|
121
127
|
"../protocol/src/session.ts"() {
|
|
@@ -2277,7 +2283,7 @@ var init_messages = __esm({
|
|
|
2277
2283
|
agentSessionId: z24.string().optional()
|
|
2278
2284
|
});
|
|
2279
2285
|
RecentSchema = z24.object({ cwd: z24.string().min(1), project: z24.string().min(1), lastAt: z24.string().datetime() });
|
|
2280
|
-
SEMVER = /^\d+\.\d+\.\d
|
|
2286
|
+
SEMVER = /^\d+\.\d+\.\d+(?:-snapshot\.[1-9]\d*\.[1-9]\d*)?$/;
|
|
2281
2287
|
DaemonInfoSchema = z24.object({
|
|
2282
2288
|
version: z24.string(),
|
|
2283
2289
|
install: z24.enum(["npm", "git", "unknown"]),
|
|
@@ -3487,6 +3493,19 @@ var init_src = __esm({
|
|
|
3487
3493
|
}
|
|
3488
3494
|
});
|
|
3489
3495
|
|
|
3496
|
+
// src/port.ts
|
|
3497
|
+
function ledgePort(env = process.env) {
|
|
3498
|
+
const raw = env.LEDGE_PORT;
|
|
3499
|
+
const n = raw && /^\d+$/.test(raw) ? Number(raw) : NaN;
|
|
3500
|
+
return Number.isInteger(n) && n >= 1 && n <= 65535 ? n : PROTOCOL_PORT;
|
|
3501
|
+
}
|
|
3502
|
+
var init_port = __esm({
|
|
3503
|
+
"src/port.ts"() {
|
|
3504
|
+
"use strict";
|
|
3505
|
+
init_src();
|
|
3506
|
+
}
|
|
3507
|
+
});
|
|
3508
|
+
|
|
3490
3509
|
// src/paths.ts
|
|
3491
3510
|
import { homedir } from "os";
|
|
3492
3511
|
import { join } from "path";
|
|
@@ -3665,7 +3684,7 @@ async function sendMessageAndAwaitAck(client, msg, timeoutMs) {
|
|
|
3665
3684
|
return client.next("message.ack", timeoutMs);
|
|
3666
3685
|
}
|
|
3667
3686
|
function connect(opts = {}) {
|
|
3668
|
-
const port = opts.port ??
|
|
3687
|
+
const port = opts.port ?? ledgePort();
|
|
3669
3688
|
const host = opts.host ?? "127.0.0.1";
|
|
3670
3689
|
let token;
|
|
3671
3690
|
try {
|
|
@@ -3743,6 +3762,7 @@ var DaemonRefusedError;
|
|
|
3743
3762
|
var init_client = __esm({
|
|
3744
3763
|
"src/client.ts"() {
|
|
3745
3764
|
"use strict";
|
|
3765
|
+
init_port();
|
|
3746
3766
|
init_auth();
|
|
3747
3767
|
init_src();
|
|
3748
3768
|
DaemonRefusedError = class extends Error {
|
|
@@ -3788,11 +3808,11 @@ function stripNote(text7) {
|
|
|
3788
3808
|
}
|
|
3789
3809
|
function formatLedgeMessage(m) {
|
|
3790
3810
|
const from = attr(m.from);
|
|
3791
|
-
const
|
|
3792
|
-
if (!m.id || inline(m.text)) return `${
|
|
3811
|
+
const open6 = `<ledge-message from="${from}" tool="${m.tool}"${m.id ? ` id="${attr(m.id)}"` : ""}>${messageNote(from, m.to)} `;
|
|
3812
|
+
if (!m.id || inline(m.text)) return `${open6}${ledgeBody(m.text)}${LEDGE_CLOSE}`;
|
|
3793
3813
|
const first = m.summary?.trim() || m.text.trim().split(/\r\n|\r|\n/)[0].trim();
|
|
3794
3814
|
const head2 = ledgeBody(first.length > POINTER_HEAD ? `${first.slice(0, POINTER_HEAD)}\u2026` : first);
|
|
3795
|
-
return `${
|
|
3815
|
+
return `${open6}${head2} \u2026 [message de ${m.text.length} caract\xE8res, ${lineCount(m.text)} lignes : lis-le en entier avec ledge read ${attr(m.id)}]${LEDGE_CLOSE}`;
|
|
3796
3816
|
}
|
|
3797
3817
|
function ledgeMessages(text7) {
|
|
3798
3818
|
const out = [];
|
|
@@ -4576,7 +4596,7 @@ async function spawnSession(opts) {
|
|
|
4576
4596
|
const agent = opts.command ?? [AGENT_BIN[opts.tool], ...agentIdArgs(opts.tool, id), ...agentSessionId ? ["--session-id", agentSessionId] : [], ...opts.args];
|
|
4577
4597
|
const env = opts.env ?? process.env;
|
|
4578
4598
|
const transcriptPath = agentSessionId ? claudeTranscriptPath(opts.cwd, agentSessionId, env) : void 0;
|
|
4579
|
-
const command = opts.recordExit ? ["sh", "-c", '"$@"; echo $? > "$0"', exitFile(id), ...agent] : agent;
|
|
4599
|
+
const command = opts.recordExit && process.platform !== "win32" ? ["sh", "-c", '"$@"; echo $? > "$0"', exitFile(id), ...agent] : agent;
|
|
4580
4600
|
const key = sessionKey(id);
|
|
4581
4601
|
await opts.tmux.newSession({ name: tmuxSession, cwd: opts.cwd, env: agentEnv(env, { LEDGE_SESSION: id, ...key ? { [SESSION_KEY_VAR]: key } : {}, LEDGE_NAME: name, ...opts.tool === "codex" ? { [CODEX_ENV_VAR]: id } : {} }), unset: PARENT_AGENT_VARS, command });
|
|
4582
4602
|
const at = now2();
|
|
@@ -4947,7 +4967,7 @@ __export(who_exports, {
|
|
|
4947
4967
|
});
|
|
4948
4968
|
import { basename as basename5 } from "path";
|
|
4949
4969
|
function formatWho(sessions, self, projects) {
|
|
4950
|
-
const live3 = sessions.filter((s) => s.state !== "exited" && !isArchived(s));
|
|
4970
|
+
const live3 = sessions.filter((s) => s.state !== "exited" && !isArchived(s) || isStoppedOmpSeat(s));
|
|
4951
4971
|
if (live3.length === 0) return "aucune autre session";
|
|
4952
4972
|
const rows = live3.map((s) => [
|
|
4953
4973
|
s.id === self ? "*" : " ",
|
|
@@ -5195,8 +5215,8 @@ function underCodexDaemon(read = (p) => readFileSync5(p, "utf8")) {
|
|
|
5195
5215
|
let pid = process.pid;
|
|
5196
5216
|
for (let depth = 0; depth < 8; depth++) {
|
|
5197
5217
|
try {
|
|
5198
|
-
const
|
|
5199
|
-
pid = Number(
|
|
5218
|
+
const stat8 = read(`/proc/${pid}/stat`);
|
|
5219
|
+
pid = Number(stat8.slice(stat8.lastIndexOf(")") + 2).split(" ")[1]);
|
|
5200
5220
|
if (!pid || pid <= 1) return false;
|
|
5201
5221
|
if (read(`/proc/${pid}/cmdline`).split("\0").includes("app-server")) return true;
|
|
5202
5222
|
} catch {
|
|
@@ -6764,9 +6784,9 @@ ${done.output}` : command;
|
|
|
6764
6784
|
const peer = (m.sender ? this.peerOf(m.sender) : void 0) ?? this.peerOf(m.from) ?? (m.name ? this.peerOf(m.name) : void 0);
|
|
6765
6785
|
if (!peer) return this.put(id, recv);
|
|
6766
6786
|
const head2 = (t) => ledgeBody(t).trim().slice(0, 200);
|
|
6767
|
-
const
|
|
6768
|
-
const byId2 = m.id ?
|
|
6769
|
-
const sent = byId2 ?? [...
|
|
6787
|
+
const open6 = this.items(peer).filter((i) => i.kind === "sent" && i.peer === id && i.status !== "read");
|
|
6788
|
+
const byId2 = m.id ? open6.find((i) => mailId(i.id) === m.id) : void 0;
|
|
6789
|
+
const sent = byId2 ?? [...open6].reverse().find((i) => head2(i.text) === head2(recv.text)) ?? open6.at(-1);
|
|
6770
6790
|
if (sent) this.put(peer, { ...sent, status: "read", received: recv.id });
|
|
6771
6791
|
return this.put(id, { ...recv, ...byId2 ? { text: byId2.text } : {}, peer, ...sent ? { sent: sent.id } : {} });
|
|
6772
6792
|
}
|
|
@@ -7147,9 +7167,9 @@ function parseTrust(pane) {
|
|
|
7147
7167
|
while (o >= 0 && isBlank(lines[o])) o--;
|
|
7148
7168
|
const block2 = [];
|
|
7149
7169
|
while (o >= 0 && !isBlank(lines[o])) block2.unshift(lines[o--]);
|
|
7150
|
-
const options = block2.map((l) => l.trim().replace(
|
|
7170
|
+
const options = block2.map((l) => l.trim().replace(CURSOR, ""));
|
|
7151
7171
|
const trust = options.findIndex((l) => TRUST_OPTION.test(l));
|
|
7152
|
-
const selected = block2.findIndex((l) => l.trim()
|
|
7172
|
+
const selected = block2.findIndex((l) => CURSOR.test(l.trim()));
|
|
7153
7173
|
if (trust < 0 || selected < 0) return null;
|
|
7154
7174
|
const at = lines.findIndex((l) => /^\s*Accessing workspace:/.test(l));
|
|
7155
7175
|
const path3 = at < 0 ? "" : lines.slice(at + 1, footer).find((l) => !isBlank(l))?.trim() ?? "";
|
|
@@ -7195,7 +7215,7 @@ function inputBox(lines) {
|
|
|
7195
7215
|
if (bottom < 0) return null;
|
|
7196
7216
|
for (let top = bottom - 1; top >= 0 && bottom - top <= INPUT_MAX2 + 1; top--) {
|
|
7197
7217
|
if (!isInputTop(lines[top])) continue;
|
|
7198
|
-
return lines[top + 1].trimStart()
|
|
7218
|
+
return CURSOR.test(lines[top + 1].trimStart()) ? { top, bottom } : null;
|
|
7199
7219
|
}
|
|
7200
7220
|
return null;
|
|
7201
7221
|
}
|
|
@@ -7234,7 +7254,7 @@ function sentFrom(text7) {
|
|
|
7234
7254
|
const lines = pane.split("\n").map((l) => l.replace(/\s+$/, ""));
|
|
7235
7255
|
const box = inputBox(lines);
|
|
7236
7256
|
if (!box) return true;
|
|
7237
|
-
const draft = lines.slice(box.top + 1, box.bottom).join("").replace(/\s+/g, "").replace(
|
|
7257
|
+
const draft = lines.slice(box.top + 1, box.bottom).join("").replace(/\s+/g, "").replace(CURSOR, "");
|
|
7238
7258
|
return !draft.includes("[Pastedtext#") && !(head2 && draft.startsWith(head2));
|
|
7239
7259
|
};
|
|
7240
7260
|
}
|
|
@@ -7270,16 +7290,18 @@ function notePlan(note, pane) {
|
|
|
7270
7290
|
if (row3 >= 0) return rowPlan(row3 + 1, text7);
|
|
7271
7291
|
return [{ keys: ["Escape"] }, { until: atRest, denied: true }, ...CLEAR, ...submit(text7, true)];
|
|
7272
7292
|
}
|
|
7273
|
-
var OPTION, FOOTER, PLAN_FOOTER, PREVIEW_FOOTER, CHAT_LINE, PREVIEW_EDGE, GUTTER, REVIEW_QUESTION, RULE, DASHED, INPUT_TOP, FOOTER_SLACK, isBlank, isRule, dashedBody, TRUST_FOOTER, TRUST_OPTION, squash, STATUS_SLACK, INPUT_MAX2, isInputRule, isInputTop, TITLE_RULE, BUSY, SPINNER, digit, focused, FREE_ROW, CHAT_FOCUSED, NO_FREE_ROW, CLEAR, REPLY_ENTER_DELAY_MS, send, submit, PLAN_ACCEPT, ALWAYS, AUTO_MODE, PLAN_ALWAYS, CLEAR_CONTEXT, PLAN_NOTE_ROW, claudeAdapter;
|
|
7293
|
+
var CUR, CURSOR, OPTION, FOOTER, PLAN_FOOTER, PREVIEW_FOOTER, CHAT_LINE, PREVIEW_EDGE, GUTTER, REVIEW_QUESTION, RULE, DASHED, INPUT_TOP, FOOTER_SLACK, isBlank, isRule, dashedBody, TRUST_FOOTER, TRUST_OPTION, squash, STATUS_SLACK, INPUT_MAX2, isInputRule, isInputTop, TITLE_RULE, BUSY, SPINNER, digit, focused, FREE_ROW, CHAT_FOCUSED, NO_FREE_ROW, CLEAR, REPLY_ENTER_DELAY_MS, send, submit, PLAN_ACCEPT, ALWAYS, AUTO_MODE, PLAN_ALWAYS, CLEAR_CONTEXT, PLAN_NOTE_ROW, claudeAdapter;
|
|
7274
7294
|
var init_claude = __esm({
|
|
7275
7295
|
"src/adapters/claude.ts"() {
|
|
7276
7296
|
"use strict";
|
|
7277
7297
|
init_types();
|
|
7278
|
-
|
|
7298
|
+
CUR = process.platform === "win32" ? "[\u276F>]" : "\u276F";
|
|
7299
|
+
CURSOR = new RegExp(`^${CUR}\\s*`);
|
|
7300
|
+
OPTION = new RegExp(`^\\s*(?:${CUR}\\s*)?(\\d+)\\.\\s+(\\S.*?)\\s*$`);
|
|
7279
7301
|
FOOTER = /^\s*(?:(?:Enter to select\b.*)?Esc to cancel\b|ctrl\+g to edit\b)/;
|
|
7280
7302
|
PLAN_FOOTER = /^\s*ctrl\+g to edit\b/;
|
|
7281
7303
|
PREVIEW_FOOTER = /\bn to add notes\b/;
|
|
7282
|
-
CHAT_LINE =
|
|
7304
|
+
CHAT_LINE = new RegExp(`^\\s*(?:${CUR}\\s*)?Chat about this$`);
|
|
7283
7305
|
PREVIEW_EDGE = /\s{2,}[┌│└]/;
|
|
7284
7306
|
GUTTER = /^│\s*/;
|
|
7285
7307
|
REVIEW_QUESTION = /^\s*Ready to submit your answers\?$/;
|
|
@@ -7299,14 +7321,14 @@ var init_claude = __esm({
|
|
|
7299
7321
|
isInputTop = (l) => INPUT_TOP.test(l.trim());
|
|
7300
7322
|
TITLE_RULE = /^─{10,} (\S.*?) ─+$/;
|
|
7301
7323
|
BUSY = /\besc to interrupt\b/;
|
|
7302
|
-
SPINNER =
|
|
7324
|
+
SPINNER = new RegExp(`^[^\\s\u276F${process.platform === "win32" ? ">" : ""}\u25CF\u23BF\u2502\u2500]\\s\\S+\u2026(?: \\(|$)`);
|
|
7303
7325
|
digit = (n) => [{ text: String(n) }];
|
|
7304
7326
|
focused = (n) => {
|
|
7305
|
-
const row3 = new RegExp(`^\\s
|
|
7327
|
+
const row3 = new RegExp(`^\\s*${CUR}\\s*${n}\\.\\s`, "m");
|
|
7306
7328
|
return (p) => row3.test(p);
|
|
7307
7329
|
};
|
|
7308
7330
|
FREE_ROW = /^Type something\.?$/;
|
|
7309
|
-
CHAT_FOCUSED =
|
|
7331
|
+
CHAT_FOCUSED = new RegExp(`^\\s*${CUR}\\s*Chat about this\\s*$`, "m");
|
|
7310
7332
|
NO_FREE_ROW = "no free-text row in this dialog, answer in the terminal";
|
|
7311
7333
|
CLEAR = [{ keys: ["C-u"] }];
|
|
7312
7334
|
REPLY_ENTER_DELAY_MS = 300;
|
|
@@ -7388,6 +7410,7 @@ function parseDialog2(pane) {
|
|
|
7388
7410
|
}
|
|
7389
7411
|
const f = lines[footer];
|
|
7390
7412
|
const kind = APPROVAL_FOOTER.test(f) ? "approval" : QUESTION_FOOTER.test(f) ? "question" : SELECT_FOOTER.test(f) ? "plan" : "confirm";
|
|
7413
|
+
if (kind === "plan" && header.some((l) => SANDBOX_SETUP.test(l))) return { kind: "sandbox", header, options: raw.map(label), details: raw.map(detail), raw };
|
|
7391
7414
|
if (kind === "plan" && !header.some((l) => /^\s*Implement this plan\?\s*$/.test(l))) return null;
|
|
7392
7415
|
return { kind, header, options: raw.map(label), details: raw.map(detail), raw };
|
|
7393
7416
|
}
|
|
@@ -7430,6 +7453,7 @@ function detectPrompt2(pane) {
|
|
|
7430
7453
|
if (!d) return null;
|
|
7431
7454
|
if (d.kind === "question") return { kind: "question", text: questionText(d.header), options: d.options, ...d.details.some(Boolean) ? { optionDetails: d.details } : {} };
|
|
7432
7455
|
if (d.kind === "plan") return { kind: "plan", text: "Implement this plan?", options: d.options };
|
|
7456
|
+
if (d.kind === "sandbox") return { kind: "question", text: "Set up the Codex agent sandbox (Windows)", options: d.options };
|
|
7433
7457
|
if (d.kind === "confirm") return { kind: "permission", text: confirmText(d.header), options: d.options };
|
|
7434
7458
|
const cmd = d.header.findIndex((l) => /^\s*\$\s/.test(l));
|
|
7435
7459
|
const command = cmd >= 0 ? shownCommand(rawRun(d.header, cmd)) : void 0;
|
|
@@ -7479,7 +7503,25 @@ function noteKeys(d, note) {
|
|
|
7479
7503
|
const no2 = d?.kind === "plan" ? optionNumber2(d.options, /^No\b/) : null;
|
|
7480
7504
|
return [...no2 ? digit2(no2) : [{ keys: ["Escape"] }], { until: atRest2, denied: true }, { text: flatten(note) }, { waitMs: REPLY_ENTER_DELAY_MS2 }, { keys: ["Enter"] }];
|
|
7481
7505
|
}
|
|
7482
|
-
|
|
7506
|
+
function draftShown(text7) {
|
|
7507
|
+
const head2 = kept(text7).slice(0, 20);
|
|
7508
|
+
return (pane) => {
|
|
7509
|
+
if (!head2) return true;
|
|
7510
|
+
const lines = split(pane);
|
|
7511
|
+
const i = inputLine(lines);
|
|
7512
|
+
if (i < 0) return false;
|
|
7513
|
+
const draft = lines.slice(i).join("").replace(/\s+/g, "").replace(/^›/, "");
|
|
7514
|
+
return /\[PastedContent\d+chars\]/.test(draft) || kept(draft).startsWith(head2);
|
|
7515
|
+
};
|
|
7516
|
+
}
|
|
7517
|
+
function codexReplyPlan(text7, pending, platform, pane) {
|
|
7518
|
+
const flat = flatten(text7);
|
|
7519
|
+
if (pending?.kind === "question" && /^[1-9]$/.test(flat) && pane && parseDialog2(pane)?.kind === "sandbox") return [...digit2(Number(flat)), { waitMs: REPLY_ENTER_DELAY_MS2 }, { keys: ["Enter"] }];
|
|
7520
|
+
if (pending?.kind === "question" && /^[1-9]$/.test(flat)) return digit2(Number(flat));
|
|
7521
|
+
if (platform === "win32") return [{ text: flat }, { until: draftShown(flat), ms: DRAFT_SHOWN_MS }, { waitMs: REPLY_ENTER_DELAY_MS2 }, { keys: ["Enter"] }];
|
|
7522
|
+
return [{ text: flat }, { waitMs: REPLY_ENTER_DELAY_MS2 }, { keys: ["Enter"] }];
|
|
7523
|
+
}
|
|
7524
|
+
var APPROVAL_FOOTER, QUESTION_FOOTER, SELECT_FOOTER, SANDBOX_SETUP, CONFIRM_FOOTER, FOOTERS2, OPTION2, HEADER_SPAN, isBlank2, isFooter, split, label, detail, run, BELOW_INPUT, BUSY2, BUSY_SPAN, digit2, REPLY_ENTER_DELAY_MS2, ALWAYS2, DRAFT_SHOWN_MS, kept, codexAdapter;
|
|
7483
7525
|
var init_codex = __esm({
|
|
7484
7526
|
"src/adapters/codex.ts"() {
|
|
7485
7527
|
"use strict";
|
|
@@ -7487,6 +7529,7 @@ var init_codex = __esm({
|
|
|
7487
7529
|
APPROVAL_FOOTER = /^\s*Press enter to confirm or esc to cancel\s*$/;
|
|
7488
7530
|
QUESTION_FOOTER = /\benter to submit\b/;
|
|
7489
7531
|
SELECT_FOOTER = /^\s*enter select · esc back\s*$/;
|
|
7532
|
+
SANDBOX_SETUP = /^\s*Set up the Codex agent sandbox\b/;
|
|
7490
7533
|
CONFIRM_FOOTER = /^\s*enter confirm · esc skip\s*$/;
|
|
7491
7534
|
FOOTERS2 = [APPROVAL_FOOTER, QUESTION_FOOTER, SELECT_FOOTER, CONFIRM_FOOTER];
|
|
7492
7535
|
OPTION2 = /^\s*(?:›\s*)?(\d+)\.\s+(\S.*)$/;
|
|
@@ -7503,6 +7546,8 @@ var init_codex = __esm({
|
|
|
7503
7546
|
digit2 = (n) => [{ text: String(n) }];
|
|
7504
7547
|
REPLY_ENTER_DELAY_MS2 = 300;
|
|
7505
7548
|
ALWAYS2 = /don['’]t ask again/i;
|
|
7549
|
+
DRAFT_SHOWN_MS = 1e4;
|
|
7550
|
+
kept = (s) => s.replace(/[^\p{L}\p{N}]/gu, "");
|
|
7506
7551
|
codexAdapter = {
|
|
7507
7552
|
tool: "codex",
|
|
7508
7553
|
detectPrompt: detectPrompt2,
|
|
@@ -7511,7 +7556,7 @@ var init_codex = __esm({
|
|
|
7511
7556
|
atRest: atRest2,
|
|
7512
7557
|
keysFor(decision, pending, pane, note) {
|
|
7513
7558
|
const d = pane ? parseDialog2(pane) : null;
|
|
7514
|
-
if (d) assertDialogIs(pending, d.kind === "approval" || d.kind === "confirm" ? "permission" : d.kind);
|
|
7559
|
+
if (d) assertDialogIs(pending, d.kind === "approval" || d.kind === "confirm" ? "permission" : d.kind === "sandbox" ? "question" : d.kind);
|
|
7515
7560
|
if (decision === "always") return alwaysKeys(d);
|
|
7516
7561
|
if (decision === "deny" && note) return noteKeys(d, note);
|
|
7517
7562
|
if (decision === "deny") {
|
|
@@ -7529,11 +7574,7 @@ var init_codex = __esm({
|
|
|
7529
7574
|
if (pending.kind === "question") return digit2(1);
|
|
7530
7575
|
return [{ text: "y" }];
|
|
7531
7576
|
},
|
|
7532
|
-
replyPlan(text7, pending)
|
|
7533
|
-
const flat = flatten(text7);
|
|
7534
|
-
if (pending?.kind === "question" && /^[1-9]$/.test(flat)) return digit2(Number(flat));
|
|
7535
|
-
return [{ text: flat }, { waitMs: REPLY_ENTER_DELAY_MS2 }, { keys: ["Enter"] }];
|
|
7536
|
-
},
|
|
7577
|
+
replyPlan: (text7, pending, pane) => codexReplyPlan(text7, pending, process.platform, pane),
|
|
7537
7578
|
// Esc interrupts the turn; Ctrl+C clears the draft and a second one quits Codex.
|
|
7538
7579
|
interruptKeys: () => ["Escape"],
|
|
7539
7580
|
// Codex 0.162, seen in tmux: Ctrl+U clears a typed draft, then `/exit` and `/quit` both quit it (a draft cleared that
|
|
@@ -7838,6 +7879,203 @@ var init_verify = __esm({
|
|
|
7838
7879
|
}
|
|
7839
7880
|
});
|
|
7840
7881
|
|
|
7882
|
+
// src/daemon/omp/seats.ts
|
|
7883
|
+
import { homedir as homedir5 } from "os";
|
|
7884
|
+
import { join as join10 } from "path";
|
|
7885
|
+
import { open, readFile as readFile7, stat as stat2 } from "fs/promises";
|
|
7886
|
+
function entryState(raw) {
|
|
7887
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
7888
|
+
const e = raw;
|
|
7889
|
+
const at = typeof e.timestamp === "string" && !Number.isNaN(Date.parse(e.timestamp)) ? new Date(e.timestamp).toISOString() : void 0;
|
|
7890
|
+
const of = (state2) => ({ state: state2, ...at ? { at } : {} });
|
|
7891
|
+
if (e.type === "custom_message") return of("working");
|
|
7892
|
+
if (e.type === "custom") return e.customType === "tool_execution_start" ? of("working") : null;
|
|
7893
|
+
if (e.type !== "message" || !e.message || typeof e.message !== "object") return null;
|
|
7894
|
+
const m = e.message;
|
|
7895
|
+
if (m.role === "user" || m.role === "toolResult") return of("working");
|
|
7896
|
+
if (m.role !== "assistant") return null;
|
|
7897
|
+
return typeof m.stopReason === "string" ? of(m.stopReason === "toolUse" ? "working" : "idle") : null;
|
|
7898
|
+
}
|
|
7899
|
+
async function nextNewline(fh, from, size) {
|
|
7900
|
+
const buf2 = Buffer.alloc(BLOCK3);
|
|
7901
|
+
for (let at = from; at < size; ) {
|
|
7902
|
+
const { bytesRead } = await fh.read(buf2, 0, Math.min(BLOCK3, size - at), at);
|
|
7903
|
+
if (bytesRead === 0) return -1;
|
|
7904
|
+
const nl = buf2.subarray(0, bytesRead).indexOf(10);
|
|
7905
|
+
if (nl !== -1) return at + nl;
|
|
7906
|
+
at += bytesRead;
|
|
7907
|
+
}
|
|
7908
|
+
return -1;
|
|
7909
|
+
}
|
|
7910
|
+
function createOmpSeats(deps) {
|
|
7911
|
+
const { store } = deps;
|
|
7912
|
+
const root2 = deps.root ?? ompSeatsRoot();
|
|
7913
|
+
const follows = /* @__PURE__ */ new Map();
|
|
7914
|
+
let running = false, timer2 = null;
|
|
7915
|
+
async function journalOf(seat2) {
|
|
7916
|
+
try {
|
|
7917
|
+
const file = JSON.parse(await readFile7(join10(root2, seat2, "runner-state.json"), "utf8")).sessionFile;
|
|
7918
|
+
return typeof file === "string" && file ? file : null;
|
|
7919
|
+
} catch {
|
|
7920
|
+
return null;
|
|
7921
|
+
}
|
|
7922
|
+
}
|
|
7923
|
+
async function followOne(s) {
|
|
7924
|
+
const file = await journalOf(s.rig.seat);
|
|
7925
|
+
if (!file) return;
|
|
7926
|
+
let size;
|
|
7927
|
+
try {
|
|
7928
|
+
size = (await stat2(file)).size;
|
|
7929
|
+
} catch {
|
|
7930
|
+
return;
|
|
7931
|
+
}
|
|
7932
|
+
let f = follows.get(s.id);
|
|
7933
|
+
if (!f || f.file !== file) {
|
|
7934
|
+
const known2 = await lastState(file, size);
|
|
7935
|
+
f = { file, offset: known2.end };
|
|
7936
|
+
follows.set(s.id, f);
|
|
7937
|
+
if (known2.state) apply(s.id, known2.state);
|
|
7938
|
+
return;
|
|
7939
|
+
}
|
|
7940
|
+
if (size < f.offset) {
|
|
7941
|
+
f.offset = size;
|
|
7942
|
+
return;
|
|
7943
|
+
}
|
|
7944
|
+
if (size === f.offset) return;
|
|
7945
|
+
const fh = await open(file, "r");
|
|
7946
|
+
let chunk, end2;
|
|
7947
|
+
try {
|
|
7948
|
+
const readAt2 = async (want) => {
|
|
7949
|
+
const b3 = Buffer.alloc(want);
|
|
7950
|
+
return b3.subarray(0, (await fh.read(b3, 0, want, f.offset)).bytesRead);
|
|
7951
|
+
};
|
|
7952
|
+
chunk = await readAt2(Math.min(size - f.offset, READ_BYTES));
|
|
7953
|
+
end2 = chunk.lastIndexOf(10);
|
|
7954
|
+
if (end2 === -1 && chunk.length < size - f.offset) {
|
|
7955
|
+
chunk = await readAt2(Math.min(size - f.offset, BACK_BYTES));
|
|
7956
|
+
end2 = chunk.lastIndexOf(10);
|
|
7957
|
+
}
|
|
7958
|
+
if (end2 === -1 && chunk.length === BACK_BYTES) {
|
|
7959
|
+
const next4 = await nextNewline(fh, f.offset + chunk.length, size);
|
|
7960
|
+
if (next4 !== -1) {
|
|
7961
|
+
log(`omp seats: an entry of ${s.rig.seat} over ${BACK_BYTES} bytes was skipped`);
|
|
7962
|
+
f.offset = next4 + 1;
|
|
7963
|
+
}
|
|
7964
|
+
return;
|
|
7965
|
+
}
|
|
7966
|
+
} finally {
|
|
7967
|
+
await fh.close();
|
|
7968
|
+
}
|
|
7969
|
+
if (end2 === -1) return;
|
|
7970
|
+
const lines = chunk.subarray(0, end2).toString("utf8").split("\n");
|
|
7971
|
+
f.offset += end2 + 1;
|
|
7972
|
+
let last = null;
|
|
7973
|
+
for (const line2 of lines) {
|
|
7974
|
+
if (!line2.trim()) continue;
|
|
7975
|
+
let entry2;
|
|
7976
|
+
try {
|
|
7977
|
+
entry2 = JSON.parse(line2);
|
|
7978
|
+
} catch {
|
|
7979
|
+
continue;
|
|
7980
|
+
}
|
|
7981
|
+
last = entryState(entry2) ?? last;
|
|
7982
|
+
}
|
|
7983
|
+
if (last) apply(s.id, last);
|
|
7984
|
+
}
|
|
7985
|
+
function apply(id, last) {
|
|
7986
|
+
const cur = store.get(id);
|
|
7987
|
+
if (!cur || cur.state === "exited") return;
|
|
7988
|
+
const at = last.at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
7989
|
+
if (cur.state !== last.state || at > cur.lastActivityAt) store.upsert({ ...cur, state: last.state, lastActivityAt: at > cur.lastActivityAt ? at : cur.lastActivityAt });
|
|
7990
|
+
}
|
|
7991
|
+
async function lastState(file, size) {
|
|
7992
|
+
const fh = await open(file, "r");
|
|
7993
|
+
try {
|
|
7994
|
+
const newlineBefore = async (before2) => {
|
|
7995
|
+
const buf2 = Buffer.alloc(BLOCK3);
|
|
7996
|
+
for (let to = before2; to > 0; ) {
|
|
7997
|
+
if (size - to > BACK_BYTES) return null;
|
|
7998
|
+
const from = Math.max(0, to - BLOCK3);
|
|
7999
|
+
const { bytesRead } = await fh.read(buf2, 0, to - from, from);
|
|
8000
|
+
const at = buf2.subarray(0, bytesRead).lastIndexOf(10);
|
|
8001
|
+
if (at !== -1) return from + at;
|
|
8002
|
+
to = from;
|
|
8003
|
+
}
|
|
8004
|
+
return -1;
|
|
8005
|
+
};
|
|
8006
|
+
const last = await newlineBefore(size);
|
|
8007
|
+
if (last === null || last === -1) return { state: null, end: last === -1 ? 0 : size };
|
|
8008
|
+
const end2 = last + 1;
|
|
8009
|
+
for (let lineEnd = last; lineEnd > 0; ) {
|
|
8010
|
+
const start = await newlineBefore(lineEnd);
|
|
8011
|
+
if (start === null) break;
|
|
8012
|
+
const from = start + 1;
|
|
8013
|
+
const line2 = Buffer.alloc(lineEnd - from);
|
|
8014
|
+
await fh.read(line2, 0, line2.length, from);
|
|
8015
|
+
let entry2 = null;
|
|
8016
|
+
try {
|
|
8017
|
+
entry2 = JSON.parse(line2.toString("utf8"));
|
|
8018
|
+
} catch {
|
|
8019
|
+
}
|
|
8020
|
+
const state2 = entryState(entry2);
|
|
8021
|
+
if (state2) return { state: state2, end: end2 };
|
|
8022
|
+
if (start === -1) break;
|
|
8023
|
+
lineEnd = start;
|
|
8024
|
+
}
|
|
8025
|
+
return { state: null, end: end2 };
|
|
8026
|
+
} finally {
|
|
8027
|
+
await fh.close();
|
|
8028
|
+
}
|
|
8029
|
+
}
|
|
8030
|
+
async function tick() {
|
|
8031
|
+
if (running) return;
|
|
8032
|
+
running = true;
|
|
8033
|
+
try {
|
|
8034
|
+
const live3 = /* @__PURE__ */ new Set();
|
|
8035
|
+
for (const s of store.all()) {
|
|
8036
|
+
if (!isOmpSeat2(s) || !s.rig || s.state === "exited" || isArchived(s)) continue;
|
|
8037
|
+
live3.add(s.id);
|
|
8038
|
+
try {
|
|
8039
|
+
await followOne(s);
|
|
8040
|
+
} catch (e) {
|
|
8041
|
+
log(`omp seats: journal of ${s.rig.seat} not read: ${e.message}`);
|
|
8042
|
+
}
|
|
8043
|
+
}
|
|
8044
|
+
for (const id of follows.keys()) if (!live3.has(id)) follows.delete(id);
|
|
8045
|
+
} finally {
|
|
8046
|
+
running = false;
|
|
8047
|
+
}
|
|
8048
|
+
}
|
|
8049
|
+
return {
|
|
8050
|
+
tick,
|
|
8051
|
+
start() {
|
|
8052
|
+
if (!timer2) {
|
|
8053
|
+
timer2 = setInterval(() => {
|
|
8054
|
+
void tick();
|
|
8055
|
+
}, deps.intervalMs ?? 1e3);
|
|
8056
|
+
timer2.unref?.();
|
|
8057
|
+
}
|
|
8058
|
+
},
|
|
8059
|
+
stop() {
|
|
8060
|
+
if (timer2) clearInterval(timer2);
|
|
8061
|
+
timer2 = null;
|
|
8062
|
+
}
|
|
8063
|
+
};
|
|
8064
|
+
}
|
|
8065
|
+
var ompSeatsRoot, BACK_BYTES, BLOCK3, READ_BYTES, isOmpSeat2;
|
|
8066
|
+
var init_seats = __esm({
|
|
8067
|
+
"src/daemon/omp/seats.ts"() {
|
|
8068
|
+
"use strict";
|
|
8069
|
+
init_src();
|
|
8070
|
+
init_log();
|
|
8071
|
+
ompSeatsRoot = (env = process.env) => join10(env.OPENRIG_HOME?.trim() || join10(homedir5(), ".openrig"), "state", "omp");
|
|
8072
|
+
BACK_BYTES = 16 * 1024 * 1024;
|
|
8073
|
+
BLOCK3 = 64 * 1024;
|
|
8074
|
+
READ_BYTES = 4 * 1024 * 1024;
|
|
8075
|
+
isOmpSeat2 = (s) => s.source === "rig" && s.tool === "omp" && s.backend === void 0;
|
|
8076
|
+
}
|
|
8077
|
+
});
|
|
8078
|
+
|
|
7841
8079
|
// src/daemon/poller.ts
|
|
7842
8080
|
import { createHash as createHash4 } from "crypto";
|
|
7843
8081
|
function startPoller(opts) {
|
|
@@ -7902,7 +8140,7 @@ function startPoller(opts) {
|
|
|
7902
8140
|
}
|
|
7903
8141
|
if (s.state === "exited") {
|
|
7904
8142
|
forget(s.id);
|
|
7905
|
-
if (!s.archivedAt && s.exitedAt) {
|
|
8143
|
+
if (!s.archivedAt && s.exitedAt && !isOmpSeat2(s)) {
|
|
7906
8144
|
const since = Math.max(Date.parse(s.exitedAt), s.restoredAt ? Date.parse(s.restoredAt) : -Infinity);
|
|
7907
8145
|
if (t - since > retireMs) store.archive(s.id, now2());
|
|
7908
8146
|
}
|
|
@@ -7915,10 +8153,12 @@ function startPoller(opts) {
|
|
|
7915
8153
|
}
|
|
7916
8154
|
if (!s.tmuxSession) return;
|
|
7917
8155
|
if (deadCheck && !await tmux.hasSession(s.tmuxSession)) {
|
|
7918
|
-
|
|
8156
|
+
const code3 = tmux.exitCode?.(s.tmuxSession);
|
|
8157
|
+
store.apply(s.id, code3 === void 0 ? { name: "PaneGone", at: now2(), source: "daemon" } : { name: "SessionEnd", at: now2(), source: "daemon", exitCode: code3 });
|
|
7919
8158
|
forget(s.id);
|
|
7920
8159
|
return;
|
|
7921
8160
|
}
|
|
8161
|
+
if (isOmpSeat2(s)) return;
|
|
7922
8162
|
const full = await tmux.capture(s.tmuxSession, PREVIEW_CAPTURE_LINES);
|
|
7923
8163
|
if (full === null) return;
|
|
7924
8164
|
const pane = full.split("\n").slice(-DETECT_LINES).join("\n");
|
|
@@ -7983,6 +8223,7 @@ var init_poller = __esm({
|
|
|
7983
8223
|
init_log();
|
|
7984
8224
|
init_git();
|
|
7985
8225
|
init_transcript();
|
|
8226
|
+
init_seats();
|
|
7986
8227
|
init_adapters();
|
|
7987
8228
|
DETECT_LINES = 60;
|
|
7988
8229
|
REST_MS = 2e3;
|
|
@@ -8528,7 +8769,7 @@ var init_project = __esm({
|
|
|
8528
8769
|
|
|
8529
8770
|
// src/daemon/omp/host.ts
|
|
8530
8771
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
8531
|
-
import { basename as basename7, join as
|
|
8772
|
+
import { basename as basename7, join as join11 } from "path";
|
|
8532
8773
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
8533
8774
|
function ompArgs(o) {
|
|
8534
8775
|
return ["--mode", "rpc-ui", "--session-dir", o.dir, "--cwd", o.cwd, "--approval-mode", o.approval ?? APPROVAL_MODE, ...ompModelArgs(o.model, o.effort), ...(o.dirs ?? []).flatMap((d) => ["--add-dir", d]), ...o.resumeFile ? ["--resume", o.resumeFile] : []];
|
|
@@ -8540,7 +8781,7 @@ function createOmpHost(deps) {
|
|
|
8540
8781
|
const live3 = /* @__PURE__ */ new Map();
|
|
8541
8782
|
let generations = 0;
|
|
8542
8783
|
async function startProcess(id, o) {
|
|
8543
|
-
const dir =
|
|
8784
|
+
const dir = join11(deps.dir, id);
|
|
8544
8785
|
await mkdir7(dir, { recursive: true });
|
|
8545
8786
|
if (await reapOwner(dir)) log(`omp ${id}: ended the process a previous daemon left`);
|
|
8546
8787
|
const key = sessionKey(id);
|
|
@@ -8720,15 +8961,15 @@ function createOmpHost(deps) {
|
|
|
8720
8961
|
async answer(id, text7) {
|
|
8721
8962
|
const l = live3.get(id);
|
|
8722
8963
|
if (!l) return "the session is not running";
|
|
8723
|
-
const
|
|
8724
|
-
if (!
|
|
8725
|
-
const a = answerFor(
|
|
8964
|
+
const open6 = l.tracking.requests[0];
|
|
8965
|
+
if (!open6) return "nothing to answer";
|
|
8966
|
+
const a = answerFor(open6, text7);
|
|
8726
8967
|
if ("refused" in a) return a.refused;
|
|
8727
8968
|
l.tracking.requests.shift();
|
|
8728
8969
|
try {
|
|
8729
8970
|
await l.rpc.write(a.frame);
|
|
8730
8971
|
} catch (e) {
|
|
8731
|
-
l.tracking.requests.unshift(
|
|
8972
|
+
l.tracking.requests.unshift(open6);
|
|
8732
8973
|
return e.message;
|
|
8733
8974
|
}
|
|
8734
8975
|
store.apply(id, { name: "Decided", at: now2(), source: "daemon" });
|
|
@@ -8777,15 +9018,15 @@ function createOmpHost(deps) {
|
|
|
8777
9018
|
async answerAsk(id, requestId3, answers, cancel = false) {
|
|
8778
9019
|
const l = live3.get(id);
|
|
8779
9020
|
if (!l) return "the session is not running";
|
|
8780
|
-
const
|
|
8781
|
-
if (!
|
|
8782
|
-
const a = cancel ? { frame: { type: "extension_ui_response", id:
|
|
9021
|
+
const open6 = l.tracking.requests[0];
|
|
9022
|
+
if (!open6 || open6.id !== requestId3) return "the request changed, look again";
|
|
9023
|
+
const a = cancel ? { frame: { type: "extension_ui_response", id: open6.id, cancelled: true } } : askAnswerFor(open6, answers);
|
|
8783
9024
|
if ("refused" in a) return a.refused;
|
|
8784
9025
|
l.tracking.requests.shift();
|
|
8785
9026
|
try {
|
|
8786
9027
|
await l.rpc.write(a.frame);
|
|
8787
9028
|
} catch (e) {
|
|
8788
|
-
l.tracking.requests.unshift(
|
|
9029
|
+
l.tracking.requests.unshift(open6);
|
|
8789
9030
|
return e.message;
|
|
8790
9031
|
}
|
|
8791
9032
|
store.apply(id, { name: "Decided", at: now2(), source: "daemon" });
|
|
@@ -8871,9 +9112,9 @@ var init_catalog = __esm({
|
|
|
8871
9112
|
// src/daemon/repos.ts
|
|
8872
9113
|
import { execFile as execFile2 } from "child_process";
|
|
8873
9114
|
import { constants } from "fs";
|
|
8874
|
-
import { open, stat as
|
|
8875
|
-
import { homedir as
|
|
8876
|
-
import { join as
|
|
9115
|
+
import { open as open2, stat as stat3 } from "fs/promises";
|
|
9116
|
+
import { homedir as homedir6 } from "os";
|
|
9117
|
+
import { join as join12, resolve as resolve2 } from "path";
|
|
8877
9118
|
function runGit(cwd, args) {
|
|
8878
9119
|
return new Promise((res) => {
|
|
8879
9120
|
execFile2("git", ["-C", cwd, ...args], { timeout: GIT_TIMEOUT_MS, windowsHide: true }, (err, out) => res(err ? null : out.trim() || null));
|
|
@@ -8891,7 +9132,7 @@ async function slot(job) {
|
|
|
8891
9132
|
}
|
|
8892
9133
|
}
|
|
8893
9134
|
async function readGitFile(path3, max = 64 * 1024) {
|
|
8894
|
-
const fh = await
|
|
9135
|
+
const fh = await open2(path3, constants.O_RDONLY | (constants.O_NONBLOCK ?? 0));
|
|
8895
9136
|
try {
|
|
8896
9137
|
if (!(await fh.stat()).isFile()) throw new Error(`not a regular file: ${path3}`);
|
|
8897
9138
|
const buf2 = Buffer.alloc(max + 1);
|
|
@@ -8905,7 +9146,7 @@ async function readGitFile(path3, max = 64 * 1024) {
|
|
|
8905
9146
|
async function headBranch(dotGit) {
|
|
8906
9147
|
let head2;
|
|
8907
9148
|
try {
|
|
8908
|
-
head2 = (await readGitFile(
|
|
9149
|
+
head2 = (await readGitFile(join12(dotGit, "HEAD"), MAX_HEAD_BYTES)).trim();
|
|
8909
9150
|
} catch {
|
|
8910
9151
|
return null;
|
|
8911
9152
|
}
|
|
@@ -8914,23 +9155,23 @@ async function headBranch(dotGit) {
|
|
|
8914
9155
|
return /^[0-9a-f]{7,}$/i.test(head2) ? head2.slice(0, 7) : null;
|
|
8915
9156
|
}
|
|
8916
9157
|
async function inspectRepos(paths2, opts = {}) {
|
|
8917
|
-
const home2 = opts.home ??
|
|
9158
|
+
const home2 = opts.home ?? homedir6();
|
|
8918
9159
|
const git = opts.git ?? runGit;
|
|
8919
9160
|
return Promise.all(paths2.map((raw) => slot(async () => {
|
|
8920
9161
|
const input = raw.trim();
|
|
8921
9162
|
const path3 = resolve2(input === "~" ? home2 : input.startsWith("~/") ? resolve2(home2, input.slice(2)) : input);
|
|
8922
9163
|
let isDir2 = false;
|
|
8923
9164
|
try {
|
|
8924
|
-
isDir2 = (await
|
|
9165
|
+
isDir2 = (await stat3(path3)).isDirectory();
|
|
8925
9166
|
} catch {
|
|
8926
9167
|
}
|
|
8927
9168
|
if (!isDir2) return { path: raw, exists: false, branch: null };
|
|
8928
9169
|
let dotDir = false;
|
|
8929
9170
|
try {
|
|
8930
|
-
dotDir = (await
|
|
9171
|
+
dotDir = (await stat3(join12(path3, ".git"))).isDirectory();
|
|
8931
9172
|
} catch {
|
|
8932
9173
|
}
|
|
8933
|
-
if (dotDir) return { path: raw, exists: true, branch: await headBranch(
|
|
9174
|
+
if (dotDir) return { path: raw, exists: true, branch: await headBranch(join12(path3, ".git")) };
|
|
8934
9175
|
const deadline = Date.now() + GIT_TIMEOUT_MS;
|
|
8935
9176
|
const bounded = (args) => new Promise((res) => {
|
|
8936
9177
|
const t = setTimeout(() => res(null), Math.max(0, deadline - Date.now()));
|
|
@@ -8962,7 +9203,7 @@ var init_repos2 = __esm({
|
|
|
8962
9203
|
|
|
8963
9204
|
// src/daemon/scan.ts
|
|
8964
9205
|
import { readdir as fsReaddir } from "fs/promises";
|
|
8965
|
-
import { dirname as dirname4, isAbsolute, join as
|
|
9206
|
+
import { dirname as dirname4, isAbsolute, join as join13, resolve as resolve3, sep as sep2 } from "path";
|
|
8966
9207
|
function extraRoots(home2, cwds) {
|
|
8967
9208
|
const h = resolve3(home2);
|
|
8968
9209
|
const out = [];
|
|
@@ -9008,19 +9249,19 @@ function originUrl(config) {
|
|
|
9008
9249
|
}
|
|
9009
9250
|
return url || void 0;
|
|
9010
9251
|
}
|
|
9011
|
-
async function remoteOf(dir, dotIsFile,
|
|
9252
|
+
async function remoteOf(dir, dotIsFile, readFile20) {
|
|
9012
9253
|
try {
|
|
9013
|
-
let gitDir =
|
|
9254
|
+
let gitDir = join13(dir, ".git");
|
|
9014
9255
|
if (dotIsFile) {
|
|
9015
|
-
const m = /^gitdir:\s*(.+)$/m.exec(await
|
|
9256
|
+
const m = /^gitdir:\s*(.+)$/m.exec(await readFile20(gitDir));
|
|
9016
9257
|
if (!m) return void 0;
|
|
9017
9258
|
gitDir = resolve3(dir, m[1].trim());
|
|
9018
9259
|
try {
|
|
9019
|
-
gitDir = resolve3(gitDir, (await
|
|
9260
|
+
gitDir = resolve3(gitDir, (await readFile20(join13(gitDir, "commondir"))).trim());
|
|
9020
9261
|
} catch {
|
|
9021
9262
|
}
|
|
9022
9263
|
}
|
|
9023
|
-
const url = originUrl(await
|
|
9264
|
+
const url = originUrl(await readFile20(join13(gitDir, "config")));
|
|
9024
9265
|
return url ? repositoryId(url) : void 0;
|
|
9025
9266
|
} catch {
|
|
9026
9267
|
return void 0;
|
|
@@ -9037,18 +9278,18 @@ function manifestName(file, text7) {
|
|
|
9037
9278
|
} else name = /<artifactId>\s*([^<]+?)\s*<\/artifactId>/.exec(text7.replace(/<!--[\s\S]*?-->/g, "").replace(/<parent>[\s\S]*?<\/parent>/g, ""))?.[1];
|
|
9038
9279
|
return typeof name === "string" && name.trim() && name.length <= 200 ? name.trim() : void 0;
|
|
9039
9280
|
}
|
|
9040
|
-
async function describe(dir, dotIsFile, home2,
|
|
9281
|
+
async function describe(dir, dotIsFile, home2, readFile20, manifest) {
|
|
9041
9282
|
const [branch, remote, named] = await Promise.all([
|
|
9042
|
-
dotIsFile ? inspectRepos([dir], { home: home2 }).then((r) => r[0]?.branch ?? null) : headBranch(
|
|
9043
|
-
remoteOf(dir, dotIsFile,
|
|
9044
|
-
manifest ?
|
|
9283
|
+
dotIsFile ? inspectRepos([dir], { home: home2 }).then((r) => r[0]?.branch ?? null) : headBranch(join13(dir, ".git")),
|
|
9284
|
+
remoteOf(dir, dotIsFile, readFile20),
|
|
9285
|
+
manifest ? readFile20(join13(dir, manifest)).then((text7) => manifestName(manifest, text7), () => void 0) : void 0
|
|
9045
9286
|
]);
|
|
9046
9287
|
return { path: dir, branch, ...remote ? { remote } : {}, ...named ? { manifest: named } : {} };
|
|
9047
9288
|
}
|
|
9048
9289
|
async function scanRepos(opts) {
|
|
9049
9290
|
const now2 = opts.now ?? Date.now;
|
|
9050
9291
|
const readdir5 = opts.readdir ?? ((p) => fsReaddir(p, { withFileTypes: true }));
|
|
9051
|
-
const
|
|
9292
|
+
const readFile20 = opts.readFile ?? ((p) => readGitFile(p));
|
|
9052
9293
|
const depth = opts.limits?.depth ?? SCAN_DEPTH, maxDirs = opts.limits?.dirs ?? SCAN_DIRS, ms = opts.limits?.ms ?? SCAN_MS, maxRepos = opts.limits?.repos ?? SCAN_REPOS;
|
|
9053
9294
|
const deadline = now2() + ms;
|
|
9054
9295
|
let timer2;
|
|
@@ -9083,11 +9324,11 @@ async function scanRepos(opts) {
|
|
|
9083
9324
|
if (!entries) return;
|
|
9084
9325
|
const dot = entries.find((e) => e.name === ".git" && (e.isDirectory() || e.isFile()));
|
|
9085
9326
|
const manifest = dot ? MANIFESTS.find((m) => entries.some((e) => e.name === m && e.isFile())) : void 0;
|
|
9086
|
-
if (dot) found.push(describe(d.path, dot.isFile(), opts.home,
|
|
9327
|
+
if (dot) found.push(describe(d.path, dot.isFile(), opts.home, readFile20, manifest));
|
|
9087
9328
|
if (dot && d.path !== home2 || d.depth >= depth) return;
|
|
9088
9329
|
const names = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && !SKIP.has(e.name)).map((e) => e.name).sort();
|
|
9089
9330
|
for (const name of names) {
|
|
9090
|
-
const path3 =
|
|
9331
|
+
const path3 = join13(d.path, name);
|
|
9091
9332
|
if (!seen.has(path3)) {
|
|
9092
9333
|
seen.add(path3);
|
|
9093
9334
|
queue.push({ path: path3, depth: d.depth + 1 });
|
|
@@ -9131,23 +9372,23 @@ var init_scan2 = __esm({
|
|
|
9131
9372
|
});
|
|
9132
9373
|
|
|
9133
9374
|
// src/daemon/folders.ts
|
|
9134
|
-
import { mkdir as mkdir8, readdir as readdir3, stat as
|
|
9135
|
-
import { homedir as
|
|
9136
|
-
import { dirname as dirname5, isAbsolute as isAbsolute2, join as
|
|
9375
|
+
import { mkdir as mkdir8, readdir as readdir3, stat as stat4 } from "fs/promises";
|
|
9376
|
+
import { homedir as homedir7 } from "os";
|
|
9377
|
+
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join14, resolve as resolve4 } from "path";
|
|
9137
9378
|
async function resolveExact(raw, home2) {
|
|
9138
9379
|
const expanded = raw === "~" ? home2 : raw.startsWith("~/") ? resolve4(home2, raw.slice(2)) : raw;
|
|
9139
9380
|
if (!isAbsolute2(expanded)) throw new Error(`not an absolute path: ${raw}`);
|
|
9140
9381
|
const path3 = resolve4(expanded);
|
|
9141
9382
|
let s;
|
|
9142
9383
|
try {
|
|
9143
|
-
s = await
|
|
9384
|
+
s = await stat4(path3);
|
|
9144
9385
|
} catch {
|
|
9145
9386
|
throw new Error(`no such folder: ${path3}`);
|
|
9146
9387
|
}
|
|
9147
9388
|
if (!s.isDirectory()) throw new Error(`not a folder: ${path3}`);
|
|
9148
9389
|
return path3;
|
|
9149
9390
|
}
|
|
9150
|
-
async function resolveFolder(input, home2 =
|
|
9391
|
+
async function resolveFolder(input, home2 = homedir7()) {
|
|
9151
9392
|
return resolveExact(input.trim(), home2);
|
|
9152
9393
|
}
|
|
9153
9394
|
async function subfolders(path3) {
|
|
@@ -9156,7 +9397,7 @@ async function subfolders(path3) {
|
|
|
9156
9397
|
if (d.isDirectory()) return d.name;
|
|
9157
9398
|
if (!d.isSymbolicLink()) return null;
|
|
9158
9399
|
try {
|
|
9159
|
-
return (await
|
|
9400
|
+
return (await stat4(join14(path3, d.name))).isDirectory() ? d.name : null;
|
|
9160
9401
|
} catch {
|
|
9161
9402
|
return null;
|
|
9162
9403
|
}
|
|
@@ -9164,42 +9405,42 @@ async function subfolders(path3) {
|
|
|
9164
9405
|
return found.filter((n) => n !== null).sort(byName.compare);
|
|
9165
9406
|
}
|
|
9166
9407
|
async function gitBranchOf(dir, git) {
|
|
9167
|
-
const dot =
|
|
9408
|
+
const dot = join14(dir, ".git");
|
|
9168
9409
|
let s;
|
|
9169
9410
|
try {
|
|
9170
|
-
s = await
|
|
9411
|
+
s = await stat4(dot);
|
|
9171
9412
|
} catch {
|
|
9172
9413
|
return null;
|
|
9173
9414
|
}
|
|
9174
9415
|
if (s.isFile()) return (await inspectRepos([dir], { git }))[0]?.branch ?? null;
|
|
9175
9416
|
return s.isDirectory() ? headBranch(dot) : null;
|
|
9176
9417
|
}
|
|
9177
|
-
async function listFolder(input, home2 =
|
|
9418
|
+
async function listFolder(input, home2 = homedir7(), requestId3, git) {
|
|
9178
9419
|
const tag = requestId3 === void 0 ? {} : { requestId: requestId3 };
|
|
9179
9420
|
const asked2 = input || "~";
|
|
9180
9421
|
try {
|
|
9181
9422
|
const path3 = await resolveExact(asked2, home2);
|
|
9182
9423
|
const parent = dirname5(path3);
|
|
9183
9424
|
const entries = await subfolders(path3);
|
|
9184
|
-
const branches = await Promise.all(entries.map((name) => gitBranchOf(
|
|
9425
|
+
const branches = await Promise.all(entries.map((name) => gitBranchOf(join14(path3, name), git)));
|
|
9185
9426
|
const repos = entries.flatMap((name, i) => branches[i] === null ? [] : [{ name, branch: branches[i] }]);
|
|
9186
9427
|
return { type: "folder.listing", path: path3, parent: parent === path3 ? null : parent, entries, repos, ...tag };
|
|
9187
9428
|
} catch (e) {
|
|
9188
9429
|
return { type: "folder.listing", path: asked2, parent: null, entries: [], error: e.message, ...tag };
|
|
9189
9430
|
}
|
|
9190
9431
|
}
|
|
9191
|
-
async function createFolder(parent, name, home2 =
|
|
9432
|
+
async function createFolder(parent, name, home2 = homedir7(), requestId3) {
|
|
9192
9433
|
const at = await listFolder(parent, home2, requestId3);
|
|
9193
9434
|
if (at.error) return at;
|
|
9194
9435
|
const clean = name.trim();
|
|
9195
9436
|
if (!clean || clean === "." || clean === ".." || /[/\0]/.test(clean)) return { ...at, error: `invalid folder name: ${name}` };
|
|
9196
9437
|
try {
|
|
9197
|
-
await mkdir8(
|
|
9438
|
+
await mkdir8(join14(at.path, clean));
|
|
9198
9439
|
} catch (e) {
|
|
9199
9440
|
const code3 = e.code;
|
|
9200
9441
|
return { ...at, error: code3 === "EEXIST" ? `already exists: ${clean}` : `cannot create ${clean}: ${e.message}` };
|
|
9201
9442
|
}
|
|
9202
|
-
return listFolder(
|
|
9443
|
+
return listFolder(join14(at.path, clean), home2, requestId3);
|
|
9203
9444
|
}
|
|
9204
9445
|
var byName;
|
|
9205
9446
|
var init_folders = __esm({
|
|
@@ -9263,7 +9504,7 @@ function taskOf2(ref, ctx) {
|
|
|
9263
9504
|
return taskClosed(t) ? no("closed", `task ${t.id} is closed`) : t;
|
|
9264
9505
|
}
|
|
9265
9506
|
function writable(s) {
|
|
9266
|
-
return s.source === "hook_only" ? no("read_only", `${s.name} is read-only`) : s.state === "exited" ? no("exited", `${s.name} has exited`) : null;
|
|
9507
|
+
return s.source === "hook_only" || isOmpSeat2(s) ? no("read_only", `${s.name} is read-only`) : s.state === "exited" ? no("exited", `${s.name} has exited`) : null;
|
|
9267
9508
|
}
|
|
9268
9509
|
function cardRefusal(row3) {
|
|
9269
9510
|
if (row3.action !== "session.decide" || !row3.detailCut || row3.args.decision !== "accept") return null;
|
|
@@ -9316,6 +9557,7 @@ var init_actions2 = __esm({
|
|
|
9316
9557
|
"src/daemon/actions.ts"() {
|
|
9317
9558
|
"use strict";
|
|
9318
9559
|
init_src();
|
|
9560
|
+
init_seats();
|
|
9319
9561
|
DECIDABLE = ["permission", "diff", "plan"];
|
|
9320
9562
|
KIND = { permission: "permission", diff: "diff", plan: "plan" };
|
|
9321
9563
|
no = (code3, refused) => ({ refused, code: code3 });
|
|
@@ -9435,13 +9677,13 @@ ${p.body}` : p.text) }
|
|
|
9435
9677
|
});
|
|
9436
9678
|
|
|
9437
9679
|
// src/chat/guide.ts
|
|
9438
|
-
import { access, constants as constants2, readFile as
|
|
9439
|
-
import { delimiter, join as
|
|
9680
|
+
import { access, constants as constants2, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
|
|
9681
|
+
import { delimiter, join as join15 } from "path";
|
|
9440
9682
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9441
9683
|
async function readGuide(files = GUIDE_FILES) {
|
|
9442
9684
|
for (const f of files) {
|
|
9443
9685
|
try {
|
|
9444
|
-
return await
|
|
9686
|
+
return await readFile8(f, "utf8");
|
|
9445
9687
|
} catch {
|
|
9446
9688
|
}
|
|
9447
9689
|
}
|
|
@@ -9453,7 +9695,7 @@ async function installedAgents(path3) {
|
|
|
9453
9695
|
for (const tool of ToolSchema.options) {
|
|
9454
9696
|
for (const d of dirs) {
|
|
9455
9697
|
try {
|
|
9456
|
-
await access(
|
|
9698
|
+
await access(join15(d, AGENT_BIN2[tool]), constants2.X_OK);
|
|
9457
9699
|
found.push(tool);
|
|
9458
9700
|
break;
|
|
9459
9701
|
} catch {
|
|
@@ -9485,7 +9727,7 @@ function ledgeMd(info) {
|
|
|
9485
9727
|
].join("\n");
|
|
9486
9728
|
}
|
|
9487
9729
|
async function writeLedgeMd(dir, info) {
|
|
9488
|
-
await writeFile8(
|
|
9730
|
+
await writeFile8(join15(dir, LEDGE_MD), ledgeMd(info));
|
|
9489
9731
|
}
|
|
9490
9732
|
var LEDGE_MD, GUIDE_FILES, AGENT_BIN2, RISK;
|
|
9491
9733
|
var init_guide = __esm({
|
|
@@ -9500,8 +9742,8 @@ var init_guide = __esm({
|
|
|
9500
9742
|
});
|
|
9501
9743
|
|
|
9502
9744
|
// src/chat/notes.ts
|
|
9503
|
-
import { open as
|
|
9504
|
-
import { join as
|
|
9745
|
+
import { open as open3, readdir as readdir4, stat as stat5, writeFile as writeFile9 } from "fs/promises";
|
|
9746
|
+
import { join as join16 } from "path";
|
|
9505
9747
|
function scalarValue(raw) {
|
|
9506
9748
|
const v2 = raw.trim();
|
|
9507
9749
|
const q = v2[0];
|
|
@@ -9533,7 +9775,7 @@ function copyName(file, taken) {
|
|
|
9533
9775
|
return name;
|
|
9534
9776
|
}
|
|
9535
9777
|
async function head(path3, max) {
|
|
9536
|
-
const fh = await
|
|
9778
|
+
const fh = await open3(path3, "r");
|
|
9537
9779
|
try {
|
|
9538
9780
|
const buf2 = Buffer.alloc(max);
|
|
9539
9781
|
const { bytesRead } = await fh.read(buf2, 0, max, 0);
|
|
@@ -9552,12 +9794,12 @@ async function writeNotes(dir, from, steps, max = NOTE_MAX2) {
|
|
|
9552
9794
|
if (!files.length) lines.push(`- (none: the user can add Markdown files to \`${from}\`, with an optional front matter \`title\` and \`when\`)`);
|
|
9553
9795
|
const taken = /* @__PURE__ */ new Set();
|
|
9554
9796
|
for (const file of files.slice(0, NOTES_MAX)) {
|
|
9555
|
-
const path3 =
|
|
9797
|
+
const path3 = join16(from, file);
|
|
9556
9798
|
let text7;
|
|
9557
9799
|
let size;
|
|
9558
9800
|
let mtime;
|
|
9559
9801
|
try {
|
|
9560
|
-
const st = await
|
|
9802
|
+
const st = await stat5(path3);
|
|
9561
9803
|
size = st.size;
|
|
9562
9804
|
mtime = st.mtime.toISOString();
|
|
9563
9805
|
text7 = await head(path3, max);
|
|
@@ -9574,15 +9816,15 @@ async function writeNotes(dir, from, steps, max = NOTE_MAX2) {
|
|
|
9574
9816
|
const when = fields.when?.trim() ? `when: ${line(fields.when)}` : `first line: ${line(body.split("\n").find((l) => l.trim()) ?? "") || "(empty)"}`;
|
|
9575
9817
|
const truncated = size > max;
|
|
9576
9818
|
const name = copyName(file, taken);
|
|
9577
|
-
await writeFile9(
|
|
9819
|
+
await writeFile9(join16(dir, name), truncated ? `${text7}
|
|
9578
9820
|
|
|
9579
9821
|
[truncated by Ledge: the first ${max} of ${size} bytes]
|
|
9580
9822
|
` : text7);
|
|
9581
|
-
steps.set(name, { kind: "repo", name: file, path:
|
|
9823
|
+
steps.set(name, { kind: "repo", name: file, path: join16(from, file) });
|
|
9582
9824
|
lines.push(`- ${name} \u2014 \xAB ${title3} \xBB \u2014 ${when} \u2014 modified ${mtime}${truncated ? ` \u2014 TRUNCATED: only the first ${max} of ${size} bytes are copied` : ""}`);
|
|
9583
9825
|
}
|
|
9584
9826
|
if (files.length > NOTES_MAX) lines.push(`- (${files.length - NOTES_MAX} more notes not copied: Ledge copies ${NOTES_MAX} at most)`);
|
|
9585
|
-
await writeFile9(
|
|
9827
|
+
await writeFile9(join16(dir, NOTES_INDEX), [...lines, ""].join("\n"));
|
|
9586
9828
|
}
|
|
9587
9829
|
var NOTE_MAX2, NOTES_MAX, NOTES_INDEX, unquote, line;
|
|
9588
9830
|
var init_notes = __esm({
|
|
@@ -9601,8 +9843,8 @@ var init_notes = __esm({
|
|
|
9601
9843
|
|
|
9602
9844
|
// src/chat/context.ts
|
|
9603
9845
|
import { mkdir as mkdir9, rm as rm2, writeFile as writeFile10 } from "fs/promises";
|
|
9604
|
-
import { homedir as
|
|
9605
|
-
import { basename as basename8, join as
|
|
9846
|
+
import { homedir as homedir8 } from "os";
|
|
9847
|
+
import { basename as basename8, join as join17, resolve as resolve5 } from "path";
|
|
9606
9848
|
async function writeContext(dir, input) {
|
|
9607
9849
|
await rm2(dir, { recursive: true, force: true });
|
|
9608
9850
|
await mkdir9(dir, { recursive: true });
|
|
@@ -9631,29 +9873,29 @@ async function writeContext(dir, input) {
|
|
|
9631
9873
|
const pane = await input.capture(s).catch(() => null);
|
|
9632
9874
|
if (pane?.length) {
|
|
9633
9875
|
const f = `pane-${s.id}.txt`;
|
|
9634
|
-
await writeFile10(
|
|
9876
|
+
await writeFile10(join17(dir, f), pane.join("\n"));
|
|
9635
9877
|
steps.set(f, { kind: "pane", id: s.id, name: s.name });
|
|
9636
9878
|
lines.push(`- terminal, last lines: ${f}`);
|
|
9637
9879
|
}
|
|
9638
9880
|
if (s.lastReply?.text.trim()) {
|
|
9639
9881
|
const f = `reply-${s.id}.md`;
|
|
9640
|
-
await writeFile10(
|
|
9882
|
+
await writeFile10(join17(dir, f), s.lastReply.text);
|
|
9641
9883
|
steps.set(f, { kind: "reply", id: s.id, name: s.name });
|
|
9642
9884
|
lines.push(`- last reply (${s.lastReply.at}): ${f}`);
|
|
9643
9885
|
}
|
|
9644
9886
|
const g = await input.git(s.cwd).catch(() => null);
|
|
9645
9887
|
if (g) {
|
|
9646
9888
|
const f = `git-${s.id}.txt`;
|
|
9647
|
-
await writeFile10(
|
|
9889
|
+
await writeFile10(join17(dir, f), g);
|
|
9648
9890
|
steps.set(f, { kind: "git", id: s.id, name: s.name });
|
|
9649
9891
|
lines.push(`- git status, log and diff stat: ${f}`);
|
|
9650
9892
|
}
|
|
9651
9893
|
lines.push("");
|
|
9652
9894
|
}
|
|
9653
|
-
await writeFile10(
|
|
9895
|
+
await writeFile10(join17(dir, "sessions.md"), lines.join("\n"));
|
|
9654
9896
|
steps.set("sessions.md", { kind: "sessions" });
|
|
9655
9897
|
const recents = input.recents ?? [];
|
|
9656
|
-
await writeFile10(
|
|
9898
|
+
await writeFile10(join17(dir, "folders.md"), [
|
|
9657
9899
|
"# Recent folders",
|
|
9658
9900
|
"",
|
|
9659
9901
|
"The folders the user started sessions in, most recent first.",
|
|
@@ -9661,7 +9903,7 @@ async function writeContext(dir, input) {
|
|
|
9661
9903
|
...recents.length ? recents.map((r) => `- ${r.project}: ${r.cwd} (last used ${r.lastAt})`) : ["- (none yet)"],
|
|
9662
9904
|
""
|
|
9663
9905
|
].join("\n"));
|
|
9664
|
-
if (withProjects) await writeFile10(
|
|
9906
|
+
if (withProjects) await writeFile10(join17(dir, "projects.md"), [
|
|
9665
9907
|
"# Projects",
|
|
9666
9908
|
"",
|
|
9667
9909
|
...projects.projects.length ? projects.projects.flatMap((p) => [`## ${oneLine(p.name)}`, ...p.description.trim() ? [oneLine(p.description)] : [], "", ...p.repos.map((r) => `- ${r.machine}: ${r.path}`), ""]) : ["No projects.", ""],
|
|
@@ -9679,7 +9921,7 @@ async function writeContext(dir, input) {
|
|
|
9679
9921
|
async function writeTasksContext(dir, taskList, steps) {
|
|
9680
9922
|
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
9681
9923
|
const shown = taskList.filter((t) => !taskClosed(t) || t.updatedAt.slice(0, 10) === day);
|
|
9682
|
-
await writeFile10(
|
|
9924
|
+
await writeFile10(join17(dir, "tasks.md"), [
|
|
9683
9925
|
"# Tasks",
|
|
9684
9926
|
"",
|
|
9685
9927
|
"Open tasks and those closed today. A giver `Ledge` is the user, through you.",
|
|
@@ -9691,7 +9933,7 @@ async function writeTasksContext(dir, taskList, steps) {
|
|
|
9691
9933
|
}
|
|
9692
9934
|
function coversHome(path3) {
|
|
9693
9935
|
const p = resolve5(path3);
|
|
9694
|
-
const home2 = resolve5(
|
|
9936
|
+
const home2 = resolve5(homedir8());
|
|
9695
9937
|
return p === home2 || home2.startsWith(p.endsWith("/") ? p : `${p}/`);
|
|
9696
9938
|
}
|
|
9697
9939
|
function stepFor(path3, ctx) {
|
|
@@ -9730,14 +9972,14 @@ var LEDGE_VERSION;
|
|
|
9730
9972
|
var init_version = __esm({
|
|
9731
9973
|
"src/version.ts"() {
|
|
9732
9974
|
"use strict";
|
|
9733
|
-
LEDGE_VERSION = true ? "0.
|
|
9975
|
+
LEDGE_VERSION = true ? "0.41.0-snapshot.41719405620.1" : "0.0.0-dev";
|
|
9734
9976
|
}
|
|
9735
9977
|
});
|
|
9736
9978
|
|
|
9737
9979
|
// src/chat/forge.ts
|
|
9738
9980
|
import { execFile as execFile4 } from "child_process";
|
|
9739
9981
|
import { writeFile as writeFile11 } from "fs/promises";
|
|
9740
|
-
import { join as
|
|
9982
|
+
import { join as join18 } from "path";
|
|
9741
9983
|
function forgeOf(remote) {
|
|
9742
9984
|
const id = repositoryId(remote);
|
|
9743
9985
|
if (!id) return null;
|
|
@@ -9802,12 +10044,12 @@ function createForge(deps = {}) {
|
|
|
9802
10044
|
return;
|
|
9803
10045
|
}
|
|
9804
10046
|
const parts2 = await Promise.all(local.map((r) => section(r.path)));
|
|
9805
|
-
await writeFile11(
|
|
10047
|
+
await writeFile11(join18(ctx.dir, file), parts2.flatMap((s) => s.lines).join("\n"));
|
|
9806
10048
|
const sources = parts2.flatMap((s) => s.source ? [s.source] : []);
|
|
9807
10049
|
ctx.steps.set(file, { kind: "repo", name, path: sources.join(", ") || file });
|
|
9808
10050
|
index2.push(`- ${name}: ${file}`);
|
|
9809
10051
|
}));
|
|
9810
|
-
await writeFile11(
|
|
10052
|
+
await writeFile11(join18(ctx.dir, "forge.md"), [...index2, ""].join("\n"));
|
|
9811
10053
|
}
|
|
9812
10054
|
};
|
|
9813
10055
|
}
|
|
@@ -10074,7 +10316,7 @@ var init_codex2 = __esm({
|
|
|
10074
10316
|
// src/chat/cursor.ts
|
|
10075
10317
|
import { spawn as nodeSpawn5 } from "child_process";
|
|
10076
10318
|
import { copyFileSync, writeFileSync } from "fs";
|
|
10077
|
-
import { extname, join as
|
|
10319
|
+
import { extname, join as join19 } from "path";
|
|
10078
10320
|
function cursorArgs(o) {
|
|
10079
10321
|
return [
|
|
10080
10322
|
"-p",
|
|
@@ -10116,10 +10358,10 @@ function parseCursorLine(line2) {
|
|
|
10116
10358
|
}
|
|
10117
10359
|
function runCursor(o, onEvent, spawnImpl = nodeSpawn5) {
|
|
10118
10360
|
const [file = "cursor-agent", ...pre] = o.bin ?? ["cursor-agent"];
|
|
10119
|
-
writeFileSync(
|
|
10361
|
+
writeFileSync(join19(o.cwd, "AGENTS.md"), `${o.persona ?? buildPersona("cursor")}
|
|
10120
10362
|
`);
|
|
10121
10363
|
const copies = (o.images ?? []).map((img, n) => {
|
|
10122
|
-
const to =
|
|
10364
|
+
const to = join19(o.cwd, `image-${n + 1}${extname(img.path)}`);
|
|
10123
10365
|
copyFileSync(img.path, to);
|
|
10124
10366
|
return to;
|
|
10125
10367
|
});
|
|
@@ -10203,15 +10445,15 @@ var init_omp = __esm({
|
|
|
10203
10445
|
|
|
10204
10446
|
// src/chat/service.ts
|
|
10205
10447
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
10206
|
-
import { mkdir as mkdir10, readFile as
|
|
10207
|
-
import { homedir as
|
|
10208
|
-
import { basename as basename9, dirname as dirname6, join as
|
|
10209
|
-
function droppedNote(total,
|
|
10210
|
-
const dropped = total -
|
|
10448
|
+
import { mkdir as mkdir10, readFile as readFile9, rm as rm3, writeFile as writeFile12 } from "fs/promises";
|
|
10449
|
+
import { homedir as homedir9 } from "os";
|
|
10450
|
+
import { basename as basename9, dirname as dirname6, join as join20, resolve as resolve6 } from "path";
|
|
10451
|
+
function droppedNote(total, kept2, reasons, counted) {
|
|
10452
|
+
const dropped = total - kept2;
|
|
10211
10453
|
if (dropped <= 0) return "";
|
|
10212
10454
|
const rest = dropped - counted;
|
|
10213
10455
|
const why = reasons.length === 0 ? [] : [...reasons, ...rest > 0 ? [`${rest} autre${rest > 1 ? "s" : ""}`] : []];
|
|
10214
|
-
const head2 = total === 1 ? "Note : la carte ne reprend pas l\u2019action propos\xE9e" :
|
|
10456
|
+
const head2 = total === 1 ? "Note : la carte ne reprend pas l\u2019action propos\xE9e" : kept2 === 0 ? `Note : la carte ne reprend aucune des ${total} actions propos\xE9es` : `Note : la carte ne reprend que ${kept2} des ${total} actions propos\xE9es`;
|
|
10215
10457
|
return why.length ? `${head2} (${why.join(" ; ")}).` : `${head2}.`;
|
|
10216
10458
|
}
|
|
10217
10459
|
function tooWide(path3, userHome) {
|
|
@@ -10226,7 +10468,7 @@ function createChat(deps) {
|
|
|
10226
10468
|
const now2 = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
10227
10469
|
const today = deps.today ?? (() => now2().slice(0, 10));
|
|
10228
10470
|
const uuid = deps.uuid ?? randomUUID7;
|
|
10229
|
-
const dayFile =
|
|
10471
|
+
const dayFile = join20(deps.home, "chat.json");
|
|
10230
10472
|
let turns2 = [];
|
|
10231
10473
|
let turnsDay = "";
|
|
10232
10474
|
let busy = false;
|
|
@@ -10249,7 +10491,7 @@ function createChat(deps) {
|
|
|
10249
10491
|
const day = today();
|
|
10250
10492
|
if (!fresh && !freshNext) {
|
|
10251
10493
|
try {
|
|
10252
|
-
const saved = JSON.parse(await
|
|
10494
|
+
const saved = JSON.parse(await readFile9(dayFile, "utf8"));
|
|
10253
10495
|
if (saved.day === day && saved.sessionId && (saved.tool ?? "claude") === tool) return { sessionId: saved.sessionId, resume: true };
|
|
10254
10496
|
} catch {
|
|
10255
10497
|
}
|
|
@@ -10265,7 +10507,7 @@ function createChat(deps) {
|
|
|
10265
10507
|
const p = await deps.tmux.capture(s.tmuxSession, PANE_LINES);
|
|
10266
10508
|
return p === null ? null : p.split("\n");
|
|
10267
10509
|
};
|
|
10268
|
-
const contextDir =
|
|
10510
|
+
const contextDir = join20(deps.home, "chat", "context");
|
|
10269
10511
|
async function proposalFrom(raw, tool, qid) {
|
|
10270
10512
|
const parsed = parseActionBlock("session", raw);
|
|
10271
10513
|
if ("error" in parsed) {
|
|
@@ -10342,7 +10584,7 @@ function createChat(deps) {
|
|
|
10342
10584
|
for (const r of block2.repos) {
|
|
10343
10585
|
try {
|
|
10344
10586
|
const path3 = await resolveFolder(r.path, deps.userHome);
|
|
10345
|
-
const wide = tooWide(path3, resolve6(deps.userHome ??
|
|
10587
|
+
const wide = tooWide(path3, resolve6(deps.userHome ?? homedir9()));
|
|
10346
10588
|
if (wide) {
|
|
10347
10589
|
log(`chat ${qid}: project path dropped: ${wide}: ${path3}`);
|
|
10348
10590
|
continue;
|
|
@@ -10458,7 +10700,7 @@ function createChat(deps) {
|
|
|
10458
10700
|
let resultError = null;
|
|
10459
10701
|
const handle2 = runTool(tool, { question: mine.told.text ? `${mine.told.text}
|
|
10460
10702
|
|
|
10461
|
-
${turn.question}` : turn.question, cwd: ctx.dir, addDirs: ctx.repoPaths, sessionId: sessionId2, resume, env, bin: deps.bins?.[tool] ?? deps.bin, ...images.length > 0 ? { images } : {}, ...tool === "omp" ? { sessionDir:
|
|
10703
|
+
${turn.question}` : turn.question, cwd: ctx.dir, addDirs: ctx.repoPaths, sessionId: sessionId2, resume, env, bin: deps.bins?.[tool] ?? deps.bin, ...images.length > 0 ? { images } : {}, ...tool === "omp" ? { sessionDir: join20(deps.home, "chat", "omp", sessionId2) } : {} }, (e) => {
|
|
10462
10704
|
if (e.type === "tool") {
|
|
10463
10705
|
const step3 = e.path ? stepFor(e.path, ctx) : null;
|
|
10464
10706
|
if (step3 && !turn.steps.some((s) => sameStep(s, step3))) {
|
|
@@ -10523,8 +10765,8 @@ ${turn.question}` : turn.question, cwd: ctx.dir, addDirs: ctx.repoPaths, session
|
|
|
10523
10765
|
if (taskBlocks.length > MAX_PROPOSALS) log(`chat ${turn.qid}: ${taskBlocks.length - MAX_PROPOSALS} tasks beyond ${MAX_PROPOSALS} ignored`);
|
|
10524
10766
|
const tasks = tasksFrom(taskBlocks.slice(0, MAX_PROPOSALS), turn.qid);
|
|
10525
10767
|
const { rows: actions, reasons, counted } = actionsFrom(generic, turn.qid, new Set(closes.map((c) => c.id)));
|
|
10526
|
-
const
|
|
10527
|
-
const note = droppedNote(found.length,
|
|
10768
|
+
const kept2 = proposals.length + (relay ? 1 : 0) + closes.length + (projectProposal ? 1 : 0) + tasks.length + actions.length;
|
|
10769
|
+
const note = droppedNote(found.length, kept2, [...reasons, ...unreadable > 0 ? [`${unreadable} illisible${unreadable > 1 ? "s" : ""}`] : []], counted + unreadable);
|
|
10528
10770
|
const text7 = note ? answerText ? `${answerText}
|
|
10529
10771
|
|
|
10530
10772
|
${note}` : note : answerText;
|
|
@@ -10745,8 +10987,8 @@ var init_service = __esm({
|
|
|
10745
10987
|
});
|
|
10746
10988
|
|
|
10747
10989
|
// src/chat/threads.ts
|
|
10748
|
-
import { readFile as
|
|
10749
|
-
import { join as
|
|
10990
|
+
import { readFile as readFile10, rm as rm4 } from "fs/promises";
|
|
10991
|
+
import { join as join21 } from "path";
|
|
10750
10992
|
function createSlots(max = 3) {
|
|
10751
10993
|
let used = 0;
|
|
10752
10994
|
return {
|
|
@@ -10767,7 +11009,7 @@ function createChatThreads(deps) {
|
|
|
10767
11009
|
const devices = /* @__PURE__ */ new Map();
|
|
10768
11010
|
const emitters = /* @__PURE__ */ new Map();
|
|
10769
11011
|
const dropping = /* @__PURE__ */ new Map();
|
|
10770
|
-
const deviceHome = (id) =>
|
|
11012
|
+
const deviceHome = (id) => join21(deps.home, "mobile", "chat", id);
|
|
10771
11013
|
const device = (id) => {
|
|
10772
11014
|
checkId(id);
|
|
10773
11015
|
if (dropping.has(id)) throw new Error("device thread is being dropped");
|
|
@@ -10796,7 +11038,7 @@ function createChatThreads(deps) {
|
|
|
10796
11038
|
/** The PC's tool of the day file, whatever its day: `claude` without one. */
|
|
10797
11039
|
async deviceTool() {
|
|
10798
11040
|
try {
|
|
10799
|
-
const t = JSON.parse(await
|
|
11041
|
+
const t = JSON.parse(await readFile10(join21(deps.home, "chat.json"), "utf8")).tool;
|
|
10800
11042
|
return t === "claude" || t === "codex" || t === "cursor" || t === "omp" ? t : "claude";
|
|
10801
11043
|
} catch {
|
|
10802
11044
|
return "claude";
|
|
@@ -10963,7 +11205,7 @@ var init_island2 = __esm({
|
|
|
10963
11205
|
|
|
10964
11206
|
// src/daemon/documents.ts
|
|
10965
11207
|
import { constants as constants3 } from "fs";
|
|
10966
|
-
import { open as
|
|
11208
|
+
import { open as open4, realpath } from "fs/promises";
|
|
10967
11209
|
import { isAbsolute as isAbsolute3, dirname as dirname7, extname as extname2, resolve as resolve7 } from "path";
|
|
10968
11210
|
async function readDocument(request2, context) {
|
|
10969
11211
|
const source2 = request2.path;
|
|
@@ -10984,10 +11226,10 @@ async function readDocument(request2, context) {
|
|
|
10984
11226
|
async function readAt(request2, path3) {
|
|
10985
11227
|
let file;
|
|
10986
11228
|
try {
|
|
10987
|
-
file = await
|
|
10988
|
-
const
|
|
10989
|
-
if (!
|
|
10990
|
-
if (
|
|
11229
|
+
file = await open4(path3, constants3.O_RDONLY | constants3.O_NONBLOCK);
|
|
11230
|
+
const stat8 = await file.stat();
|
|
11231
|
+
if (!stat8.isFile()) return failure(request2, "not_file");
|
|
11232
|
+
if (stat8.size > DOCUMENT_MAX_BYTES) return failure(request2, "too_large");
|
|
10991
11233
|
const chunk = Buffer.allocUnsafe(64 * 1024);
|
|
10992
11234
|
const parts2 = [];
|
|
10993
11235
|
let bytes4 = 0;
|
|
@@ -11010,7 +11252,7 @@ async function readAt(request2, path3) {
|
|
|
11010
11252
|
path: await realpath(path3),
|
|
11011
11253
|
text: text7,
|
|
11012
11254
|
bytes: bytes4,
|
|
11013
|
-
modifiedAt:
|
|
11255
|
+
modifiedAt: stat8.mtime.toISOString()
|
|
11014
11256
|
};
|
|
11015
11257
|
} catch (e) {
|
|
11016
11258
|
return failure(request2, errorCode(e));
|
|
@@ -11039,9 +11281,9 @@ var init_documents2 = __esm({
|
|
|
11039
11281
|
|
|
11040
11282
|
// src/daemon/models.ts
|
|
11041
11283
|
import { execFile as execFile5 } from "child_process";
|
|
11042
|
-
import { readFile as
|
|
11043
|
-
import { homedir as
|
|
11044
|
-
import { join as
|
|
11284
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
11285
|
+
import { homedir as homedir10 } from "os";
|
|
11286
|
+
import { join as join22 } from "path";
|
|
11045
11287
|
function claudeModels(settingsModel) {
|
|
11046
11288
|
const efforts = [...CLAUDE_EFFORTS];
|
|
11047
11289
|
const models = CLAUDE_MODELS.map((m) => ({ ...m, efforts, ...m.id === settingsModel ? { isDefault: true } : {} }));
|
|
@@ -11085,15 +11327,15 @@ function codexConfig(toml) {
|
|
|
11085
11327
|
function createModels(deps = {}) {
|
|
11086
11328
|
const env = deps.env ?? (async () => process.env);
|
|
11087
11329
|
const run4 = deps.run ?? execRun2(env);
|
|
11088
|
-
const readText2 = deps.readText ?? ((p) =>
|
|
11330
|
+
const readText2 = deps.readText ?? ((p) => readFile11(p, "utf8").catch(() => ""));
|
|
11089
11331
|
const now2 = deps.now ?? Date.now, ttl = deps.ttlMs ?? MODELS_TTL_MS;
|
|
11090
11332
|
const cache = /* @__PURE__ */ new Map();
|
|
11091
11333
|
const flying = /* @__PURE__ */ new Map();
|
|
11092
11334
|
async function ask3(tool) {
|
|
11093
11335
|
const e = await env();
|
|
11094
|
-
const home2 = e.HOME ??
|
|
11336
|
+
const home2 = e.HOME ?? homedir10();
|
|
11095
11337
|
if (tool === "claude") {
|
|
11096
|
-
const settings = await readText2(
|
|
11338
|
+
const settings = await readText2(join22(e.CLAUDE_CONFIG_DIR ?? join22(home2, ".claude"), "settings.json"));
|
|
11097
11339
|
let model;
|
|
11098
11340
|
try {
|
|
11099
11341
|
model = settings ? JSON.parse(settings).model : void 0;
|
|
@@ -11109,7 +11351,7 @@ function createModels(deps = {}) {
|
|
|
11109
11351
|
if (r.code === null) throw new Error("not installed");
|
|
11110
11352
|
if (r.code !== 0) throw new Error(r.stderr.trim().split("\n").pop() || `exit ${r.code}`);
|
|
11111
11353
|
if (tool === "cursor") return parseCursorModels(r.stdout);
|
|
11112
|
-
return parseCodexModels(r.stdout, codexConfig(await readText2(
|
|
11354
|
+
return parseCodexModels(r.stdout, codexConfig(await readText2(join22(e.CODEX_HOME ?? join22(home2, ".codex"), "config.toml"))));
|
|
11113
11355
|
}
|
|
11114
11356
|
return {
|
|
11115
11357
|
list(tool) {
|
|
@@ -11160,9 +11402,9 @@ var init_models2 = __esm({
|
|
|
11160
11402
|
|
|
11161
11403
|
// src/daemon/commands.ts
|
|
11162
11404
|
import { spawn as nodeSpawn7 } from "child_process";
|
|
11163
|
-
import { stat as
|
|
11164
|
-
import { homedir as
|
|
11165
|
-
import { join as
|
|
11405
|
+
import { stat as stat6 } from "fs/promises";
|
|
11406
|
+
import { homedir as homedir11 } from "os";
|
|
11407
|
+
import { join as join23 } from "path";
|
|
11166
11408
|
function claudeSlashRefusal(text7) {
|
|
11167
11409
|
const m = /^\/(\S+)(\s+\S[\s\S]*)?$/.exec(text7.trim());
|
|
11168
11410
|
if (!m) return null;
|
|
@@ -11271,13 +11513,13 @@ function probeClaude(o, spawnImpl = nodeSpawn7) {
|
|
|
11271
11513
|
function createCommands(deps = {}) {
|
|
11272
11514
|
const env = deps.env ?? (async () => process.env);
|
|
11273
11515
|
const claude = deps.claude ?? ((cwd, e) => probeClaude({ ...deps.claudeBin ? { bin: deps.claudeBin } : {}, cwd, env: e }));
|
|
11274
|
-
const mtime = deps.mtime ?? ((p) =>
|
|
11516
|
+
const mtime = deps.mtime ?? ((p) => stat6(p).then((s) => s.mtimeMs, () => null));
|
|
11275
11517
|
const now2 = deps.now ?? Date.now, ttl = deps.ttlMs ?? COMMANDS_TTL_MS;
|
|
11276
11518
|
const cache = /* @__PURE__ */ new Map();
|
|
11277
11519
|
const flying = /* @__PURE__ */ new Map();
|
|
11278
11520
|
async function mark(cwd, e) {
|
|
11279
|
-
const home2 = e.CLAUDE_CONFIG_DIR ??
|
|
11280
|
-
const paths2 = [
|
|
11521
|
+
const home2 = e.CLAUDE_CONFIG_DIR ?? join23(e.HOME ?? homedir11(), ".claude");
|
|
11522
|
+
const paths2 = [join23(cwd, ".claude", "commands"), join23(cwd, ".claude", "skills"), join23(home2, "commands"), join23(home2, "skills"), join23(home2, "plugins", "installed_plugins.json")];
|
|
11281
11523
|
return (await Promise.all(paths2.map(mtime))).join(",");
|
|
11282
11524
|
}
|
|
11283
11525
|
return {
|
|
@@ -11455,7 +11697,7 @@ var init_rigSpec = __esm({
|
|
|
11455
11697
|
});
|
|
11456
11698
|
|
|
11457
11699
|
// src/daemon/openrigRoles.ts
|
|
11458
|
-
import { basename as basename10, dirname as dirname8, isAbsolute as isAbsolute5, join as
|
|
11700
|
+
import { basename as basename10, dirname as dirname8, isAbsolute as isAbsolute5, join as join24, resolve as resolve9, sep as sep3 } from "path";
|
|
11459
11701
|
import { parse as parseYaml, stringify as stringify2, YAMLParseError } from "yaml";
|
|
11460
11702
|
function readLibrary(listed2) {
|
|
11461
11703
|
const out = [];
|
|
@@ -11487,15 +11729,15 @@ function parseSpec(text7) {
|
|
|
11487
11729
|
}
|
|
11488
11730
|
}
|
|
11489
11731
|
function specPath(p, specDir, home2, openrigHome) {
|
|
11490
|
-
if (p.startsWith("openrig-home:")) return
|
|
11732
|
+
if (p.startsWith("openrig-home:")) return join24(openrigHome, p.slice("openrig-home:".length));
|
|
11491
11733
|
if (p === "~") return home2;
|
|
11492
|
-
if (p.startsWith("~/")) return
|
|
11734
|
+
if (p.startsWith("~/")) return join24(home2, p.slice(2));
|
|
11493
11735
|
return isAbsolute5(p) ? resolve9(p) : resolve9(specDir, p);
|
|
11494
11736
|
}
|
|
11495
11737
|
function importPath(ref, specDir) {
|
|
11496
11738
|
const p = ref.startsWith("local:") ? resolve9(specDir, ref.slice(6)) : ref.startsWith("path:") ? resolve9(ref.slice(5)) : null;
|
|
11497
11739
|
if (!p) return null;
|
|
11498
|
-
return /\.ya?ml$/i.test(p) ? p :
|
|
11740
|
+
return /\.ya?ml$/i.test(p) ? p : join24(p, "agent.yaml");
|
|
11499
11741
|
}
|
|
11500
11742
|
function declared(spec, ctx, ns, origin) {
|
|
11501
11743
|
const out = [];
|
|
@@ -11724,7 +11966,7 @@ function layerOf(ownerRoot, path3, c) {
|
|
|
11724
11966
|
if (c.agentDir && root2 === c.agentDir) return c.profileFiles.has(path3) ? "profile" : "agent";
|
|
11725
11967
|
if (c.rigRoot && root2 === c.rigRoot) return path3 === c.cultureFile ? "culture" : c.memberFiles.has(path3) ? "member" : c.podFiles.has(path3) ? "pod" : "rig";
|
|
11726
11968
|
if (/[/\\]@openrig[/\\]cli[/\\]/.test(root2) || /[/\\]daemon[/\\]assets$/.test(root2)) return "openrig";
|
|
11727
|
-
if (under3(root2,
|
|
11969
|
+
if (under3(root2, join24(c.openrigHome, "state", "seats"))) return "seat";
|
|
11728
11970
|
return "unknown";
|
|
11729
11971
|
}
|
|
11730
11972
|
function layerOrigin(layer, c, path3) {
|
|
@@ -11734,7 +11976,7 @@ function layerOrigin(layer, c, path3) {
|
|
|
11734
11976
|
case "profile":
|
|
11735
11977
|
return { kind: "here", key: "profiles.startup.files" };
|
|
11736
11978
|
case "culture":
|
|
11737
|
-
return { kind: "inherited", from: "rig", ref: c.rig, key: "culture_file", ...c.rigRoot ? { path:
|
|
11979
|
+
return { kind: "inherited", from: "rig", ref: c.rig, key: "culture_file", ...c.rigRoot ? { path: join24(c.rigRoot, path3) } : {} };
|
|
11738
11980
|
case "rig":
|
|
11739
11981
|
return { kind: "inherited", from: "rig", ref: c.rig };
|
|
11740
11982
|
case "pod":
|
|
@@ -11762,12 +12004,12 @@ async function seatView(s, spec, profile, io, openrigHome, plugins) {
|
|
|
11762
12004
|
const cwdV = str10(node2.cwd) ?? str10(shown.cwd);
|
|
11763
12005
|
const cwd = cwdV ? { value: cwdV, origin: memberOrigin("cwd") } : { value: null, origin: { kind: "unknown", why: "not_exposed" } };
|
|
11764
12006
|
const cultureV = str10(s.rigDoc.culture_file) ?? str10(s.rigDoc.cultureFile);
|
|
11765
|
-
const cultureFile = sourced(cultureV, { kind: "inherited", from: "rig", ref: s.rig, key: "culture_file", ...cultureV && s.rigRoot ? { path:
|
|
12007
|
+
const cultureFile = sourced(cultureV, { kind: "inherited", from: "rig", ref: s.rig, key: "culture_file", ...cultureV && s.rigRoot ? { path: join24(s.rigRoot, cultureV) } : {} });
|
|
11766
12008
|
const base3 = s.rigRoot ?? (s.rigSpecPath ? dirname8(s.rigSpecPath) : "/");
|
|
11767
12009
|
const memberStartup = writtenStartup(m.startup, "startup", "member", base3, (key) => memberOrigin(key));
|
|
11768
12010
|
const pod = s.written?.pod ?? {};
|
|
11769
12011
|
const effectiveRuntime = str10(node2.runtime) ?? runtime.value ?? void 0;
|
|
11770
|
-
const into = cwdV ?
|
|
12012
|
+
const into = cwdV ? join24(cwdV, guidanceFile(effectiveRuntime, s.rigDoc)) : void 0;
|
|
11771
12013
|
const profileName = str10(m.profile) ?? str10(shown.profile) ?? "default";
|
|
11772
12014
|
const lc = {
|
|
11773
12015
|
agentDir: s.agentDir,
|
|
@@ -12003,10 +12245,14 @@ var init_openrigRoles2 = __esm({
|
|
|
12003
12245
|
import { execFile as execFile6 } from "child_process";
|
|
12004
12246
|
import { get as httpGet } from "http";
|
|
12005
12247
|
import { existsSync as existsSync2, readdirSync as readdirSync2 } from "fs";
|
|
12006
|
-
import { cp, mkdir as mkdir11, open as openFile, readFile as
|
|
12248
|
+
import { cp, mkdir as mkdir11, open as openFile, readFile as readFile12, stat as statFile, writeFile as writeFile13 } from "fs/promises";
|
|
12007
12249
|
import { parse as parseYaml2 } from "yaml";
|
|
12008
|
-
import { homedir as
|
|
12009
|
-
import { basename as basename11, delimiter as delimiter2, dirname as dirname9, isAbsolute as isAbsolute6, join as
|
|
12250
|
+
import { homedir as homedir12 } from "os";
|
|
12251
|
+
import { basename as basename11, delimiter as delimiter2, dirname as dirname9, isAbsolute as isAbsolute6, join as join25, resolve as resolve10, sep as sep4 } from "path";
|
|
12252
|
+
function specSource(raw) {
|
|
12253
|
+
const s = str11(raw);
|
|
12254
|
+
return s === "builtin" || s?.startsWith("builtin_") ? "builtin" : "user";
|
|
12255
|
+
}
|
|
12010
12256
|
function toSeat(raw) {
|
|
12011
12257
|
const n = obj9(raw);
|
|
12012
12258
|
const logicalId = str11(n.logicalId), session = str11(n.canonicalSessionName);
|
|
@@ -12079,17 +12325,17 @@ function findRig(env, exists = existsSync2, list3 = (dir) => {
|
|
|
12079
12325
|
return [];
|
|
12080
12326
|
}
|
|
12081
12327
|
}) {
|
|
12082
|
-
for (const dir of (env.PATH ?? "").split(delimiter2)) if (dir && exists(
|
|
12083
|
-
const home2 = env.HOME ??
|
|
12084
|
-
const managed = (root2, sub) => list3(root2).filter((v2) => /^v?\d/.test(v2)).sort(byVersionDesc).map((v2) =>
|
|
12328
|
+
for (const dir of (env.PATH ?? "").split(delimiter2)) if (dir && exists(join25(dir, "rig"))) return dir;
|
|
12329
|
+
const home2 = env.HOME ?? homedir12();
|
|
12330
|
+
const managed = (root2, sub) => list3(root2).filter((v2) => /^v?\d/.test(v2)).sort(byVersionDesc).map((v2) => join25(root2, v2, sub));
|
|
12085
12331
|
const candidates = [
|
|
12086
|
-
...managed(
|
|
12087
|
-
...managed(
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12332
|
+
...managed(join25(home2, ".local", "share", "nvm"), "bin"),
|
|
12333
|
+
...managed(join25(env.NVM_DIR ?? join25(home2, ".nvm"), "versions", "node"), "bin"),
|
|
12334
|
+
join25(home2, ".volta", "bin"),
|
|
12335
|
+
join25(home2, ".npm-global", "bin"),
|
|
12336
|
+
join25(home2, ".local", "share", "pnpm")
|
|
12091
12337
|
];
|
|
12092
|
-
return candidates.find((dir) => exists(
|
|
12338
|
+
return candidates.find((dir) => exists(join25(dir, "rig"))) ?? null;
|
|
12093
12339
|
}
|
|
12094
12340
|
function readCheck(output) {
|
|
12095
12341
|
const j = obj9(parse2(output));
|
|
@@ -12118,11 +12364,11 @@ function localGet(port, path3, timeoutMs) {
|
|
|
12118
12364
|
function createOpenRig(deps = {}) {
|
|
12119
12365
|
const run4 = deps.run ?? execRun3(deps.env ?? (async () => process.env));
|
|
12120
12366
|
const files = deps.fs ?? realFs;
|
|
12121
|
-
const home2 = deps.home ??
|
|
12122
|
-
const rigsDir = deps.rigsDir ??
|
|
12367
|
+
const home2 = deps.home ?? homedir12();
|
|
12368
|
+
const rigsDir = deps.rigsDir ?? join25(home2, ".ledge", "rigs");
|
|
12123
12369
|
const now2 = deps.now ?? Date.now;
|
|
12124
12370
|
const roleIo = deps.roleIo ?? realRoleIo;
|
|
12125
|
-
const openrigHome = deps.openrigHome ?? process.env.OPENRIG_HOME ??
|
|
12371
|
+
const openrigHome = deps.openrigHome ?? process.env.OPENRIG_HOME ?? join25(home2, ".openrig");
|
|
12126
12372
|
const http = deps.http ?? (async (path3) => {
|
|
12127
12373
|
const port = (await state2()).daemon.port;
|
|
12128
12374
|
if (!port) return void 0;
|
|
@@ -12206,7 +12452,7 @@ function createOpenRig(deps = {}) {
|
|
|
12206
12452
|
const e = obj9(raw), name = str11(e.name), path3 = str11(e.resolvedSourcePath) ?? str11(e.sourcePath);
|
|
12207
12453
|
if (e.kind !== "rig" || !name || !path3 || name === KERNEL_RIG) continue;
|
|
12208
12454
|
const text7 = await files.read(path3).catch(() => null);
|
|
12209
|
-
const spec = text7 === null ? null : readSpec(text7, { name, source: e.sourceType
|
|
12455
|
+
const spec = text7 === null ? null : readSpec(text7, { name, source: specSource(e.sourceType), path: path3, ...str11(e.summary) ? { summary: str11(e.summary).trim() } : {} });
|
|
12210
12456
|
if (spec) out.push(spec);
|
|
12211
12457
|
}
|
|
12212
12458
|
return { specs: out.sort((a, b3) => a.source === b3.source ? a.name.localeCompare(b3.name) : a.source === "user" ? -1 : 1) };
|
|
@@ -12216,7 +12462,7 @@ function createOpenRig(deps = {}) {
|
|
|
12216
12462
|
if (acting) return { ok: false, plan: plan2, commands: [], error: "busy" };
|
|
12217
12463
|
acting = true;
|
|
12218
12464
|
try {
|
|
12219
|
-
const cwd = l.cwd === "~" ? home2 : l.cwd.startsWith("~/") ?
|
|
12465
|
+
const cwd = l.cwd === "~" ? home2 : l.cwd.startsWith("~/") ? join25(home2, l.cwd.slice(2)) : l.cwd;
|
|
12220
12466
|
if (!isAbsolute6(cwd) || !files.exists(cwd)) return { ok: false, plan: plan2, commands: [], error: "cwd_missing" };
|
|
12221
12467
|
const listed2 = await specs();
|
|
12222
12468
|
if (listed2.error) return { ok: false, plan: plan2, commands: [], error: listed2.error };
|
|
@@ -12228,17 +12474,17 @@ function createOpenRig(deps = {}) {
|
|
|
12228
12474
|
const rename7 = l.name !== spec.name;
|
|
12229
12475
|
if (changesSpec(l) || l.save || rename7 && !can2.name) {
|
|
12230
12476
|
const composed = composeSpec(parseYaml2(text7), dirname9(spec.path), l);
|
|
12231
|
-
dir =
|
|
12477
|
+
dir = join25(rigsDir, l.name);
|
|
12232
12478
|
await files.copyDir(dirname9(spec.path), dir);
|
|
12233
|
-
await files.write(
|
|
12234
|
-
source2 =
|
|
12479
|
+
await files.write(join25(dir, "rig.yaml"), composed.yaml);
|
|
12480
|
+
source2 = join25(dir, "rig.yaml");
|
|
12235
12481
|
yaml = composed.yaml;
|
|
12236
12482
|
seats = composed.seats;
|
|
12237
12483
|
} else {
|
|
12238
12484
|
yaml = text7;
|
|
12239
12485
|
seats = specSeats(spec);
|
|
12240
12486
|
}
|
|
12241
|
-
if (!dir && rename7 && !plan2) await files.write(
|
|
12487
|
+
if (!dir && rename7 && !plan2) await files.write(join25(rigsDir, l.name, "source.json"), JSON.stringify({ spec: spec.name, path: spec.path }));
|
|
12242
12488
|
const commands = [["up", source2, ...!dir && rename7 ? ["--name", l.name] : [], "--cwd", cwd, ...l.nonInterruptive ? ["--non-interruptive"] : [], ...plan2 ? ["--plan"] : ["--yes"], "--json"]];
|
|
12243
12489
|
if (!plan2 && l.save && dir) commands.push(["specs", "add", dir, "--json"]);
|
|
12244
12490
|
const shown = commands.map(showRigCommand);
|
|
@@ -12262,9 +12508,9 @@ function createOpenRig(deps = {}) {
|
|
|
12262
12508
|
}
|
|
12263
12509
|
}
|
|
12264
12510
|
async function rootOf(rig) {
|
|
12265
|
-
const mine =
|
|
12266
|
-
if (files.exists(
|
|
12267
|
-
const marker = await files.read(
|
|
12511
|
+
const mine = join25(rigsDir, rig);
|
|
12512
|
+
if (files.exists(join25(mine, "rig.yaml"))) return mine;
|
|
12513
|
+
const marker = await files.read(join25(mine, "source.json")).catch(() => null);
|
|
12268
12514
|
const path3 = marker === null ? void 0 : str11(obj9(parse2(marker)).path);
|
|
12269
12515
|
if (path3) return dirname9(path3);
|
|
12270
12516
|
const listed2 = await specs();
|
|
@@ -12294,7 +12540,7 @@ function createOpenRig(deps = {}) {
|
|
|
12294
12540
|
const member = memberOf(show, a.pod, a.from.slice(a.pod.length + 1));
|
|
12295
12541
|
const fragment2 = member ? fragmentOf(member, a.id, root2) : null;
|
|
12296
12542
|
if (!fragment2) return { ok: false, commands: [], ...none, error: "rig_root_unknown" };
|
|
12297
|
-
const path3 =
|
|
12543
|
+
const path3 = join25(rigsDir, rigName, "seats", `${a.pod}.${a.id}.json`);
|
|
12298
12544
|
await files.write(path3, JSON.stringify(fragment2, null, 2));
|
|
12299
12545
|
commands.push(["add", rig.id, a.pod, path3, ...root2 ? ["--rig-root", root2] : [], "--json"]);
|
|
12300
12546
|
}
|
|
@@ -12344,7 +12590,7 @@ function createOpenRig(deps = {}) {
|
|
|
12344
12590
|
const r = await run4(["spec", "show", rig.id, "--json"], STATUS_MS);
|
|
12345
12591
|
if (r.code !== 0) continue;
|
|
12346
12592
|
const root2 = rig.name === KERNEL_RIG ? null : await rootOf(rig.name).catch(() => null);
|
|
12347
|
-
const specFile = root2 ?
|
|
12593
|
+
const specFile = root2 ? join25(root2, "rig.yaml") : null;
|
|
12348
12594
|
const text7 = specFile ? await roleIo.read(specFile) : null;
|
|
12349
12595
|
const written = parseSpec(text7);
|
|
12350
12596
|
const shown = obj9(parse2(r.stdout));
|
|
@@ -12508,14 +12754,14 @@ var init_openrig2 = __esm({
|
|
|
12508
12754
|
if (!dir) return { code: null, stdout: "", stderr: "rig: command not found" };
|
|
12509
12755
|
const e = { ...base3, PATH: [dir, base3.PATH].filter(Boolean).join(delimiter2), NO_COLOR: "1", CLICOLOR: "0", FORCE_COLOR: "0" };
|
|
12510
12756
|
return new Promise((res) => {
|
|
12511
|
-
execFile6(
|
|
12757
|
+
execFile6(join25(dir, "rig"), args, { env: e, timeout: timeoutMs, maxBuffer: 8 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
12512
12758
|
const code3 = !err ? 0 : err.code === "ENOENT" ? null : typeof err.code === "number" ? err.code : 1;
|
|
12513
12759
|
res({ code: code3, stdout: String(stdout), stderr: String(stderr) || (err && code3 !== 0 ? err.message : "") });
|
|
12514
12760
|
});
|
|
12515
12761
|
});
|
|
12516
12762
|
};
|
|
12517
12763
|
realRoleIo = {
|
|
12518
|
-
read: (p) =>
|
|
12764
|
+
read: (p) => readFile12(p, "utf8").catch(() => null),
|
|
12519
12765
|
stat: (p) => statFile(p).then((st) => ({ size: st.size, mtimeMs: st.mtimeMs, file: st.isFile() })).catch(() => null),
|
|
12520
12766
|
head: async (p, max) => {
|
|
12521
12767
|
const h = await openFile(p, "r").catch(() => null);
|
|
@@ -12533,7 +12779,7 @@ var init_openrig2 = __esm({
|
|
|
12533
12779
|
FILES_TTL_MS = 5 * 6e4;
|
|
12534
12780
|
HTTP_MS = 3e3;
|
|
12535
12781
|
realFs = {
|
|
12536
|
-
read: (p) =>
|
|
12782
|
+
read: (p) => readFile12(p, "utf8"),
|
|
12537
12783
|
exists: (p) => existsSync2(p),
|
|
12538
12784
|
copyDir: (from, to) => cp(from, to, { recursive: true, force: true, filter: (src) => basename11(src) !== "rig.yaml" }),
|
|
12539
12785
|
write: async (p, text7) => {
|
|
@@ -12553,7 +12799,7 @@ function goalOf(tags) {
|
|
|
12553
12799
|
return t ? t.slice(GOAL_TAG.length + 1) || null : null;
|
|
12554
12800
|
}
|
|
12555
12801
|
function parseTeam(body) {
|
|
12556
|
-
const m =
|
|
12802
|
+
const m = BLOCK4.exec(body);
|
|
12557
12803
|
if (!m) return null;
|
|
12558
12804
|
let raw;
|
|
12559
12805
|
try {
|
|
@@ -12578,13 +12824,13 @@ function parseTeam(body) {
|
|
|
12578
12824
|
launch: kind === "launch" ? { agents: typeof l.agents === "number" && Number.isInteger(l.agents) ? l.agents : null, runtimes: strings(l.runtimes), plan: text2(l.plan), writes: strings(l.writes), permissions: text2(l.permissions) } : null
|
|
12579
12825
|
};
|
|
12580
12826
|
}
|
|
12581
|
-
var GOAL_TAG, goalTag,
|
|
12827
|
+
var GOAL_TAG, goalTag, BLOCK4, obj10, text2, strings, roleOf3, withoutTeam;
|
|
12582
12828
|
var init_goalTeam = __esm({
|
|
12583
12829
|
"src/daemon/goalTeam.ts"() {
|
|
12584
12830
|
"use strict";
|
|
12585
12831
|
GOAL_TAG = "ledge-goal";
|
|
12586
12832
|
goalTag = (goalQitemId2) => `${GOAL_TAG}:${goalQitemId2}`;
|
|
12587
|
-
|
|
12833
|
+
BLOCK4 = /```ledge-team[^\n]*\n([\s\S]*?)```/;
|
|
12588
12834
|
obj10 = (v2) => v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : {};
|
|
12589
12835
|
text2 = (v2) => typeof v2 === "string" && v2.trim() ? v2.trim() : null;
|
|
12590
12836
|
strings = (v2) => Array.isArray(v2) ? v2.filter((x) => typeof x === "string" && x.trim() !== "").map((x) => x.trim()) : [];
|
|
@@ -12592,7 +12838,7 @@ var init_goalTeam = __esm({
|
|
|
12592
12838
|
const m = /^([A-Za-z0-9][A-Za-z0-9_]*)-([A-Za-z0-9][A-Za-z0-9_-]*)@/.exec(seat2);
|
|
12593
12839
|
return m ? `${m[1]}.${m[2]}` : null;
|
|
12594
12840
|
};
|
|
12595
|
-
withoutTeam = (body) => body.replace(
|
|
12841
|
+
withoutTeam = (body) => body.replace(BLOCK4, "").trim();
|
|
12596
12842
|
}
|
|
12597
12843
|
});
|
|
12598
12844
|
|
|
@@ -12727,8 +12973,8 @@ function createHumanChannel(deps) {
|
|
|
12727
12973
|
return byAddress;
|
|
12728
12974
|
};
|
|
12729
12975
|
const row3 = async (url, id, at) => {
|
|
12730
|
-
const
|
|
12731
|
-
if (
|
|
12976
|
+
const kept2 = rows.get(id);
|
|
12977
|
+
if (kept2 && at && kept2.at === at) return { row: kept2.row };
|
|
12732
12978
|
const r = await http("GET", `${url}/api/queue/${encodeURIComponent(id)}`);
|
|
12733
12979
|
if (r.status === 404) return "gone";
|
|
12734
12980
|
if (r.status !== 200 || !RowShape.safeParse(r.json).success || obj11(r.json).qitemId !== id) return "failed";
|
|
@@ -13076,8 +13322,8 @@ function createGoals(deps) {
|
|
|
13076
13322
|
known2 = /* @__PURE__ */ new Map();
|
|
13077
13323
|
return { goals: [] };
|
|
13078
13324
|
}
|
|
13079
|
-
const
|
|
13080
|
-
if (st.daemon.state !== "running" || !st.daemon.port) return
|
|
13325
|
+
const kept2 = () => ({ goals: [...known2.values()], partial: true });
|
|
13326
|
+
if (st.daemon.state !== "running" || !st.daemon.port) return kept2();
|
|
13081
13327
|
const url = `http://127.0.0.1:${st.daemon.port}`;
|
|
13082
13328
|
let partial = false, missed = false;
|
|
13083
13329
|
const rows = async (path3, limit = LIST_LIMIT) => {
|
|
@@ -13090,7 +13336,7 @@ function createGoals(deps) {
|
|
|
13090
13336
|
return rowsOf(r.json);
|
|
13091
13337
|
};
|
|
13092
13338
|
const listed2 = await rows(`/api/queue/list?sourceSession=${encodeURIComponent(PERSON)}`);
|
|
13093
|
-
if (!listed2) return
|
|
13339
|
+
if (!listed2) return kept2();
|
|
13094
13340
|
const mine = listed2.filter((r) => str13(r.qitemId)?.startsWith("qitem-ledge-goal-") && !str13(r.qitemId).startsWith("qitem-ledge-goal-reply-") && tagsOf(r).includes(GOAL_TAG)).sort((a, b3) => (str13(b3.tsCreated) ?? "").localeCompare(str13(a.tsCreated) ?? "")).slice(0, GOALS_MAX);
|
|
13095
13341
|
if (!mine.length) {
|
|
13096
13342
|
known2 = /* @__PURE__ */ new Map();
|
|
@@ -13217,7 +13463,7 @@ function createRelay(d) {
|
|
|
13217
13463
|
const now2 = d.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
13218
13464
|
const typing = d.typing ?? /* @__PURE__ */ new Set();
|
|
13219
13465
|
const sending = /* @__PURE__ */ new Set();
|
|
13220
|
-
const
|
|
13466
|
+
const open6 = (id) => free(d.store.get(id)) && !typing.has(id) && !d.closing?.has(id);
|
|
13221
13467
|
const emitTo = (thread) => (m) => {
|
|
13222
13468
|
try {
|
|
13223
13469
|
d.threads.emitterOf(thread)?.(m);
|
|
@@ -13261,14 +13507,14 @@ function createRelay(d) {
|
|
|
13261
13507
|
}
|
|
13262
13508
|
function kick(id) {
|
|
13263
13509
|
const q = queued().find((x) => x.relay.id === id);
|
|
13264
|
-
if (!q || !
|
|
13510
|
+
if (!q || !open6(id)) return;
|
|
13265
13511
|
void deliverAndKick(q.thread, q.qid, q.relay).catch((e) => log(`relay ${q.qid} failed: ${e.message}`));
|
|
13266
13512
|
}
|
|
13267
13513
|
async function deliverAndKick(thread, qid, r) {
|
|
13268
13514
|
const refused = await deliver(thread, qid, r);
|
|
13269
13515
|
if (refused) {
|
|
13270
13516
|
if (queued().some((q) => q.relay.id === r.id)) kick(r.id);
|
|
13271
|
-
else if (
|
|
13517
|
+
else if (open6(r.id)) d.next?.(r.id);
|
|
13272
13518
|
}
|
|
13273
13519
|
return refused;
|
|
13274
13520
|
}
|
|
@@ -13285,7 +13531,7 @@ function createRelay(d) {
|
|
|
13285
13531
|
}
|
|
13286
13532
|
return;
|
|
13287
13533
|
}
|
|
13288
|
-
if (
|
|
13534
|
+
if (open6(id)) kick(id);
|
|
13289
13535
|
} catch (e) {
|
|
13290
13536
|
log(`relay change handler failed: ${e.message}`);
|
|
13291
13537
|
}
|
|
@@ -13303,7 +13549,7 @@ function createRelay(d) {
|
|
|
13303
13549
|
mark(thread, qid, { error: m, code: STOPPED_CODE });
|
|
13304
13550
|
throw new Error(m);
|
|
13305
13551
|
}
|
|
13306
|
-
if (
|
|
13552
|
+
if (open6(r.id)) {
|
|
13307
13553
|
if (queued().some((q) => q.relay.id === r.id)) {
|
|
13308
13554
|
markQueued(thread, qid);
|
|
13309
13555
|
kick(r.id);
|
|
@@ -13324,7 +13570,7 @@ function createRelay(d) {
|
|
|
13324
13570
|
settle(id) {
|
|
13325
13571
|
try {
|
|
13326
13572
|
if (queued().some((q) => q.relay.id === id)) kick(id);
|
|
13327
|
-
else if (
|
|
13573
|
+
else if (open6(id)) d.next?.(id);
|
|
13328
13574
|
} catch (e) {
|
|
13329
13575
|
log(`relay settle failed: ${e.message}`);
|
|
13330
13576
|
}
|
|
@@ -13553,7 +13799,7 @@ var init_act = __esm({
|
|
|
13553
13799
|
|
|
13554
13800
|
// src/daemon/handover.ts
|
|
13555
13801
|
import { mkdirSync, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
|
|
13556
|
-
import { join as
|
|
13802
|
+
import { join as join26 } from "path";
|
|
13557
13803
|
function reliefOf(get, s) {
|
|
13558
13804
|
let cur = s;
|
|
13559
13805
|
for (let hops = 0; cur && hops < 50; hops++) {
|
|
@@ -13571,7 +13817,7 @@ function createHandovers(d) {
|
|
|
13571
13817
|
const now2 = d.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
13572
13818
|
const briefMs = d.briefMs ?? BRIEF_MS;
|
|
13573
13819
|
const waiting2 = /* @__PURE__ */ new Map();
|
|
13574
|
-
const file = (id) =>
|
|
13820
|
+
const file = (id) => join26(d.dir, `${id}.md`);
|
|
13575
13821
|
const mark = (id, h) => {
|
|
13576
13822
|
const cur = d.store.get(id);
|
|
13577
13823
|
if (cur) d.store.upsert({ ...cur, handover: { ...h, at: now2() } });
|
|
@@ -14025,7 +14271,7 @@ var init_broadcast2 = __esm({
|
|
|
14025
14271
|
});
|
|
14026
14272
|
|
|
14027
14273
|
// src/daemon/context.ts
|
|
14028
|
-
import { open as
|
|
14274
|
+
import { open as open5 } from "fs/promises";
|
|
14029
14275
|
function longWindow(model) {
|
|
14030
14276
|
if (!model) return false;
|
|
14031
14277
|
if (model.includes("[1m]")) return true;
|
|
@@ -14033,7 +14279,7 @@ function longWindow(model) {
|
|
|
14033
14279
|
return !!m && Number(m[1]) >= 5;
|
|
14034
14280
|
}
|
|
14035
14281
|
async function tail(path3) {
|
|
14036
|
-
const f = await
|
|
14282
|
+
const f = await open5(path3, "r");
|
|
14037
14283
|
try {
|
|
14038
14284
|
const { size } = await f.stat();
|
|
14039
14285
|
const start = Math.max(0, size - TAIL_BYTES2);
|
|
@@ -14117,7 +14363,7 @@ var init_context2 = __esm({
|
|
|
14117
14363
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
14118
14364
|
import { mkdtemp, rm as rm5 } from "fs/promises";
|
|
14119
14365
|
import { tmpdir } from "os";
|
|
14120
|
-
import { join as
|
|
14366
|
+
import { join as join27 } from "path";
|
|
14121
14367
|
function parseRoute(answer, context) {
|
|
14122
14368
|
const raw = answer.trim().replace(/^```(?:json)?\s*/, "").replace(/\s*```$/, "");
|
|
14123
14369
|
const result = RouteResultSchema.parse(JSON.parse(raw));
|
|
@@ -14143,7 +14389,7 @@ function createRouter(deps) {
|
|
|
14143
14389
|
let dir;
|
|
14144
14390
|
let timer2;
|
|
14145
14391
|
try {
|
|
14146
|
-
dir = await mkdtemp(
|
|
14392
|
+
dir = await mkdtemp(join27(tmpdir(), "ledge-routing-"));
|
|
14147
14393
|
const env = { ...await deps.env(), LEDGE_CHAT: "1" };
|
|
14148
14394
|
delete env.ANTHROPIC_API_KEY;
|
|
14149
14395
|
delete env.LEDGE_SESSION;
|
|
@@ -14379,7 +14625,16 @@ function compareVersions(a, b3) {
|
|
|
14379
14625
|
const d = (x[i] ?? 0) - (y[i] ?? 0);
|
|
14380
14626
|
if (d) return d > 0 ? 1 : -1;
|
|
14381
14627
|
}
|
|
14382
|
-
|
|
14628
|
+
if (a === b3) return 0;
|
|
14629
|
+
const ax = a.match(/-snapshot\.(\d+)\.(\d+)$/), bx = b3.match(/-snapshot\.(\d+)\.(\d+)$/);
|
|
14630
|
+
if (ax && bx) {
|
|
14631
|
+
for (const i of [1, 2]) {
|
|
14632
|
+
const x2 = BigInt(ax[i]), y2 = BigInt(bx[i]);
|
|
14633
|
+
if (x2 !== y2) return x2 > y2 ? 1 : -1;
|
|
14634
|
+
}
|
|
14635
|
+
return 0;
|
|
14636
|
+
}
|
|
14637
|
+
return a.includes("-") ? -1 : b3.includes("-") ? 1 : 0;
|
|
14383
14638
|
}
|
|
14384
14639
|
var parts;
|
|
14385
14640
|
var init_semver = __esm({
|
|
@@ -15173,15 +15428,15 @@ var init_operations = __esm({
|
|
|
15173
15428
|
});
|
|
15174
15429
|
|
|
15175
15430
|
// src/mobile/risk.ts
|
|
15176
|
-
import { homedir as
|
|
15177
|
-
import { resolve as resolve11, sep as sep5, join as
|
|
15431
|
+
import { homedir as homedir13 } from "os";
|
|
15432
|
+
import { resolve as resolve11, sep as sep5, join as join28 } from "path";
|
|
15178
15433
|
function isSensitive(cwd, p) {
|
|
15179
15434
|
if (p.includes("~")) return true;
|
|
15180
15435
|
const full = resolve11(cwd, p);
|
|
15181
15436
|
const segs = full.split(sep5);
|
|
15182
15437
|
if (segs.includes(".git") || segs.some((s) => s.startsWith(".env"))) return true;
|
|
15183
|
-
const home2 =
|
|
15184
|
-
return [
|
|
15438
|
+
const home2 = homedir13();
|
|
15439
|
+
return [join28(home2, ".ssh"), join28(home2, ".ledge")].some((d) => full === d || full.startsWith(d + sep5));
|
|
15185
15440
|
}
|
|
15186
15441
|
function classifyShell(command, cwd) {
|
|
15187
15442
|
if (PUSH.test(command)) return high("pushes");
|
|
@@ -15435,18 +15690,18 @@ function toPhoneItem(item) {
|
|
|
15435
15690
|
return { item: out, cut: cut3 };
|
|
15436
15691
|
}
|
|
15437
15692
|
function toPhoneThread(items) {
|
|
15438
|
-
const
|
|
15693
|
+
const kept2 = [];
|
|
15439
15694
|
const cut3 = [];
|
|
15440
15695
|
let used = 2;
|
|
15441
|
-
for (let i = items.length - 1; i >= 0 &&
|
|
15696
|
+
for (let i = items.length - 1; i >= 0 && kept2.length < THREAD_ITEMS_MAX; i--) {
|
|
15442
15697
|
const projected = toPhoneItem(items[i]);
|
|
15443
|
-
const size = bytes2(projected.item) + (
|
|
15698
|
+
const size = bytes2(projected.item) + (kept2.length > 0 ? 1 : 0);
|
|
15444
15699
|
if (used + size > THREAD_BUDGET) break;
|
|
15445
15700
|
used += size;
|
|
15446
|
-
|
|
15701
|
+
kept2.push(projected.item);
|
|
15447
15702
|
if (projected.cut) cut3.push(projected.item.id);
|
|
15448
15703
|
}
|
|
15449
|
-
return { items:
|
|
15704
|
+
return { items: kept2.reverse(), cut: cut3.reverse(), more: kept2.length < items.length };
|
|
15450
15705
|
}
|
|
15451
15706
|
function capabilities() {
|
|
15452
15707
|
const caps = {};
|
|
@@ -16951,9 +17206,13 @@ var init_notifier = __esm({
|
|
|
16951
17206
|
// src/daemon/server.ts
|
|
16952
17207
|
import { createServer } from "http";
|
|
16953
17208
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
16954
|
-
import { homedir as
|
|
16955
|
-
import { basename as basename12, join as
|
|
17209
|
+
import { homedir as homedir14 } from "os";
|
|
17210
|
+
import { basename as basename12, join as join29, resolve as resolve12 } from "path";
|
|
16956
17211
|
import { WebSocketServer, WebSocket as WebSocket3 } from "ws";
|
|
17212
|
+
function withoutHarness(s) {
|
|
17213
|
+
const { pending: _p, lastReply: _r, transcriptPath: _t, agentSessionId: _a, agentSessionFile: _f, model: _m, effort: _e, modelNext: _n, queue: _q, backend: _b, ...rest } = s;
|
|
17214
|
+
return { ...rest, helpers: [] };
|
|
17215
|
+
}
|
|
16957
17216
|
function resumeKey(s) {
|
|
16958
17217
|
if (s.tool === "omp") return s.agentSessionFile;
|
|
16959
17218
|
if (s.agentSessionId) return s.agentSessionId;
|
|
@@ -17003,11 +17262,12 @@ async function startServer(opts) {
|
|
|
17003
17262
|
const agentEnv2 = opts.agentEnv ?? (async () => ({ ...process.env, ...await loginShellEnv() }));
|
|
17004
17263
|
const attachments = opts.attachments ?? new AttachmentStore(paths().attachments);
|
|
17005
17264
|
const reducer = new CaptureReducer();
|
|
17006
|
-
const threads = createChatThreads({ attachments, store, tmux, home: paths().dir, notesDir:
|
|
17265
|
+
const threads = createChatThreads({ attachments, store, tmux, home: paths().dir, notesDir: join29(paths().dir, "context"), userHome: opts.home, env: agentEnv2, bin: opts.claudeBin, now: now2, projects: () => projects, forge: createForge({ env: agentEnv2 }), tasks: () => tasks.all() });
|
|
17007
17266
|
const chat = opts.chat ?? threads.desktop;
|
|
17008
17267
|
const router = createRouter({ env: agentEnv2 });
|
|
17009
|
-
const omp2 = opts.omp ?? createOmpHost({ store, env: agentEnv2, dir:
|
|
17268
|
+
const omp2 = opts.omp ?? createOmpHost({ store, env: agentEnv2, dir: join29(paths().dir, "omp"), now: now2, attachments });
|
|
17010
17269
|
const openrig = opts.openrig ?? createOpenRig({ env: agentEnv2 });
|
|
17270
|
+
const ompSeatReadOnly = (s) => new Refusal(`${s.name} is the OpenRig OMP seat ${s.rig?.seat ?? ""}: Ledge follows its state only`, "read_only");
|
|
17011
17271
|
const rigOwned = (s) => new Refusal(`${s.name} is the OpenRig seat ${s.rig?.seat ?? s.tmuxSession ?? ""}: stop or restart it from OpenRig`, "read_only");
|
|
17012
17272
|
const adoptSeat = (id, seat2) => {
|
|
17013
17273
|
const session = store.get(id);
|
|
@@ -17020,9 +17280,22 @@ async function startServer(opts) {
|
|
|
17020
17280
|
const seatId = (seat2) => `rig-${seat2.replace(/[^A-Za-z0-9._-]/g, "-")}`;
|
|
17021
17281
|
async function discoverSeats(st) {
|
|
17022
17282
|
if (st.daemon.state !== "running") return;
|
|
17023
|
-
const followed = new
|
|
17283
|
+
const followed = new Map(store.all().filter((x) => x.rig && x.state !== "exited").map((x) => [x.rig.seat, x.id]));
|
|
17024
17284
|
for (const r of st.rigs) for (const n of r.nodes) {
|
|
17025
|
-
|
|
17285
|
+
const tool = SEAT_TOOLS[n.runtime];
|
|
17286
|
+
if (!tool || n.sessionStatus !== "running") continue;
|
|
17287
|
+
const live3 = followed.get(n.session);
|
|
17288
|
+
if (live3 !== void 0) {
|
|
17289
|
+
const s = store.get(live3);
|
|
17290
|
+
if (s && s.tool !== tool && (s.source === "rig" || s.source === "hook_only")) {
|
|
17291
|
+
if (s.source === "hook_only" && !await tmux.hasSession(n.session).catch(() => false)) continue;
|
|
17292
|
+
const cur = store.get(live3);
|
|
17293
|
+
if (!cur || cur.state === "exited" || cur.tool === tool) continue;
|
|
17294
|
+
store.upsert({ ...withoutHarness(cur), tool, state: "idle", source: "rig", tmuxSession: n.session, lastActivityAt: now2() });
|
|
17295
|
+
log(`openrig: seat ${n.session} now runs ${n.runtime}`);
|
|
17296
|
+
}
|
|
17297
|
+
continue;
|
|
17298
|
+
}
|
|
17026
17299
|
if (!await tmux.hasSession(n.session).catch(() => false)) continue;
|
|
17027
17300
|
const before2 = store.all().filter((x) => x.rig?.seat === n.session).sort((a, b3) => b3.lastActivityAt.localeCompare(a.lastActivityAt))[0];
|
|
17028
17301
|
const id = before2?.id ?? seatId(n.session), old = store.get(id), at = now2();
|
|
@@ -17031,13 +17304,14 @@ async function startServer(opts) {
|
|
|
17031
17304
|
if (old) {
|
|
17032
17305
|
if (old.archivedAt) store.unarchive(id, at);
|
|
17033
17306
|
const { exitedAt: _e, exitCode: _c, pending: _p, ...rest } = store.get(id);
|
|
17034
|
-
|
|
17307
|
+
const same2 = rest.tool === tool ? rest : { ...withoutHarness(rest), tool };
|
|
17308
|
+
store.upsert({ ...same2, state: "idle", tmuxSession: n.session, source: "rig", rig, lastActivityAt: at });
|
|
17035
17309
|
} else {
|
|
17036
17310
|
const name = pickName([...namesInUse(store.all()), ...heldNames(store.all())]);
|
|
17037
17311
|
store.upsert({
|
|
17038
17312
|
id,
|
|
17039
17313
|
name,
|
|
17040
|
-
tool
|
|
17314
|
+
tool,
|
|
17041
17315
|
cwd,
|
|
17042
17316
|
project: basename12(cwd) || r.name,
|
|
17043
17317
|
title: `${name} \xB7 ${n.logicalId}`,
|
|
@@ -17051,10 +17325,21 @@ async function startServer(opts) {
|
|
|
17051
17325
|
});
|
|
17052
17326
|
log(`openrig: seat ${n.session} followed as ${name}`);
|
|
17053
17327
|
}
|
|
17054
|
-
followed.
|
|
17328
|
+
followed.set(n.session, id);
|
|
17329
|
+
}
|
|
17330
|
+
if (!st.rigs.some((r) => r.nodes.length > 0)) return;
|
|
17331
|
+
const listed2 = new Set(st.rigs.flatMap((r) => r.nodes.map((n) => n.session)));
|
|
17332
|
+
const unsure = new Set(st.rigs.filter((r) => r.seats > 0 && r.nodes.length === 0).map((r) => r.name));
|
|
17333
|
+
for (const s of store.all()) {
|
|
17334
|
+
if (isOmpSeat2(s) && s.rig && s.state === "exited" && !isArchived(s) && !listed2.has(s.rig.seat) && !unsure.has(s.rig.name)) {
|
|
17335
|
+
store.archive(s.id, now2());
|
|
17336
|
+
log(`openrig: seat ${s.rig.seat} is gone from OpenRig`);
|
|
17337
|
+
}
|
|
17055
17338
|
}
|
|
17056
17339
|
}
|
|
17057
17340
|
const SEATS_EVERY_MS = 3e4;
|
|
17341
|
+
const ompSeats = opts.ompSeats ?? createOmpSeats({ store });
|
|
17342
|
+
ompSeats.start();
|
|
17058
17343
|
const seatTimer = setInterval(() => {
|
|
17059
17344
|
void openrig.state().then(discoverSeats).catch((e) => log(`openrig: seats not read: ${e.message}`));
|
|
17060
17345
|
}, SEATS_EVERY_MS);
|
|
@@ -17100,7 +17385,7 @@ async function startServer(opts) {
|
|
|
17100
17385
|
}).catch((e) => log(`openrig: seat ${seat2} not read: ${e.message}`));
|
|
17101
17386
|
};
|
|
17102
17387
|
const commands = opts.commands ?? createCommands({ env: agentEnv2, ...opts.claudeBin ? { claudeBin: opts.claudeBin } : {} });
|
|
17103
|
-
const models = opts.models ?? createModels({ env: agentEnv2, omp: async () => ompChoices(await omp2.catalog(opts.home ??
|
|
17388
|
+
const models = opts.models ?? createModels({ env: agentEnv2, omp: async () => ompChoices(await omp2.catalog(opts.home ?? homedir14())) });
|
|
17104
17389
|
const token = await ensureDaemonToken();
|
|
17105
17390
|
const httpServer = createServer(unauthorized);
|
|
17106
17391
|
const sockets = /* @__PURE__ */ new Set();
|
|
@@ -17124,7 +17409,7 @@ async function startServer(opts) {
|
|
|
17124
17409
|
await new Promise((res, rej) => {
|
|
17125
17410
|
const onError = (e) => rej(e);
|
|
17126
17411
|
wss.once("error", onError);
|
|
17127
|
-
httpServer.listen(opts.port ??
|
|
17412
|
+
httpServer.listen(opts.port ?? ledgePort(), opts.host ?? "127.0.0.1", () => {
|
|
17128
17413
|
wss.off("error", onError);
|
|
17129
17414
|
res();
|
|
17130
17415
|
});
|
|
@@ -17140,7 +17425,7 @@ async function startServer(opts) {
|
|
|
17140
17425
|
const islandFile = opts.islandFile ?? paths().island;
|
|
17141
17426
|
let island = readIsland(islandFile);
|
|
17142
17427
|
const islandMsg = () => ({ type: "island", prefs: island });
|
|
17143
|
-
const contextFile = opts.contextFile ??
|
|
17428
|
+
const contextFile = opts.contextFile ?? join29(paths().dir, "context.json");
|
|
17144
17429
|
let contextPrefs = readContextPrefs(contextFile);
|
|
17145
17430
|
const contextMsg = () => ({ type: "context.prefs", prefs: contextPrefs });
|
|
17146
17431
|
const mobile = opts.mobile;
|
|
@@ -17239,7 +17524,7 @@ async function startServer(opts) {
|
|
|
17239
17524
|
tasks,
|
|
17240
17525
|
launch: (req) => launch3(req),
|
|
17241
17526
|
close: (s) => closeSession(s),
|
|
17242
|
-
dir:
|
|
17527
|
+
dir: join29(paths().dir, "handovers"),
|
|
17243
17528
|
now: now2,
|
|
17244
17529
|
...opts.handoverBriefMs !== void 0 ? { briefMs: opts.handoverBriefMs } : {}
|
|
17245
17530
|
});
|
|
@@ -17327,7 +17612,7 @@ async function startServer(opts) {
|
|
|
17327
17612
|
else if ("waitMs" in step3) await new Promise((r) => setTimeout(r, step3.waitMs));
|
|
17328
17613
|
else if ("keys" in step3) await tmux.sendKeys(s.tmuxSession, step3.keys);
|
|
17329
17614
|
else {
|
|
17330
|
-
const end2 = Date.now() + UNTIL_MS;
|
|
17615
|
+
const end2 = Date.now() + (step3.ms ?? UNTIL_MS);
|
|
17331
17616
|
for (; ; ) {
|
|
17332
17617
|
const pane = await tmux.capture(s.tmuxSession, UNTIL_LINES);
|
|
17333
17618
|
if (pane !== null && step3.until(pane)) break;
|
|
@@ -17515,6 +17800,7 @@ async function startServer(opts) {
|
|
|
17515
17800
|
const s = store.get(id);
|
|
17516
17801
|
if (!s) return unknownSession();
|
|
17517
17802
|
if (s.source === "hook_only") return readOnly();
|
|
17803
|
+
if (isOmpSeat2(s)) return ompSeatReadOnly(s);
|
|
17518
17804
|
if (closing.has(id)) return new Refusal("the session is being closed", "busy");
|
|
17519
17805
|
const ids = how.attachments ?? [];
|
|
17520
17806
|
const refs = [];
|
|
@@ -17848,7 +18134,7 @@ async function startServer(opts) {
|
|
|
17848
18134
|
let scanning = null;
|
|
17849
18135
|
function sharedScan() {
|
|
17850
18136
|
if (scanning) return scanning;
|
|
17851
|
-
const home2 = opts.home ??
|
|
18137
|
+
const home2 = opts.home ?? homedir14();
|
|
17852
18138
|
const extra = extraRoots(home2, [...store.recents().map((r) => r.cwd), ...store.all().filter((s) => s.state !== "exited").map((s) => s.cwd)]);
|
|
17853
18139
|
const walk = (opts.scan ?? scanRepos)({ home: home2, extra }).finally(() => {
|
|
17854
18140
|
if (scanning === walk) scanning = null;
|
|
@@ -18127,6 +18413,7 @@ async function startServer(opts) {
|
|
|
18127
18413
|
return void await omp2.interruptAndRestore(s.id).then(({ entries, imagesDropped }) => reply({ type: "session.queue.restored", id: s.id, texts: entries.map((e) => e.text), entries, ...imagesDropped ? { imagesDropped } : {} }), (e) => refuse(e.message, msg.id));
|
|
18128
18414
|
}
|
|
18129
18415
|
if (!s?.tmuxSession) return;
|
|
18416
|
+
if (isOmpSeat2(s)) return refuse(ompSeatReadOnly(s), msg.id);
|
|
18130
18417
|
await tmux.sendKeys(s.tmuxSession, adapterFor(s.tool).interruptKeys());
|
|
18131
18418
|
return;
|
|
18132
18419
|
}
|
|
@@ -18357,7 +18644,7 @@ async function startServer(opts) {
|
|
|
18357
18644
|
relay,
|
|
18358
18645
|
launchChat: (thread, qid, rows) => {
|
|
18359
18646
|
const c = thread === "desktop" ? threads.desktop : threads.device(thread.slice("device:".length));
|
|
18360
|
-
const userHome = resolve12(opts.home ??
|
|
18647
|
+
const userHome = resolve12(opts.home ?? homedir14());
|
|
18361
18648
|
return launchFromChat({
|
|
18362
18649
|
chat: c,
|
|
18363
18650
|
emit: threads.emitterOf(thread) ?? (() => {
|
|
@@ -18501,6 +18788,7 @@ async function startServer(opts) {
|
|
|
18501
18788
|
close: () => new Promise((res) => {
|
|
18502
18789
|
clearInterval(sweepTimer);
|
|
18503
18790
|
clearInterval(seatTimer);
|
|
18791
|
+
ompSeats.stop();
|
|
18504
18792
|
human.stop();
|
|
18505
18793
|
if (goalsTimer) clearTimeout(goalsTimer);
|
|
18506
18794
|
void reducer.close();
|
|
@@ -18520,10 +18808,11 @@ async function startServer(opts) {
|
|
|
18520
18808
|
})
|
|
18521
18809
|
};
|
|
18522
18810
|
}
|
|
18523
|
-
var CONTINUE_ARGS, RESUME_ARGS, MULTI_DIR_ARGS, MODEL_ARGS, EFFORT_ARGS, NoteNotTyped, DELETED_TTL_MS, RESPAWN_HOOKS_MS, ALLOWED_ORIGINS;
|
|
18811
|
+
var SEAT_TOOLS, CONTINUE_ARGS, RESUME_ARGS, MULTI_DIR_ARGS, MODEL_ARGS, EFFORT_ARGS, NoteNotTyped, DELETED_TTL_MS, RESPAWN_HOOKS_MS, ALLOWED_ORIGINS;
|
|
18524
18812
|
var init_server = __esm({
|
|
18525
18813
|
"src/daemon/server.ts"() {
|
|
18526
18814
|
"use strict";
|
|
18815
|
+
init_port();
|
|
18527
18816
|
init_src();
|
|
18528
18817
|
init_src();
|
|
18529
18818
|
init_attachments2();
|
|
@@ -18540,6 +18829,7 @@ var init_server = __esm({
|
|
|
18540
18829
|
init_spawn();
|
|
18541
18830
|
init_host();
|
|
18542
18831
|
init_catalog();
|
|
18832
|
+
init_seats();
|
|
18543
18833
|
init_repos2();
|
|
18544
18834
|
init_scan2();
|
|
18545
18835
|
init_folders();
|
|
@@ -18578,6 +18868,7 @@ var init_server = __esm({
|
|
|
18578
18868
|
init_challenges();
|
|
18579
18869
|
init_webauthn();
|
|
18580
18870
|
init_frames();
|
|
18871
|
+
SEAT_TOOLS = { "claude-code": "claude", codex: "codex", omp: "omp" };
|
|
18581
18872
|
CONTINUE_ARGS = { claude: ["--continue"], codex: ["resume", "--last"], cursor: ["--continue"] };
|
|
18582
18873
|
RESUME_ARGS = { claude: (id) => ["--resume", id], codex: (id) => ["resume", id], cursor: (id) => ["--resume", id] };
|
|
18583
18874
|
MULTI_DIR_ARGS = { claude: (dirs) => ["--add-dir", ...dirs] };
|
|
@@ -18606,7 +18897,7 @@ var init_server = __esm({
|
|
|
18606
18897
|
// src/tmux.ts
|
|
18607
18898
|
import { execFile as execFile7 } from "child_process";
|
|
18608
18899
|
import { existsSync as existsSync3 } from "fs";
|
|
18609
|
-
import { join as
|
|
18900
|
+
import { join as join30 } from "path";
|
|
18610
18901
|
function runFile(file, args, env, opts) {
|
|
18611
18902
|
return new Promise((resolve13) => {
|
|
18612
18903
|
execFile7(file, args, { maxBuffer: 4 * 1024 * 1024, env, ...opts?.timeoutMs ? { timeout: opts.timeoutMs, killSignal: "SIGTERM" } : {} }, (err, stdout, stderr) => {
|
|
@@ -18616,7 +18907,7 @@ function runFile(file, args, env, opts) {
|
|
|
18616
18907
|
});
|
|
18617
18908
|
}
|
|
18618
18909
|
function systemdAvailable(env = process.env, exists = existsSync3) {
|
|
18619
|
-
return !!env.XDG_RUNTIME_DIR && exists(
|
|
18910
|
+
return !!env.XDG_RUNTIME_DIR && exists(join30(env.XDG_RUNTIME_DIR, "systemd", "private"));
|
|
18620
18911
|
}
|
|
18621
18912
|
function newSessionCommand(tmuxArgs, scoped, unit) {
|
|
18622
18913
|
if (!scoped) return { file: "tmux", args: tmuxArgs };
|
|
@@ -18730,6 +19021,203 @@ var init_tmux = __esm({
|
|
|
18730
19021
|
}
|
|
18731
19022
|
});
|
|
18732
19023
|
|
|
19024
|
+
// src/panes/keys.ts
|
|
19025
|
+
function keyBytes(key) {
|
|
19026
|
+
const named = NAMED[key];
|
|
19027
|
+
if (named !== void 0) return named;
|
|
19028
|
+
const ctrl = /^C-([a-z])$/.exec(key);
|
|
19029
|
+
if (ctrl) return String.fromCharCode(ctrl[1].charCodeAt(0) - 96);
|
|
19030
|
+
if ([...key].length === 1) return key;
|
|
19031
|
+
throw new Error(`unknown key: ${key}`);
|
|
19032
|
+
}
|
|
19033
|
+
var NAMED;
|
|
19034
|
+
var init_keys = __esm({
|
|
19035
|
+
"src/panes/keys.ts"() {
|
|
19036
|
+
"use strict";
|
|
19037
|
+
NAMED = {
|
|
19038
|
+
Enter: "\r",
|
|
19039
|
+
Escape: "\x1B",
|
|
19040
|
+
Tab: " ",
|
|
19041
|
+
BTab: "\x1B[Z",
|
|
19042
|
+
BSpace: "\x7F",
|
|
19043
|
+
Space: " ",
|
|
19044
|
+
Up: "\x1B[A",
|
|
19045
|
+
Down: "\x1B[B",
|
|
19046
|
+
Right: "\x1B[C",
|
|
19047
|
+
Left: "\x1B[D"
|
|
19048
|
+
};
|
|
19049
|
+
}
|
|
19050
|
+
});
|
|
19051
|
+
|
|
19052
|
+
// src/panes/capture.ts
|
|
19053
|
+
function captureLines(buffer, lines, opts = {}) {
|
|
19054
|
+
const join42 = opts.join ?? true;
|
|
19055
|
+
const out = [];
|
|
19056
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
19057
|
+
const line2 = buffer.getLine(i);
|
|
19058
|
+
if (!line2) continue;
|
|
19059
|
+
const text7 = line2.translateToString(true);
|
|
19060
|
+
if (join42 && line2.isWrapped && out.length) out[out.length - 1] += text7;
|
|
19061
|
+
else out.push(text7);
|
|
19062
|
+
}
|
|
19063
|
+
return out.join("\n").replace(/\s+$/, "").split("\n").slice(-lines).join("\n");
|
|
19064
|
+
}
|
|
19065
|
+
var init_capture = __esm({
|
|
19066
|
+
"src/panes/capture.ts"() {
|
|
19067
|
+
"use strict";
|
|
19068
|
+
}
|
|
19069
|
+
});
|
|
19070
|
+
|
|
19071
|
+
// src/panes/resolve.ts
|
|
19072
|
+
import { existsSync as existsSync4, statSync as statSync3 } from "fs";
|
|
19073
|
+
import { join as join31, isAbsolute as isAbsolute7, win32 } from "path";
|
|
19074
|
+
function resolveCommand(file, env, platform = process.platform) {
|
|
19075
|
+
if (platform !== "win32" || isAbsolute7(file) || win32.isAbsolute(file) || /[\\/]/.test(file)) return file;
|
|
19076
|
+
const key = Object.keys(env).find((k) => k.toUpperCase() === "PATH");
|
|
19077
|
+
const dirs = (key ? env[key] ?? "" : "").split(";").filter(Boolean);
|
|
19078
|
+
const names = /\.(exe|com)$/i.test(file) ? [file] : [`${file}.exe`, `${file}.com`];
|
|
19079
|
+
for (const dir of dirs) for (const name of names) {
|
|
19080
|
+
const p = join31(dir, name);
|
|
19081
|
+
if (existsSync4(p) && statSync3(p).isFile()) return p;
|
|
19082
|
+
}
|
|
19083
|
+
throw new Error(`${file}: no .exe found on the PATH`);
|
|
19084
|
+
}
|
|
19085
|
+
var init_resolve = __esm({
|
|
19086
|
+
"src/panes/resolve.ts"() {
|
|
19087
|
+
"use strict";
|
|
19088
|
+
}
|
|
19089
|
+
});
|
|
19090
|
+
|
|
19091
|
+
// src/panes/conpty.ts
|
|
19092
|
+
import { execFile as execFile8 } from "child_process";
|
|
19093
|
+
async function load(spawn8) {
|
|
19094
|
+
const xterm = await import("@xterm/headless");
|
|
19095
|
+
return { spawn: spawn8 ?? (await import("node-pty")).spawn, Terminal: (xterm.default ?? xterm).Terminal };
|
|
19096
|
+
}
|
|
19097
|
+
function killTree(pane) {
|
|
19098
|
+
const pid = pane.pty.pid;
|
|
19099
|
+
if (process.platform === "win32") {
|
|
19100
|
+
execFile8("taskkill", ["/PID", String(pid), "/T", "/F"], { windowsHide: true }, () => {
|
|
19101
|
+
});
|
|
19102
|
+
return;
|
|
19103
|
+
}
|
|
19104
|
+
try {
|
|
19105
|
+
process.kill(-pid, "SIGKILL");
|
|
19106
|
+
} catch {
|
|
19107
|
+
try {
|
|
19108
|
+
pane.pty.kill("SIGKILL");
|
|
19109
|
+
} catch {
|
|
19110
|
+
}
|
|
19111
|
+
}
|
|
19112
|
+
}
|
|
19113
|
+
function conptyPanes(opts = {}) {
|
|
19114
|
+
const cols = opts.cols ?? 200, rows = opts.rows ?? 50;
|
|
19115
|
+
let loaded;
|
|
19116
|
+
const live3 = /* @__PURE__ */ new Map();
|
|
19117
|
+
const codes = /* @__PURE__ */ new Map();
|
|
19118
|
+
const get = (name) => live3.get(name);
|
|
19119
|
+
const settle = (term) => new Promise((r) => term.write("", r));
|
|
19120
|
+
return {
|
|
19121
|
+
async newSession({ name, cwd, env, unset = [], command }) {
|
|
19122
|
+
if (live3.has(name)) throw new Error(`pane ${name} exists`);
|
|
19123
|
+
const { spawn: spawn8, Terminal } = await (loaded ??= load(opts.spawn));
|
|
19124
|
+
if (live3.has(name)) throw new Error(`pane ${name} exists`);
|
|
19125
|
+
const childEnv = {};
|
|
19126
|
+
for (const [k, v2] of Object.entries(env)) if (!unset.includes(k)) childEnv[k] = v2;
|
|
19127
|
+
const term = new Terminal({ cols, rows, scrollback: 5e3, allowProposedApi: true });
|
|
19128
|
+
const pty = spawn8(resolveCommand(command[0], childEnv), command.slice(1), { name: "xterm-256color", cols, rows, cwd, env: childEnv });
|
|
19129
|
+
codes.delete(name);
|
|
19130
|
+
let done;
|
|
19131
|
+
const exited = new Promise((r) => {
|
|
19132
|
+
done = r;
|
|
19133
|
+
});
|
|
19134
|
+
pty.onData((d) => term.write(d));
|
|
19135
|
+
pty.onExit((e) => {
|
|
19136
|
+
codes.set(name, e.exitCode);
|
|
19137
|
+
live3.delete(name);
|
|
19138
|
+
term.dispose();
|
|
19139
|
+
done();
|
|
19140
|
+
});
|
|
19141
|
+
live3.set(name, { pty, term, exited });
|
|
19142
|
+
},
|
|
19143
|
+
async sendText(name, text7) {
|
|
19144
|
+
if (/[\r\n]/.test(text7)) throw new Error("sendText: text must not contain newlines; flatten it first");
|
|
19145
|
+
const p = get(name);
|
|
19146
|
+
if (!p) throw new Error(`no pane ${name}`);
|
|
19147
|
+
const chars = [...text7];
|
|
19148
|
+
for (let i = 0; i < chars.length; i += SLICE) {
|
|
19149
|
+
if (i) await new Promise((r) => setTimeout(r, SLICE_GAP_MS));
|
|
19150
|
+
p.pty.write(chars.slice(i, i + SLICE).join(""));
|
|
19151
|
+
}
|
|
19152
|
+
},
|
|
19153
|
+
async sendKeys(name, keys) {
|
|
19154
|
+
const p = get(name);
|
|
19155
|
+
if (!p) throw new Error(`no pane ${name}`);
|
|
19156
|
+
const bytes4 = keys.map(keyBytes);
|
|
19157
|
+
for (const b3 of bytes4) p.pty.write(b3);
|
|
19158
|
+
},
|
|
19159
|
+
async capture(name, lines) {
|
|
19160
|
+
const p = get(name);
|
|
19161
|
+
if (!p) return null;
|
|
19162
|
+
await settle(p.term);
|
|
19163
|
+
return captureLines(p.term.buffer.active, lines, { join: false });
|
|
19164
|
+
},
|
|
19165
|
+
async width(name) {
|
|
19166
|
+
return get(name)?.term.cols ?? null;
|
|
19167
|
+
},
|
|
19168
|
+
async paneCwd() {
|
|
19169
|
+
return null;
|
|
19170
|
+
},
|
|
19171
|
+
async hasSession(name) {
|
|
19172
|
+
return live3.has(name);
|
|
19173
|
+
},
|
|
19174
|
+
async listSessions() {
|
|
19175
|
+
return [...live3.keys()];
|
|
19176
|
+
},
|
|
19177
|
+
async killSession(name) {
|
|
19178
|
+
const p = get(name);
|
|
19179
|
+
if (!p) return;
|
|
19180
|
+
killTree(p);
|
|
19181
|
+
await Promise.race([p.exited, new Promise((r) => setTimeout(r, 5e3))]);
|
|
19182
|
+
},
|
|
19183
|
+
async setOption() {
|
|
19184
|
+
},
|
|
19185
|
+
attachArgs() {
|
|
19186
|
+
throw new Error("attach is not available in the ConPTY spike");
|
|
19187
|
+
},
|
|
19188
|
+
controlArgs() {
|
|
19189
|
+
return null;
|
|
19190
|
+
},
|
|
19191
|
+
exitCode(name) {
|
|
19192
|
+
return codes.get(name);
|
|
19193
|
+
}
|
|
19194
|
+
};
|
|
19195
|
+
}
|
|
19196
|
+
var SLICE, SLICE_GAP_MS;
|
|
19197
|
+
var init_conpty = __esm({
|
|
19198
|
+
"src/panes/conpty.ts"() {
|
|
19199
|
+
"use strict";
|
|
19200
|
+
init_keys();
|
|
19201
|
+
init_capture();
|
|
19202
|
+
init_resolve();
|
|
19203
|
+
SLICE = 256;
|
|
19204
|
+
SLICE_GAP_MS = 20;
|
|
19205
|
+
}
|
|
19206
|
+
});
|
|
19207
|
+
|
|
19208
|
+
// src/panes/pick.ts
|
|
19209
|
+
function pickTmux(platform, env, socket = "ledge") {
|
|
19210
|
+
if (platform === "win32" || env.LEDGE_PANES === "conpty") return conptyPanes();
|
|
19211
|
+
return routedTmux(realTmux(socket), realTmux(null));
|
|
19212
|
+
}
|
|
19213
|
+
var init_pick = __esm({
|
|
19214
|
+
"src/panes/pick.ts"() {
|
|
19215
|
+
"use strict";
|
|
19216
|
+
init_tmux();
|
|
19217
|
+
init_conpty();
|
|
19218
|
+
}
|
|
19219
|
+
});
|
|
19220
|
+
|
|
18733
19221
|
// src/cli/setup.ts
|
|
18734
19222
|
var setup_exports = {};
|
|
18735
19223
|
__export(setup_exports, {
|
|
@@ -18749,10 +19237,10 @@ __export(setup_exports, {
|
|
|
18749
19237
|
systemdUnit: () => systemdUnit,
|
|
18750
19238
|
writeBin: () => writeBin
|
|
18751
19239
|
});
|
|
18752
|
-
import { readFile as
|
|
18753
|
-
import { execFile as
|
|
18754
|
-
import { homedir as
|
|
18755
|
-
import { join as
|
|
19240
|
+
import { readFile as readFile13, writeFile as writeFile14, mkdir as mkdir12, rename as rename5, realpath as realpath2, rm as rm6, stat as stat7, lstat, readlink } from "fs/promises";
|
|
19241
|
+
import { execFile as execFile9 } from "child_process";
|
|
19242
|
+
import { homedir as homedir15, userInfo } from "os";
|
|
19243
|
+
import { join as join32, dirname as dirname10 } from "path";
|
|
18756
19244
|
function mergeHooks(settings, events, command, file) {
|
|
18757
19245
|
if (settings === void 0) settings = {};
|
|
18758
19246
|
if (!settings || typeof settings !== "object" || Array.isArray(settings)) throw new Error(`${file} root is not an object, not modified`);
|
|
@@ -18800,7 +19288,7 @@ function mergeCursorHooks(config, command = "ledge hook cursor") {
|
|
|
18800
19288
|
async function mergeJsonFile(path3, merge, name) {
|
|
18801
19289
|
let raw = null;
|
|
18802
19290
|
try {
|
|
18803
|
-
raw = await
|
|
19291
|
+
raw = await readFile13(path3, "utf8");
|
|
18804
19292
|
} catch (e) {
|
|
18805
19293
|
if (e.code !== "ENOENT") throw e;
|
|
18806
19294
|
}
|
|
@@ -18836,7 +19324,7 @@ WantedBy=default.target
|
|
|
18836
19324
|
async function writeIfChanged(path3, content3) {
|
|
18837
19325
|
let current2 = null;
|
|
18838
19326
|
try {
|
|
18839
|
-
current2 = await
|
|
19327
|
+
current2 = await readFile13(path3, "utf8");
|
|
18840
19328
|
} catch (e) {
|
|
18841
19329
|
if (e.code !== "ENOENT") throw e;
|
|
18842
19330
|
}
|
|
@@ -18850,7 +19338,7 @@ async function writeIfChanged(path3, content3) {
|
|
|
18850
19338
|
async function stopDetachedDaemon(pidFile) {
|
|
18851
19339
|
let pid;
|
|
18852
19340
|
try {
|
|
18853
|
-
pid = Number((await
|
|
19341
|
+
pid = Number((await readFile13(pidFile, "utf8")).trim());
|
|
18854
19342
|
} catch {
|
|
18855
19343
|
return null;
|
|
18856
19344
|
}
|
|
@@ -18880,10 +19368,10 @@ async function stopDetachedDaemon(pidFile) {
|
|
|
18880
19368
|
async function installService(opts = {}) {
|
|
18881
19369
|
const out = [];
|
|
18882
19370
|
const run4 = opts.run ?? execRunner;
|
|
18883
|
-
const unitDir = opts.unitDir ??
|
|
19371
|
+
const unitDir = opts.unitDir ?? join32(homedir15(), ".config", "systemd", "user");
|
|
18884
19372
|
const node2 = opts.node ?? await realpath2(process.execPath);
|
|
18885
19373
|
const script = opts.script ?? await realpath2(process.argv[1]);
|
|
18886
|
-
const changed = await writeIfChanged(
|
|
19374
|
+
const changed = await writeIfChanged(join32(unitDir, SERVICE), systemdUnit({ node: node2, script }));
|
|
18887
19375
|
const reload = await run4("systemctl", ["--user", "daemon-reload"]);
|
|
18888
19376
|
if (reload.code !== 0) throw new Error(`systemd user session unavailable (systemctl --user daemon-reload failed: ${reload.stderr.trim() || reload.code}); the daemon will still be started on demand by ledge claude`);
|
|
18889
19377
|
const active = (await run4("systemctl", ["--user", "is-active", SERVICE])).code === 0;
|
|
@@ -18912,7 +19400,7 @@ async function installService(opts = {}) {
|
|
|
18912
19400
|
}
|
|
18913
19401
|
async function isDir(path3) {
|
|
18914
19402
|
try {
|
|
18915
|
-
return (await
|
|
19403
|
+
return (await stat7(path3)).isDirectory();
|
|
18916
19404
|
} catch {
|
|
18917
19405
|
return false;
|
|
18918
19406
|
}
|
|
@@ -18921,7 +19409,7 @@ async function writeBin(opts) {
|
|
|
18921
19409
|
const st = await lstat(opts.link).catch(() => null);
|
|
18922
19410
|
const was = st?.isSymbolicLink() ? await readlink(opts.link) : null;
|
|
18923
19411
|
if (was !== null && !was.endsWith("/dist/cli/index.js")) return null;
|
|
18924
|
-
const current2 = st && was === null && st.isFile() ? await
|
|
19412
|
+
const current2 = st && was === null && st.isFile() ? await readFile13(opts.link, "utf8").catch(() => "") : "";
|
|
18925
19413
|
if (st && was === null && !current2.startsWith(BIN_MARK)) return null;
|
|
18926
19414
|
const script = `${BIN_MARK}: the hooks run \`ledge\` wherever node is not on the PATH.
|
|
18927
19415
|
exec ${shQuote(opts.node)} ${shQuote(opts.target)} "$@"
|
|
@@ -18937,24 +19425,24 @@ async function runSetup(opts = {}) {
|
|
|
18937
19425
|
const out = [];
|
|
18938
19426
|
await ensureLedgeDir();
|
|
18939
19427
|
out.push("~/.ledge ready (tmux.conf written if missing)");
|
|
18940
|
-
const settingsPath2 = opts.settingsPath ??
|
|
19428
|
+
const settingsPath2 = opts.settingsPath ?? join32(homedir15(), ".claude", "settings.json");
|
|
18941
19429
|
if (await mergeJsonFile(settingsPath2, mergeClaudeHooks, "settings.json")) out.push(`Claude Code hooks added in ${settingsPath2}`);
|
|
18942
19430
|
else out.push("Claude Code hooks already present");
|
|
18943
|
-
const codexDir = opts.codexDir ?? process.env.CODEX_HOME ??
|
|
19431
|
+
const codexDir = opts.codexDir ?? process.env.CODEX_HOME ?? join32(homedir15(), ".codex");
|
|
18944
19432
|
if (!await isDir(codexDir)) out.push(`Codex not found (${codexDir} missing), Codex hooks skipped`);
|
|
18945
|
-
else if (await mergeJsonFile(
|
|
18946
|
-
out.push(`Codex hooks added in ${
|
|
19433
|
+
else if (await mergeJsonFile(join32(codexDir, "hooks.json"), mergeCodexHooks, "hooks.json")) {
|
|
19434
|
+
out.push(`Codex hooks added in ${join32(codexDir, "hooks.json")}; Codex asks to review them on its next start: trust them there or from Ledge`);
|
|
18947
19435
|
} else out.push("Codex hooks already present");
|
|
18948
|
-
const cursorDir = opts.cursorDir ??
|
|
19436
|
+
const cursorDir = opts.cursorDir ?? join32(homedir15(), ".cursor");
|
|
18949
19437
|
if (!await isDir(cursorDir)) out.push(`Cursor Agent not found (${cursorDir} missing), Cursor Agent hooks skipped`);
|
|
18950
|
-
else if (await mergeJsonFile(
|
|
19438
|
+
else if (await mergeJsonFile(join32(cursorDir, "hooks.json"), mergeCursorHooks, "hooks.json")) out.push(`Cursor Agent hooks added in ${join32(cursorDir, "hooks.json")}`);
|
|
18951
19439
|
else out.push("Cursor Agent hooks already present");
|
|
18952
19440
|
if (opts.service ?? true) {
|
|
18953
19441
|
const cli = opts.cli ?? process.argv[1] ?? "";
|
|
18954
19442
|
if (cli.endsWith(".ts")) out.push("systemd service skipped: run setup from the built CLI (pnpm --filter @paradow/ledge build, then ledge setup)");
|
|
18955
19443
|
else {
|
|
18956
19444
|
const script = await realpath2(cli);
|
|
18957
|
-
const line2 = await writeBin({ link: opts.link ??
|
|
19445
|
+
const line2 = await writeBin({ link: opts.link ?? join32(homedir15(), ".local", "bin", "ledge"), node: process.execPath, target: script });
|
|
18958
19446
|
if (line2) out.push(line2);
|
|
18959
19447
|
out.push(...await installService({ script, unitDir: opts.unitDir, run: opts.run }));
|
|
18960
19448
|
}
|
|
@@ -19005,7 +19493,7 @@ var init_setup = __esm({
|
|
|
19005
19493
|
];
|
|
19006
19494
|
SERVICE = "ledge-daemon.service";
|
|
19007
19495
|
execRunner = (cmd, args) => new Promise((resolve13) => {
|
|
19008
|
-
|
|
19496
|
+
execFile9(cmd, args, (err, stdout, stderr) => {
|
|
19009
19497
|
const code3 = err && typeof err.code === "number" ? err.code : err ? 1 : 0;
|
|
19010
19498
|
resolve13({ code: code3, stdout: String(stdout), stderr: String(stderr) });
|
|
19011
19499
|
});
|
|
@@ -19016,10 +19504,10 @@ var init_setup = __esm({
|
|
|
19016
19504
|
});
|
|
19017
19505
|
|
|
19018
19506
|
// src/update/guard.ts
|
|
19019
|
-
import { readFile as
|
|
19507
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
19020
19508
|
async function tmuxServerAtRisk(opts = {}) {
|
|
19021
19509
|
const runTmux = opts.runTmux ?? run2;
|
|
19022
|
-
const readCgroup = opts.readCgroup ?? ((pid2) =>
|
|
19510
|
+
const readCgroup = opts.readCgroup ?? ((pid2) => readFile14(`/proc/${pid2}/cgroup`, "utf8"));
|
|
19023
19511
|
const r = await runTmux(["-L", opts.socket ?? "ledge", "list-sessions", "-F", "#{pid}"]);
|
|
19024
19512
|
if (r.code !== 0) return false;
|
|
19025
19513
|
const pid = r.stdout.split("\n").map((s) => s.trim()).find(Boolean);
|
|
@@ -19041,26 +19529,26 @@ var init_guard = __esm({
|
|
|
19041
19529
|
});
|
|
19042
19530
|
|
|
19043
19531
|
// src/update/self.ts
|
|
19044
|
-
import { existsSync as
|
|
19045
|
-
import { readFile as
|
|
19046
|
-
import { homedir as
|
|
19047
|
-
import { dirname as dirname11, join as
|
|
19048
|
-
function gitRoot(script, exists =
|
|
19532
|
+
import { existsSync as existsSync5 } from "fs";
|
|
19533
|
+
import { readFile as readFile15, realpath as realpath3 } from "fs/promises";
|
|
19534
|
+
import { homedir as homedir16 } from "os";
|
|
19535
|
+
import { dirname as dirname11, join as join33 } from "path";
|
|
19536
|
+
function gitRoot(script, exists = existsSync5) {
|
|
19049
19537
|
for (let dir = dirname11(script); ; dir = dirname11(dir)) {
|
|
19050
|
-
if (exists(
|
|
19538
|
+
if (exists(join33(dir, ".git"))) return dir;
|
|
19051
19539
|
if (dirname11(dir) === dir) return null;
|
|
19052
19540
|
}
|
|
19053
19541
|
}
|
|
19054
|
-
function installKind(script, exists =
|
|
19542
|
+
function installKind(script, exists = existsSync5) {
|
|
19055
19543
|
if (script.includes("/node_modules/@paradow/ledge/")) return "npm";
|
|
19056
19544
|
return gitRoot(script, exists) ? "git" : "unknown";
|
|
19057
19545
|
}
|
|
19058
19546
|
async function daemonInfo() {
|
|
19059
19547
|
const script = await realpath3(process.argv[1] ?? "").catch(() => process.argv[1] ?? "");
|
|
19060
|
-
const cgroup = await
|
|
19548
|
+
const cgroup = await readFile15("/proc/self/cgroup", "utf8").catch(() => "");
|
|
19061
19549
|
const install = installKind(script);
|
|
19062
19550
|
const root2 = install === "git" ? gitRoot(script) : null;
|
|
19063
|
-
return { version: LEDGE_VERSION, install, systemd: underSystemd(process.env, cgroup), home:
|
|
19551
|
+
return { version: LEDGE_VERSION, install, systemd: underSystemd(process.env, cgroup), home: homedir16(), ...root2 ? { manual: manualCommand(root2) } : {} };
|
|
19064
19552
|
}
|
|
19065
19553
|
var underSystemd, manualCommand;
|
|
19066
19554
|
var init_self = __esm({
|
|
@@ -19074,7 +19562,7 @@ var init_self = __esm({
|
|
|
19074
19562
|
});
|
|
19075
19563
|
|
|
19076
19564
|
// src/update/run-update.ts
|
|
19077
|
-
import { dirname as dirname12, join as
|
|
19565
|
+
import { dirname as dirname12, join as join34 } from "path";
|
|
19078
19566
|
function createDaemonUpdater(deps) {
|
|
19079
19567
|
let busy = false;
|
|
19080
19568
|
let restarting = false;
|
|
@@ -19163,7 +19651,7 @@ var init_run_update = __esm({
|
|
|
19163
19651
|
PACKAGE = "@paradow/ledge";
|
|
19164
19652
|
NPM_TIMEOUT_MS = 3 * 6e4;
|
|
19165
19653
|
RESTART_COMMAND = "systemctl --user restart ledge-daemon";
|
|
19166
|
-
npmCommand = (execPath, version) => ({ file:
|
|
19654
|
+
npmCommand = (execPath, version) => ({ file: join34(dirname12(execPath), "npm"), args: ["i", "-g", `${PACKAGE}@${version}`] });
|
|
19167
19655
|
lastLines = (output, n = 3) => output.split("\n").map((l) => l.trimEnd()).filter(Boolean).slice(-n).join("\n");
|
|
19168
19656
|
npmEnv = (execPath, env) => ({ ...env, PATH: `${dirname12(execPath)}:${env.PATH ?? ""}` });
|
|
19169
19657
|
}
|
|
@@ -20677,12 +21165,12 @@ __export(daemon_exports, {
|
|
|
20677
21165
|
startDaemon: () => startDaemon
|
|
20678
21166
|
});
|
|
20679
21167
|
import { writeFile as writeFile15, rm as rm7 } from "fs/promises";
|
|
20680
|
-
import { join as
|
|
21168
|
+
import { join as join35 } from "path";
|
|
20681
21169
|
async function startDaemon(opts = {}) {
|
|
20682
21170
|
await ensureLedgeDir();
|
|
20683
21171
|
const store = new Store();
|
|
20684
21172
|
const loaded = await store.load();
|
|
20685
|
-
const tmux = opts.tmux ??
|
|
21173
|
+
const tmux = opts.tmux ?? pickTmux(process.platform, process.env, opts.tmuxSocket ?? "ledge");
|
|
20686
21174
|
let live3 = null;
|
|
20687
21175
|
try {
|
|
20688
21176
|
live3 = new Set(await tmux.listSessions());
|
|
@@ -20694,7 +21182,7 @@ async function startDaemon(opts = {}) {
|
|
|
20694
21182
|
}
|
|
20695
21183
|
for (const s of store.all()) {
|
|
20696
21184
|
if (backendOf(s) !== "omp-rpc") continue;
|
|
20697
|
-
await reapOwner(
|
|
21185
|
+
await reapOwner(join35(paths().dir, "omp", s.id)).catch(() => false);
|
|
20698
21186
|
if (s.state === "exited") continue;
|
|
20699
21187
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
20700
21188
|
store.apply(s.id, { name: "StopFailure", at, source: "daemon", text: "Ledge restarted: the OMP process ended with it" });
|
|
@@ -20760,7 +21248,7 @@ var init_daemon = __esm({
|
|
|
20760
21248
|
init_attachments2();
|
|
20761
21249
|
init_server();
|
|
20762
21250
|
init_poller();
|
|
20763
|
-
|
|
21251
|
+
init_pick();
|
|
20764
21252
|
init_paths();
|
|
20765
21253
|
init_log();
|
|
20766
21254
|
init_self();
|
|
@@ -20781,9 +21269,9 @@ __export(uninstall_exports, {
|
|
|
20781
21269
|
runUninstall: () => runUninstall,
|
|
20782
21270
|
uninstallArgs: () => uninstallArgs
|
|
20783
21271
|
});
|
|
20784
|
-
import { readFile as
|
|
20785
|
-
import { homedir as
|
|
20786
|
-
import { join as
|
|
21272
|
+
import { readFile as readFile16, writeFile as writeFile16, rename as rename6, rm as rm8, lstat as lstat2, realpath as realpath4 } from "fs/promises";
|
|
21273
|
+
import { homedir as homedir17 } from "os";
|
|
21274
|
+
import { join as join36 } from "path";
|
|
20787
21275
|
function prune(config, file, keep) {
|
|
20788
21276
|
if (!isObject(config)) throw new Error(`${file} root is not an object, not modified`);
|
|
20789
21277
|
if (config.hooks === void 0) return { settings: config, removed: 0 };
|
|
@@ -20792,16 +21280,16 @@ function prune(config, file, keep) {
|
|
|
20792
21280
|
let removed = 0;
|
|
20793
21281
|
for (const [ev, list3] of Object.entries(config.hooks)) {
|
|
20794
21282
|
if (!Array.isArray(list3)) throw new Error(`${file} hooks.${ev} is not an array, not modified`);
|
|
20795
|
-
const
|
|
21283
|
+
const kept2 = [];
|
|
20796
21284
|
for (const entry2 of list3) {
|
|
20797
21285
|
const next4 = keep(entry2);
|
|
20798
21286
|
if (next4 === null) removed++;
|
|
20799
21287
|
else {
|
|
20800
21288
|
if (next4 !== entry2) removed++;
|
|
20801
|
-
|
|
21289
|
+
kept2.push(next4);
|
|
20802
21290
|
}
|
|
20803
21291
|
}
|
|
20804
|
-
if (
|
|
21292
|
+
if (kept2.length || !list3.length) hooks[ev] = kept2;
|
|
20805
21293
|
}
|
|
20806
21294
|
const { hooks: _drop, ...rest } = config;
|
|
20807
21295
|
return { settings: Object.keys(hooks).length ? { ...rest, hooks } : rest, removed };
|
|
@@ -20820,7 +21308,7 @@ function removeCursorHooks(config) {
|
|
|
20820
21308
|
async function pruneJsonFile(path3, remove, name) {
|
|
20821
21309
|
let raw;
|
|
20822
21310
|
try {
|
|
20823
|
-
raw = await
|
|
21311
|
+
raw = await readFile16(path3, "utf8");
|
|
20824
21312
|
} catch (e) {
|
|
20825
21313
|
if (e.code === "ENOENT") return null;
|
|
20826
21314
|
throw e;
|
|
@@ -20841,7 +21329,7 @@ async function pruneJsonFile(path3, remove, name) {
|
|
|
20841
21329
|
async function removeService(opts = {}) {
|
|
20842
21330
|
const out = [];
|
|
20843
21331
|
const run4 = opts.run ?? execRunner;
|
|
20844
|
-
const unit =
|
|
21332
|
+
const unit = join36(opts.unitDir ?? join36(homedir17(), ".config", "systemd", "user"), SERVICE);
|
|
20845
21333
|
let present = true;
|
|
20846
21334
|
try {
|
|
20847
21335
|
await lstat2(unit);
|
|
@@ -20866,7 +21354,7 @@ async function runningSessions(run4, socket) {
|
|
|
20866
21354
|
async function removeLink(link3, script) {
|
|
20867
21355
|
try {
|
|
20868
21356
|
const st = await lstat2(link3);
|
|
20869
|
-
if (st.isFile() ? !(await
|
|
21357
|
+
if (st.isFile() ? !(await readFile16(link3, "utf8")).startsWith(BIN_MARK) : !st.isSymbolicLink() || await realpath4(link3) !== await realpath4(script)) return false;
|
|
20870
21358
|
} catch {
|
|
20871
21359
|
return false;
|
|
20872
21360
|
}
|
|
@@ -20889,9 +21377,9 @@ async function runUninstall(opts = {}) {
|
|
|
20889
21377
|
};
|
|
20890
21378
|
const run4 = opts.run ?? execRunner;
|
|
20891
21379
|
const files = [
|
|
20892
|
-
["Claude Code", opts.settingsPath ??
|
|
20893
|
-
["Codex",
|
|
20894
|
-
["Cursor Agent",
|
|
21380
|
+
["Claude Code", opts.settingsPath ?? join36(homedir17(), ".claude", "settings.json"), (c) => removeClaudeHooks(c, "settings.json")],
|
|
21381
|
+
["Codex", join36(opts.codexDir ?? process.env.CODEX_HOME ?? join36(homedir17(), ".codex"), "hooks.json"), (c) => removeClaudeHooks(c, "hooks.json")],
|
|
21382
|
+
["Cursor Agent", join36(opts.cursorDir ?? join36(homedir17(), ".cursor"), "hooks.json"), removeCursorHooks]
|
|
20895
21383
|
];
|
|
20896
21384
|
for (const [agent, path3, remove] of files) {
|
|
20897
21385
|
await step3(async () => {
|
|
@@ -20913,7 +21401,7 @@ async function runUninstall(opts = {}) {
|
|
|
20913
21401
|
});
|
|
20914
21402
|
else lines.push(`${dir} kept (session journal and log): \`ledge uninstall --purge\` removes it`);
|
|
20915
21403
|
const script = opts.script ?? process.argv[1];
|
|
20916
|
-
const link3 = opts.link ??
|
|
21404
|
+
const link3 = opts.link ?? join36(homedir17(), ".local", "bin", "ledge");
|
|
20917
21405
|
if (script) await step3(async () => {
|
|
20918
21406
|
if (await removeLink(link3, script)) lines.push(`${link3} removed`);
|
|
20919
21407
|
});
|
|
@@ -21111,7 +21599,7 @@ function createTokenSource(read) {
|
|
|
21111
21599
|
fns.forEach((f) => f(m));
|
|
21112
21600
|
}
|
|
21113
21601
|
};
|
|
21114
|
-
const
|
|
21602
|
+
const load2 = async () => {
|
|
21115
21603
|
let r, timer2;
|
|
21116
21604
|
const unavailable = { kind: "unavailable" };
|
|
21117
21605
|
try {
|
|
@@ -21132,7 +21620,7 @@ function createTokenSource(read) {
|
|
|
21132
21620
|
return {
|
|
21133
21621
|
get(fresh) {
|
|
21134
21622
|
if (!fresh && cached !== null) return Promise.resolve({ kind: "token", token: cached });
|
|
21135
|
-
inflight ??=
|
|
21623
|
+
inflight ??= load2().finally(() => {
|
|
21136
21624
|
inflight = null;
|
|
21137
21625
|
});
|
|
21138
21626
|
return inflight;
|
|
@@ -21171,10 +21659,10 @@ function createDaemonClient(opts) {
|
|
|
21171
21659
|
};
|
|
21172
21660
|
const schedule = () => {
|
|
21173
21661
|
if (closed) return;
|
|
21174
|
-
timer2 = setTimeout(
|
|
21662
|
+
timer2 = setTimeout(open6, backoff);
|
|
21175
21663
|
backoff = Math.min(max, backoff * 2);
|
|
21176
21664
|
};
|
|
21177
|
-
function
|
|
21665
|
+
function open6() {
|
|
21178
21666
|
if (closed) return;
|
|
21179
21667
|
setStatus(status === "open" ? "connecting" : status);
|
|
21180
21668
|
if (!token) {
|
|
@@ -21238,7 +21726,7 @@ function createDaemonClient(opts) {
|
|
|
21238
21726
|
ws = null;
|
|
21239
21727
|
old?.close();
|
|
21240
21728
|
setStatus("connecting");
|
|
21241
|
-
|
|
21729
|
+
open6();
|
|
21242
21730
|
},
|
|
21243
21731
|
close: () => {
|
|
21244
21732
|
closed = true;
|
|
@@ -21690,8 +22178,8 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
|
|
|
21690
22178
|
}
|
|
21691
22179
|
function history() {
|
|
21692
22180
|
const list3 = [...turns2.values()].sort((a, b3) => a.askedAt.localeCompare(b3.askedAt)).slice(-50);
|
|
21693
|
-
const
|
|
21694
|
-
for (const id of turns2.keys()) if (!
|
|
22181
|
+
const kept2 = new Set(list3.map((t) => t.qid));
|
|
22182
|
+
for (const id of turns2.keys()) if (!kept2.has(id)) {
|
|
21695
22183
|
turns2.delete(id);
|
|
21696
22184
|
bindings.delete(id);
|
|
21697
22185
|
}
|
|
@@ -23011,7 +23499,7 @@ __export(launch_exports, {
|
|
|
23011
23499
|
parseLaunchArgs: () => parseLaunchArgs
|
|
23012
23500
|
});
|
|
23013
23501
|
import { spawn as spawn3 } from "child_process";
|
|
23014
|
-
import { readFile as
|
|
23502
|
+
import { readFile as readFile17, rm as rm9 } from "fs/promises";
|
|
23015
23503
|
import { createConnection } from "net";
|
|
23016
23504
|
function parseLaunchArgs(argv) {
|
|
23017
23505
|
const args = [];
|
|
@@ -23063,7 +23551,7 @@ async function probe(port, timeoutMs) {
|
|
|
23063
23551
|
}
|
|
23064
23552
|
async function ensureDaemon(opts = {}) {
|
|
23065
23553
|
await ensureLedgeDir();
|
|
23066
|
-
const port = opts.port ??
|
|
23554
|
+
const port = opts.port ?? ledgePort();
|
|
23067
23555
|
const timeoutMs = opts.timeoutMs ?? 2e3;
|
|
23068
23556
|
const first = await probe(port, timeoutMs);
|
|
23069
23557
|
if (first.client) return { client: first.client };
|
|
@@ -23124,7 +23612,7 @@ async function readExitCode(id) {
|
|
|
23124
23612
|
const file = exitFile(id);
|
|
23125
23613
|
let raw = null;
|
|
23126
23614
|
try {
|
|
23127
|
-
raw = await
|
|
23615
|
+
raw = await readFile17(file, "utf8");
|
|
23128
23616
|
} catch {
|
|
23129
23617
|
}
|
|
23130
23618
|
await rm9(file, { force: true });
|
|
@@ -23134,7 +23622,7 @@ async function readExitCode(id) {
|
|
|
23134
23622
|
async function launch(tool, argv, opts = {}) {
|
|
23135
23623
|
await ensureLedgeDir();
|
|
23136
23624
|
const { name, args } = parseLaunchArgs(argv);
|
|
23137
|
-
const port = opts.port ??
|
|
23625
|
+
const port = opts.port ?? ledgePort();
|
|
23138
23626
|
const socket = opts.tmuxSocket ?? TMUX_SOCKET;
|
|
23139
23627
|
const tmux = realTmux(socket);
|
|
23140
23628
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -23176,6 +23664,7 @@ var TMUX_SOCKET, sleep2, closeQuietly, DaemonError, UNTRACKED;
|
|
|
23176
23664
|
var init_launch2 = __esm({
|
|
23177
23665
|
"src/cli/launch.ts"() {
|
|
23178
23666
|
"use strict";
|
|
23667
|
+
init_port();
|
|
23179
23668
|
init_src();
|
|
23180
23669
|
init_client();
|
|
23181
23670
|
init_tmux();
|
|
@@ -23201,6 +23690,7 @@ var init_launch2 = __esm({
|
|
|
23201
23690
|
import { spawn as spawn4 } from "child_process";
|
|
23202
23691
|
function attachCommand(fleet, s) {
|
|
23203
23692
|
if (s.offline) return { why: "offline" };
|
|
23693
|
+
if (isStoppedOmpSeat(s)) return { why: "stopped" };
|
|
23204
23694
|
if (backendOf(s) === "omp-rpc") return { why: "omp" };
|
|
23205
23695
|
if (!s.tmuxSession) return { why: "none" };
|
|
23206
23696
|
const argv = fleet.terminal(s.id);
|
|
@@ -23222,10 +23712,10 @@ var init_attach = __esm({
|
|
|
23222
23712
|
});
|
|
23223
23713
|
|
|
23224
23714
|
// src/tui/clipboard.ts
|
|
23225
|
-
import { execFile as
|
|
23226
|
-
import { mkdtemp as mkdtemp2, readFile as
|
|
23715
|
+
import { execFile as execFile10 } from "child_process";
|
|
23716
|
+
import { mkdtemp as mkdtemp2, readFile as readFile18, rm as rm10 } from "fs/promises";
|
|
23227
23717
|
import { tmpdir as tmpdir2 } from "os";
|
|
23228
|
-
import { join as
|
|
23718
|
+
import { join as join37 } from "path";
|
|
23229
23719
|
async function fromWindows(run4) {
|
|
23230
23720
|
const { stdout } = await run4(POWERSHELL, ["-NoProfile", "-STA", "-Command", SCRIPT], { cwd: "/mnt/c", maxBuffer: MAX_BUFFER, encoding: "buffer" });
|
|
23231
23721
|
const line2 = stdout.toString("utf8").trim();
|
|
@@ -23250,15 +23740,15 @@ async function fromLinux(run4) {
|
|
|
23250
23740
|
return null;
|
|
23251
23741
|
}
|
|
23252
23742
|
async function fromMac(run4) {
|
|
23253
|
-
const dir = await mkdtemp2(
|
|
23254
|
-
const file =
|
|
23743
|
+
const dir = await mkdtemp2(join37(tmpdir2(), "ledge-clip-"));
|
|
23744
|
+
const file = join37(dir, "clip.png");
|
|
23255
23745
|
try {
|
|
23256
23746
|
const script = `set f to open for access POSIX file "${file}" with write permission
|
|
23257
23747
|
set eof f to 0
|
|
23258
23748
|
write (the clipboard as \xABclass PNGf\xBB) to f
|
|
23259
23749
|
close access f`;
|
|
23260
23750
|
await run4("osascript", ["-e", script], { maxBuffer: MAX_BUFFER, encoding: "buffer" });
|
|
23261
|
-
return fromBytes(await
|
|
23751
|
+
return fromBytes(await readFile18(file));
|
|
23262
23752
|
} catch {
|
|
23263
23753
|
return null;
|
|
23264
23754
|
} finally {
|
|
@@ -23281,7 +23771,7 @@ var init_clipboard = __esm({
|
|
|
23281
23771
|
init_imageInfo();
|
|
23282
23772
|
CLIPBOARD_TIMEOUT_MS = 1e4;
|
|
23283
23773
|
runFile2 = (file, args, o) => new Promise((resolve13, reject) => {
|
|
23284
|
-
|
|
23774
|
+
execFile10(file, args, { ...o, timeout: CLIPBOARD_TIMEOUT_MS }, (err, stdout) => err ? reject(err) : resolve13({ stdout }));
|
|
23285
23775
|
});
|
|
23286
23776
|
POWERSHELL = "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe";
|
|
23287
23777
|
MAX_BUFFER = 4 * 1024 * 1024;
|
|
@@ -23689,7 +24179,7 @@ var init_fr = __esm({
|
|
|
23689
24179
|
plural = (n, one2, many) => n > 1 ? many : one2;
|
|
23690
24180
|
fr3 = {
|
|
23691
24181
|
lang: { fr: "Fran\xE7ais", en: "English" },
|
|
23692
|
-
state: { approval: "Votre accord", question: "Une question", starting: "D\xE9marrage", idle: "Disponible", working: "Au travail", done: "Termin\xE9e", error: "Erreur", readonly: "Lecture seule" },
|
|
24182
|
+
state: { approval: "Votre accord", question: "Une question", starting: "D\xE9marrage", idle: "Disponible", working: "Au travail", done: "Termin\xE9e", error: "Erreur", readonly: "Lecture seule", stopped: "Arr\xEAt\xE9" },
|
|
23693
24183
|
time: {
|
|
23694
24184
|
now: "\xE0 l\u2019instant",
|
|
23695
24185
|
decimal: ",",
|
|
@@ -23712,6 +24202,7 @@ var init_fr = __esm({
|
|
|
23712
24202
|
openImage: "ouvrir l'image",
|
|
23713
24203
|
navigate: "naviguer",
|
|
23714
24204
|
open: "ouvrir",
|
|
24205
|
+
attachSeat: "attacher son terminal",
|
|
23715
24206
|
answer: "r\xE9pondre",
|
|
23716
24207
|
attach: "attacher (tmux)",
|
|
23717
24208
|
ask: "demander \xE0 Ledge",
|
|
@@ -23786,6 +24277,7 @@ var init_fr = __esm({
|
|
|
23786
24277
|
noPreview: "Pas d\u2019aper\xE7u.",
|
|
23787
24278
|
noExchange: "Aucun \xE9change.",
|
|
23788
24279
|
sessions: (n) => `${n} ${plural(n, "session", "sessions")}`,
|
|
24280
|
+
sessionsOf: (live3, all2) => `${live3} ${plural(live3, "session", "sessions")} sur ${all2}`,
|
|
23789
24281
|
outside: "Hors projet",
|
|
23790
24282
|
offline: "hors ligne",
|
|
23791
24283
|
readOnly: "lecture seule",
|
|
@@ -24009,6 +24501,7 @@ var init_fr = __esm({
|
|
|
24009
24501
|
noteTooLong: (max) => `Note trop longue : ${max} caract\xE8res au plus. Elle reste dans le champ.`,
|
|
24010
24502
|
noNote: (n) => `${n} ne prend pas de note ici.`,
|
|
24011
24503
|
noTerminalOmp: (n) => `${n} est pilot\xE9 par Ledge via le protocole d\u2019OMP : pas de terminal \xE0 attacher.`,
|
|
24504
|
+
seatStopped: (n) => `${n} est arr\xEAt\xE9 : son terminal n\u2019existe plus. 6 ouvre OpenRig, o\xF9 est son rig.`,
|
|
24012
24505
|
noTerminal: (n) => `Pas de terminal pour ${n}.`,
|
|
24013
24506
|
attachFailed: (code3) => `tmux s\u2019est arr\xEAt\xE9 avec le code ${code3}.`,
|
|
24014
24507
|
modelSwitch: "Ce daemon ne change pas le mod\xE8le d\u2019une session en cours.",
|
|
@@ -24257,7 +24750,7 @@ var init_en = __esm({
|
|
|
24257
24750
|
plural2 = (n, one2, many) => n > 1 ? many : one2;
|
|
24258
24751
|
en3 = {
|
|
24259
24752
|
lang: { fr: "Fran\xE7ais", en: "English" },
|
|
24260
|
-
state: { approval: "Needs you", question: "A question", starting: "Starting", idle: "Available", working: "Working", done: "Finished", error: "Error", readonly: "Read-only" },
|
|
24753
|
+
state: { approval: "Needs you", question: "A question", starting: "Starting", idle: "Available", working: "Working", done: "Finished", error: "Error", readonly: "Read-only", stopped: "Stopped" },
|
|
24261
24754
|
time: { now: "just now", decimal: ".", s: (n) => `${n}s`, min: (n) => `${n} min`, h: (n) => `${n} h`, d: (n) => `${n} d` },
|
|
24262
24755
|
tabs: { overview: "Overview", session: "Session", ask: "Ask Ledge", launch: "New session", settings: "Settings", openrig: "OpenRig" },
|
|
24263
24756
|
bar: {
|
|
@@ -24273,6 +24766,7 @@ var init_en = __esm({
|
|
|
24273
24766
|
openImage: "open image",
|
|
24274
24767
|
navigate: "move",
|
|
24275
24768
|
open: "open",
|
|
24769
|
+
attachSeat: "attach its terminal",
|
|
24276
24770
|
answer: "answer",
|
|
24277
24771
|
attach: "attach (tmux)",
|
|
24278
24772
|
ask: "ask Ledge",
|
|
@@ -24346,6 +24840,7 @@ var init_en = __esm({
|
|
|
24346
24840
|
noPreview: "No preview.",
|
|
24347
24841
|
noExchange: "No message yet.",
|
|
24348
24842
|
sessions: (n) => `${n} ${plural2(n, "session", "sessions")}`,
|
|
24843
|
+
sessionsOf: (live3, all2) => `${live3} ${live3 === 1 ? "session" : "sessions"} of ${all2}`,
|
|
24349
24844
|
outside: "No project",
|
|
24350
24845
|
offline: "offline",
|
|
24351
24846
|
readOnly: "read-only",
|
|
@@ -24565,6 +25060,7 @@ var init_en = __esm({
|
|
|
24565
25060
|
noteTooLong: (max) => `Note too long: ${max} characters at most. It stays in the field.`,
|
|
24566
25061
|
noNote: (n) => `${n} takes no note here.`,
|
|
24567
25062
|
noTerminalOmp: (n) => `${n} is driven by Ledge over OMP's protocol: there is no terminal to attach to.`,
|
|
25063
|
+
seatStopped: (n) => `${n} is stopped: its terminal is gone. 6 opens OpenRig, where its rig is.`,
|
|
24568
25064
|
noTerminal: (n) => `No terminal for ${n}.`,
|
|
24569
25065
|
attachFailed: (code3) => `tmux stopped with code ${code3}.`,
|
|
24570
25066
|
modelSwitch: "This daemon cannot change a running session\u2019s model.",
|
|
@@ -24829,15 +25325,19 @@ function overview(sessions, projects) {
|
|
|
24829
25325
|
const known2 = new Map((projects?.projects ?? []).map((p) => [p.id, p]));
|
|
24830
25326
|
for (const p of projects?.projects ?? []) nodes.set(`p:${p.id}`, { key: `p:${p.id}`, label: p.name, color: p.color, branches: [], count: 0 });
|
|
24831
25327
|
const rigNodes = /* @__PURE__ */ new Map();
|
|
25328
|
+
const rigNodeOf = (rig) => {
|
|
25329
|
+
const kind = rig === "kernel" ? "kernel" : "rig";
|
|
25330
|
+
const key = kind === "kernel" ? "g:kernel" : `g:rig:${rig}`;
|
|
25331
|
+
let node2 = rigNodes.get(key);
|
|
25332
|
+
if (!node2) {
|
|
25333
|
+
node2 = { key, label: kind === "kernel" ? "Kernel" : rig, kind, branches: [{ branch: "", sessions: [] }], count: 0 };
|
|
25334
|
+
rigNodes.set(key, node2);
|
|
25335
|
+
}
|
|
25336
|
+
return node2;
|
|
25337
|
+
};
|
|
24832
25338
|
for (const s of live3) {
|
|
24833
25339
|
if (s.rig) {
|
|
24834
|
-
const
|
|
24835
|
-
const key2 = kind === "kernel" ? "g:kernel" : `g:rig:${s.rig.name}`;
|
|
24836
|
-
let node3 = rigNodes.get(key2);
|
|
24837
|
-
if (!node3) {
|
|
24838
|
-
node3 = { key: key2, label: kind === "kernel" ? "Kernel" : s.rig.name, kind, branches: [{ branch: "", sessions: [] }], count: 0 };
|
|
24839
|
-
rigNodes.set(key2, node3);
|
|
24840
|
-
}
|
|
25340
|
+
const node3 = rigNodeOf(s.rig.name);
|
|
24841
25341
|
node3.branches[0].sessions.push(s);
|
|
24842
25342
|
node3.count += 1;
|
|
24843
25343
|
continue;
|
|
@@ -24858,6 +25358,12 @@ function overview(sessions, projects) {
|
|
|
24858
25358
|
row3.sessions.push(s);
|
|
24859
25359
|
node2.count += 1;
|
|
24860
25360
|
}
|
|
25361
|
+
for (const s of sessions) {
|
|
25362
|
+
if (!isStoppedOmpSeat(s) || !s.rig) continue;
|
|
25363
|
+
const node2 = rigNodeOf(s.rig.name);
|
|
25364
|
+
node2.branches[0].sessions.push(s);
|
|
25365
|
+
node2.shown = (node2.shown ?? node2.count) + 1;
|
|
25366
|
+
}
|
|
24861
25367
|
const groups = [...rigNodes.values()].sort((a, b3) => a.kind === b3.kind ? a.label.localeCompare(b3.label) : a.kind === "kernel" ? 1 : -1);
|
|
24862
25368
|
const tree2 = [...[...nodes.values()].filter((n) => n.count > 0), ...groups];
|
|
24863
25369
|
const order2 = tree2.flatMap((n) => n.branches.flatMap((b3) => b3.sessions.map((s) => s.id)));
|
|
@@ -25118,7 +25624,7 @@ var init_theme = __esm({
|
|
|
25118
25624
|
|
|
25119
25625
|
// src/tui/model/cells.ts
|
|
25120
25626
|
import stringWidth from "string-width";
|
|
25121
|
-
function
|
|
25627
|
+
function join38(line2, a) {
|
|
25122
25628
|
const out = [];
|
|
25123
25629
|
for (const c of a) {
|
|
25124
25630
|
const last = out[out.length - 1];
|
|
@@ -25133,9 +25639,9 @@ function cut2(line2, width, ellipsis = "\u2026") {
|
|
|
25133
25639
|
const a = split2(line2), room = width - cells(ellipsis);
|
|
25134
25640
|
let n = 0, i = 0;
|
|
25135
25641
|
while (i < a.length && n + a[i].w <= room) n += a[i++].w;
|
|
25136
|
-
const
|
|
25642
|
+
const kept2 = join38(line2, a.slice(0, i));
|
|
25137
25643
|
const color2 = line2[a[Math.max(0, i - 1)]?.seg ?? 0]?.color;
|
|
25138
|
-
return [...
|
|
25644
|
+
return [...kept2, { text: ellipsis, ...color2 ? { color: color2 } : {} }];
|
|
25139
25645
|
}
|
|
25140
25646
|
function wrapSegs(line2, width, first = width) {
|
|
25141
25647
|
const a = split2(line2);
|
|
@@ -25152,7 +25658,7 @@ function wrapSegs(line2, width, first = width) {
|
|
|
25152
25658
|
}
|
|
25153
25659
|
w = Math.max(4, width);
|
|
25154
25660
|
if (j >= a.length || a[j].g === "\n") {
|
|
25155
|
-
out.push(
|
|
25661
|
+
out.push(join38(line2, a.slice(i, j)));
|
|
25156
25662
|
i = j + 1;
|
|
25157
25663
|
if (j >= a.length) break;
|
|
25158
25664
|
if (i >= a.length) out.push([]);
|
|
@@ -25161,7 +25667,7 @@ function wrapSegs(line2, width, first = width) {
|
|
|
25161
25667
|
const end2 = a[j].g === " " ? j : space > i ? space : Math.max(j, i + 1);
|
|
25162
25668
|
let k = end2;
|
|
25163
25669
|
while (k > i && a[k - 1].g === " ") k--;
|
|
25164
|
-
out.push(
|
|
25670
|
+
out.push(join38(line2, a.slice(i, k)));
|
|
25165
25671
|
i = end2;
|
|
25166
25672
|
while (i < a.length && a[i].g === " ") i++;
|
|
25167
25673
|
if (a[i]?.g === "\n") {
|
|
@@ -30374,16 +30880,16 @@ function resolveAllAttention(events, context) {
|
|
|
30374
30880
|
let nextEvents;
|
|
30375
30881
|
while (++index2 < events.length) {
|
|
30376
30882
|
if (events[index2][0] === "enter" && events[index2][1].type === "attentionSequence" && events[index2][1]._close) {
|
|
30377
|
-
let
|
|
30378
|
-
while (
|
|
30379
|
-
if (events[
|
|
30380
|
-
context.sliceSerialize(events[
|
|
30381
|
-
if ((events[
|
|
30883
|
+
let open6 = index2;
|
|
30884
|
+
while (open6--) {
|
|
30885
|
+
if (events[open6][0] === "exit" && events[open6][1].type === "attentionSequence" && events[open6][1]._open && // If the markers are the same:
|
|
30886
|
+
context.sliceSerialize(events[open6][1]).charCodeAt(0) === context.sliceSerialize(events[index2][1]).charCodeAt(0)) {
|
|
30887
|
+
if ((events[open6][1]._close || events[index2][1]._open) && (events[index2][1].end.offset - events[index2][1].start.offset) % 3 && !((events[open6][1].end.offset - events[open6][1].start.offset + events[index2][1].end.offset - events[index2][1].start.offset) % 3)) {
|
|
30382
30888
|
continue;
|
|
30383
30889
|
}
|
|
30384
|
-
const use = events[
|
|
30890
|
+
const use = events[open6][1].end.offset - events[open6][1].start.offset > 1 && events[index2][1].end.offset - events[index2][1].start.offset > 1 ? 2 : 1;
|
|
30385
30891
|
const start = {
|
|
30386
|
-
...events[
|
|
30892
|
+
...events[open6][1].end
|
|
30387
30893
|
};
|
|
30388
30894
|
const end2 = {
|
|
30389
30895
|
...events[index2][1].start
|
|
@@ -30394,7 +30900,7 @@ function resolveAllAttention(events, context) {
|
|
|
30394
30900
|
type: use > 1 ? "strongSequence" : "emphasisSequence",
|
|
30395
30901
|
start,
|
|
30396
30902
|
end: {
|
|
30397
|
-
...events[
|
|
30903
|
+
...events[open6][1].end
|
|
30398
30904
|
}
|
|
30399
30905
|
};
|
|
30400
30906
|
const closingSequence = {
|
|
@@ -30407,7 +30913,7 @@ function resolveAllAttention(events, context) {
|
|
|
30407
30913
|
const text7 = {
|
|
30408
30914
|
type: use > 1 ? "strongText" : "emphasisText",
|
|
30409
30915
|
start: {
|
|
30410
|
-
...events[
|
|
30916
|
+
...events[open6][1].end
|
|
30411
30917
|
},
|
|
30412
30918
|
end: {
|
|
30413
30919
|
...events[index2][1].start
|
|
@@ -30422,26 +30928,26 @@ function resolveAllAttention(events, context) {
|
|
|
30422
30928
|
...closingSequence.end
|
|
30423
30929
|
}
|
|
30424
30930
|
};
|
|
30425
|
-
events[
|
|
30931
|
+
events[open6][1].end = {
|
|
30426
30932
|
...openingSequence.start
|
|
30427
30933
|
};
|
|
30428
30934
|
events[index2][1].start = {
|
|
30429
30935
|
...closingSequence.end
|
|
30430
30936
|
};
|
|
30431
30937
|
nextEvents = [];
|
|
30432
|
-
if (events[
|
|
30433
|
-
nextEvents = push(nextEvents, [["enter", events[
|
|
30938
|
+
if (events[open6][1].end.offset - events[open6][1].start.offset) {
|
|
30939
|
+
nextEvents = push(nextEvents, [["enter", events[open6][1], context], ["exit", events[open6][1], context]]);
|
|
30434
30940
|
}
|
|
30435
30941
|
nextEvents = push(nextEvents, [["enter", group, context], ["enter", openingSequence, context], ["exit", openingSequence, context], ["enter", text7, context]]);
|
|
30436
|
-
nextEvents = push(nextEvents, resolveAll(context.parser.constructs.insideSpan.null, events.slice(
|
|
30942
|
+
nextEvents = push(nextEvents, resolveAll(context.parser.constructs.insideSpan.null, events.slice(open6 + 1, index2), context));
|
|
30437
30943
|
nextEvents = push(nextEvents, [["exit", text7, context], ["enter", closingSequence, context], ["exit", closingSequence, context], ["exit", group, context]]);
|
|
30438
30944
|
let offset = 0;
|
|
30439
30945
|
if (events[index2][1].end.offset - events[index2][1].start.offset) {
|
|
30440
30946
|
offset = 2;
|
|
30441
30947
|
nextEvents = push(nextEvents, [["enter", events[index2][1], context], ["exit", events[index2][1], context]]);
|
|
30442
30948
|
}
|
|
30443
|
-
splice(events,
|
|
30444
|
-
index2 =
|
|
30949
|
+
splice(events, open6 - 1, index2 - open6 + 3, nextEvents);
|
|
30950
|
+
index2 = open6 + nextEvents.length - offset - 2;
|
|
30445
30951
|
break;
|
|
30446
30952
|
}
|
|
30447
30953
|
}
|
|
@@ -30473,10 +30979,10 @@ function tokenizeAttention(effects, ok4) {
|
|
|
30473
30979
|
}
|
|
30474
30980
|
const token = effects.exit("attentionSequence");
|
|
30475
30981
|
const after2 = classifyCharacter(code3);
|
|
30476
|
-
const
|
|
30982
|
+
const open6 = !after2 || after2 === 2 && before2 || attentionMarkers2.includes(code3) && code3 !== 42 && code3 !== 95;
|
|
30477
30983
|
const close = !before2 || before2 === 2 && after2 || attentionMarkers2.includes(previous3) && previous3 !== 42 && previous3 !== 95;
|
|
30478
|
-
token._open = Boolean(marker === 42 ?
|
|
30479
|
-
token._close = Boolean(marker === 42 ? close : close && (after2 || !
|
|
30984
|
+
token._open = Boolean(marker === 42 ? open6 : open6 && (before2 || !close));
|
|
30985
|
+
token._close = Boolean(marker === 42 ? close : close && (after2 || !open6));
|
|
30480
30986
|
return ok4(code3);
|
|
30481
30987
|
}
|
|
30482
30988
|
}
|
|
@@ -30510,9 +31016,9 @@ function tokenizeAutolink(effects, ok4, nok) {
|
|
|
30510
31016
|
effects.consume(code3);
|
|
30511
31017
|
effects.exit("autolinkMarker");
|
|
30512
31018
|
effects.enter("autolinkProtocol");
|
|
30513
|
-
return
|
|
31019
|
+
return open6;
|
|
30514
31020
|
}
|
|
30515
|
-
function
|
|
31021
|
+
function open6(code3) {
|
|
30516
31022
|
if (asciiAlpha(code3)) {
|
|
30517
31023
|
effects.consume(code3);
|
|
30518
31024
|
return schemeOrEmailAtext;
|
|
@@ -30793,9 +31299,9 @@ function tokenizeCharacterReference(effects, ok4, nok) {
|
|
|
30793
31299
|
effects.enter("characterReferenceMarker");
|
|
30794
31300
|
effects.consume(code3);
|
|
30795
31301
|
effects.exit("characterReferenceMarker");
|
|
30796
|
-
return
|
|
31302
|
+
return open6;
|
|
30797
31303
|
}
|
|
30798
|
-
function
|
|
31304
|
+
function open6(code3) {
|
|
30799
31305
|
if (code3 === 35) {
|
|
30800
31306
|
effects.enter("characterReferenceMarkerNumeric");
|
|
30801
31307
|
effects.consume(code3);
|
|
@@ -32196,9 +32702,9 @@ function tokenizeHtmlFlow(effects, ok4, nok) {
|
|
|
32196
32702
|
effects.enter("htmlFlow");
|
|
32197
32703
|
effects.enter("htmlFlowData");
|
|
32198
32704
|
effects.consume(code3);
|
|
32199
|
-
return
|
|
32705
|
+
return open6;
|
|
32200
32706
|
}
|
|
32201
|
-
function
|
|
32707
|
+
function open6(code3) {
|
|
32202
32708
|
if (code3 === 33) {
|
|
32203
32709
|
effects.consume(code3);
|
|
32204
32710
|
return declarationOpen;
|
|
@@ -32546,9 +33052,9 @@ function tokenizeHtmlText(effects, ok4, nok) {
|
|
|
32546
33052
|
effects.enter("htmlText");
|
|
32547
33053
|
effects.enter("htmlTextData");
|
|
32548
33054
|
effects.consume(code3);
|
|
32549
|
-
return
|
|
33055
|
+
return open6;
|
|
32550
33056
|
}
|
|
32551
|
-
function
|
|
33057
|
+
function open6(code3) {
|
|
32552
33058
|
if (code3 === 33) {
|
|
32553
33059
|
effects.consume(code3);
|
|
32554
33060
|
return declarationOpen;
|
|
@@ -32870,12 +33376,12 @@ function resolveAllLabelEnd(events) {
|
|
|
32870
33376
|
function resolveToLabelEnd(events, context) {
|
|
32871
33377
|
let index2 = events.length;
|
|
32872
33378
|
let offset = 0;
|
|
32873
|
-
let
|
|
33379
|
+
let open6;
|
|
32874
33380
|
let close;
|
|
32875
33381
|
let media;
|
|
32876
33382
|
while (index2--) {
|
|
32877
33383
|
const token = events[index2][1];
|
|
32878
|
-
if (
|
|
33384
|
+
if (open6) {
|
|
32879
33385
|
if (token.type === "link" || token.type === "labelLink" && token._inactive) {
|
|
32880
33386
|
break;
|
|
32881
33387
|
}
|
|
@@ -32884,7 +33390,7 @@ function resolveToLabelEnd(events, context) {
|
|
|
32884
33390
|
}
|
|
32885
33391
|
} else if (close) {
|
|
32886
33392
|
if (events[index2][0] === "enter" && (token.type === "labelImage" || token.type === "labelLink") && !token._balanced) {
|
|
32887
|
-
|
|
33393
|
+
open6 = index2;
|
|
32888
33394
|
if (token.type !== "labelLink") {
|
|
32889
33395
|
offset = 2;
|
|
32890
33396
|
break;
|
|
@@ -32895,9 +33401,9 @@ function resolveToLabelEnd(events, context) {
|
|
|
32895
33401
|
}
|
|
32896
33402
|
}
|
|
32897
33403
|
const group = {
|
|
32898
|
-
type: events[
|
|
33404
|
+
type: events[open6][1].type === "labelLink" ? "link" : "image",
|
|
32899
33405
|
start: {
|
|
32900
|
-
...events[
|
|
33406
|
+
...events[open6][1].start
|
|
32901
33407
|
},
|
|
32902
33408
|
end: {
|
|
32903
33409
|
...events[events.length - 1][1].end
|
|
@@ -32906,7 +33412,7 @@ function resolveToLabelEnd(events, context) {
|
|
|
32906
33412
|
const label2 = {
|
|
32907
33413
|
type: "label",
|
|
32908
33414
|
start: {
|
|
32909
|
-
...events[
|
|
33415
|
+
...events[open6][1].start
|
|
32910
33416
|
},
|
|
32911
33417
|
end: {
|
|
32912
33418
|
...events[close][1].end
|
|
@@ -32915,20 +33421,20 @@ function resolveToLabelEnd(events, context) {
|
|
|
32915
33421
|
const text7 = {
|
|
32916
33422
|
type: "labelText",
|
|
32917
33423
|
start: {
|
|
32918
|
-
...events[
|
|
33424
|
+
...events[open6 + offset + 2][1].end
|
|
32919
33425
|
},
|
|
32920
33426
|
end: {
|
|
32921
33427
|
...events[close - 2][1].start
|
|
32922
33428
|
}
|
|
32923
33429
|
};
|
|
32924
33430
|
media = [["enter", group, context], ["enter", label2, context]];
|
|
32925
|
-
media = push(media, events.slice(
|
|
33431
|
+
media = push(media, events.slice(open6 + 1, open6 + offset + 3));
|
|
32926
33432
|
media = push(media, [["enter", text7, context]]);
|
|
32927
|
-
media = push(media, resolveAll(context.parser.constructs.insideSpan.null, events.slice(
|
|
33433
|
+
media = push(media, resolveAll(context.parser.constructs.insideSpan.null, events.slice(open6 + offset + 4, close - 3), context));
|
|
32928
33434
|
media = push(media, [["exit", text7, context], events[close - 2], events[close - 1], ["exit", label2, context]]);
|
|
32929
33435
|
media = push(media, events.slice(close + 1));
|
|
32930
33436
|
media = push(media, [["exit", group, context]]);
|
|
32931
|
-
splice(events,
|
|
33437
|
+
splice(events, open6, events.length, media);
|
|
32932
33438
|
return events;
|
|
32933
33439
|
}
|
|
32934
33440
|
function tokenizeLabelEnd(effects, ok4, nok) {
|
|
@@ -33099,9 +33605,9 @@ function tokenizeLabelStartImage(effects, ok4, nok) {
|
|
|
33099
33605
|
effects.enter("labelImageMarker");
|
|
33100
33606
|
effects.consume(code3);
|
|
33101
33607
|
effects.exit("labelImageMarker");
|
|
33102
|
-
return
|
|
33608
|
+
return open6;
|
|
33103
33609
|
}
|
|
33104
|
-
function
|
|
33610
|
+
function open6(code3) {
|
|
33105
33611
|
if (code3 === 91) {
|
|
33106
33612
|
effects.enter("labelMarker");
|
|
33107
33613
|
effects.consume(code3);
|
|
@@ -33933,30 +34439,30 @@ function gfmStrikethrough(options) {
|
|
|
33933
34439
|
let index2 = -1;
|
|
33934
34440
|
while (++index2 < events.length) {
|
|
33935
34441
|
if (events[index2][0] === "enter" && events[index2][1].type === "strikethroughSequenceTemporary" && events[index2][1]._close) {
|
|
33936
|
-
let
|
|
33937
|
-
while (
|
|
33938
|
-
if (events[
|
|
33939
|
-
events[index2][1].end.offset - events[index2][1].start.offset === events[
|
|
34442
|
+
let open6 = index2;
|
|
34443
|
+
while (open6--) {
|
|
34444
|
+
if (events[open6][0] === "exit" && events[open6][1].type === "strikethroughSequenceTemporary" && events[open6][1]._open && // If the sizes are the same:
|
|
34445
|
+
events[index2][1].end.offset - events[index2][1].start.offset === events[open6][1].end.offset - events[open6][1].start.offset) {
|
|
33940
34446
|
events[index2][1].type = "strikethroughSequence";
|
|
33941
|
-
events[
|
|
34447
|
+
events[open6][1].type = "strikethroughSequence";
|
|
33942
34448
|
const strikethrough = {
|
|
33943
34449
|
type: "strikethrough",
|
|
33944
|
-
start: Object.assign({}, events[
|
|
34450
|
+
start: Object.assign({}, events[open6][1].start),
|
|
33945
34451
|
end: Object.assign({}, events[index2][1].end)
|
|
33946
34452
|
};
|
|
33947
34453
|
const text7 = {
|
|
33948
34454
|
type: "strikethroughText",
|
|
33949
|
-
start: Object.assign({}, events[
|
|
34455
|
+
start: Object.assign({}, events[open6][1].end),
|
|
33950
34456
|
end: Object.assign({}, events[index2][1].start)
|
|
33951
34457
|
};
|
|
33952
|
-
const nextEvents = [["enter", strikethrough, context], ["enter", events[
|
|
34458
|
+
const nextEvents = [["enter", strikethrough, context], ["enter", events[open6][1], context], ["exit", events[open6][1], context], ["enter", text7, context]];
|
|
33953
34459
|
const insideSpan2 = context.parser.constructs.insideSpan.null;
|
|
33954
34460
|
if (insideSpan2) {
|
|
33955
|
-
splice(nextEvents, nextEvents.length, 0, resolveAll(insideSpan2, events.slice(
|
|
34461
|
+
splice(nextEvents, nextEvents.length, 0, resolveAll(insideSpan2, events.slice(open6 + 1, index2), context));
|
|
33956
34462
|
}
|
|
33957
34463
|
splice(nextEvents, nextEvents.length, 0, [["exit", text7, context], ["enter", events[index2][1], context], ["exit", events[index2][1], context], ["exit", strikethrough, context]]);
|
|
33958
|
-
splice(events,
|
|
33959
|
-
index2 =
|
|
34464
|
+
splice(events, open6 - 1, index2 - open6 + 3, nextEvents);
|
|
34465
|
+
index2 = open6 + nextEvents.length - 2;
|
|
33960
34466
|
break;
|
|
33961
34467
|
}
|
|
33962
34468
|
}
|
|
@@ -34558,8 +35064,8 @@ function gfmTaskListItem() {
|
|
|
34558
35064
|
}
|
|
34559
35065
|
function tokenizeTasklistCheck(effects, ok4, nok) {
|
|
34560
35066
|
const self = this;
|
|
34561
|
-
return
|
|
34562
|
-
function
|
|
35067
|
+
return open6;
|
|
35068
|
+
function open6(code3) {
|
|
34563
35069
|
if (
|
|
34564
35070
|
// Exit if there’s stuff before.
|
|
34565
35071
|
self.previous !== null || // Exit if not in the first content that is the first child of a list
|
|
@@ -36048,8 +36554,8 @@ function compiler(options) {
|
|
|
36048
36554
|
events[start][1]._spread = listSpread;
|
|
36049
36555
|
}
|
|
36050
36556
|
function opener(create, and) {
|
|
36051
|
-
return
|
|
36052
|
-
function
|
|
36557
|
+
return open6;
|
|
36558
|
+
function open6(token) {
|
|
36053
36559
|
enter.call(this, create(token), token);
|
|
36054
36560
|
if (and) and.call(this, token);
|
|
36055
36561
|
}
|
|
@@ -36081,19 +36587,19 @@ function compiler(options) {
|
|
|
36081
36587
|
}
|
|
36082
36588
|
function exit3(token, onExitError) {
|
|
36083
36589
|
const node2 = this.stack.pop();
|
|
36084
|
-
const
|
|
36085
|
-
if (!
|
|
36590
|
+
const open6 = this.tokenStack.pop();
|
|
36591
|
+
if (!open6) {
|
|
36086
36592
|
throw new Error("Cannot close `" + token.type + "` (" + stringifyPosition({
|
|
36087
36593
|
start: token.start,
|
|
36088
36594
|
end: token.end
|
|
36089
36595
|
}) + "): it\u2019s not open");
|
|
36090
36596
|
}
|
|
36091
|
-
if (
|
|
36597
|
+
if (open6[0].type !== token.type) {
|
|
36092
36598
|
if (onExitError) {
|
|
36093
|
-
onExitError.call(this, token,
|
|
36599
|
+
onExitError.call(this, token, open6[0]);
|
|
36094
36600
|
} else {
|
|
36095
|
-
const handler =
|
|
36096
|
-
handler.call(this, token,
|
|
36601
|
+
const handler = open6[1] || defaultOnError;
|
|
36602
|
+
handler.call(this, token, open6[0]);
|
|
36097
36603
|
}
|
|
36098
36604
|
}
|
|
36099
36605
|
node2.position.end = point2(token.end);
|
|
@@ -38351,10 +38857,10 @@ function quote4(text7, width, max, g) {
|
|
|
38351
38857
|
const w = width - 4;
|
|
38352
38858
|
const lines = wrapSegs([{ text: `\xAB ${oneLine2(text7)} \xBB`, color: C.dim }], w);
|
|
38353
38859
|
if (lines.length <= max) return lines.map((l) => [pad(4), ...l]);
|
|
38354
|
-
const
|
|
38355
|
-
const last =
|
|
38356
|
-
|
|
38357
|
-
return
|
|
38860
|
+
const kept2 = lines.slice(0, max);
|
|
38861
|
+
const last = kept2[max - 1];
|
|
38862
|
+
kept2[max - 1] = [...cut2(last, w - 2 - cells(g.ellipsis), ""), { text: `${g.ellipsis} \xBB`, color: C.dim }];
|
|
38863
|
+
return kept2.map((l) => [pad(4), ...l]);
|
|
38358
38864
|
}
|
|
38359
38865
|
function aboutDraft(body, t, g) {
|
|
38360
38866
|
const line2 = firstLine2(body);
|
|
@@ -39205,7 +39711,7 @@ var init_KeyBar = __esm({
|
|
|
39205
39711
|
// src/tui/ui/Frame.tsx
|
|
39206
39712
|
import { Box as Box5, Text as Text4 } from "ink";
|
|
39207
39713
|
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
39208
|
-
function Frame({ crumb, crumbShort, keys, kept, children }) {
|
|
39714
|
+
function Frame({ crumb, crumbShort, keys, kept: kept2, children }) {
|
|
39209
39715
|
const { d, machines, t, g, lastToast, editing, client } = useTui();
|
|
39210
39716
|
const { rows, columns: columns2 } = useScreenSize();
|
|
39211
39717
|
const live3 = listed(d.sessions);
|
|
@@ -39213,7 +39719,7 @@ function Frame({ crumb, crumbShort, keys, kept, children }) {
|
|
|
39213
39719
|
const waiting2 = pending.length + openDecisions(useTasks(client)).length;
|
|
39214
39720
|
const status = d.status === "open" ? null : d.status === "connecting" ? t.bar.connecting : t.bar.unreachable;
|
|
39215
39721
|
if (columns2 < MIN.columns || rows < MIN.rows) {
|
|
39216
|
-
const why = [...pending[0] ? [t.small.waits(pending[0].name, pending[0].pending?.kind === "question")] : [], ...
|
|
39722
|
+
const why = [...pending[0] ? [t.small.waits(pending[0].name, pending[0].pending?.kind === "question")] : [], ...kept2 ? [t.small.kept] : []].join(" \xB7 ");
|
|
39217
39723
|
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", height: rows, width: columns2, alignItems: "center", justifyContent: "center", children: [
|
|
39218
39724
|
/* @__PURE__ */ jsxs4(Text4, { color: C.coral, bold: true, children: [
|
|
39219
39725
|
g.logo,
|
|
@@ -39513,7 +40019,8 @@ function Overview() {
|
|
|
39513
40019
|
return true;
|
|
39514
40020
|
}
|
|
39515
40021
|
if (key.return) {
|
|
39516
|
-
if (selected)
|
|
40022
|
+
if (selected && isOmpSeat(selected)) attach2(selected);
|
|
40023
|
+
else if (selected) go("session");
|
|
39517
40024
|
return true;
|
|
39518
40025
|
}
|
|
39519
40026
|
if (!selected) return false;
|
|
@@ -39534,7 +40041,7 @@ function Overview() {
|
|
|
39534
40041
|
return false;
|
|
39535
40042
|
});
|
|
39536
40043
|
const groupRows = (kind) => view.tree.filter((n) => n.kind === kind).flatMap((n) => [
|
|
39537
|
-
...kind === "rig" ? [{ kind: "project", key: n.key, label: n.label, color: C.blue, count: n.count, rig: true, folder: folderOf(n.branches[0]?.sessions[0]?.cwd) }] : [],
|
|
40044
|
+
...kind === "rig" ? [{ kind: "project", key: n.key, label: n.label, color: C.blue, count: n.count, ...n.shown !== void 0 && { shown: n.shown }, rig: true, folder: folderOf(n.branches[0]?.sessions[0]?.cwd) }] : [],
|
|
39538
40045
|
...n.branches.flatMap((b3) => b3.sessions.map((s) => ({ kind: "session", key: s.id, s, indent: kind === "rig" ? 2 : 0, group: kind })))
|
|
39539
40046
|
]);
|
|
39540
40047
|
const rigRows = groupRows("rig"), kernelRows = groupRows("kernel");
|
|
@@ -39556,6 +40063,7 @@ function Overview() {
|
|
|
39556
40063
|
const stateW = Math.max(...Object.values(t.state).map((x) => x.length)) + 1;
|
|
39557
40064
|
const topicW = Math.max(6, inner - 6 - nameW - stateW - 3);
|
|
39558
40065
|
const askW = Math.max(6, inner - 2 - nameW - 9 - 7);
|
|
40066
|
+
const roleW = Math.max(11, ...rigRows.map((r) => r.kind === "session" ? [...r.s.rig?.role ?? ""].length + 2 : 0));
|
|
39559
40067
|
const yoursBox = /* @__PURE__ */ jsx10(TitledBox, { title: t.overview.yours(yoursCount), color: yoursCount ? C.red : C.line, titleColor: yoursCount ? C.red : C.dim, height: yoursHeight + 2, children: yoursCount === 0 ? /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.overview.nobodyWaits }) : [...view.yours.slice(0, yoursHeight).map((s) => {
|
|
39560
40068
|
const st = botState(s);
|
|
39561
40069
|
return /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: s.id === sel ? C.sel : void 0, children: [
|
|
@@ -39621,16 +40129,18 @@ function Overview() {
|
|
|
39621
40129
|
] }) : null,
|
|
39622
40130
|
/* @__PURE__ */ jsxs6(Text7, { color: C.faint, children: [
|
|
39623
40131
|
" ",
|
|
39624
|
-
t.overview.sessions(r.count)
|
|
40132
|
+
r.shown !== void 0 ? t.overview.sessionsOf(r.count, r.shown) : t.overview.sessions(r.count)
|
|
39625
40133
|
] })
|
|
39626
|
-
] }, r.key) : r.kind === "session" ? /* @__PURE__ */ jsx10(SessionRow, { s: r.s, on: r.s.id === sel, nameW, topicW: Math.max(6, topicW - r.indent), stateW, indent: r.indent, group: r.group }, r.key) : null) });
|
|
40134
|
+
] }, r.key) : r.kind === "session" ? /* @__PURE__ */ jsx10(SessionRow, { s: r.s, on: r.s.id === sel, nameW, topicW: Math.max(6, topicW - r.indent), stateW, indent: r.indent, group: r.group, roleW }, r.key) : null) });
|
|
39627
40135
|
const rigsBox = groupBox(t.overview.rigsGroup, rigRows, rigsHeight);
|
|
39628
40136
|
const kernelBox = groupBox(t.overview.kernelGroup, kernelRows, kernelHeight);
|
|
40137
|
+
const stopped2 = !!selected && isStoppedOmpSeat(selected);
|
|
39629
40138
|
const keys = writing ? [["\u21B5", t.keys.sendOnly], ["alt+\u21B5", t.keys.newLine], ["esc", t.keys.cancel]] : deciding?.confirm ? [["y", t.keys.sendAnswer], ["n esc", t.keys.edit]] : deciding ? [["\u21B5", t.keys.review], ["alt+\u21B5", t.keys.newLine], ["esc", t.keys.cancel]] : [
|
|
40139
|
+
// On an OMP seat ↵ attaches its terminal; on a stopped one, neither ↵ nor a has anything to attach.
|
|
39630
40140
|
["j k", t.keys.navigate],
|
|
39631
|
-
["\u21B5", t.keys.open],
|
|
40141
|
+
...stopped2 ? [] : [["\u21B5", selected && isOmpSeat(selected) ? t.keys.attachSeat : t.keys.open]],
|
|
39632
40142
|
...view.yours.length ? [["tab", t.keys.next], ["y n", t.keys.answer]] : [],
|
|
39633
|
-
["a", t.keys.attach],
|
|
40143
|
+
...stopped2 ? [] : [["a", t.keys.attach]],
|
|
39634
40144
|
...writable2 ? [["m", t.keys.writeProject]] : [],
|
|
39635
40145
|
...decisions.length ? [["D", t.keys.decide]] : [],
|
|
39636
40146
|
...near.length ? [["H", t.keys.handover]] : [],
|
|
@@ -39705,21 +40215,23 @@ function Overview() {
|
|
|
39705
40215
|
) : null
|
|
39706
40216
|
] });
|
|
39707
40217
|
}
|
|
39708
|
-
function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0, group }) {
|
|
40218
|
+
function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0, group, roleW = 11 }) {
|
|
39709
40219
|
const { t, g } = useTui();
|
|
39710
40220
|
const st = botState(s);
|
|
39711
40221
|
const n = helperCount(s);
|
|
40222
|
+
const stopped2 = isStoppedOmpSeat(s);
|
|
40223
|
+
const roleTool = group === "kernel" ? `${(s.rig?.role ?? "").replace(/\.(agent|lead|worker)$/, "").padEnd(11)}${s.tool}` : `${(s.rig?.role ?? "").padEnd(roleW)}${s.tool}`;
|
|
39712
40224
|
return /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: on ? C.sel : void 0, children: [
|
|
39713
40225
|
/* @__PURE__ */ jsx10(Text7, { color: on ? C.coral : C.faint, children: on ? "\u258C" : " " }),
|
|
39714
40226
|
/* @__PURE__ */ jsx10(Text7, { children: " ".repeat(2 + indent2) }),
|
|
39715
|
-
/* @__PURE__ */ jsxs6(Text7, { color: s.offline ? C.faint : STATE_COLOR[st], children: [
|
|
40227
|
+
/* @__PURE__ */ jsxs6(Text7, { color: s.offline || stopped2 ? C.faint : STATE_COLOR[st], children: [
|
|
39716
40228
|
g[st],
|
|
39717
40229
|
" "
|
|
39718
40230
|
] }),
|
|
39719
|
-
/* @__PURE__ */ jsx10(Text7, { color: s.offline ? C.dim : C.fg, bold:
|
|
39720
|
-
group ? /* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
|
|
40231
|
+
/* @__PURE__ */ jsx10(Text7, { color: stopped2 ? C.faint : s.offline ? C.dim : C.fg, bold: !stopped2, children: fit3(s.name, nameW) }),
|
|
40232
|
+
group ? /* @__PURE__ */ jsxs6(Text7, { color: stopped2 ? C.faint : C.dim, children: [
|
|
39721
40233
|
" ",
|
|
39722
|
-
fit3(
|
|
40234
|
+
fit3(roleTool, topicW - 1),
|
|
39723
40235
|
" "
|
|
39724
40236
|
] }) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
39725
40237
|
s.rig ? /* @__PURE__ */ jsxs6(Text7, { color: C.blue, children: [
|
|
@@ -39731,7 +40243,7 @@ function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0, group }
|
|
|
39731
40243
|
" "
|
|
39732
40244
|
] })
|
|
39733
40245
|
] }),
|
|
39734
|
-
/* @__PURE__ */ jsx10(Text7, { color: s.offline ? C.faint : STATE_COLOR[st], children: fit3(s.offline ? t.overview.offline : t.state[st], stateW).trimEnd() }),
|
|
40246
|
+
/* @__PURE__ */ jsx10(Text7, { color: s.offline || stopped2 ? C.faint : STATE_COLOR[st], children: fit3(s.offline ? t.overview.offline : stopped2 ? t.state.stopped : t.state[st], stateW).trimEnd() }),
|
|
39735
40247
|
n > 0 ? /* @__PURE__ */ jsxs6(Text7, { color: C.violet, children: [
|
|
39736
40248
|
" +",
|
|
39737
40249
|
n
|
|
@@ -39770,7 +40282,7 @@ var init_Overview = __esm({
|
|
|
39770
40282
|
// src/tui/images.tsx
|
|
39771
40283
|
import { mkdir as mkdir13, writeFile as writeFile17 } from "fs/promises";
|
|
39772
40284
|
import { tmpdir as tmpdir3 } from "os";
|
|
39773
|
-
import { join as
|
|
40285
|
+
import { join as join39 } from "path";
|
|
39774
40286
|
import { useRef as useRef8, useState as useState4 } from "react";
|
|
39775
40287
|
import { Text as Text8 } from "ink";
|
|
39776
40288
|
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
@@ -39873,9 +40385,9 @@ function useOpenImage() {
|
|
|
39873
40385
|
const { ref, route } = item;
|
|
39874
40386
|
void (async () => {
|
|
39875
40387
|
const { mime, data } = await getImage(client, ref.id, route);
|
|
39876
|
-
const dir =
|
|
40388
|
+
const dir = join39(tmpdir3(), "ledge-images");
|
|
39877
40389
|
await mkdir13(dir, { recursive: true, mode: 448 });
|
|
39878
|
-
const file =
|
|
40390
|
+
const file = join39(dir, `${ref.id}.${EXT2[mime]}`);
|
|
39879
40391
|
await writeFile17(file, Buffer.from(data, "base64"), { mode: 384 });
|
|
39880
40392
|
await openFile2(file);
|
|
39881
40393
|
})().catch((e) => toast({ text: t.images.openFailed(e instanceof AttachmentFailure ? e.reason : e.message), tone: "error" }));
|
|
@@ -40258,18 +40770,18 @@ function SessionScreen() {
|
|
|
40258
40770
|
return /* @__PURE__ */ jsx13(SessionView, { s }, s.id);
|
|
40259
40771
|
}
|
|
40260
40772
|
function SessionView({ s }) {
|
|
40261
|
-
const { client, d, t, g, actions, attach: attach2, toast, machines, kept, lastToast, tone: tone2, select, go } = useTui();
|
|
40773
|
+
const { client, d, t, g, actions, attach: attach2, toast, machines, kept: kept2, lastToast, tone: tone2, select, go } = useTui();
|
|
40262
40774
|
const { columns: columns2, rows } = useScreenSize();
|
|
40263
40775
|
const [mode, setMode] = useState8(() => {
|
|
40264
|
-
if (
|
|
40265
|
-
|
|
40776
|
+
if (kept2.openReply !== s.id) return "read";
|
|
40777
|
+
kept2.openReply = void 0;
|
|
40266
40778
|
return "reply";
|
|
40267
40779
|
});
|
|
40268
|
-
const [draft, setDraftState] = useState8(() =>
|
|
40780
|
+
const [draft, setDraftState] = useState8(() => kept2.drafts.get(s.id) ?? "");
|
|
40269
40781
|
const setDraft = (x) => {
|
|
40270
40782
|
setDraftState(x);
|
|
40271
|
-
if (x)
|
|
40272
|
-
else
|
|
40783
|
+
if (x) kept2.drafts.set(s.id, x);
|
|
40784
|
+
else kept2.drafts.delete(s.id);
|
|
40273
40785
|
};
|
|
40274
40786
|
const [browsing, setBrowsing] = useState8(false);
|
|
40275
40787
|
const [diffs, setDiffs] = useState8(false);
|
|
@@ -40333,9 +40845,9 @@ function SessionView({ s }) {
|
|
|
40333
40845
|
toast({ text: t.toast.readOnly(target.name), tone: "info" });
|
|
40334
40846
|
return;
|
|
40335
40847
|
}
|
|
40336
|
-
const quote5 = aboutDraft(text7, t, g), was =
|
|
40337
|
-
|
|
40338
|
-
|
|
40848
|
+
const quote5 = aboutDraft(text7, t, g), was = kept2.drafts.get(target.id) ?? "";
|
|
40849
|
+
kept2.drafts.set(target.id, was.startsWith(quote5) ? was : `${quote5}${was}`);
|
|
40850
|
+
kept2.openReply = target.id;
|
|
40339
40851
|
select(target.id);
|
|
40340
40852
|
go("session");
|
|
40341
40853
|
};
|
|
@@ -40357,7 +40869,7 @@ function SessionView({ s }) {
|
|
|
40357
40869
|
}
|
|
40358
40870
|
const taken = withImages2 ? chips2.clear() : [];
|
|
40359
40871
|
const back2 = (x) => {
|
|
40360
|
-
if (!
|
|
40872
|
+
if (!kept2.drafts.get(s.id)) setDraft(x);
|
|
40361
40873
|
if (taken.length) chips2.putBack(taken);
|
|
40362
40874
|
};
|
|
40363
40875
|
const how = taken.length ? { attachments: taken.flatMap((c) => c.id ? [c.id] : []) } : void 0;
|
|
@@ -40365,7 +40877,7 @@ function SessionView({ s }) {
|
|
|
40365
40877
|
if (taken.length) chips2.putBack(taken);
|
|
40366
40878
|
return false;
|
|
40367
40879
|
}
|
|
40368
|
-
if (text7.trim())
|
|
40880
|
+
if (text7.trim()) kept2.sent.set(s.id, [...kept2.sent.get(s.id) ?? [], text7]);
|
|
40369
40881
|
if (!fromHistory) setDraft("");
|
|
40370
40882
|
setMode("read");
|
|
40371
40883
|
return true;
|
|
@@ -40517,7 +41029,7 @@ function SessionView({ s }) {
|
|
|
40517
41029
|
onSubmit: send2,
|
|
40518
41030
|
onEscape: leave,
|
|
40519
41031
|
onBrowse: setBrowsing,
|
|
40520
|
-
history:
|
|
41032
|
+
history: kept2.sent.get(s.id) ?? [],
|
|
40521
41033
|
active: true,
|
|
40522
41034
|
placeholder: mode === "note" ? t.session.noteFor(s.name) : t.session.replyTo(s.name)
|
|
40523
41035
|
}
|
|
@@ -40660,11 +41172,11 @@ function cardPlan(actions, room) {
|
|
|
40660
41172
|
const raw = rows.map((r) => r.state ?? "proposed");
|
|
40661
41173
|
const stopAt = raw.findIndex((s) => s === "failed" || s === "refused" || s === "stale");
|
|
40662
41174
|
const shown = states.map((s, i) => s === "proposed" && stopAt >= 0 && i > stopAt ? "held" : s);
|
|
40663
|
-
const
|
|
41175
|
+
const open6 = shown.flatMap((s, i) => ["done", "running", "denied", "stale", "cut"].includes(s) ? [] : [i]);
|
|
40664
41176
|
const skip = stopAt >= 0 && raw[stopAt] !== "failed";
|
|
40665
|
-
const rowsAll = skip ?
|
|
41177
|
+
const rowsAll = skip ? open6.filter((i) => i !== stopAt) : stopAt >= 0 && states[stopAt] === "cut" ? [] : open6;
|
|
40666
41178
|
const all2 = { kind: stopAt < 0 ? "all" : skip ? "skip" : "resume", rows: rowsAll, k: stopAt + 1 };
|
|
40667
|
-
return { rows, shown, open:
|
|
41179
|
+
return { rows, shown, open: open6, stopAt, all: all2, cutLines };
|
|
40668
41180
|
}
|
|
40669
41181
|
function firstRow(p) {
|
|
40670
41182
|
const act = p.shown.findIndex((s) => s === "proposed" || s === "held" || s === "failed");
|
|
@@ -40965,13 +41477,13 @@ import { useEffect as useEffect11, useMemo as useMemo4, useRef as useRef11, useS
|
|
|
40965
41477
|
import { Box as Box12, Text as Text12, useInput as useInput4 } from "ink";
|
|
40966
41478
|
import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
40967
41479
|
function Ask() {
|
|
40968
|
-
const { client, d, machines, t, g, toast, go, kept, small, tone: tone2, select } = useTui();
|
|
41480
|
+
const { client, d, machines, t, g, toast, go, kept: kept2, small, tone: tone2, select } = useTui();
|
|
40969
41481
|
const { columns: columns2, rows: height } = useScreenSize();
|
|
40970
|
-
const [draft, setDraftState] = useState10(() =>
|
|
41482
|
+
const [draft, setDraftState] = useState10(() => kept2.drafts.get("ask") ?? "");
|
|
40971
41483
|
const setDraft = (x) => {
|
|
40972
41484
|
setDraftState(x);
|
|
40973
|
-
if (x)
|
|
40974
|
-
else
|
|
41485
|
+
if (x) kept2.drafts.set("ask", x);
|
|
41486
|
+
else kept2.drafts.delete("ask");
|
|
40975
41487
|
};
|
|
40976
41488
|
const [browsing, setBrowsing] = useState10(false);
|
|
40977
41489
|
const [confirm, setConfirm] = useState10(false);
|
|
@@ -40994,7 +41506,7 @@ function Ask() {
|
|
|
40994
41506
|
if (turn.error) {
|
|
40995
41507
|
sentAsk.current = null;
|
|
40996
41508
|
chips2.putBack(s.taken);
|
|
40997
|
-
setDraft(
|
|
41509
|
+
setDraft(kept2.drafts.get("ask") || s.text);
|
|
40998
41510
|
} else if (turn.answeredAt) sentAsk.current = null;
|
|
40999
41511
|
}, [d.chat]);
|
|
41000
41512
|
const cards = useMemo4(() => openCards(d.chat).slice(0, 2), [d.chat]);
|
|
@@ -41051,7 +41563,7 @@ function Ask() {
|
|
|
41051
41563
|
const asked2 = { question: text7.trim(), askedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
41052
41564
|
daemonStore.beginTurn({ qid, ...asked2, steps: [] });
|
|
41053
41565
|
client.send({ type: "chat.ask", qid, text: asked2.question, ...images.length ? { images } : {} });
|
|
41054
|
-
|
|
41566
|
+
kept2.asked.push(asked2);
|
|
41055
41567
|
if (images.length) sentAsk.current = { qid, taken: chips2.clear(), text: asked2.question };
|
|
41056
41568
|
if (!fromHistory) setDraft("");
|
|
41057
41569
|
return true;
|
|
@@ -41262,7 +41774,7 @@ function Ask() {
|
|
|
41262
41774
|
["ctrl+n", t.keys.newChat]
|
|
41263
41775
|
];
|
|
41264
41776
|
};
|
|
41265
|
-
const history = askHistory(d.chat,
|
|
41777
|
+
const history = askHistory(d.chat, kept2.asked);
|
|
41266
41778
|
const keys = confirm ? [["n esc", t.keys.keep], ["y", t.keys.wipe]] : focus === -1 && browsing ? [["\u21B5", t.keys.resend], ["\u2191 \u2193", t.keys.history], ["esc", t.keys.backToDraft]] : focus === -1 ? [["\u21B5", t.keys.sendOnly], ...review$, ["alt+\u21B5", t.keys.newLine], ["esc", t.keys.back], ["PgUp PgDn", t.keys.scroll], ["\u2191 \u2193", t.keys.history], ["tab", cards.length ? t.keys.nextCard : t.keys.conversation], ["alt+v", t.keys.image], ...turnImages.length ? [["alt+o", t.keys.openImage]] : [], ["ctrl+n", t.keys.newChat], ...stop] : focus === CONVO ? [["j k", t.keys.line], ["PgUp PgDn", t.keys.page], ["G", t.keys.end], ...stop, ["tab", t.keys.question], ["esc", t.keys.question]] : card?.kind === "action" ? [...actionKeys(card.qid), ...stop, ...review$] : [["\u21B5", t.keys.send], ["tab", focus + 1 < cards.length ? t.keys.nextCard : t.keys.conversation], ["ctrl+n", t.keys.newChat], ...stop, ["esc", t.keys.back]];
|
|
41267
41779
|
const foot = up > 0 ? asking && live3?.qid === asking.qid ? t.ask.answering : t.session.scrolled(lines.length - view - up, up) : lines.length > view ? focus === CONVO ? t.session.above(lines.length - view) : t.ask.above(lines.length - view) : void 0;
|
|
41268
41780
|
const reading = focus === CONVO;
|
|
@@ -42052,10 +42564,10 @@ function matches(r, q) {
|
|
|
42052
42564
|
}
|
|
42053
42565
|
function listItems2(roles, query, builtinOpen) {
|
|
42054
42566
|
const by = (a, b3) => a.name.localeCompare(b3.name) || a.id.localeCompare(b3.id);
|
|
42055
|
-
const
|
|
42056
|
-
const mine =
|
|
42057
|
-
const
|
|
42058
|
-
return { mine, builtin, items: [...mine.map((role) => ({ kind: "role", role })), ...builtin.length ? [{ kind: "builtin" }] : [], ...
|
|
42567
|
+
const kept2 = roles.filter((r) => matches(r, query)).sort(by);
|
|
42568
|
+
const mine = kept2.filter((r) => r.source === "user"), builtin = kept2.filter((r) => r.source === "builtin");
|
|
42569
|
+
const open6 = builtinOpen || query.trim() !== "";
|
|
42570
|
+
return { mine, builtin, items: [...mine.map((role) => ({ kind: "role", role })), ...builtin.length ? [{ kind: "builtin" }] : [], ...open6 ? builtin.map((role) => ({ kind: "role", role })) : []] };
|
|
42059
42571
|
}
|
|
42060
42572
|
function listLines(roles, o, t, g) {
|
|
42061
42573
|
const r = t.roles, w = o.width;
|
|
@@ -42087,11 +42599,11 @@ function listLines(roles, o, t, g) {
|
|
|
42087
42599
|
if (mine.length) lines.push([]);
|
|
42088
42600
|
const s = i === o.sel;
|
|
42089
42601
|
if (s) selLine = lines.length;
|
|
42090
|
-
const
|
|
42602
|
+
const open6 = o.builtinOpen || o.query.trim() !== "";
|
|
42091
42603
|
const bg = s ? C.sel : void 0;
|
|
42092
|
-
lines.push([{ text: s ? "\u258C" : " ", color: s && o.focused ? C.coral : C.faint, ...bg ? { bg } : {} }, { text: `${
|
|
42604
|
+
lines.push([{ text: s ? "\u258C" : " ", color: s && o.focused ? C.coral : C.faint, ...bg ? { bg } : {} }, { text: `${open6 ? g.unfolded : g.folded} ${r.builtin}`, color: C.dim, ...bg ? { bg } : {} }, { text: ` \xB7 ${builtin.length}`, color: C.faint, ...bg ? { bg } : {} }]);
|
|
42093
42605
|
i++;
|
|
42094
|
-
if (
|
|
42606
|
+
if (open6) builtin.forEach(roleLine);
|
|
42095
42607
|
}
|
|
42096
42608
|
return { lines, selLine };
|
|
42097
42609
|
}
|
|
@@ -42187,7 +42699,7 @@ function param(label2, value, o, t, g, w, narrow, extra = {}) {
|
|
|
42187
42699
|
const usedL = cells(lab.map((x) => x.text).join("")), usedV = cells(val.map((x) => x.text).join(""));
|
|
42188
42700
|
return { line: [...lab, { text: " ".repeat(lw - usedL) }, ...val, { text: " ".repeat(Math.max(1, w - lw - usedV - cells(p))) }, { text: p, color: provColor(k) }], rail: k, ...extra };
|
|
42189
42701
|
}
|
|
42190
|
-
function paramRows(d, seat2, files,
|
|
42702
|
+
function paramRows(d, seat2, files, open6, o, t) {
|
|
42191
42703
|
const r = t.roles, w = o.width, g = o.g, n = o.narrow;
|
|
42192
42704
|
const P = (label2, value, origin, extra = {}) => param(label2, value, origin, t, g, w, n, extra);
|
|
42193
42705
|
const out = [];
|
|
@@ -42228,8 +42740,8 @@ function paramRows(d, seat2, files, open5, o, t) {
|
|
|
42228
42740
|
const u = prof.uses;
|
|
42229
42741
|
const loadout = seat2?.live?.skills;
|
|
42230
42742
|
if (loadout) {
|
|
42231
|
-
out.push(P(r.p.skills, v(`${r.skillsProjected(loadout.length)} ${
|
|
42232
|
-
if (
|
|
42743
|
+
out.push(P(r.p.skills, v(`${r.skillsProjected(loadout.length)} ${open6.has("loadout") ? g.unfolded : g.folded}`), { kind: "inherited", from: "launch" }, { fold: "loadout" }));
|
|
42744
|
+
if (open6.has("loadout")) for (const s of loadout) out.push({ line: [{ text: " ".repeat(n ? 16 : 19) }, { text: s.id, color: C.dim }], rail: "inh" });
|
|
42233
42745
|
}
|
|
42234
42746
|
const list3 = (label2, l, what, fold) => {
|
|
42235
42747
|
if (!l.declared) {
|
|
@@ -42243,9 +42755,9 @@ function paramRows(d, seat2, files, open5, o, t) {
|
|
|
42243
42755
|
l.items.forEach((x, i) => {
|
|
42244
42756
|
const na = !!(seat2 && x.runtime && seat2.runtime.value && x.runtime !== seat2.runtime.value);
|
|
42245
42757
|
const key = fold?.(x);
|
|
42246
|
-
const row3 = P(i === 0 ? label2 : "", v(`${what(x)}${na ? ` \xB7 ${r.na(x.runtime)}` : ""}${key ? ` ${
|
|
42758
|
+
const row3 = P(i === 0 ? label2 : "", v(`${what(x)}${na ? ` \xB7 ${r.na(x.runtime)}` : ""}${key ? ` ${open6.has(key) ? g.unfolded : g.folded}` : ""}`, na ? C.faint : C.fg), x.origin, key ? { fold: key } : {});
|
|
42247
42759
|
out.push(na ? { ...row3, line: row3.line.map((s) => ({ ...s, color: C.faint })) } : row3);
|
|
42248
|
-
if (key &&
|
|
42760
|
+
if (key && open6.has(key)) {
|
|
42249
42761
|
const body = x.plugin ? x.plugin.skills.join(", ") : (x.absolutePath && files[x.absolutePath]?.content) ?? t.roles.reading;
|
|
42250
42762
|
for (const bl of body.split("\n")) out.push({ line: cut2([{ text: " ".repeat(n ? 16 : 19) }, { text: bl, color: C.dim }], w), rail: "inh" });
|
|
42251
42763
|
}
|
|
@@ -42333,9 +42845,9 @@ function yamlRows(d, seat2, files, o, t) {
|
|
|
42333
42845
|
}
|
|
42334
42846
|
return out;
|
|
42335
42847
|
}
|
|
42336
|
-
function filesFor(d, seat2, tab, at,
|
|
42848
|
+
function filesFor(d, seat2, tab, at, open6) {
|
|
42337
42849
|
if (tab === 0) return roleDocs(d, seat2).map((e) => e.absolutePath).filter((x) => !!x);
|
|
42338
|
-
if (tab === 1) return (profileOf(d, seat2)?.uses.runtimeResources.items ?? []).filter((x) => x.absolutePath &&
|
|
42850
|
+
if (tab === 1) return (profileOf(d, seat2)?.uses.runtimeResources.items ?? []).filter((x) => x.absolutePath && open6.has(`rr:${x.id}`)).map((x) => x.absolutePath);
|
|
42339
42851
|
if (tab === 2) {
|
|
42340
42852
|
const p = pieces(d, seat2)[at];
|
|
42341
42853
|
return p?.kind === "entry" && p.entry.absolutePath && !isMissing(p.entry) ? [p.entry.absolutePath] : [];
|
|
@@ -42412,7 +42924,7 @@ function RolesScreen({ onRigs, machineId, onMachine }) {
|
|
|
42412
42924
|
const [scroll, setScroll] = useState14(0);
|
|
42413
42925
|
const [at, setAt] = useState14(0);
|
|
42414
42926
|
const [reading, setReading] = useState14(false);
|
|
42415
|
-
const [
|
|
42927
|
+
const [open6, setOpen] = useState14(/* @__PURE__ */ new Set());
|
|
42416
42928
|
const [files, setFiles] = useState14({});
|
|
42417
42929
|
const { items } = listItems2(roles.roles, query, builtinOpen);
|
|
42418
42930
|
const firstRole = items.find((i) => i.kind === "role");
|
|
@@ -42438,7 +42950,7 @@ function RolesScreen({ onRigs, machineId, onMachine }) {
|
|
|
42438
42950
|
asks.current.reset();
|
|
42439
42951
|
setFiles({});
|
|
42440
42952
|
}, [currentId, machine?.id]);
|
|
42441
|
-
const wanted = (d ? filesFor(d, seat2, shownTab, at,
|
|
42953
|
+
const wanted = (d ? filesFor(d, seat2, shownTab, at, open6) : []).join("\n");
|
|
42442
42954
|
useEffect13(() => {
|
|
42443
42955
|
if (!wanted || !machine || !currentId) return;
|
|
42444
42956
|
const id = currentId, mid = machine.id;
|
|
@@ -42451,7 +42963,7 @@ function RolesScreen({ onRigs, machineId, onMachine }) {
|
|
|
42451
42963
|
const bodyH = Math.max(3, rows - 4);
|
|
42452
42964
|
const head2 = d && summary ? headRows(d, summary, { seat: seat2, tab: shownTab, width: w, narrow: !wide, seats }, t) : [];
|
|
42453
42965
|
const ctx = d && shownTab === 2 ? contextRows(d, seat2, files, at, { width: w, raw, narrow: !wide, g, lang }, t) : null;
|
|
42454
|
-
const body = !d ? [] : shownTab === 0 ? roleRows(d, seat2, files, { width: w, raw, narrow: !wide, g, lang }, t) : shownTab === 1 ? paramRows(d, seat2, files,
|
|
42966
|
+
const body = !d ? [] : shownTab === 0 ? roleRows(d, seat2, files, { width: w, raw, narrow: !wide, g, lang }, t) : shownTab === 1 ? paramRows(d, seat2, files, open6, { width: w, narrow: !wide, g }, t) : shownTab === 2 ? ctx.rows : yamlRows(d, seat2, files, { width: w }, t);
|
|
42455
42967
|
const room = Math.max(1, bodyH - head2.length);
|
|
42456
42968
|
const maxScroll = Math.max(0, body.length - room);
|
|
42457
42969
|
const params = shownTab === 1 ? body.map((x, i) => x.rail ? i : -1).filter((i) => i >= 0) : [];
|
|
@@ -42709,7 +43221,7 @@ function RigsView({ initialMachine, onRoles }) {
|
|
|
42709
43221
|
const rigs = listedRigs(state2);
|
|
42710
43222
|
const [at, setAt] = useState15(0);
|
|
42711
43223
|
const selectedId = useRef13(null);
|
|
42712
|
-
const [
|
|
43224
|
+
const [open6, setOpen] = useState15(/* @__PURE__ */ new Set());
|
|
42713
43225
|
const [seatAt, setSeatAt] = useState15(null);
|
|
42714
43226
|
const [confirm, setConfirm] = useState15(null);
|
|
42715
43227
|
const [seats, setSeats] = useState15(null);
|
|
@@ -42719,7 +43231,7 @@ function RigsView({ initialMachine, onRoles }) {
|
|
|
42719
43231
|
}, [ids]);
|
|
42720
43232
|
const current2 = rigs[Math.min(at, rigs.length - 1)];
|
|
42721
43233
|
selectedId.current = current2?.id ?? null;
|
|
42722
|
-
const seatNodes = current2 &&
|
|
43234
|
+
const seatNodes = current2 && open6.has(current2.id) ? current2.nodes : [];
|
|
42723
43235
|
const seatSel = seatAt !== null && seatAt < seatNodes.length ? seatAt : null;
|
|
42724
43236
|
const down = () => {
|
|
42725
43237
|
if (seatSel !== null ? seatSel + 1 < seatNodes.length : seatNodes.length > 0) {
|
|
@@ -42737,7 +43249,7 @@ function RigsView({ initialMachine, onRoles }) {
|
|
|
42737
43249
|
}
|
|
42738
43250
|
const n = step2(at, -1, rigs.length), prev = rigs[n];
|
|
42739
43251
|
setAt(n);
|
|
42740
|
-
setSeatAt(prev &&
|
|
43252
|
+
setSeatAt(prev && open6.has(prev.id) && prev.nodes.length ? prev.nodes.length - 1 : null);
|
|
42741
43253
|
};
|
|
42742
43254
|
const run4 = (a) => {
|
|
42743
43255
|
void rig.act(a).then((d) => {
|
|
@@ -42897,12 +43409,12 @@ function RigsView({ initialMachine, onRoles }) {
|
|
|
42897
43409
|
const keys = seats ? [["\u2191 \u2193", t.rigSeats.role], ["- +", t.rigSeats.count], ["\u2190 \u2192", t.rigSeats.delegationKey], ["\u21B5", t.rigSeats.apply], ["esc", t.rigSeats.cancel]] : confirm ? [["y", o.yes], ["n", o.no]] : [["tab", o.machine], ["v", t.roles.k.view], ["j k", t.keys.navigate], ["\u21B5", o.unfold], ...seatSel !== null ? [["i", t.roles.k.role]] : [], ["s", o.toggle], ["R", o.restart], ["\xB1 =", t.rigSeats.key], ["+", t.rigLaunch.launchKey], ["D", o.daemonKey], ["K", o.kernelKey], ["O", o.operatorKey], ["esc", t.keys.back]];
|
|
42898
43410
|
const crumb = [state2?.version ? o.version(state2.version) : o.title, machine?.label, t.roles.rigs].filter(Boolean).join(" \xB7 ");
|
|
42899
43411
|
return /* @__PURE__ */ jsxs17(Frame, { crumb, crumbShort: o.title, keys, children: [
|
|
42900
|
-
!machine ? null : rig.error ? /* @__PURE__ */ jsx22(TitledBox, { title: o.title, mark: { text: machine.label, color: C.dim }, flexGrow: 1, children: /* @__PURE__ */ jsx22(Text18, { color: C.yellow, children: rig.error === "offline" ? o.offline : o.outdated }) }) : !state2 ? /* @__PURE__ */ jsx22(TitledBox, { title: o.title, mark: { text: machine.label, color: C.dim }, flexGrow: 1, children: /* @__PURE__ */ jsx22(Text18, { color: C.faint, children: o.loading }) }) : !state2.installed ? /* @__PURE__ */ jsx22(TitledBox, { title: o.title, mark: { text: machine.label, color: C.dim }, flexGrow: 1, children: /* @__PURE__ */ jsx22(Text18, { color: C.dim, children: o.notInstalled }) }) : seats && seatRig ? /* @__PURE__ */ jsx22(RigSeats, { rig: seatRig, machine: machine.label, rows, counts: seats.counts, at: seats.at, delegation: seats.delegation, plan: planOf(seatRig, rows, seats.counts), applying: seats.applying }) : /* @__PURE__ */ jsx22(Body, { state: state2, rigs, at, seatAt: seatSel, open:
|
|
43412
|
+
!machine ? null : rig.error ? /* @__PURE__ */ jsx22(TitledBox, { title: o.title, mark: { text: machine.label, color: C.dim }, flexGrow: 1, children: /* @__PURE__ */ jsx22(Text18, { color: C.yellow, children: rig.error === "offline" ? o.offline : o.outdated }) }) : !state2 ? /* @__PURE__ */ jsx22(TitledBox, { title: o.title, mark: { text: machine.label, color: C.dim }, flexGrow: 1, children: /* @__PURE__ */ jsx22(Text18, { color: C.faint, children: o.loading }) }) : !state2.installed ? /* @__PURE__ */ jsx22(TitledBox, { title: o.title, mark: { text: machine.label, color: C.dim }, flexGrow: 1, children: /* @__PURE__ */ jsx22(Text18, { color: C.dim, children: o.notInstalled }) }) : seats && seatRig ? /* @__PURE__ */ jsx22(RigSeats, { rig: seatRig, machine: machine.label, rows, counts: seats.counts, at: seats.at, delegation: seats.delegation, plan: planOf(seatRig, rows, seats.counts), applying: seats.applying }) : /* @__PURE__ */ jsx22(Body, { state: state2, rigs, at, seatAt: seatSel, open: open6, machineLabel: machine.label, roleOf: roleOf4 }),
|
|
42901
43413
|
confirm ? /* @__PURE__ */ jsx22(ConfirmBox, { confirm }) : null,
|
|
42902
43414
|
rig.pending ? /* @__PURE__ */ jsx22(Box17, { paddingX: 1, height: 1, children: /* @__PURE__ */ jsx22(Text18, { wrap: "truncate", color: C.blue, children: o.running(confirmOf(state2 ?? EMPTY2, rig.pending).commands.join(" && ")) }) }) : null
|
|
42903
43415
|
] });
|
|
42904
43416
|
}
|
|
42905
|
-
function Body({ state: state2, rigs, at, seatAt, open:
|
|
43417
|
+
function Body({ state: state2, rigs, at, seatAt, open: open6, machineLabel: machineLabel2, roleOf: roleOf4 }) {
|
|
42906
43418
|
const { t } = useTui();
|
|
42907
43419
|
const o = t.openrig;
|
|
42908
43420
|
const up = daemonUp(state2);
|
|
@@ -42940,10 +43452,10 @@ function Body({ state: state2, rigs, at, seatAt, open: open5, machineLabel: mach
|
|
|
42940
43452
|
] }, n.session))
|
|
42941
43453
|
] })
|
|
42942
43454
|
] }),
|
|
42943
|
-
/* @__PURE__ */ jsx22(TitledBox, { title: `${o.rigs} \xB7 ${rigs.length}`, flexGrow: 1, children: !up ? /* @__PURE__ */ jsx22(Text18, { color: C.faint, children: o.stoppedDaemon }) : !rigs.length ? /* @__PURE__ */ jsx22(Text18, { color: C.faint, children: o.noRigs }) : rigs.map((r, i) => /* @__PURE__ */ jsx22(RigRow, { rig: r, selected: i === at && seatAt === null, seatAt: i === at ? seatAt : null, open:
|
|
43455
|
+
/* @__PURE__ */ jsx22(TitledBox, { title: `${o.rigs} \xB7 ${rigs.length}`, flexGrow: 1, children: !up ? /* @__PURE__ */ jsx22(Text18, { color: C.faint, children: o.stoppedDaemon }) : !rigs.length ? /* @__PURE__ */ jsx22(Text18, { color: C.faint, children: o.noRigs }) : rigs.map((r, i) => /* @__PURE__ */ jsx22(RigRow, { rig: r, selected: i === at && seatAt === null, seatAt: i === at ? seatAt : null, open: open6.has(r.id), roleOf: roleOf4 }, r.id)) })
|
|
42944
43456
|
] });
|
|
42945
43457
|
}
|
|
42946
|
-
function RigRow({ rig, selected, seatAt, open:
|
|
43458
|
+
function RigRow({ rig, selected, seatAt, open: open6, roleOf: roleOf4 }) {
|
|
42947
43459
|
const { t } = useTui();
|
|
42948
43460
|
const o = t.openrig;
|
|
42949
43461
|
const look = rigLook(rig), l = LOOK[look];
|
|
@@ -42953,14 +43465,14 @@ function RigRow({ rig, selected, seatAt, open: open5, roleOf: roleOf4 }) {
|
|
|
42953
43465
|
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
42954
43466
|
/* @__PURE__ */ jsx22(Box17, { backgroundColor: selected ? C.sel : void 0, children: /* @__PURE__ */ jsxs17(Text18, { wrap: "truncate", children: [
|
|
42955
43467
|
/* @__PURE__ */ jsx22(Text18, { color: selected ? C.coral : C.faint, children: selected ? "\u258C" : " " }),
|
|
42956
|
-
/* @__PURE__ */ jsx22(Text18, { color: C.dim, children:
|
|
43468
|
+
/* @__PURE__ */ jsx22(Text18, { color: C.dim, children: open6 ? "\u25BE " : "\u25B8 " }),
|
|
42957
43469
|
/* @__PURE__ */ jsx22(Text18, { color: C.fg, bold: true, children: rig.name.padEnd(16) }),
|
|
42958
43470
|
/* @__PURE__ */ jsx22(Text18, { color: l.color(), children: ` ${l.glyph} ${o.look[look]}`.padEnd(18) }),
|
|
42959
43471
|
/* @__PURE__ */ jsx22(Text18, { color: C.fg, children: o.seats(rig.running, rig.seats).padEnd(7) }),
|
|
42960
43472
|
note ? /* @__PURE__ */ jsx22(Text18, { color: note.color, children: note.text.padEnd(34) }) : /* @__PURE__ */ jsx22(Text18, { children: "".padEnd(34) }),
|
|
42961
43473
|
/* @__PURE__ */ jsx22(Text18, { color: C.dim, children: when })
|
|
42962
43474
|
] }) }),
|
|
42963
|
-
|
|
43475
|
+
open6 ? rig.nodes.map((n, i) => {
|
|
42964
43476
|
const s = i === seatAt, bg = s ? C.sel : void 0, role = roleOf4(rig.name, n.logicalId);
|
|
42965
43477
|
return /* @__PURE__ */ jsx22(Box17, { backgroundColor: bg, children: /* @__PURE__ */ jsxs17(Text18, { wrap: "truncate", children: [
|
|
42966
43478
|
/* @__PURE__ */ jsx22(Text18, { color: C.coral, children: s ? "\u258C" : " " }),
|
|
@@ -43417,7 +43929,7 @@ function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeS
|
|
|
43417
43929
|
const [lastToast, setToast] = useState17(null);
|
|
43418
43930
|
const [editing, setEditingState] = useState17(false);
|
|
43419
43931
|
const keys = useRef15(null);
|
|
43420
|
-
const
|
|
43932
|
+
const kept2 = useRef15({ drafts: /* @__PURE__ */ new Map(), sent: /* @__PURE__ */ new Map(), asked: [] }).current;
|
|
43421
43933
|
const armed = useRef15(0);
|
|
43422
43934
|
useEffect16(() => {
|
|
43423
43935
|
if (!lastToast) return;
|
|
@@ -43472,7 +43984,7 @@ function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeS
|
|
|
43472
43984
|
const attach2 = useCallback3((s) => {
|
|
43473
43985
|
const cmd = attachCommand(fleet, s);
|
|
43474
43986
|
if ("why" in cmd) {
|
|
43475
|
-
toast({ text: cmd.why === "offline" ? t.toast.offline(s.name) : cmd.why === "omp" ? t.toast.noTerminalOmp(s.name) : t.toast.noTerminal(s.name), tone: "info" });
|
|
43987
|
+
toast({ text: cmd.why === "offline" ? t.toast.offline(s.name) : cmd.why === "omp" ? t.toast.noTerminalOmp(s.name) : cmd.why === "stopped" ? t.toast.seatStopped(s.name) : t.toast.noTerminal(s.name), tone: "info" });
|
|
43476
43988
|
return;
|
|
43477
43989
|
}
|
|
43478
43990
|
void suspendTerminal(async () => {
|
|
@@ -43517,14 +44029,14 @@ function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeS
|
|
|
43517
44029
|
runInTerminal,
|
|
43518
44030
|
env,
|
|
43519
44031
|
keys,
|
|
43520
|
-
kept,
|
|
44032
|
+
kept: kept2,
|
|
43521
44033
|
readImage: readImage ?? (() => readClipboardImage(env)),
|
|
43522
44034
|
openFile: openFile2 ?? ((file) => openImage(file, env))
|
|
43523
44035
|
};
|
|
43524
44036
|
useInput8((input, key) => {
|
|
43525
44037
|
if (key.ctrl && input === "c") {
|
|
43526
44038
|
if (keys.current?.(input, key)) return;
|
|
43527
|
-
const drafts = [...
|
|
44039
|
+
const drafts = [...kept2.drafts].some(([id, x]) => x.trim() && (id === "ask" || d.byId.has(id)));
|
|
43528
44040
|
if (drafts && Date.now() - armed.current > QUIT_MS) {
|
|
43529
44041
|
armed.current = Date.now();
|
|
43530
44042
|
toast({ text: t.toast.draftQuit, tone: "info", ms: QUIT_MS });
|
|
@@ -43610,7 +44122,7 @@ async function readToken() {
|
|
|
43610
44122
|
}
|
|
43611
44123
|
}
|
|
43612
44124
|
function createTuiFleet(opts = {}) {
|
|
43613
|
-
const local = LOCAL_MACHINE.url, port = opts.port ??
|
|
44125
|
+
const local = LOCAL_MACHINE.url, port = opts.port ?? ledgePort();
|
|
43614
44126
|
const backoff = { ...opts.minBackoffMs ? { minBackoffMs: opts.minBackoffMs } : {}, ...opts.maxBackoffMs ? { maxBackoffMs: opts.maxBackoffMs } : {} };
|
|
43615
44127
|
return createFleetClient(
|
|
43616
44128
|
[LOCAL_MACHINE],
|
|
@@ -43621,7 +44133,7 @@ function createTuiFleet(opts = {}) {
|
|
|
43621
44133
|
var init_fleet2 = __esm({
|
|
43622
44134
|
"src/tui/fleet.ts"() {
|
|
43623
44135
|
"use strict";
|
|
43624
|
-
|
|
44136
|
+
init_port();
|
|
43625
44137
|
init_src3();
|
|
43626
44138
|
init_auth();
|
|
43627
44139
|
}
|
|
@@ -43772,7 +44284,7 @@ var init_glyphs = __esm({
|
|
|
43772
44284
|
|
|
43773
44285
|
// src/tui/settings.ts
|
|
43774
44286
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync13, renameSync as renameSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
43775
|
-
import { join as
|
|
44287
|
+
import { join as join40 } from "path";
|
|
43776
44288
|
import { z as z32 } from "zod";
|
|
43777
44289
|
function defaultLang(env) {
|
|
43778
44290
|
const locale = env.LC_ALL || env.LC_MESSAGES || env.LANG || "";
|
|
@@ -43798,14 +44310,14 @@ var init_settings = __esm({
|
|
|
43798
44310
|
"use strict";
|
|
43799
44311
|
init_paths();
|
|
43800
44312
|
SettingsSchema = z32.object({ lang: z32.enum(["fr", "en"]).optional(), theme: z32.enum(["auto", "dark", "light"]).optional() }).passthrough();
|
|
43801
|
-
settingsPath = () =>
|
|
44313
|
+
settingsPath = () => join40(ledgeDir(), "tui.json");
|
|
43802
44314
|
}
|
|
43803
44315
|
});
|
|
43804
44316
|
|
|
43805
44317
|
// src/tui/terminalProfile.ts
|
|
43806
|
-
import { execFile as
|
|
43807
|
-
import { mkdir as mkdir14, readFile as
|
|
43808
|
-
import { dirname as dirname13, join as
|
|
44318
|
+
import { execFile as execFile11, spawn as spawn6 } from "child_process";
|
|
44319
|
+
import { mkdir as mkdir14, readFile as readFile19, realpath as realpath5, writeFile as writeFile18 } from "fs/promises";
|
|
44320
|
+
import { dirname as dirname13, join as join41 } from "path";
|
|
43809
44321
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
43810
44322
|
import { promisify } from "util";
|
|
43811
44323
|
function movesToLedgeTab(env, here, script) {
|
|
@@ -43833,13 +44345,13 @@ function fragment(commandline, iconWin) {
|
|
|
43833
44345
|
}
|
|
43834
44346
|
async function profileLoaded(localAppData) {
|
|
43835
44347
|
for (const rel of SETTINGS) {
|
|
43836
|
-
const text7 = await
|
|
44348
|
+
const text7 = await readFile19(join41(localAppData, rel), "utf8").catch(() => "");
|
|
43837
44349
|
if (new RegExp(`"source"\\s*:\\s*"${PROFILE}"`).test(text7)) return true;
|
|
43838
44350
|
}
|
|
43839
44351
|
return false;
|
|
43840
44352
|
}
|
|
43841
44353
|
async function writeIfChanged2(path3, content3) {
|
|
43842
|
-
const current2 = await
|
|
44354
|
+
const current2 = await readFile19(path3).catch(() => null);
|
|
43843
44355
|
if (current2 && current2.equals(Buffer.from(content3))) return false;
|
|
43844
44356
|
await writeFile18(path3, content3);
|
|
43845
44357
|
return true;
|
|
@@ -43851,9 +44363,9 @@ async function openInLedgeTab(o) {
|
|
|
43851
44363
|
await writeIfChanged2(o.launcher, launcherScript(o.node, o.script, o.ledgeHome));
|
|
43852
44364
|
const dir = fragmentDir(o.localAppData.linux);
|
|
43853
44365
|
await mkdir14(dir, { recursive: true });
|
|
43854
|
-
await writeIfChanged2(
|
|
44366
|
+
await writeIfChanged2(join41(dir, "ledge.ico"), await readFile19(o.icon ?? ICON));
|
|
43855
44367
|
const iconWin = `${o.localAppData.win}\\Microsoft\\Windows Terminal\\Fragments\\${PROFILE}\\ledge.ico`;
|
|
43856
|
-
await writeIfChanged2(
|
|
44368
|
+
await writeIfChanged2(join41(dir, "ledge.json"), fragment(commandline, iconWin));
|
|
43857
44369
|
const deadline = Date.now() + (o.waitMs ?? 15e3);
|
|
43858
44370
|
for (let first = true; !await profileLoaded(o.localAppData.linux); first = false) {
|
|
43859
44371
|
if (Date.now() > deadline) return false;
|
|
@@ -43874,11 +44386,11 @@ async function openTuiInLedgeTab(env, onWait) {
|
|
|
43874
44386
|
distro: env.WSL_DISTRO_NAME,
|
|
43875
44387
|
node: await realpath5(process.execPath),
|
|
43876
44388
|
script: await realpath5(process.argv[1]),
|
|
43877
|
-
launcher:
|
|
44389
|
+
launcher: join41(ledgeDir(), "tui.sh"),
|
|
43878
44390
|
ledgeHome: env.LEDGE_HOME,
|
|
43879
44391
|
onWait,
|
|
43880
44392
|
wt: (args) => new Promise((resolve13, reject) => {
|
|
43881
|
-
const child = spawn6(
|
|
44393
|
+
const child = spawn6(join41(linux, "Microsoft", "WindowsApps", "wt.exe"), args, { cwd: "/mnt/c", stdio: "ignore", detached: true });
|
|
43882
44394
|
child.once("error", reject);
|
|
43883
44395
|
child.once("spawn", () => {
|
|
43884
44396
|
child.unref();
|
|
@@ -43902,10 +44414,10 @@ var init_terminalProfile = __esm({
|
|
|
43902
44414
|
"Packages/Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe/LocalState/settings.json",
|
|
43903
44415
|
"Microsoft/Windows Terminal/settings.json"
|
|
43904
44416
|
];
|
|
43905
|
-
fragmentDir = (localAppData) =>
|
|
44417
|
+
fragmentDir = (localAppData) => join41(localAppData, "Microsoft", "Windows Terminal", "Fragments", PROFILE);
|
|
43906
44418
|
PROFILE_ID = "{d10fee13-0978-595f-a851-b60ec80f11c9}";
|
|
43907
44419
|
shQuote2 = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
43908
|
-
run3 = promisify(
|
|
44420
|
+
run3 = promisify(execFile11);
|
|
43909
44421
|
}
|
|
43910
44422
|
});
|
|
43911
44423
|
|
|
@@ -44074,7 +44586,7 @@ function bridge(opts = {}) {
|
|
|
44074
44586
|
process.stderr.write(`${line2}
|
|
44075
44587
|
`);
|
|
44076
44588
|
}), "ledge bridge");
|
|
44077
|
-
const ws = new WebSocket6(`ws://127.0.0.1:${opts.port ??
|
|
44589
|
+
const ws = new WebSocket6(`ws://127.0.0.1:${opts.port ?? ledgePort()}`, { maxPayload: MAX_FRAME, handshakeTimeout: 1e4, headers: bearerHeaders() });
|
|
44078
44590
|
return new Promise((resolve13, reject) => {
|
|
44079
44591
|
let settled4 = false, off = () => {
|
|
44080
44592
|
};
|
|
@@ -44142,6 +44654,7 @@ function bridge(opts = {}) {
|
|
|
44142
44654
|
var init_bridge = __esm({
|
|
44143
44655
|
"src/cli/bridge.ts"() {
|
|
44144
44656
|
"use strict";
|
|
44657
|
+
init_port();
|
|
44145
44658
|
init_src();
|
|
44146
44659
|
init_auth();
|
|
44147
44660
|
init_frames();
|
|
@@ -44192,20 +44705,20 @@ async function attach(id, opts = {}) {
|
|
|
44192
44705
|
let tmux = realTmux(opts.tmuxSocket ?? TMUX_SOCKET2);
|
|
44193
44706
|
let tmuxName = `ledge-${id}`;
|
|
44194
44707
|
if (!await tmux.hasSession(tmuxName)) {
|
|
44195
|
-
const seat2 = await seatSession(id, opts.port ??
|
|
44708
|
+
const seat2 = await seatSession(id, opts.port ?? ledgePort());
|
|
44196
44709
|
if (seat2) {
|
|
44197
44710
|
tmux = realTmux(opts.seatSocket === void 0 ? null : opts.seatSocket);
|
|
44198
44711
|
tmuxName = seat2;
|
|
44199
44712
|
}
|
|
44200
44713
|
}
|
|
44201
44714
|
if (!await tmux.hasSession(tmuxName)) {
|
|
44202
|
-
const native = await nativeSession(id, opts.port ??
|
|
44715
|
+
const native = await nativeSession(id, opts.port ?? ledgePort());
|
|
44203
44716
|
console.error(native ? `ledge attach: ${native} is driven by Ledge over OMP's own protocol: there is no terminal to attach to (open a shell from the app)` : `ledge attach: no session ${id}`);
|
|
44204
44717
|
return 1;
|
|
44205
44718
|
}
|
|
44206
44719
|
let title3 = tmuxName;
|
|
44207
44720
|
try {
|
|
44208
|
-
const c = await connect({ port: opts.port ??
|
|
44721
|
+
const c = await connect({ port: opts.port ?? ledgePort(), timeoutMs: 800 });
|
|
44209
44722
|
const snap = await c.next("snapshot");
|
|
44210
44723
|
title3 = snap.sessions.find((s) => s.id === id)?.title ?? title3;
|
|
44211
44724
|
await Promise.race([c.close(), new Promise((r) => setTimeout(r, 500))]);
|
|
@@ -44221,6 +44734,7 @@ var TMUX_SOCKET2;
|
|
|
44221
44734
|
var init_attach2 = __esm({
|
|
44222
44735
|
"src/cli/attach.ts"() {
|
|
44223
44736
|
"use strict";
|
|
44737
|
+
init_port();
|
|
44224
44738
|
init_src();
|
|
44225
44739
|
init_client();
|
|
44226
44740
|
init_tmux();
|