@paradow/ledge 0.39.1 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +634 -366
- package/package.json +1 -1
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"() {
|
|
@@ -3788,11 +3794,11 @@ function stripNote(text7) {
|
|
|
3788
3794
|
}
|
|
3789
3795
|
function formatLedgeMessage(m) {
|
|
3790
3796
|
const from = attr(m.from);
|
|
3791
|
-
const
|
|
3792
|
-
if (!m.id || inline(m.text)) return `${
|
|
3797
|
+
const open6 = `<ledge-message from="${from}" tool="${m.tool}"${m.id ? ` id="${attr(m.id)}"` : ""}>${messageNote(from, m.to)} `;
|
|
3798
|
+
if (!m.id || inline(m.text)) return `${open6}${ledgeBody(m.text)}${LEDGE_CLOSE}`;
|
|
3793
3799
|
const first = m.summary?.trim() || m.text.trim().split(/\r\n|\r|\n/)[0].trim();
|
|
3794
3800
|
const head2 = ledgeBody(first.length > POINTER_HEAD ? `${first.slice(0, POINTER_HEAD)}\u2026` : first);
|
|
3795
|
-
return `${
|
|
3801
|
+
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
3802
|
}
|
|
3797
3803
|
function ledgeMessages(text7) {
|
|
3798
3804
|
const out = [];
|
|
@@ -4947,7 +4953,7 @@ __export(who_exports, {
|
|
|
4947
4953
|
});
|
|
4948
4954
|
import { basename as basename5 } from "path";
|
|
4949
4955
|
function formatWho(sessions, self, projects) {
|
|
4950
|
-
const live3 = sessions.filter((s) => s.state !== "exited" && !isArchived(s));
|
|
4956
|
+
const live3 = sessions.filter((s) => s.state !== "exited" && !isArchived(s) || isStoppedOmpSeat(s));
|
|
4951
4957
|
if (live3.length === 0) return "aucune autre session";
|
|
4952
4958
|
const rows = live3.map((s) => [
|
|
4953
4959
|
s.id === self ? "*" : " ",
|
|
@@ -5195,8 +5201,8 @@ function underCodexDaemon(read = (p) => readFileSync5(p, "utf8")) {
|
|
|
5195
5201
|
let pid = process.pid;
|
|
5196
5202
|
for (let depth = 0; depth < 8; depth++) {
|
|
5197
5203
|
try {
|
|
5198
|
-
const
|
|
5199
|
-
pid = Number(
|
|
5204
|
+
const stat8 = read(`/proc/${pid}/stat`);
|
|
5205
|
+
pid = Number(stat8.slice(stat8.lastIndexOf(")") + 2).split(" ")[1]);
|
|
5200
5206
|
if (!pid || pid <= 1) return false;
|
|
5201
5207
|
if (read(`/proc/${pid}/cmdline`).split("\0").includes("app-server")) return true;
|
|
5202
5208
|
} catch {
|
|
@@ -6764,9 +6770,9 @@ ${done.output}` : command;
|
|
|
6764
6770
|
const peer = (m.sender ? this.peerOf(m.sender) : void 0) ?? this.peerOf(m.from) ?? (m.name ? this.peerOf(m.name) : void 0);
|
|
6765
6771
|
if (!peer) return this.put(id, recv);
|
|
6766
6772
|
const head2 = (t) => ledgeBody(t).trim().slice(0, 200);
|
|
6767
|
-
const
|
|
6768
|
-
const byId2 = m.id ?
|
|
6769
|
-
const sent = byId2 ?? [...
|
|
6773
|
+
const open6 = this.items(peer).filter((i) => i.kind === "sent" && i.peer === id && i.status !== "read");
|
|
6774
|
+
const byId2 = m.id ? open6.find((i) => mailId(i.id) === m.id) : void 0;
|
|
6775
|
+
const sent = byId2 ?? [...open6].reverse().find((i) => head2(i.text) === head2(recv.text)) ?? open6.at(-1);
|
|
6770
6776
|
if (sent) this.put(peer, { ...sent, status: "read", received: recv.id });
|
|
6771
6777
|
return this.put(id, { ...recv, ...byId2 ? { text: byId2.text } : {}, peer, ...sent ? { sent: sent.id } : {} });
|
|
6772
6778
|
}
|
|
@@ -7838,6 +7844,203 @@ var init_verify = __esm({
|
|
|
7838
7844
|
}
|
|
7839
7845
|
});
|
|
7840
7846
|
|
|
7847
|
+
// src/daemon/omp/seats.ts
|
|
7848
|
+
import { homedir as homedir5 } from "os";
|
|
7849
|
+
import { join as join10 } from "path";
|
|
7850
|
+
import { open, readFile as readFile7, stat as stat2 } from "fs/promises";
|
|
7851
|
+
function entryState(raw) {
|
|
7852
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
7853
|
+
const e = raw;
|
|
7854
|
+
const at = typeof e.timestamp === "string" && !Number.isNaN(Date.parse(e.timestamp)) ? new Date(e.timestamp).toISOString() : void 0;
|
|
7855
|
+
const of = (state2) => ({ state: state2, ...at ? { at } : {} });
|
|
7856
|
+
if (e.type === "custom_message") return of("working");
|
|
7857
|
+
if (e.type === "custom") return e.customType === "tool_execution_start" ? of("working") : null;
|
|
7858
|
+
if (e.type !== "message" || !e.message || typeof e.message !== "object") return null;
|
|
7859
|
+
const m = e.message;
|
|
7860
|
+
if (m.role === "user" || m.role === "toolResult") return of("working");
|
|
7861
|
+
if (m.role !== "assistant") return null;
|
|
7862
|
+
return typeof m.stopReason === "string" ? of(m.stopReason === "toolUse" ? "working" : "idle") : null;
|
|
7863
|
+
}
|
|
7864
|
+
async function nextNewline(fh, from, size) {
|
|
7865
|
+
const buf2 = Buffer.alloc(BLOCK3);
|
|
7866
|
+
for (let at = from; at < size; ) {
|
|
7867
|
+
const { bytesRead } = await fh.read(buf2, 0, Math.min(BLOCK3, size - at), at);
|
|
7868
|
+
if (bytesRead === 0) return -1;
|
|
7869
|
+
const nl = buf2.subarray(0, bytesRead).indexOf(10);
|
|
7870
|
+
if (nl !== -1) return at + nl;
|
|
7871
|
+
at += bytesRead;
|
|
7872
|
+
}
|
|
7873
|
+
return -1;
|
|
7874
|
+
}
|
|
7875
|
+
function createOmpSeats(deps) {
|
|
7876
|
+
const { store } = deps;
|
|
7877
|
+
const root2 = deps.root ?? ompSeatsRoot();
|
|
7878
|
+
const follows = /* @__PURE__ */ new Map();
|
|
7879
|
+
let running = false, timer2 = null;
|
|
7880
|
+
async function journalOf(seat2) {
|
|
7881
|
+
try {
|
|
7882
|
+
const file = JSON.parse(await readFile7(join10(root2, seat2, "runner-state.json"), "utf8")).sessionFile;
|
|
7883
|
+
return typeof file === "string" && file ? file : null;
|
|
7884
|
+
} catch {
|
|
7885
|
+
return null;
|
|
7886
|
+
}
|
|
7887
|
+
}
|
|
7888
|
+
async function followOne(s) {
|
|
7889
|
+
const file = await journalOf(s.rig.seat);
|
|
7890
|
+
if (!file) return;
|
|
7891
|
+
let size;
|
|
7892
|
+
try {
|
|
7893
|
+
size = (await stat2(file)).size;
|
|
7894
|
+
} catch {
|
|
7895
|
+
return;
|
|
7896
|
+
}
|
|
7897
|
+
let f = follows.get(s.id);
|
|
7898
|
+
if (!f || f.file !== file) {
|
|
7899
|
+
const known2 = await lastState(file, size);
|
|
7900
|
+
f = { file, offset: known2.end };
|
|
7901
|
+
follows.set(s.id, f);
|
|
7902
|
+
if (known2.state) apply(s.id, known2.state);
|
|
7903
|
+
return;
|
|
7904
|
+
}
|
|
7905
|
+
if (size < f.offset) {
|
|
7906
|
+
f.offset = size;
|
|
7907
|
+
return;
|
|
7908
|
+
}
|
|
7909
|
+
if (size === f.offset) return;
|
|
7910
|
+
const fh = await open(file, "r");
|
|
7911
|
+
let chunk, end2;
|
|
7912
|
+
try {
|
|
7913
|
+
const readAt2 = async (want) => {
|
|
7914
|
+
const b3 = Buffer.alloc(want);
|
|
7915
|
+
return b3.subarray(0, (await fh.read(b3, 0, want, f.offset)).bytesRead);
|
|
7916
|
+
};
|
|
7917
|
+
chunk = await readAt2(Math.min(size - f.offset, READ_BYTES));
|
|
7918
|
+
end2 = chunk.lastIndexOf(10);
|
|
7919
|
+
if (end2 === -1 && chunk.length < size - f.offset) {
|
|
7920
|
+
chunk = await readAt2(Math.min(size - f.offset, BACK_BYTES));
|
|
7921
|
+
end2 = chunk.lastIndexOf(10);
|
|
7922
|
+
}
|
|
7923
|
+
if (end2 === -1 && chunk.length === BACK_BYTES) {
|
|
7924
|
+
const next4 = await nextNewline(fh, f.offset + chunk.length, size);
|
|
7925
|
+
if (next4 !== -1) {
|
|
7926
|
+
log(`omp seats: an entry of ${s.rig.seat} over ${BACK_BYTES} bytes was skipped`);
|
|
7927
|
+
f.offset = next4 + 1;
|
|
7928
|
+
}
|
|
7929
|
+
return;
|
|
7930
|
+
}
|
|
7931
|
+
} finally {
|
|
7932
|
+
await fh.close();
|
|
7933
|
+
}
|
|
7934
|
+
if (end2 === -1) return;
|
|
7935
|
+
const lines = chunk.subarray(0, end2).toString("utf8").split("\n");
|
|
7936
|
+
f.offset += end2 + 1;
|
|
7937
|
+
let last = null;
|
|
7938
|
+
for (const line2 of lines) {
|
|
7939
|
+
if (!line2.trim()) continue;
|
|
7940
|
+
let entry2;
|
|
7941
|
+
try {
|
|
7942
|
+
entry2 = JSON.parse(line2);
|
|
7943
|
+
} catch {
|
|
7944
|
+
continue;
|
|
7945
|
+
}
|
|
7946
|
+
last = entryState(entry2) ?? last;
|
|
7947
|
+
}
|
|
7948
|
+
if (last) apply(s.id, last);
|
|
7949
|
+
}
|
|
7950
|
+
function apply(id, last) {
|
|
7951
|
+
const cur = store.get(id);
|
|
7952
|
+
if (!cur || cur.state === "exited") return;
|
|
7953
|
+
const at = last.at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
7954
|
+
if (cur.state !== last.state || at > cur.lastActivityAt) store.upsert({ ...cur, state: last.state, lastActivityAt: at > cur.lastActivityAt ? at : cur.lastActivityAt });
|
|
7955
|
+
}
|
|
7956
|
+
async function lastState(file, size) {
|
|
7957
|
+
const fh = await open(file, "r");
|
|
7958
|
+
try {
|
|
7959
|
+
const newlineBefore = async (before2) => {
|
|
7960
|
+
const buf2 = Buffer.alloc(BLOCK3);
|
|
7961
|
+
for (let to = before2; to > 0; ) {
|
|
7962
|
+
if (size - to > BACK_BYTES) return null;
|
|
7963
|
+
const from = Math.max(0, to - BLOCK3);
|
|
7964
|
+
const { bytesRead } = await fh.read(buf2, 0, to - from, from);
|
|
7965
|
+
const at = buf2.subarray(0, bytesRead).lastIndexOf(10);
|
|
7966
|
+
if (at !== -1) return from + at;
|
|
7967
|
+
to = from;
|
|
7968
|
+
}
|
|
7969
|
+
return -1;
|
|
7970
|
+
};
|
|
7971
|
+
const last = await newlineBefore(size);
|
|
7972
|
+
if (last === null || last === -1) return { state: null, end: last === -1 ? 0 : size };
|
|
7973
|
+
const end2 = last + 1;
|
|
7974
|
+
for (let lineEnd = last; lineEnd > 0; ) {
|
|
7975
|
+
const start = await newlineBefore(lineEnd);
|
|
7976
|
+
if (start === null) break;
|
|
7977
|
+
const from = start + 1;
|
|
7978
|
+
const line2 = Buffer.alloc(lineEnd - from);
|
|
7979
|
+
await fh.read(line2, 0, line2.length, from);
|
|
7980
|
+
let entry2 = null;
|
|
7981
|
+
try {
|
|
7982
|
+
entry2 = JSON.parse(line2.toString("utf8"));
|
|
7983
|
+
} catch {
|
|
7984
|
+
}
|
|
7985
|
+
const state2 = entryState(entry2);
|
|
7986
|
+
if (state2) return { state: state2, end: end2 };
|
|
7987
|
+
if (start === -1) break;
|
|
7988
|
+
lineEnd = start;
|
|
7989
|
+
}
|
|
7990
|
+
return { state: null, end: end2 };
|
|
7991
|
+
} finally {
|
|
7992
|
+
await fh.close();
|
|
7993
|
+
}
|
|
7994
|
+
}
|
|
7995
|
+
async function tick() {
|
|
7996
|
+
if (running) return;
|
|
7997
|
+
running = true;
|
|
7998
|
+
try {
|
|
7999
|
+
const live3 = /* @__PURE__ */ new Set();
|
|
8000
|
+
for (const s of store.all()) {
|
|
8001
|
+
if (!isOmpSeat2(s) || !s.rig || s.state === "exited" || isArchived(s)) continue;
|
|
8002
|
+
live3.add(s.id);
|
|
8003
|
+
try {
|
|
8004
|
+
await followOne(s);
|
|
8005
|
+
} catch (e) {
|
|
8006
|
+
log(`omp seats: journal of ${s.rig.seat} not read: ${e.message}`);
|
|
8007
|
+
}
|
|
8008
|
+
}
|
|
8009
|
+
for (const id of follows.keys()) if (!live3.has(id)) follows.delete(id);
|
|
8010
|
+
} finally {
|
|
8011
|
+
running = false;
|
|
8012
|
+
}
|
|
8013
|
+
}
|
|
8014
|
+
return {
|
|
8015
|
+
tick,
|
|
8016
|
+
start() {
|
|
8017
|
+
if (!timer2) {
|
|
8018
|
+
timer2 = setInterval(() => {
|
|
8019
|
+
void tick();
|
|
8020
|
+
}, deps.intervalMs ?? 1e3);
|
|
8021
|
+
timer2.unref?.();
|
|
8022
|
+
}
|
|
8023
|
+
},
|
|
8024
|
+
stop() {
|
|
8025
|
+
if (timer2) clearInterval(timer2);
|
|
8026
|
+
timer2 = null;
|
|
8027
|
+
}
|
|
8028
|
+
};
|
|
8029
|
+
}
|
|
8030
|
+
var ompSeatsRoot, BACK_BYTES, BLOCK3, READ_BYTES, isOmpSeat2;
|
|
8031
|
+
var init_seats = __esm({
|
|
8032
|
+
"src/daemon/omp/seats.ts"() {
|
|
8033
|
+
"use strict";
|
|
8034
|
+
init_src();
|
|
8035
|
+
init_log();
|
|
8036
|
+
ompSeatsRoot = (env = process.env) => join10(env.OPENRIG_HOME?.trim() || join10(homedir5(), ".openrig"), "state", "omp");
|
|
8037
|
+
BACK_BYTES = 16 * 1024 * 1024;
|
|
8038
|
+
BLOCK3 = 64 * 1024;
|
|
8039
|
+
READ_BYTES = 4 * 1024 * 1024;
|
|
8040
|
+
isOmpSeat2 = (s) => s.source === "rig" && s.tool === "omp" && s.backend === void 0;
|
|
8041
|
+
}
|
|
8042
|
+
});
|
|
8043
|
+
|
|
7841
8044
|
// src/daemon/poller.ts
|
|
7842
8045
|
import { createHash as createHash4 } from "crypto";
|
|
7843
8046
|
function startPoller(opts) {
|
|
@@ -7902,7 +8105,7 @@ function startPoller(opts) {
|
|
|
7902
8105
|
}
|
|
7903
8106
|
if (s.state === "exited") {
|
|
7904
8107
|
forget(s.id);
|
|
7905
|
-
if (!s.archivedAt && s.exitedAt) {
|
|
8108
|
+
if (!s.archivedAt && s.exitedAt && !isOmpSeat2(s)) {
|
|
7906
8109
|
const since = Math.max(Date.parse(s.exitedAt), s.restoredAt ? Date.parse(s.restoredAt) : -Infinity);
|
|
7907
8110
|
if (t - since > retireMs) store.archive(s.id, now2());
|
|
7908
8111
|
}
|
|
@@ -7919,6 +8122,7 @@ function startPoller(opts) {
|
|
|
7919
8122
|
forget(s.id);
|
|
7920
8123
|
return;
|
|
7921
8124
|
}
|
|
8125
|
+
if (isOmpSeat2(s)) return;
|
|
7922
8126
|
const full = await tmux.capture(s.tmuxSession, PREVIEW_CAPTURE_LINES);
|
|
7923
8127
|
if (full === null) return;
|
|
7924
8128
|
const pane = full.split("\n").slice(-DETECT_LINES).join("\n");
|
|
@@ -7983,6 +8187,7 @@ var init_poller = __esm({
|
|
|
7983
8187
|
init_log();
|
|
7984
8188
|
init_git();
|
|
7985
8189
|
init_transcript();
|
|
8190
|
+
init_seats();
|
|
7986
8191
|
init_adapters();
|
|
7987
8192
|
DETECT_LINES = 60;
|
|
7988
8193
|
REST_MS = 2e3;
|
|
@@ -8528,7 +8733,7 @@ var init_project = __esm({
|
|
|
8528
8733
|
|
|
8529
8734
|
// src/daemon/omp/host.ts
|
|
8530
8735
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
8531
|
-
import { basename as basename7, join as
|
|
8736
|
+
import { basename as basename7, join as join11 } from "path";
|
|
8532
8737
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
8533
8738
|
function ompArgs(o) {
|
|
8534
8739
|
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 +8745,7 @@ function createOmpHost(deps) {
|
|
|
8540
8745
|
const live3 = /* @__PURE__ */ new Map();
|
|
8541
8746
|
let generations = 0;
|
|
8542
8747
|
async function startProcess(id, o) {
|
|
8543
|
-
const dir =
|
|
8748
|
+
const dir = join11(deps.dir, id);
|
|
8544
8749
|
await mkdir7(dir, { recursive: true });
|
|
8545
8750
|
if (await reapOwner(dir)) log(`omp ${id}: ended the process a previous daemon left`);
|
|
8546
8751
|
const key = sessionKey(id);
|
|
@@ -8720,15 +8925,15 @@ function createOmpHost(deps) {
|
|
|
8720
8925
|
async answer(id, text7) {
|
|
8721
8926
|
const l = live3.get(id);
|
|
8722
8927
|
if (!l) return "the session is not running";
|
|
8723
|
-
const
|
|
8724
|
-
if (!
|
|
8725
|
-
const a = answerFor(
|
|
8928
|
+
const open6 = l.tracking.requests[0];
|
|
8929
|
+
if (!open6) return "nothing to answer";
|
|
8930
|
+
const a = answerFor(open6, text7);
|
|
8726
8931
|
if ("refused" in a) return a.refused;
|
|
8727
8932
|
l.tracking.requests.shift();
|
|
8728
8933
|
try {
|
|
8729
8934
|
await l.rpc.write(a.frame);
|
|
8730
8935
|
} catch (e) {
|
|
8731
|
-
l.tracking.requests.unshift(
|
|
8936
|
+
l.tracking.requests.unshift(open6);
|
|
8732
8937
|
return e.message;
|
|
8733
8938
|
}
|
|
8734
8939
|
store.apply(id, { name: "Decided", at: now2(), source: "daemon" });
|
|
@@ -8777,15 +8982,15 @@ function createOmpHost(deps) {
|
|
|
8777
8982
|
async answerAsk(id, requestId3, answers, cancel = false) {
|
|
8778
8983
|
const l = live3.get(id);
|
|
8779
8984
|
if (!l) return "the session is not running";
|
|
8780
|
-
const
|
|
8781
|
-
if (!
|
|
8782
|
-
const a = cancel ? { frame: { type: "extension_ui_response", id:
|
|
8985
|
+
const open6 = l.tracking.requests[0];
|
|
8986
|
+
if (!open6 || open6.id !== requestId3) return "the request changed, look again";
|
|
8987
|
+
const a = cancel ? { frame: { type: "extension_ui_response", id: open6.id, cancelled: true } } : askAnswerFor(open6, answers);
|
|
8783
8988
|
if ("refused" in a) return a.refused;
|
|
8784
8989
|
l.tracking.requests.shift();
|
|
8785
8990
|
try {
|
|
8786
8991
|
await l.rpc.write(a.frame);
|
|
8787
8992
|
} catch (e) {
|
|
8788
|
-
l.tracking.requests.unshift(
|
|
8993
|
+
l.tracking.requests.unshift(open6);
|
|
8789
8994
|
return e.message;
|
|
8790
8995
|
}
|
|
8791
8996
|
store.apply(id, { name: "Decided", at: now2(), source: "daemon" });
|
|
@@ -8871,9 +9076,9 @@ var init_catalog = __esm({
|
|
|
8871
9076
|
// src/daemon/repos.ts
|
|
8872
9077
|
import { execFile as execFile2 } from "child_process";
|
|
8873
9078
|
import { constants } from "fs";
|
|
8874
|
-
import { open, stat as
|
|
8875
|
-
import { homedir as
|
|
8876
|
-
import { join as
|
|
9079
|
+
import { open as open2, stat as stat3 } from "fs/promises";
|
|
9080
|
+
import { homedir as homedir6 } from "os";
|
|
9081
|
+
import { join as join12, resolve as resolve2 } from "path";
|
|
8877
9082
|
function runGit(cwd, args) {
|
|
8878
9083
|
return new Promise((res) => {
|
|
8879
9084
|
execFile2("git", ["-C", cwd, ...args], { timeout: GIT_TIMEOUT_MS, windowsHide: true }, (err, out) => res(err ? null : out.trim() || null));
|
|
@@ -8891,7 +9096,7 @@ async function slot(job) {
|
|
|
8891
9096
|
}
|
|
8892
9097
|
}
|
|
8893
9098
|
async function readGitFile(path3, max = 64 * 1024) {
|
|
8894
|
-
const fh = await
|
|
9099
|
+
const fh = await open2(path3, constants.O_RDONLY | (constants.O_NONBLOCK ?? 0));
|
|
8895
9100
|
try {
|
|
8896
9101
|
if (!(await fh.stat()).isFile()) throw new Error(`not a regular file: ${path3}`);
|
|
8897
9102
|
const buf2 = Buffer.alloc(max + 1);
|
|
@@ -8905,7 +9110,7 @@ async function readGitFile(path3, max = 64 * 1024) {
|
|
|
8905
9110
|
async function headBranch(dotGit) {
|
|
8906
9111
|
let head2;
|
|
8907
9112
|
try {
|
|
8908
|
-
head2 = (await readGitFile(
|
|
9113
|
+
head2 = (await readGitFile(join12(dotGit, "HEAD"), MAX_HEAD_BYTES)).trim();
|
|
8909
9114
|
} catch {
|
|
8910
9115
|
return null;
|
|
8911
9116
|
}
|
|
@@ -8914,23 +9119,23 @@ async function headBranch(dotGit) {
|
|
|
8914
9119
|
return /^[0-9a-f]{7,}$/i.test(head2) ? head2.slice(0, 7) : null;
|
|
8915
9120
|
}
|
|
8916
9121
|
async function inspectRepos(paths2, opts = {}) {
|
|
8917
|
-
const home2 = opts.home ??
|
|
9122
|
+
const home2 = opts.home ?? homedir6();
|
|
8918
9123
|
const git = opts.git ?? runGit;
|
|
8919
9124
|
return Promise.all(paths2.map((raw) => slot(async () => {
|
|
8920
9125
|
const input = raw.trim();
|
|
8921
9126
|
const path3 = resolve2(input === "~" ? home2 : input.startsWith("~/") ? resolve2(home2, input.slice(2)) : input);
|
|
8922
9127
|
let isDir2 = false;
|
|
8923
9128
|
try {
|
|
8924
|
-
isDir2 = (await
|
|
9129
|
+
isDir2 = (await stat3(path3)).isDirectory();
|
|
8925
9130
|
} catch {
|
|
8926
9131
|
}
|
|
8927
9132
|
if (!isDir2) return { path: raw, exists: false, branch: null };
|
|
8928
9133
|
let dotDir = false;
|
|
8929
9134
|
try {
|
|
8930
|
-
dotDir = (await
|
|
9135
|
+
dotDir = (await stat3(join12(path3, ".git"))).isDirectory();
|
|
8931
9136
|
} catch {
|
|
8932
9137
|
}
|
|
8933
|
-
if (dotDir) return { path: raw, exists: true, branch: await headBranch(
|
|
9138
|
+
if (dotDir) return { path: raw, exists: true, branch: await headBranch(join12(path3, ".git")) };
|
|
8934
9139
|
const deadline = Date.now() + GIT_TIMEOUT_MS;
|
|
8935
9140
|
const bounded = (args) => new Promise((res) => {
|
|
8936
9141
|
const t = setTimeout(() => res(null), Math.max(0, deadline - Date.now()));
|
|
@@ -8962,7 +9167,7 @@ var init_repos2 = __esm({
|
|
|
8962
9167
|
|
|
8963
9168
|
// src/daemon/scan.ts
|
|
8964
9169
|
import { readdir as fsReaddir } from "fs/promises";
|
|
8965
|
-
import { dirname as dirname4, isAbsolute, join as
|
|
9170
|
+
import { dirname as dirname4, isAbsolute, join as join13, resolve as resolve3, sep as sep2 } from "path";
|
|
8966
9171
|
function extraRoots(home2, cwds) {
|
|
8967
9172
|
const h = resolve3(home2);
|
|
8968
9173
|
const out = [];
|
|
@@ -9008,19 +9213,19 @@ function originUrl(config) {
|
|
|
9008
9213
|
}
|
|
9009
9214
|
return url || void 0;
|
|
9010
9215
|
}
|
|
9011
|
-
async function remoteOf(dir, dotIsFile,
|
|
9216
|
+
async function remoteOf(dir, dotIsFile, readFile20) {
|
|
9012
9217
|
try {
|
|
9013
|
-
let gitDir =
|
|
9218
|
+
let gitDir = join13(dir, ".git");
|
|
9014
9219
|
if (dotIsFile) {
|
|
9015
|
-
const m = /^gitdir:\s*(.+)$/m.exec(await
|
|
9220
|
+
const m = /^gitdir:\s*(.+)$/m.exec(await readFile20(gitDir));
|
|
9016
9221
|
if (!m) return void 0;
|
|
9017
9222
|
gitDir = resolve3(dir, m[1].trim());
|
|
9018
9223
|
try {
|
|
9019
|
-
gitDir = resolve3(gitDir, (await
|
|
9224
|
+
gitDir = resolve3(gitDir, (await readFile20(join13(gitDir, "commondir"))).trim());
|
|
9020
9225
|
} catch {
|
|
9021
9226
|
}
|
|
9022
9227
|
}
|
|
9023
|
-
const url = originUrl(await
|
|
9228
|
+
const url = originUrl(await readFile20(join13(gitDir, "config")));
|
|
9024
9229
|
return url ? repositoryId(url) : void 0;
|
|
9025
9230
|
} catch {
|
|
9026
9231
|
return void 0;
|
|
@@ -9037,18 +9242,18 @@ function manifestName(file, text7) {
|
|
|
9037
9242
|
} else name = /<artifactId>\s*([^<]+?)\s*<\/artifactId>/.exec(text7.replace(/<!--[\s\S]*?-->/g, "").replace(/<parent>[\s\S]*?<\/parent>/g, ""))?.[1];
|
|
9038
9243
|
return typeof name === "string" && name.trim() && name.length <= 200 ? name.trim() : void 0;
|
|
9039
9244
|
}
|
|
9040
|
-
async function describe(dir, dotIsFile, home2,
|
|
9245
|
+
async function describe(dir, dotIsFile, home2, readFile20, manifest) {
|
|
9041
9246
|
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 ?
|
|
9247
|
+
dotIsFile ? inspectRepos([dir], { home: home2 }).then((r) => r[0]?.branch ?? null) : headBranch(join13(dir, ".git")),
|
|
9248
|
+
remoteOf(dir, dotIsFile, readFile20),
|
|
9249
|
+
manifest ? readFile20(join13(dir, manifest)).then((text7) => manifestName(manifest, text7), () => void 0) : void 0
|
|
9045
9250
|
]);
|
|
9046
9251
|
return { path: dir, branch, ...remote ? { remote } : {}, ...named ? { manifest: named } : {} };
|
|
9047
9252
|
}
|
|
9048
9253
|
async function scanRepos(opts) {
|
|
9049
9254
|
const now2 = opts.now ?? Date.now;
|
|
9050
9255
|
const readdir5 = opts.readdir ?? ((p) => fsReaddir(p, { withFileTypes: true }));
|
|
9051
|
-
const
|
|
9256
|
+
const readFile20 = opts.readFile ?? ((p) => readGitFile(p));
|
|
9052
9257
|
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
9258
|
const deadline = now2() + ms;
|
|
9054
9259
|
let timer2;
|
|
@@ -9083,11 +9288,11 @@ async function scanRepos(opts) {
|
|
|
9083
9288
|
if (!entries) return;
|
|
9084
9289
|
const dot = entries.find((e) => e.name === ".git" && (e.isDirectory() || e.isFile()));
|
|
9085
9290
|
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,
|
|
9291
|
+
if (dot) found.push(describe(d.path, dot.isFile(), opts.home, readFile20, manifest));
|
|
9087
9292
|
if (dot && d.path !== home2 || d.depth >= depth) return;
|
|
9088
9293
|
const names = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && !SKIP.has(e.name)).map((e) => e.name).sort();
|
|
9089
9294
|
for (const name of names) {
|
|
9090
|
-
const path3 =
|
|
9295
|
+
const path3 = join13(d.path, name);
|
|
9091
9296
|
if (!seen.has(path3)) {
|
|
9092
9297
|
seen.add(path3);
|
|
9093
9298
|
queue.push({ path: path3, depth: d.depth + 1 });
|
|
@@ -9131,23 +9336,23 @@ var init_scan2 = __esm({
|
|
|
9131
9336
|
});
|
|
9132
9337
|
|
|
9133
9338
|
// 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
|
|
9339
|
+
import { mkdir as mkdir8, readdir as readdir3, stat as stat4 } from "fs/promises";
|
|
9340
|
+
import { homedir as homedir7 } from "os";
|
|
9341
|
+
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join14, resolve as resolve4 } from "path";
|
|
9137
9342
|
async function resolveExact(raw, home2) {
|
|
9138
9343
|
const expanded = raw === "~" ? home2 : raw.startsWith("~/") ? resolve4(home2, raw.slice(2)) : raw;
|
|
9139
9344
|
if (!isAbsolute2(expanded)) throw new Error(`not an absolute path: ${raw}`);
|
|
9140
9345
|
const path3 = resolve4(expanded);
|
|
9141
9346
|
let s;
|
|
9142
9347
|
try {
|
|
9143
|
-
s = await
|
|
9348
|
+
s = await stat4(path3);
|
|
9144
9349
|
} catch {
|
|
9145
9350
|
throw new Error(`no such folder: ${path3}`);
|
|
9146
9351
|
}
|
|
9147
9352
|
if (!s.isDirectory()) throw new Error(`not a folder: ${path3}`);
|
|
9148
9353
|
return path3;
|
|
9149
9354
|
}
|
|
9150
|
-
async function resolveFolder(input, home2 =
|
|
9355
|
+
async function resolveFolder(input, home2 = homedir7()) {
|
|
9151
9356
|
return resolveExact(input.trim(), home2);
|
|
9152
9357
|
}
|
|
9153
9358
|
async function subfolders(path3) {
|
|
@@ -9156,7 +9361,7 @@ async function subfolders(path3) {
|
|
|
9156
9361
|
if (d.isDirectory()) return d.name;
|
|
9157
9362
|
if (!d.isSymbolicLink()) return null;
|
|
9158
9363
|
try {
|
|
9159
|
-
return (await
|
|
9364
|
+
return (await stat4(join14(path3, d.name))).isDirectory() ? d.name : null;
|
|
9160
9365
|
} catch {
|
|
9161
9366
|
return null;
|
|
9162
9367
|
}
|
|
@@ -9164,42 +9369,42 @@ async function subfolders(path3) {
|
|
|
9164
9369
|
return found.filter((n) => n !== null).sort(byName.compare);
|
|
9165
9370
|
}
|
|
9166
9371
|
async function gitBranchOf(dir, git) {
|
|
9167
|
-
const dot =
|
|
9372
|
+
const dot = join14(dir, ".git");
|
|
9168
9373
|
let s;
|
|
9169
9374
|
try {
|
|
9170
|
-
s = await
|
|
9375
|
+
s = await stat4(dot);
|
|
9171
9376
|
} catch {
|
|
9172
9377
|
return null;
|
|
9173
9378
|
}
|
|
9174
9379
|
if (s.isFile()) return (await inspectRepos([dir], { git }))[0]?.branch ?? null;
|
|
9175
9380
|
return s.isDirectory() ? headBranch(dot) : null;
|
|
9176
9381
|
}
|
|
9177
|
-
async function listFolder(input, home2 =
|
|
9382
|
+
async function listFolder(input, home2 = homedir7(), requestId3, git) {
|
|
9178
9383
|
const tag = requestId3 === void 0 ? {} : { requestId: requestId3 };
|
|
9179
9384
|
const asked2 = input || "~";
|
|
9180
9385
|
try {
|
|
9181
9386
|
const path3 = await resolveExact(asked2, home2);
|
|
9182
9387
|
const parent = dirname5(path3);
|
|
9183
9388
|
const entries = await subfolders(path3);
|
|
9184
|
-
const branches = await Promise.all(entries.map((name) => gitBranchOf(
|
|
9389
|
+
const branches = await Promise.all(entries.map((name) => gitBranchOf(join14(path3, name), git)));
|
|
9185
9390
|
const repos = entries.flatMap((name, i) => branches[i] === null ? [] : [{ name, branch: branches[i] }]);
|
|
9186
9391
|
return { type: "folder.listing", path: path3, parent: parent === path3 ? null : parent, entries, repos, ...tag };
|
|
9187
9392
|
} catch (e) {
|
|
9188
9393
|
return { type: "folder.listing", path: asked2, parent: null, entries: [], error: e.message, ...tag };
|
|
9189
9394
|
}
|
|
9190
9395
|
}
|
|
9191
|
-
async function createFolder(parent, name, home2 =
|
|
9396
|
+
async function createFolder(parent, name, home2 = homedir7(), requestId3) {
|
|
9192
9397
|
const at = await listFolder(parent, home2, requestId3);
|
|
9193
9398
|
if (at.error) return at;
|
|
9194
9399
|
const clean = name.trim();
|
|
9195
9400
|
if (!clean || clean === "." || clean === ".." || /[/\0]/.test(clean)) return { ...at, error: `invalid folder name: ${name}` };
|
|
9196
9401
|
try {
|
|
9197
|
-
await mkdir8(
|
|
9402
|
+
await mkdir8(join14(at.path, clean));
|
|
9198
9403
|
} catch (e) {
|
|
9199
9404
|
const code3 = e.code;
|
|
9200
9405
|
return { ...at, error: code3 === "EEXIST" ? `already exists: ${clean}` : `cannot create ${clean}: ${e.message}` };
|
|
9201
9406
|
}
|
|
9202
|
-
return listFolder(
|
|
9407
|
+
return listFolder(join14(at.path, clean), home2, requestId3);
|
|
9203
9408
|
}
|
|
9204
9409
|
var byName;
|
|
9205
9410
|
var init_folders = __esm({
|
|
@@ -9263,7 +9468,7 @@ function taskOf2(ref, ctx) {
|
|
|
9263
9468
|
return taskClosed(t) ? no("closed", `task ${t.id} is closed`) : t;
|
|
9264
9469
|
}
|
|
9265
9470
|
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;
|
|
9471
|
+
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
9472
|
}
|
|
9268
9473
|
function cardRefusal(row3) {
|
|
9269
9474
|
if (row3.action !== "session.decide" || !row3.detailCut || row3.args.decision !== "accept") return null;
|
|
@@ -9316,6 +9521,7 @@ var init_actions2 = __esm({
|
|
|
9316
9521
|
"src/daemon/actions.ts"() {
|
|
9317
9522
|
"use strict";
|
|
9318
9523
|
init_src();
|
|
9524
|
+
init_seats();
|
|
9319
9525
|
DECIDABLE = ["permission", "diff", "plan"];
|
|
9320
9526
|
KIND = { permission: "permission", diff: "diff", plan: "plan" };
|
|
9321
9527
|
no = (code3, refused) => ({ refused, code: code3 });
|
|
@@ -9435,13 +9641,13 @@ ${p.body}` : p.text) }
|
|
|
9435
9641
|
});
|
|
9436
9642
|
|
|
9437
9643
|
// src/chat/guide.ts
|
|
9438
|
-
import { access, constants as constants2, readFile as
|
|
9439
|
-
import { delimiter, join as
|
|
9644
|
+
import { access, constants as constants2, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
|
|
9645
|
+
import { delimiter, join as join15 } from "path";
|
|
9440
9646
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9441
9647
|
async function readGuide(files = GUIDE_FILES) {
|
|
9442
9648
|
for (const f of files) {
|
|
9443
9649
|
try {
|
|
9444
|
-
return await
|
|
9650
|
+
return await readFile8(f, "utf8");
|
|
9445
9651
|
} catch {
|
|
9446
9652
|
}
|
|
9447
9653
|
}
|
|
@@ -9453,7 +9659,7 @@ async function installedAgents(path3) {
|
|
|
9453
9659
|
for (const tool of ToolSchema.options) {
|
|
9454
9660
|
for (const d of dirs) {
|
|
9455
9661
|
try {
|
|
9456
|
-
await access(
|
|
9662
|
+
await access(join15(d, AGENT_BIN2[tool]), constants2.X_OK);
|
|
9457
9663
|
found.push(tool);
|
|
9458
9664
|
break;
|
|
9459
9665
|
} catch {
|
|
@@ -9485,7 +9691,7 @@ function ledgeMd(info) {
|
|
|
9485
9691
|
].join("\n");
|
|
9486
9692
|
}
|
|
9487
9693
|
async function writeLedgeMd(dir, info) {
|
|
9488
|
-
await writeFile8(
|
|
9694
|
+
await writeFile8(join15(dir, LEDGE_MD), ledgeMd(info));
|
|
9489
9695
|
}
|
|
9490
9696
|
var LEDGE_MD, GUIDE_FILES, AGENT_BIN2, RISK;
|
|
9491
9697
|
var init_guide = __esm({
|
|
@@ -9500,8 +9706,8 @@ var init_guide = __esm({
|
|
|
9500
9706
|
});
|
|
9501
9707
|
|
|
9502
9708
|
// src/chat/notes.ts
|
|
9503
|
-
import { open as
|
|
9504
|
-
import { join as
|
|
9709
|
+
import { open as open3, readdir as readdir4, stat as stat5, writeFile as writeFile9 } from "fs/promises";
|
|
9710
|
+
import { join as join16 } from "path";
|
|
9505
9711
|
function scalarValue(raw) {
|
|
9506
9712
|
const v2 = raw.trim();
|
|
9507
9713
|
const q = v2[0];
|
|
@@ -9533,7 +9739,7 @@ function copyName(file, taken) {
|
|
|
9533
9739
|
return name;
|
|
9534
9740
|
}
|
|
9535
9741
|
async function head(path3, max) {
|
|
9536
|
-
const fh = await
|
|
9742
|
+
const fh = await open3(path3, "r");
|
|
9537
9743
|
try {
|
|
9538
9744
|
const buf2 = Buffer.alloc(max);
|
|
9539
9745
|
const { bytesRead } = await fh.read(buf2, 0, max, 0);
|
|
@@ -9552,12 +9758,12 @@ async function writeNotes(dir, from, steps, max = NOTE_MAX2) {
|
|
|
9552
9758
|
if (!files.length) lines.push(`- (none: the user can add Markdown files to \`${from}\`, with an optional front matter \`title\` and \`when\`)`);
|
|
9553
9759
|
const taken = /* @__PURE__ */ new Set();
|
|
9554
9760
|
for (const file of files.slice(0, NOTES_MAX)) {
|
|
9555
|
-
const path3 =
|
|
9761
|
+
const path3 = join16(from, file);
|
|
9556
9762
|
let text7;
|
|
9557
9763
|
let size;
|
|
9558
9764
|
let mtime;
|
|
9559
9765
|
try {
|
|
9560
|
-
const st = await
|
|
9766
|
+
const st = await stat5(path3);
|
|
9561
9767
|
size = st.size;
|
|
9562
9768
|
mtime = st.mtime.toISOString();
|
|
9563
9769
|
text7 = await head(path3, max);
|
|
@@ -9574,15 +9780,15 @@ async function writeNotes(dir, from, steps, max = NOTE_MAX2) {
|
|
|
9574
9780
|
const when = fields.when?.trim() ? `when: ${line(fields.when)}` : `first line: ${line(body.split("\n").find((l) => l.trim()) ?? "") || "(empty)"}`;
|
|
9575
9781
|
const truncated = size > max;
|
|
9576
9782
|
const name = copyName(file, taken);
|
|
9577
|
-
await writeFile9(
|
|
9783
|
+
await writeFile9(join16(dir, name), truncated ? `${text7}
|
|
9578
9784
|
|
|
9579
9785
|
[truncated by Ledge: the first ${max} of ${size} bytes]
|
|
9580
9786
|
` : text7);
|
|
9581
|
-
steps.set(name, { kind: "repo", name: file, path:
|
|
9787
|
+
steps.set(name, { kind: "repo", name: file, path: join16(from, file) });
|
|
9582
9788
|
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
9789
|
}
|
|
9584
9790
|
if (files.length > NOTES_MAX) lines.push(`- (${files.length - NOTES_MAX} more notes not copied: Ledge copies ${NOTES_MAX} at most)`);
|
|
9585
|
-
await writeFile9(
|
|
9791
|
+
await writeFile9(join16(dir, NOTES_INDEX), [...lines, ""].join("\n"));
|
|
9586
9792
|
}
|
|
9587
9793
|
var NOTE_MAX2, NOTES_MAX, NOTES_INDEX, unquote, line;
|
|
9588
9794
|
var init_notes = __esm({
|
|
@@ -9601,8 +9807,8 @@ var init_notes = __esm({
|
|
|
9601
9807
|
|
|
9602
9808
|
// src/chat/context.ts
|
|
9603
9809
|
import { mkdir as mkdir9, rm as rm2, writeFile as writeFile10 } from "fs/promises";
|
|
9604
|
-
import { homedir as
|
|
9605
|
-
import { basename as basename8, join as
|
|
9810
|
+
import { homedir as homedir8 } from "os";
|
|
9811
|
+
import { basename as basename8, join as join17, resolve as resolve5 } from "path";
|
|
9606
9812
|
async function writeContext(dir, input) {
|
|
9607
9813
|
await rm2(dir, { recursive: true, force: true });
|
|
9608
9814
|
await mkdir9(dir, { recursive: true });
|
|
@@ -9631,29 +9837,29 @@ async function writeContext(dir, input) {
|
|
|
9631
9837
|
const pane = await input.capture(s).catch(() => null);
|
|
9632
9838
|
if (pane?.length) {
|
|
9633
9839
|
const f = `pane-${s.id}.txt`;
|
|
9634
|
-
await writeFile10(
|
|
9840
|
+
await writeFile10(join17(dir, f), pane.join("\n"));
|
|
9635
9841
|
steps.set(f, { kind: "pane", id: s.id, name: s.name });
|
|
9636
9842
|
lines.push(`- terminal, last lines: ${f}`);
|
|
9637
9843
|
}
|
|
9638
9844
|
if (s.lastReply?.text.trim()) {
|
|
9639
9845
|
const f = `reply-${s.id}.md`;
|
|
9640
|
-
await writeFile10(
|
|
9846
|
+
await writeFile10(join17(dir, f), s.lastReply.text);
|
|
9641
9847
|
steps.set(f, { kind: "reply", id: s.id, name: s.name });
|
|
9642
9848
|
lines.push(`- last reply (${s.lastReply.at}): ${f}`);
|
|
9643
9849
|
}
|
|
9644
9850
|
const g = await input.git(s.cwd).catch(() => null);
|
|
9645
9851
|
if (g) {
|
|
9646
9852
|
const f = `git-${s.id}.txt`;
|
|
9647
|
-
await writeFile10(
|
|
9853
|
+
await writeFile10(join17(dir, f), g);
|
|
9648
9854
|
steps.set(f, { kind: "git", id: s.id, name: s.name });
|
|
9649
9855
|
lines.push(`- git status, log and diff stat: ${f}`);
|
|
9650
9856
|
}
|
|
9651
9857
|
lines.push("");
|
|
9652
9858
|
}
|
|
9653
|
-
await writeFile10(
|
|
9859
|
+
await writeFile10(join17(dir, "sessions.md"), lines.join("\n"));
|
|
9654
9860
|
steps.set("sessions.md", { kind: "sessions" });
|
|
9655
9861
|
const recents = input.recents ?? [];
|
|
9656
|
-
await writeFile10(
|
|
9862
|
+
await writeFile10(join17(dir, "folders.md"), [
|
|
9657
9863
|
"# Recent folders",
|
|
9658
9864
|
"",
|
|
9659
9865
|
"The folders the user started sessions in, most recent first.",
|
|
@@ -9661,7 +9867,7 @@ async function writeContext(dir, input) {
|
|
|
9661
9867
|
...recents.length ? recents.map((r) => `- ${r.project}: ${r.cwd} (last used ${r.lastAt})`) : ["- (none yet)"],
|
|
9662
9868
|
""
|
|
9663
9869
|
].join("\n"));
|
|
9664
|
-
if (withProjects) await writeFile10(
|
|
9870
|
+
if (withProjects) await writeFile10(join17(dir, "projects.md"), [
|
|
9665
9871
|
"# Projects",
|
|
9666
9872
|
"",
|
|
9667
9873
|
...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 +9885,7 @@ async function writeContext(dir, input) {
|
|
|
9679
9885
|
async function writeTasksContext(dir, taskList, steps) {
|
|
9680
9886
|
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
9681
9887
|
const shown = taskList.filter((t) => !taskClosed(t) || t.updatedAt.slice(0, 10) === day);
|
|
9682
|
-
await writeFile10(
|
|
9888
|
+
await writeFile10(join17(dir, "tasks.md"), [
|
|
9683
9889
|
"# Tasks",
|
|
9684
9890
|
"",
|
|
9685
9891
|
"Open tasks and those closed today. A giver `Ledge` is the user, through you.",
|
|
@@ -9691,7 +9897,7 @@ async function writeTasksContext(dir, taskList, steps) {
|
|
|
9691
9897
|
}
|
|
9692
9898
|
function coversHome(path3) {
|
|
9693
9899
|
const p = resolve5(path3);
|
|
9694
|
-
const home2 = resolve5(
|
|
9900
|
+
const home2 = resolve5(homedir8());
|
|
9695
9901
|
return p === home2 || home2.startsWith(p.endsWith("/") ? p : `${p}/`);
|
|
9696
9902
|
}
|
|
9697
9903
|
function stepFor(path3, ctx) {
|
|
@@ -9730,14 +9936,14 @@ var LEDGE_VERSION;
|
|
|
9730
9936
|
var init_version = __esm({
|
|
9731
9937
|
"src/version.ts"() {
|
|
9732
9938
|
"use strict";
|
|
9733
|
-
LEDGE_VERSION = true ? "0.
|
|
9939
|
+
LEDGE_VERSION = true ? "0.41.0" : "0.0.0-dev";
|
|
9734
9940
|
}
|
|
9735
9941
|
});
|
|
9736
9942
|
|
|
9737
9943
|
// src/chat/forge.ts
|
|
9738
9944
|
import { execFile as execFile4 } from "child_process";
|
|
9739
9945
|
import { writeFile as writeFile11 } from "fs/promises";
|
|
9740
|
-
import { join as
|
|
9946
|
+
import { join as join18 } from "path";
|
|
9741
9947
|
function forgeOf(remote) {
|
|
9742
9948
|
const id = repositoryId(remote);
|
|
9743
9949
|
if (!id) return null;
|
|
@@ -9802,12 +10008,12 @@ function createForge(deps = {}) {
|
|
|
9802
10008
|
return;
|
|
9803
10009
|
}
|
|
9804
10010
|
const parts2 = await Promise.all(local.map((r) => section(r.path)));
|
|
9805
|
-
await writeFile11(
|
|
10011
|
+
await writeFile11(join18(ctx.dir, file), parts2.flatMap((s) => s.lines).join("\n"));
|
|
9806
10012
|
const sources = parts2.flatMap((s) => s.source ? [s.source] : []);
|
|
9807
10013
|
ctx.steps.set(file, { kind: "repo", name, path: sources.join(", ") || file });
|
|
9808
10014
|
index2.push(`- ${name}: ${file}`);
|
|
9809
10015
|
}));
|
|
9810
|
-
await writeFile11(
|
|
10016
|
+
await writeFile11(join18(ctx.dir, "forge.md"), [...index2, ""].join("\n"));
|
|
9811
10017
|
}
|
|
9812
10018
|
};
|
|
9813
10019
|
}
|
|
@@ -10074,7 +10280,7 @@ var init_codex2 = __esm({
|
|
|
10074
10280
|
// src/chat/cursor.ts
|
|
10075
10281
|
import { spawn as nodeSpawn5 } from "child_process";
|
|
10076
10282
|
import { copyFileSync, writeFileSync } from "fs";
|
|
10077
|
-
import { extname, join as
|
|
10283
|
+
import { extname, join as join19 } from "path";
|
|
10078
10284
|
function cursorArgs(o) {
|
|
10079
10285
|
return [
|
|
10080
10286
|
"-p",
|
|
@@ -10116,10 +10322,10 @@ function parseCursorLine(line2) {
|
|
|
10116
10322
|
}
|
|
10117
10323
|
function runCursor(o, onEvent, spawnImpl = nodeSpawn5) {
|
|
10118
10324
|
const [file = "cursor-agent", ...pre] = o.bin ?? ["cursor-agent"];
|
|
10119
|
-
writeFileSync(
|
|
10325
|
+
writeFileSync(join19(o.cwd, "AGENTS.md"), `${o.persona ?? buildPersona("cursor")}
|
|
10120
10326
|
`);
|
|
10121
10327
|
const copies = (o.images ?? []).map((img, n) => {
|
|
10122
|
-
const to =
|
|
10328
|
+
const to = join19(o.cwd, `image-${n + 1}${extname(img.path)}`);
|
|
10123
10329
|
copyFileSync(img.path, to);
|
|
10124
10330
|
return to;
|
|
10125
10331
|
});
|
|
@@ -10203,9 +10409,9 @@ var init_omp = __esm({
|
|
|
10203
10409
|
|
|
10204
10410
|
// src/chat/service.ts
|
|
10205
10411
|
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
|
|
10412
|
+
import { mkdir as mkdir10, readFile as readFile9, rm as rm3, writeFile as writeFile12 } from "fs/promises";
|
|
10413
|
+
import { homedir as homedir9 } from "os";
|
|
10414
|
+
import { basename as basename9, dirname as dirname6, join as join20, resolve as resolve6 } from "path";
|
|
10209
10415
|
function droppedNote(total, kept, reasons, counted) {
|
|
10210
10416
|
const dropped = total - kept;
|
|
10211
10417
|
if (dropped <= 0) return "";
|
|
@@ -10226,7 +10432,7 @@ function createChat(deps) {
|
|
|
10226
10432
|
const now2 = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
10227
10433
|
const today = deps.today ?? (() => now2().slice(0, 10));
|
|
10228
10434
|
const uuid = deps.uuid ?? randomUUID7;
|
|
10229
|
-
const dayFile =
|
|
10435
|
+
const dayFile = join20(deps.home, "chat.json");
|
|
10230
10436
|
let turns2 = [];
|
|
10231
10437
|
let turnsDay = "";
|
|
10232
10438
|
let busy = false;
|
|
@@ -10249,7 +10455,7 @@ function createChat(deps) {
|
|
|
10249
10455
|
const day = today();
|
|
10250
10456
|
if (!fresh && !freshNext) {
|
|
10251
10457
|
try {
|
|
10252
|
-
const saved = JSON.parse(await
|
|
10458
|
+
const saved = JSON.parse(await readFile9(dayFile, "utf8"));
|
|
10253
10459
|
if (saved.day === day && saved.sessionId && (saved.tool ?? "claude") === tool) return { sessionId: saved.sessionId, resume: true };
|
|
10254
10460
|
} catch {
|
|
10255
10461
|
}
|
|
@@ -10265,7 +10471,7 @@ function createChat(deps) {
|
|
|
10265
10471
|
const p = await deps.tmux.capture(s.tmuxSession, PANE_LINES);
|
|
10266
10472
|
return p === null ? null : p.split("\n");
|
|
10267
10473
|
};
|
|
10268
|
-
const contextDir =
|
|
10474
|
+
const contextDir = join20(deps.home, "chat", "context");
|
|
10269
10475
|
async function proposalFrom(raw, tool, qid) {
|
|
10270
10476
|
const parsed = parseActionBlock("session", raw);
|
|
10271
10477
|
if ("error" in parsed) {
|
|
@@ -10342,7 +10548,7 @@ function createChat(deps) {
|
|
|
10342
10548
|
for (const r of block2.repos) {
|
|
10343
10549
|
try {
|
|
10344
10550
|
const path3 = await resolveFolder(r.path, deps.userHome);
|
|
10345
|
-
const wide = tooWide(path3, resolve6(deps.userHome ??
|
|
10551
|
+
const wide = tooWide(path3, resolve6(deps.userHome ?? homedir9()));
|
|
10346
10552
|
if (wide) {
|
|
10347
10553
|
log(`chat ${qid}: project path dropped: ${wide}: ${path3}`);
|
|
10348
10554
|
continue;
|
|
@@ -10458,7 +10664,7 @@ function createChat(deps) {
|
|
|
10458
10664
|
let resultError = null;
|
|
10459
10665
|
const handle2 = runTool(tool, { question: mine.told.text ? `${mine.told.text}
|
|
10460
10666
|
|
|
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:
|
|
10667
|
+
${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
10668
|
if (e.type === "tool") {
|
|
10463
10669
|
const step3 = e.path ? stepFor(e.path, ctx) : null;
|
|
10464
10670
|
if (step3 && !turn.steps.some((s) => sameStep(s, step3))) {
|
|
@@ -10745,8 +10951,8 @@ var init_service = __esm({
|
|
|
10745
10951
|
});
|
|
10746
10952
|
|
|
10747
10953
|
// src/chat/threads.ts
|
|
10748
|
-
import { readFile as
|
|
10749
|
-
import { join as
|
|
10954
|
+
import { readFile as readFile10, rm as rm4 } from "fs/promises";
|
|
10955
|
+
import { join as join21 } from "path";
|
|
10750
10956
|
function createSlots(max = 3) {
|
|
10751
10957
|
let used = 0;
|
|
10752
10958
|
return {
|
|
@@ -10767,7 +10973,7 @@ function createChatThreads(deps) {
|
|
|
10767
10973
|
const devices = /* @__PURE__ */ new Map();
|
|
10768
10974
|
const emitters = /* @__PURE__ */ new Map();
|
|
10769
10975
|
const dropping = /* @__PURE__ */ new Map();
|
|
10770
|
-
const deviceHome = (id) =>
|
|
10976
|
+
const deviceHome = (id) => join21(deps.home, "mobile", "chat", id);
|
|
10771
10977
|
const device = (id) => {
|
|
10772
10978
|
checkId(id);
|
|
10773
10979
|
if (dropping.has(id)) throw new Error("device thread is being dropped");
|
|
@@ -10796,7 +11002,7 @@ function createChatThreads(deps) {
|
|
|
10796
11002
|
/** The PC's tool of the day file, whatever its day: `claude` without one. */
|
|
10797
11003
|
async deviceTool() {
|
|
10798
11004
|
try {
|
|
10799
|
-
const t = JSON.parse(await
|
|
11005
|
+
const t = JSON.parse(await readFile10(join21(deps.home, "chat.json"), "utf8")).tool;
|
|
10800
11006
|
return t === "claude" || t === "codex" || t === "cursor" || t === "omp" ? t : "claude";
|
|
10801
11007
|
} catch {
|
|
10802
11008
|
return "claude";
|
|
@@ -10963,7 +11169,7 @@ var init_island2 = __esm({
|
|
|
10963
11169
|
|
|
10964
11170
|
// src/daemon/documents.ts
|
|
10965
11171
|
import { constants as constants3 } from "fs";
|
|
10966
|
-
import { open as
|
|
11172
|
+
import { open as open4, realpath } from "fs/promises";
|
|
10967
11173
|
import { isAbsolute as isAbsolute3, dirname as dirname7, extname as extname2, resolve as resolve7 } from "path";
|
|
10968
11174
|
async function readDocument(request2, context) {
|
|
10969
11175
|
const source2 = request2.path;
|
|
@@ -10984,10 +11190,10 @@ async function readDocument(request2, context) {
|
|
|
10984
11190
|
async function readAt(request2, path3) {
|
|
10985
11191
|
let file;
|
|
10986
11192
|
try {
|
|
10987
|
-
file = await
|
|
10988
|
-
const
|
|
10989
|
-
if (!
|
|
10990
|
-
if (
|
|
11193
|
+
file = await open4(path3, constants3.O_RDONLY | constants3.O_NONBLOCK);
|
|
11194
|
+
const stat8 = await file.stat();
|
|
11195
|
+
if (!stat8.isFile()) return failure(request2, "not_file");
|
|
11196
|
+
if (stat8.size > DOCUMENT_MAX_BYTES) return failure(request2, "too_large");
|
|
10991
11197
|
const chunk = Buffer.allocUnsafe(64 * 1024);
|
|
10992
11198
|
const parts2 = [];
|
|
10993
11199
|
let bytes4 = 0;
|
|
@@ -11010,7 +11216,7 @@ async function readAt(request2, path3) {
|
|
|
11010
11216
|
path: await realpath(path3),
|
|
11011
11217
|
text: text7,
|
|
11012
11218
|
bytes: bytes4,
|
|
11013
|
-
modifiedAt:
|
|
11219
|
+
modifiedAt: stat8.mtime.toISOString()
|
|
11014
11220
|
};
|
|
11015
11221
|
} catch (e) {
|
|
11016
11222
|
return failure(request2, errorCode(e));
|
|
@@ -11039,9 +11245,9 @@ var init_documents2 = __esm({
|
|
|
11039
11245
|
|
|
11040
11246
|
// src/daemon/models.ts
|
|
11041
11247
|
import { execFile as execFile5 } from "child_process";
|
|
11042
|
-
import { readFile as
|
|
11043
|
-
import { homedir as
|
|
11044
|
-
import { join as
|
|
11248
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
11249
|
+
import { homedir as homedir10 } from "os";
|
|
11250
|
+
import { join as join22 } from "path";
|
|
11045
11251
|
function claudeModels(settingsModel) {
|
|
11046
11252
|
const efforts = [...CLAUDE_EFFORTS];
|
|
11047
11253
|
const models = CLAUDE_MODELS.map((m) => ({ ...m, efforts, ...m.id === settingsModel ? { isDefault: true } : {} }));
|
|
@@ -11085,15 +11291,15 @@ function codexConfig(toml) {
|
|
|
11085
11291
|
function createModels(deps = {}) {
|
|
11086
11292
|
const env = deps.env ?? (async () => process.env);
|
|
11087
11293
|
const run4 = deps.run ?? execRun2(env);
|
|
11088
|
-
const readText2 = deps.readText ?? ((p) =>
|
|
11294
|
+
const readText2 = deps.readText ?? ((p) => readFile11(p, "utf8").catch(() => ""));
|
|
11089
11295
|
const now2 = deps.now ?? Date.now, ttl = deps.ttlMs ?? MODELS_TTL_MS;
|
|
11090
11296
|
const cache = /* @__PURE__ */ new Map();
|
|
11091
11297
|
const flying = /* @__PURE__ */ new Map();
|
|
11092
11298
|
async function ask3(tool) {
|
|
11093
11299
|
const e = await env();
|
|
11094
|
-
const home2 = e.HOME ??
|
|
11300
|
+
const home2 = e.HOME ?? homedir10();
|
|
11095
11301
|
if (tool === "claude") {
|
|
11096
|
-
const settings = await readText2(
|
|
11302
|
+
const settings = await readText2(join22(e.CLAUDE_CONFIG_DIR ?? join22(home2, ".claude"), "settings.json"));
|
|
11097
11303
|
let model;
|
|
11098
11304
|
try {
|
|
11099
11305
|
model = settings ? JSON.parse(settings).model : void 0;
|
|
@@ -11109,7 +11315,7 @@ function createModels(deps = {}) {
|
|
|
11109
11315
|
if (r.code === null) throw new Error("not installed");
|
|
11110
11316
|
if (r.code !== 0) throw new Error(r.stderr.trim().split("\n").pop() || `exit ${r.code}`);
|
|
11111
11317
|
if (tool === "cursor") return parseCursorModels(r.stdout);
|
|
11112
|
-
return parseCodexModels(r.stdout, codexConfig(await readText2(
|
|
11318
|
+
return parseCodexModels(r.stdout, codexConfig(await readText2(join22(e.CODEX_HOME ?? join22(home2, ".codex"), "config.toml"))));
|
|
11113
11319
|
}
|
|
11114
11320
|
return {
|
|
11115
11321
|
list(tool) {
|
|
@@ -11160,9 +11366,9 @@ var init_models2 = __esm({
|
|
|
11160
11366
|
|
|
11161
11367
|
// src/daemon/commands.ts
|
|
11162
11368
|
import { spawn as nodeSpawn7 } from "child_process";
|
|
11163
|
-
import { stat as
|
|
11164
|
-
import { homedir as
|
|
11165
|
-
import { join as
|
|
11369
|
+
import { stat as stat6 } from "fs/promises";
|
|
11370
|
+
import { homedir as homedir11 } from "os";
|
|
11371
|
+
import { join as join23 } from "path";
|
|
11166
11372
|
function claudeSlashRefusal(text7) {
|
|
11167
11373
|
const m = /^\/(\S+)(\s+\S[\s\S]*)?$/.exec(text7.trim());
|
|
11168
11374
|
if (!m) return null;
|
|
@@ -11271,13 +11477,13 @@ function probeClaude(o, spawnImpl = nodeSpawn7) {
|
|
|
11271
11477
|
function createCommands(deps = {}) {
|
|
11272
11478
|
const env = deps.env ?? (async () => process.env);
|
|
11273
11479
|
const claude = deps.claude ?? ((cwd, e) => probeClaude({ ...deps.claudeBin ? { bin: deps.claudeBin } : {}, cwd, env: e }));
|
|
11274
|
-
const mtime = deps.mtime ?? ((p) =>
|
|
11480
|
+
const mtime = deps.mtime ?? ((p) => stat6(p).then((s) => s.mtimeMs, () => null));
|
|
11275
11481
|
const now2 = deps.now ?? Date.now, ttl = deps.ttlMs ?? COMMANDS_TTL_MS;
|
|
11276
11482
|
const cache = /* @__PURE__ */ new Map();
|
|
11277
11483
|
const flying = /* @__PURE__ */ new Map();
|
|
11278
11484
|
async function mark(cwd, e) {
|
|
11279
|
-
const home2 = e.CLAUDE_CONFIG_DIR ??
|
|
11280
|
-
const paths2 = [
|
|
11485
|
+
const home2 = e.CLAUDE_CONFIG_DIR ?? join23(e.HOME ?? homedir11(), ".claude");
|
|
11486
|
+
const paths2 = [join23(cwd, ".claude", "commands"), join23(cwd, ".claude", "skills"), join23(home2, "commands"), join23(home2, "skills"), join23(home2, "plugins", "installed_plugins.json")];
|
|
11281
11487
|
return (await Promise.all(paths2.map(mtime))).join(",");
|
|
11282
11488
|
}
|
|
11283
11489
|
return {
|
|
@@ -11455,7 +11661,7 @@ var init_rigSpec = __esm({
|
|
|
11455
11661
|
});
|
|
11456
11662
|
|
|
11457
11663
|
// src/daemon/openrigRoles.ts
|
|
11458
|
-
import { basename as basename10, dirname as dirname8, isAbsolute as isAbsolute5, join as
|
|
11664
|
+
import { basename as basename10, dirname as dirname8, isAbsolute as isAbsolute5, join as join24, resolve as resolve9, sep as sep3 } from "path";
|
|
11459
11665
|
import { parse as parseYaml, stringify as stringify2, YAMLParseError } from "yaml";
|
|
11460
11666
|
function readLibrary(listed2) {
|
|
11461
11667
|
const out = [];
|
|
@@ -11487,15 +11693,15 @@ function parseSpec(text7) {
|
|
|
11487
11693
|
}
|
|
11488
11694
|
}
|
|
11489
11695
|
function specPath(p, specDir, home2, openrigHome) {
|
|
11490
|
-
if (p.startsWith("openrig-home:")) return
|
|
11696
|
+
if (p.startsWith("openrig-home:")) return join24(openrigHome, p.slice("openrig-home:".length));
|
|
11491
11697
|
if (p === "~") return home2;
|
|
11492
|
-
if (p.startsWith("~/")) return
|
|
11698
|
+
if (p.startsWith("~/")) return join24(home2, p.slice(2));
|
|
11493
11699
|
return isAbsolute5(p) ? resolve9(p) : resolve9(specDir, p);
|
|
11494
11700
|
}
|
|
11495
11701
|
function importPath(ref, specDir) {
|
|
11496
11702
|
const p = ref.startsWith("local:") ? resolve9(specDir, ref.slice(6)) : ref.startsWith("path:") ? resolve9(ref.slice(5)) : null;
|
|
11497
11703
|
if (!p) return null;
|
|
11498
|
-
return /\.ya?ml$/i.test(p) ? p :
|
|
11704
|
+
return /\.ya?ml$/i.test(p) ? p : join24(p, "agent.yaml");
|
|
11499
11705
|
}
|
|
11500
11706
|
function declared(spec, ctx, ns, origin) {
|
|
11501
11707
|
const out = [];
|
|
@@ -11724,7 +11930,7 @@ function layerOf(ownerRoot, path3, c) {
|
|
|
11724
11930
|
if (c.agentDir && root2 === c.agentDir) return c.profileFiles.has(path3) ? "profile" : "agent";
|
|
11725
11931
|
if (c.rigRoot && root2 === c.rigRoot) return path3 === c.cultureFile ? "culture" : c.memberFiles.has(path3) ? "member" : c.podFiles.has(path3) ? "pod" : "rig";
|
|
11726
11932
|
if (/[/\\]@openrig[/\\]cli[/\\]/.test(root2) || /[/\\]daemon[/\\]assets$/.test(root2)) return "openrig";
|
|
11727
|
-
if (under3(root2,
|
|
11933
|
+
if (under3(root2, join24(c.openrigHome, "state", "seats"))) return "seat";
|
|
11728
11934
|
return "unknown";
|
|
11729
11935
|
}
|
|
11730
11936
|
function layerOrigin(layer, c, path3) {
|
|
@@ -11734,7 +11940,7 @@ function layerOrigin(layer, c, path3) {
|
|
|
11734
11940
|
case "profile":
|
|
11735
11941
|
return { kind: "here", key: "profiles.startup.files" };
|
|
11736
11942
|
case "culture":
|
|
11737
|
-
return { kind: "inherited", from: "rig", ref: c.rig, key: "culture_file", ...c.rigRoot ? { path:
|
|
11943
|
+
return { kind: "inherited", from: "rig", ref: c.rig, key: "culture_file", ...c.rigRoot ? { path: join24(c.rigRoot, path3) } : {} };
|
|
11738
11944
|
case "rig":
|
|
11739
11945
|
return { kind: "inherited", from: "rig", ref: c.rig };
|
|
11740
11946
|
case "pod":
|
|
@@ -11762,12 +11968,12 @@ async function seatView(s, spec, profile, io, openrigHome, plugins) {
|
|
|
11762
11968
|
const cwdV = str10(node2.cwd) ?? str10(shown.cwd);
|
|
11763
11969
|
const cwd = cwdV ? { value: cwdV, origin: memberOrigin("cwd") } : { value: null, origin: { kind: "unknown", why: "not_exposed" } };
|
|
11764
11970
|
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:
|
|
11971
|
+
const cultureFile = sourced(cultureV, { kind: "inherited", from: "rig", ref: s.rig, key: "culture_file", ...cultureV && s.rigRoot ? { path: join24(s.rigRoot, cultureV) } : {} });
|
|
11766
11972
|
const base3 = s.rigRoot ?? (s.rigSpecPath ? dirname8(s.rigSpecPath) : "/");
|
|
11767
11973
|
const memberStartup = writtenStartup(m.startup, "startup", "member", base3, (key) => memberOrigin(key));
|
|
11768
11974
|
const pod = s.written?.pod ?? {};
|
|
11769
11975
|
const effectiveRuntime = str10(node2.runtime) ?? runtime.value ?? void 0;
|
|
11770
|
-
const into = cwdV ?
|
|
11976
|
+
const into = cwdV ? join24(cwdV, guidanceFile(effectiveRuntime, s.rigDoc)) : void 0;
|
|
11771
11977
|
const profileName = str10(m.profile) ?? str10(shown.profile) ?? "default";
|
|
11772
11978
|
const lc = {
|
|
11773
11979
|
agentDir: s.agentDir,
|
|
@@ -12003,10 +12209,14 @@ var init_openrigRoles2 = __esm({
|
|
|
12003
12209
|
import { execFile as execFile6 } from "child_process";
|
|
12004
12210
|
import { get as httpGet } from "http";
|
|
12005
12211
|
import { existsSync as existsSync2, readdirSync as readdirSync2 } from "fs";
|
|
12006
|
-
import { cp, mkdir as mkdir11, open as openFile, readFile as
|
|
12212
|
+
import { cp, mkdir as mkdir11, open as openFile, readFile as readFile12, stat as statFile, writeFile as writeFile13 } from "fs/promises";
|
|
12007
12213
|
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
|
|
12214
|
+
import { homedir as homedir12 } from "os";
|
|
12215
|
+
import { basename as basename11, delimiter as delimiter2, dirname as dirname9, isAbsolute as isAbsolute6, join as join25, resolve as resolve10, sep as sep4 } from "path";
|
|
12216
|
+
function specSource(raw) {
|
|
12217
|
+
const s = str11(raw);
|
|
12218
|
+
return s === "builtin" || s?.startsWith("builtin_") ? "builtin" : "user";
|
|
12219
|
+
}
|
|
12010
12220
|
function toSeat(raw) {
|
|
12011
12221
|
const n = obj9(raw);
|
|
12012
12222
|
const logicalId = str11(n.logicalId), session = str11(n.canonicalSessionName);
|
|
@@ -12079,17 +12289,17 @@ function findRig(env, exists = existsSync2, list3 = (dir) => {
|
|
|
12079
12289
|
return [];
|
|
12080
12290
|
}
|
|
12081
12291
|
}) {
|
|
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) =>
|
|
12292
|
+
for (const dir of (env.PATH ?? "").split(delimiter2)) if (dir && exists(join25(dir, "rig"))) return dir;
|
|
12293
|
+
const home2 = env.HOME ?? homedir12();
|
|
12294
|
+
const managed = (root2, sub) => list3(root2).filter((v2) => /^v?\d/.test(v2)).sort(byVersionDesc).map((v2) => join25(root2, v2, sub));
|
|
12085
12295
|
const candidates = [
|
|
12086
|
-
...managed(
|
|
12087
|
-
...managed(
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12296
|
+
...managed(join25(home2, ".local", "share", "nvm"), "bin"),
|
|
12297
|
+
...managed(join25(env.NVM_DIR ?? join25(home2, ".nvm"), "versions", "node"), "bin"),
|
|
12298
|
+
join25(home2, ".volta", "bin"),
|
|
12299
|
+
join25(home2, ".npm-global", "bin"),
|
|
12300
|
+
join25(home2, ".local", "share", "pnpm")
|
|
12091
12301
|
];
|
|
12092
|
-
return candidates.find((dir) => exists(
|
|
12302
|
+
return candidates.find((dir) => exists(join25(dir, "rig"))) ?? null;
|
|
12093
12303
|
}
|
|
12094
12304
|
function readCheck(output) {
|
|
12095
12305
|
const j = obj9(parse2(output));
|
|
@@ -12118,11 +12328,11 @@ function localGet(port, path3, timeoutMs) {
|
|
|
12118
12328
|
function createOpenRig(deps = {}) {
|
|
12119
12329
|
const run4 = deps.run ?? execRun3(deps.env ?? (async () => process.env));
|
|
12120
12330
|
const files = deps.fs ?? realFs;
|
|
12121
|
-
const home2 = deps.home ??
|
|
12122
|
-
const rigsDir = deps.rigsDir ??
|
|
12331
|
+
const home2 = deps.home ?? homedir12();
|
|
12332
|
+
const rigsDir = deps.rigsDir ?? join25(home2, ".ledge", "rigs");
|
|
12123
12333
|
const now2 = deps.now ?? Date.now;
|
|
12124
12334
|
const roleIo = deps.roleIo ?? realRoleIo;
|
|
12125
|
-
const openrigHome = deps.openrigHome ?? process.env.OPENRIG_HOME ??
|
|
12335
|
+
const openrigHome = deps.openrigHome ?? process.env.OPENRIG_HOME ?? join25(home2, ".openrig");
|
|
12126
12336
|
const http = deps.http ?? (async (path3) => {
|
|
12127
12337
|
const port = (await state2()).daemon.port;
|
|
12128
12338
|
if (!port) return void 0;
|
|
@@ -12206,7 +12416,7 @@ function createOpenRig(deps = {}) {
|
|
|
12206
12416
|
const e = obj9(raw), name = str11(e.name), path3 = str11(e.resolvedSourcePath) ?? str11(e.sourcePath);
|
|
12207
12417
|
if (e.kind !== "rig" || !name || !path3 || name === KERNEL_RIG) continue;
|
|
12208
12418
|
const text7 = await files.read(path3).catch(() => null);
|
|
12209
|
-
const spec = text7 === null ? null : readSpec(text7, { name, source: e.sourceType
|
|
12419
|
+
const spec = text7 === null ? null : readSpec(text7, { name, source: specSource(e.sourceType), path: path3, ...str11(e.summary) ? { summary: str11(e.summary).trim() } : {} });
|
|
12210
12420
|
if (spec) out.push(spec);
|
|
12211
12421
|
}
|
|
12212
12422
|
return { specs: out.sort((a, b3) => a.source === b3.source ? a.name.localeCompare(b3.name) : a.source === "user" ? -1 : 1) };
|
|
@@ -12216,7 +12426,7 @@ function createOpenRig(deps = {}) {
|
|
|
12216
12426
|
if (acting) return { ok: false, plan: plan2, commands: [], error: "busy" };
|
|
12217
12427
|
acting = true;
|
|
12218
12428
|
try {
|
|
12219
|
-
const cwd = l.cwd === "~" ? home2 : l.cwd.startsWith("~/") ?
|
|
12429
|
+
const cwd = l.cwd === "~" ? home2 : l.cwd.startsWith("~/") ? join25(home2, l.cwd.slice(2)) : l.cwd;
|
|
12220
12430
|
if (!isAbsolute6(cwd) || !files.exists(cwd)) return { ok: false, plan: plan2, commands: [], error: "cwd_missing" };
|
|
12221
12431
|
const listed2 = await specs();
|
|
12222
12432
|
if (listed2.error) return { ok: false, plan: plan2, commands: [], error: listed2.error };
|
|
@@ -12228,17 +12438,17 @@ function createOpenRig(deps = {}) {
|
|
|
12228
12438
|
const rename7 = l.name !== spec.name;
|
|
12229
12439
|
if (changesSpec(l) || l.save || rename7 && !can2.name) {
|
|
12230
12440
|
const composed = composeSpec(parseYaml2(text7), dirname9(spec.path), l);
|
|
12231
|
-
dir =
|
|
12441
|
+
dir = join25(rigsDir, l.name);
|
|
12232
12442
|
await files.copyDir(dirname9(spec.path), dir);
|
|
12233
|
-
await files.write(
|
|
12234
|
-
source2 =
|
|
12443
|
+
await files.write(join25(dir, "rig.yaml"), composed.yaml);
|
|
12444
|
+
source2 = join25(dir, "rig.yaml");
|
|
12235
12445
|
yaml = composed.yaml;
|
|
12236
12446
|
seats = composed.seats;
|
|
12237
12447
|
} else {
|
|
12238
12448
|
yaml = text7;
|
|
12239
12449
|
seats = specSeats(spec);
|
|
12240
12450
|
}
|
|
12241
|
-
if (!dir && rename7 && !plan2) await files.write(
|
|
12451
|
+
if (!dir && rename7 && !plan2) await files.write(join25(rigsDir, l.name, "source.json"), JSON.stringify({ spec: spec.name, path: spec.path }));
|
|
12242
12452
|
const commands = [["up", source2, ...!dir && rename7 ? ["--name", l.name] : [], "--cwd", cwd, ...l.nonInterruptive ? ["--non-interruptive"] : [], ...plan2 ? ["--plan"] : ["--yes"], "--json"]];
|
|
12243
12453
|
if (!plan2 && l.save && dir) commands.push(["specs", "add", dir, "--json"]);
|
|
12244
12454
|
const shown = commands.map(showRigCommand);
|
|
@@ -12262,9 +12472,9 @@ function createOpenRig(deps = {}) {
|
|
|
12262
12472
|
}
|
|
12263
12473
|
}
|
|
12264
12474
|
async function rootOf(rig) {
|
|
12265
|
-
const mine =
|
|
12266
|
-
if (files.exists(
|
|
12267
|
-
const marker = await files.read(
|
|
12475
|
+
const mine = join25(rigsDir, rig);
|
|
12476
|
+
if (files.exists(join25(mine, "rig.yaml"))) return mine;
|
|
12477
|
+
const marker = await files.read(join25(mine, "source.json")).catch(() => null);
|
|
12268
12478
|
const path3 = marker === null ? void 0 : str11(obj9(parse2(marker)).path);
|
|
12269
12479
|
if (path3) return dirname9(path3);
|
|
12270
12480
|
const listed2 = await specs();
|
|
@@ -12294,7 +12504,7 @@ function createOpenRig(deps = {}) {
|
|
|
12294
12504
|
const member = memberOf(show, a.pod, a.from.slice(a.pod.length + 1));
|
|
12295
12505
|
const fragment2 = member ? fragmentOf(member, a.id, root2) : null;
|
|
12296
12506
|
if (!fragment2) return { ok: false, commands: [], ...none, error: "rig_root_unknown" };
|
|
12297
|
-
const path3 =
|
|
12507
|
+
const path3 = join25(rigsDir, rigName, "seats", `${a.pod}.${a.id}.json`);
|
|
12298
12508
|
await files.write(path3, JSON.stringify(fragment2, null, 2));
|
|
12299
12509
|
commands.push(["add", rig.id, a.pod, path3, ...root2 ? ["--rig-root", root2] : [], "--json"]);
|
|
12300
12510
|
}
|
|
@@ -12344,7 +12554,7 @@ function createOpenRig(deps = {}) {
|
|
|
12344
12554
|
const r = await run4(["spec", "show", rig.id, "--json"], STATUS_MS);
|
|
12345
12555
|
if (r.code !== 0) continue;
|
|
12346
12556
|
const root2 = rig.name === KERNEL_RIG ? null : await rootOf(rig.name).catch(() => null);
|
|
12347
|
-
const specFile = root2 ?
|
|
12557
|
+
const specFile = root2 ? join25(root2, "rig.yaml") : null;
|
|
12348
12558
|
const text7 = specFile ? await roleIo.read(specFile) : null;
|
|
12349
12559
|
const written = parseSpec(text7);
|
|
12350
12560
|
const shown = obj9(parse2(r.stdout));
|
|
@@ -12508,14 +12718,14 @@ var init_openrig2 = __esm({
|
|
|
12508
12718
|
if (!dir) return { code: null, stdout: "", stderr: "rig: command not found" };
|
|
12509
12719
|
const e = { ...base3, PATH: [dir, base3.PATH].filter(Boolean).join(delimiter2), NO_COLOR: "1", CLICOLOR: "0", FORCE_COLOR: "0" };
|
|
12510
12720
|
return new Promise((res) => {
|
|
12511
|
-
execFile6(
|
|
12721
|
+
execFile6(join25(dir, "rig"), args, { env: e, timeout: timeoutMs, maxBuffer: 8 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
12512
12722
|
const code3 = !err ? 0 : err.code === "ENOENT" ? null : typeof err.code === "number" ? err.code : 1;
|
|
12513
12723
|
res({ code: code3, stdout: String(stdout), stderr: String(stderr) || (err && code3 !== 0 ? err.message : "") });
|
|
12514
12724
|
});
|
|
12515
12725
|
});
|
|
12516
12726
|
};
|
|
12517
12727
|
realRoleIo = {
|
|
12518
|
-
read: (p) =>
|
|
12728
|
+
read: (p) => readFile12(p, "utf8").catch(() => null),
|
|
12519
12729
|
stat: (p) => statFile(p).then((st) => ({ size: st.size, mtimeMs: st.mtimeMs, file: st.isFile() })).catch(() => null),
|
|
12520
12730
|
head: async (p, max) => {
|
|
12521
12731
|
const h = await openFile(p, "r").catch(() => null);
|
|
@@ -12533,7 +12743,7 @@ var init_openrig2 = __esm({
|
|
|
12533
12743
|
FILES_TTL_MS = 5 * 6e4;
|
|
12534
12744
|
HTTP_MS = 3e3;
|
|
12535
12745
|
realFs = {
|
|
12536
|
-
read: (p) =>
|
|
12746
|
+
read: (p) => readFile12(p, "utf8"),
|
|
12537
12747
|
exists: (p) => existsSync2(p),
|
|
12538
12748
|
copyDir: (from, to) => cp(from, to, { recursive: true, force: true, filter: (src) => basename11(src) !== "rig.yaml" }),
|
|
12539
12749
|
write: async (p, text7) => {
|
|
@@ -12553,7 +12763,7 @@ function goalOf(tags) {
|
|
|
12553
12763
|
return t ? t.slice(GOAL_TAG.length + 1) || null : null;
|
|
12554
12764
|
}
|
|
12555
12765
|
function parseTeam(body) {
|
|
12556
|
-
const m =
|
|
12766
|
+
const m = BLOCK4.exec(body);
|
|
12557
12767
|
if (!m) return null;
|
|
12558
12768
|
let raw;
|
|
12559
12769
|
try {
|
|
@@ -12578,13 +12788,13 @@ function parseTeam(body) {
|
|
|
12578
12788
|
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
12789
|
};
|
|
12580
12790
|
}
|
|
12581
|
-
var GOAL_TAG, goalTag,
|
|
12791
|
+
var GOAL_TAG, goalTag, BLOCK4, obj10, text2, strings, roleOf3, withoutTeam;
|
|
12582
12792
|
var init_goalTeam = __esm({
|
|
12583
12793
|
"src/daemon/goalTeam.ts"() {
|
|
12584
12794
|
"use strict";
|
|
12585
12795
|
GOAL_TAG = "ledge-goal";
|
|
12586
12796
|
goalTag = (goalQitemId2) => `${GOAL_TAG}:${goalQitemId2}`;
|
|
12587
|
-
|
|
12797
|
+
BLOCK4 = /```ledge-team[^\n]*\n([\s\S]*?)```/;
|
|
12588
12798
|
obj10 = (v2) => v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : {};
|
|
12589
12799
|
text2 = (v2) => typeof v2 === "string" && v2.trim() ? v2.trim() : null;
|
|
12590
12800
|
strings = (v2) => Array.isArray(v2) ? v2.filter((x) => typeof x === "string" && x.trim() !== "").map((x) => x.trim()) : [];
|
|
@@ -12592,7 +12802,7 @@ var init_goalTeam = __esm({
|
|
|
12592
12802
|
const m = /^([A-Za-z0-9][A-Za-z0-9_]*)-([A-Za-z0-9][A-Za-z0-9_-]*)@/.exec(seat2);
|
|
12593
12803
|
return m ? `${m[1]}.${m[2]}` : null;
|
|
12594
12804
|
};
|
|
12595
|
-
withoutTeam = (body) => body.replace(
|
|
12805
|
+
withoutTeam = (body) => body.replace(BLOCK4, "").trim();
|
|
12596
12806
|
}
|
|
12597
12807
|
});
|
|
12598
12808
|
|
|
@@ -13217,7 +13427,7 @@ function createRelay(d) {
|
|
|
13217
13427
|
const now2 = d.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
13218
13428
|
const typing = d.typing ?? /* @__PURE__ */ new Set();
|
|
13219
13429
|
const sending = /* @__PURE__ */ new Set();
|
|
13220
|
-
const
|
|
13430
|
+
const open6 = (id) => free(d.store.get(id)) && !typing.has(id) && !d.closing?.has(id);
|
|
13221
13431
|
const emitTo = (thread) => (m) => {
|
|
13222
13432
|
try {
|
|
13223
13433
|
d.threads.emitterOf(thread)?.(m);
|
|
@@ -13261,14 +13471,14 @@ function createRelay(d) {
|
|
|
13261
13471
|
}
|
|
13262
13472
|
function kick(id) {
|
|
13263
13473
|
const q = queued().find((x) => x.relay.id === id);
|
|
13264
|
-
if (!q || !
|
|
13474
|
+
if (!q || !open6(id)) return;
|
|
13265
13475
|
void deliverAndKick(q.thread, q.qid, q.relay).catch((e) => log(`relay ${q.qid} failed: ${e.message}`));
|
|
13266
13476
|
}
|
|
13267
13477
|
async function deliverAndKick(thread, qid, r) {
|
|
13268
13478
|
const refused = await deliver(thread, qid, r);
|
|
13269
13479
|
if (refused) {
|
|
13270
13480
|
if (queued().some((q) => q.relay.id === r.id)) kick(r.id);
|
|
13271
|
-
else if (
|
|
13481
|
+
else if (open6(r.id)) d.next?.(r.id);
|
|
13272
13482
|
}
|
|
13273
13483
|
return refused;
|
|
13274
13484
|
}
|
|
@@ -13285,7 +13495,7 @@ function createRelay(d) {
|
|
|
13285
13495
|
}
|
|
13286
13496
|
return;
|
|
13287
13497
|
}
|
|
13288
|
-
if (
|
|
13498
|
+
if (open6(id)) kick(id);
|
|
13289
13499
|
} catch (e) {
|
|
13290
13500
|
log(`relay change handler failed: ${e.message}`);
|
|
13291
13501
|
}
|
|
@@ -13303,7 +13513,7 @@ function createRelay(d) {
|
|
|
13303
13513
|
mark(thread, qid, { error: m, code: STOPPED_CODE });
|
|
13304
13514
|
throw new Error(m);
|
|
13305
13515
|
}
|
|
13306
|
-
if (
|
|
13516
|
+
if (open6(r.id)) {
|
|
13307
13517
|
if (queued().some((q) => q.relay.id === r.id)) {
|
|
13308
13518
|
markQueued(thread, qid);
|
|
13309
13519
|
kick(r.id);
|
|
@@ -13324,7 +13534,7 @@ function createRelay(d) {
|
|
|
13324
13534
|
settle(id) {
|
|
13325
13535
|
try {
|
|
13326
13536
|
if (queued().some((q) => q.relay.id === id)) kick(id);
|
|
13327
|
-
else if (
|
|
13537
|
+
else if (open6(id)) d.next?.(id);
|
|
13328
13538
|
} catch (e) {
|
|
13329
13539
|
log(`relay settle failed: ${e.message}`);
|
|
13330
13540
|
}
|
|
@@ -13553,7 +13763,7 @@ var init_act = __esm({
|
|
|
13553
13763
|
|
|
13554
13764
|
// src/daemon/handover.ts
|
|
13555
13765
|
import { mkdirSync, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
|
|
13556
|
-
import { join as
|
|
13766
|
+
import { join as join26 } from "path";
|
|
13557
13767
|
function reliefOf(get, s) {
|
|
13558
13768
|
let cur = s;
|
|
13559
13769
|
for (let hops = 0; cur && hops < 50; hops++) {
|
|
@@ -13571,7 +13781,7 @@ function createHandovers(d) {
|
|
|
13571
13781
|
const now2 = d.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
13572
13782
|
const briefMs = d.briefMs ?? BRIEF_MS;
|
|
13573
13783
|
const waiting2 = /* @__PURE__ */ new Map();
|
|
13574
|
-
const file = (id) =>
|
|
13784
|
+
const file = (id) => join26(d.dir, `${id}.md`);
|
|
13575
13785
|
const mark = (id, h) => {
|
|
13576
13786
|
const cur = d.store.get(id);
|
|
13577
13787
|
if (cur) d.store.upsert({ ...cur, handover: { ...h, at: now2() } });
|
|
@@ -14025,7 +14235,7 @@ var init_broadcast2 = __esm({
|
|
|
14025
14235
|
});
|
|
14026
14236
|
|
|
14027
14237
|
// src/daemon/context.ts
|
|
14028
|
-
import { open as
|
|
14238
|
+
import { open as open5 } from "fs/promises";
|
|
14029
14239
|
function longWindow(model) {
|
|
14030
14240
|
if (!model) return false;
|
|
14031
14241
|
if (model.includes("[1m]")) return true;
|
|
@@ -14033,7 +14243,7 @@ function longWindow(model) {
|
|
|
14033
14243
|
return !!m && Number(m[1]) >= 5;
|
|
14034
14244
|
}
|
|
14035
14245
|
async function tail(path3) {
|
|
14036
|
-
const f = await
|
|
14246
|
+
const f = await open5(path3, "r");
|
|
14037
14247
|
try {
|
|
14038
14248
|
const { size } = await f.stat();
|
|
14039
14249
|
const start = Math.max(0, size - TAIL_BYTES2);
|
|
@@ -14117,7 +14327,7 @@ var init_context2 = __esm({
|
|
|
14117
14327
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
14118
14328
|
import { mkdtemp, rm as rm5 } from "fs/promises";
|
|
14119
14329
|
import { tmpdir } from "os";
|
|
14120
|
-
import { join as
|
|
14330
|
+
import { join as join27 } from "path";
|
|
14121
14331
|
function parseRoute(answer, context) {
|
|
14122
14332
|
const raw = answer.trim().replace(/^```(?:json)?\s*/, "").replace(/\s*```$/, "");
|
|
14123
14333
|
const result = RouteResultSchema.parse(JSON.parse(raw));
|
|
@@ -14143,7 +14353,7 @@ function createRouter(deps) {
|
|
|
14143
14353
|
let dir;
|
|
14144
14354
|
let timer2;
|
|
14145
14355
|
try {
|
|
14146
|
-
dir = await mkdtemp(
|
|
14356
|
+
dir = await mkdtemp(join27(tmpdir(), "ledge-routing-"));
|
|
14147
14357
|
const env = { ...await deps.env(), LEDGE_CHAT: "1" };
|
|
14148
14358
|
delete env.ANTHROPIC_API_KEY;
|
|
14149
14359
|
delete env.LEDGE_SESSION;
|
|
@@ -15173,15 +15383,15 @@ var init_operations = __esm({
|
|
|
15173
15383
|
});
|
|
15174
15384
|
|
|
15175
15385
|
// src/mobile/risk.ts
|
|
15176
|
-
import { homedir as
|
|
15177
|
-
import { resolve as resolve11, sep as sep5, join as
|
|
15386
|
+
import { homedir as homedir13 } from "os";
|
|
15387
|
+
import { resolve as resolve11, sep as sep5, join as join28 } from "path";
|
|
15178
15388
|
function isSensitive(cwd, p) {
|
|
15179
15389
|
if (p.includes("~")) return true;
|
|
15180
15390
|
const full = resolve11(cwd, p);
|
|
15181
15391
|
const segs = full.split(sep5);
|
|
15182
15392
|
if (segs.includes(".git") || segs.some((s) => s.startsWith(".env"))) return true;
|
|
15183
|
-
const home2 =
|
|
15184
|
-
return [
|
|
15393
|
+
const home2 = homedir13();
|
|
15394
|
+
return [join28(home2, ".ssh"), join28(home2, ".ledge")].some((d) => full === d || full.startsWith(d + sep5));
|
|
15185
15395
|
}
|
|
15186
15396
|
function classifyShell(command, cwd) {
|
|
15187
15397
|
if (PUSH.test(command)) return high("pushes");
|
|
@@ -16951,9 +17161,13 @@ var init_notifier = __esm({
|
|
|
16951
17161
|
// src/daemon/server.ts
|
|
16952
17162
|
import { createServer } from "http";
|
|
16953
17163
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
16954
|
-
import { homedir as
|
|
16955
|
-
import { basename as basename12, join as
|
|
17164
|
+
import { homedir as homedir14 } from "os";
|
|
17165
|
+
import { basename as basename12, join as join29, resolve as resolve12 } from "path";
|
|
16956
17166
|
import { WebSocketServer, WebSocket as WebSocket3 } from "ws";
|
|
17167
|
+
function withoutHarness(s) {
|
|
17168
|
+
const { pending: _p, lastReply: _r, transcriptPath: _t, agentSessionId: _a, agentSessionFile: _f, model: _m, effort: _e, modelNext: _n, queue: _q, backend: _b, ...rest } = s;
|
|
17169
|
+
return { ...rest, helpers: [] };
|
|
17170
|
+
}
|
|
16957
17171
|
function resumeKey(s) {
|
|
16958
17172
|
if (s.tool === "omp") return s.agentSessionFile;
|
|
16959
17173
|
if (s.agentSessionId) return s.agentSessionId;
|
|
@@ -17003,11 +17217,12 @@ async function startServer(opts) {
|
|
|
17003
17217
|
const agentEnv2 = opts.agentEnv ?? (async () => ({ ...process.env, ...await loginShellEnv() }));
|
|
17004
17218
|
const attachments = opts.attachments ?? new AttachmentStore(paths().attachments);
|
|
17005
17219
|
const reducer = new CaptureReducer();
|
|
17006
|
-
const threads = createChatThreads({ attachments, store, tmux, home: paths().dir, notesDir:
|
|
17220
|
+
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
17221
|
const chat = opts.chat ?? threads.desktop;
|
|
17008
17222
|
const router = createRouter({ env: agentEnv2 });
|
|
17009
|
-
const omp2 = opts.omp ?? createOmpHost({ store, env: agentEnv2, dir:
|
|
17223
|
+
const omp2 = opts.omp ?? createOmpHost({ store, env: agentEnv2, dir: join29(paths().dir, "omp"), now: now2, attachments });
|
|
17010
17224
|
const openrig = opts.openrig ?? createOpenRig({ env: agentEnv2 });
|
|
17225
|
+
const ompSeatReadOnly = (s) => new Refusal(`${s.name} is the OpenRig OMP seat ${s.rig?.seat ?? ""}: Ledge follows its state only`, "read_only");
|
|
17011
17226
|
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
17227
|
const adoptSeat = (id, seat2) => {
|
|
17013
17228
|
const session = store.get(id);
|
|
@@ -17020,9 +17235,22 @@ async function startServer(opts) {
|
|
|
17020
17235
|
const seatId = (seat2) => `rig-${seat2.replace(/[^A-Za-z0-9._-]/g, "-")}`;
|
|
17021
17236
|
async function discoverSeats(st) {
|
|
17022
17237
|
if (st.daemon.state !== "running") return;
|
|
17023
|
-
const followed = new
|
|
17238
|
+
const followed = new Map(store.all().filter((x) => x.rig && x.state !== "exited").map((x) => [x.rig.seat, x.id]));
|
|
17024
17239
|
for (const r of st.rigs) for (const n of r.nodes) {
|
|
17025
|
-
|
|
17240
|
+
const tool = SEAT_TOOLS[n.runtime];
|
|
17241
|
+
if (!tool || n.sessionStatus !== "running") continue;
|
|
17242
|
+
const live3 = followed.get(n.session);
|
|
17243
|
+
if (live3 !== void 0) {
|
|
17244
|
+
const s = store.get(live3);
|
|
17245
|
+
if (s && s.tool !== tool && (s.source === "rig" || s.source === "hook_only")) {
|
|
17246
|
+
if (s.source === "hook_only" && !await tmux.hasSession(n.session).catch(() => false)) continue;
|
|
17247
|
+
const cur = store.get(live3);
|
|
17248
|
+
if (!cur || cur.state === "exited" || cur.tool === tool) continue;
|
|
17249
|
+
store.upsert({ ...withoutHarness(cur), tool, state: "idle", source: "rig", tmuxSession: n.session, lastActivityAt: now2() });
|
|
17250
|
+
log(`openrig: seat ${n.session} now runs ${n.runtime}`);
|
|
17251
|
+
}
|
|
17252
|
+
continue;
|
|
17253
|
+
}
|
|
17026
17254
|
if (!await tmux.hasSession(n.session).catch(() => false)) continue;
|
|
17027
17255
|
const before2 = store.all().filter((x) => x.rig?.seat === n.session).sort((a, b3) => b3.lastActivityAt.localeCompare(a.lastActivityAt))[0];
|
|
17028
17256
|
const id = before2?.id ?? seatId(n.session), old = store.get(id), at = now2();
|
|
@@ -17031,13 +17259,14 @@ async function startServer(opts) {
|
|
|
17031
17259
|
if (old) {
|
|
17032
17260
|
if (old.archivedAt) store.unarchive(id, at);
|
|
17033
17261
|
const { exitedAt: _e, exitCode: _c, pending: _p, ...rest } = store.get(id);
|
|
17034
|
-
|
|
17262
|
+
const same2 = rest.tool === tool ? rest : { ...withoutHarness(rest), tool };
|
|
17263
|
+
store.upsert({ ...same2, state: "idle", tmuxSession: n.session, source: "rig", rig, lastActivityAt: at });
|
|
17035
17264
|
} else {
|
|
17036
17265
|
const name = pickName([...namesInUse(store.all()), ...heldNames(store.all())]);
|
|
17037
17266
|
store.upsert({
|
|
17038
17267
|
id,
|
|
17039
17268
|
name,
|
|
17040
|
-
tool
|
|
17269
|
+
tool,
|
|
17041
17270
|
cwd,
|
|
17042
17271
|
project: basename12(cwd) || r.name,
|
|
17043
17272
|
title: `${name} \xB7 ${n.logicalId}`,
|
|
@@ -17051,10 +17280,21 @@ async function startServer(opts) {
|
|
|
17051
17280
|
});
|
|
17052
17281
|
log(`openrig: seat ${n.session} followed as ${name}`);
|
|
17053
17282
|
}
|
|
17054
|
-
followed.
|
|
17283
|
+
followed.set(n.session, id);
|
|
17284
|
+
}
|
|
17285
|
+
if (!st.rigs.some((r) => r.nodes.length > 0)) return;
|
|
17286
|
+
const listed2 = new Set(st.rigs.flatMap((r) => r.nodes.map((n) => n.session)));
|
|
17287
|
+
const unsure = new Set(st.rigs.filter((r) => r.seats > 0 && r.nodes.length === 0).map((r) => r.name));
|
|
17288
|
+
for (const s of store.all()) {
|
|
17289
|
+
if (isOmpSeat2(s) && s.rig && s.state === "exited" && !isArchived(s) && !listed2.has(s.rig.seat) && !unsure.has(s.rig.name)) {
|
|
17290
|
+
store.archive(s.id, now2());
|
|
17291
|
+
log(`openrig: seat ${s.rig.seat} is gone from OpenRig`);
|
|
17292
|
+
}
|
|
17055
17293
|
}
|
|
17056
17294
|
}
|
|
17057
17295
|
const SEATS_EVERY_MS = 3e4;
|
|
17296
|
+
const ompSeats = opts.ompSeats ?? createOmpSeats({ store });
|
|
17297
|
+
ompSeats.start();
|
|
17058
17298
|
const seatTimer = setInterval(() => {
|
|
17059
17299
|
void openrig.state().then(discoverSeats).catch((e) => log(`openrig: seats not read: ${e.message}`));
|
|
17060
17300
|
}, SEATS_EVERY_MS);
|
|
@@ -17100,7 +17340,7 @@ async function startServer(opts) {
|
|
|
17100
17340
|
}).catch((e) => log(`openrig: seat ${seat2} not read: ${e.message}`));
|
|
17101
17341
|
};
|
|
17102
17342
|
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 ??
|
|
17343
|
+
const models = opts.models ?? createModels({ env: agentEnv2, omp: async () => ompChoices(await omp2.catalog(opts.home ?? homedir14())) });
|
|
17104
17344
|
const token = await ensureDaemonToken();
|
|
17105
17345
|
const httpServer = createServer(unauthorized);
|
|
17106
17346
|
const sockets = /* @__PURE__ */ new Set();
|
|
@@ -17140,7 +17380,7 @@ async function startServer(opts) {
|
|
|
17140
17380
|
const islandFile = opts.islandFile ?? paths().island;
|
|
17141
17381
|
let island = readIsland(islandFile);
|
|
17142
17382
|
const islandMsg = () => ({ type: "island", prefs: island });
|
|
17143
|
-
const contextFile = opts.contextFile ??
|
|
17383
|
+
const contextFile = opts.contextFile ?? join29(paths().dir, "context.json");
|
|
17144
17384
|
let contextPrefs = readContextPrefs(contextFile);
|
|
17145
17385
|
const contextMsg = () => ({ type: "context.prefs", prefs: contextPrefs });
|
|
17146
17386
|
const mobile = opts.mobile;
|
|
@@ -17239,7 +17479,7 @@ async function startServer(opts) {
|
|
|
17239
17479
|
tasks,
|
|
17240
17480
|
launch: (req) => launch3(req),
|
|
17241
17481
|
close: (s) => closeSession(s),
|
|
17242
|
-
dir:
|
|
17482
|
+
dir: join29(paths().dir, "handovers"),
|
|
17243
17483
|
now: now2,
|
|
17244
17484
|
...opts.handoverBriefMs !== void 0 ? { briefMs: opts.handoverBriefMs } : {}
|
|
17245
17485
|
});
|
|
@@ -17515,6 +17755,7 @@ async function startServer(opts) {
|
|
|
17515
17755
|
const s = store.get(id);
|
|
17516
17756
|
if (!s) return unknownSession();
|
|
17517
17757
|
if (s.source === "hook_only") return readOnly();
|
|
17758
|
+
if (isOmpSeat2(s)) return ompSeatReadOnly(s);
|
|
17518
17759
|
if (closing.has(id)) return new Refusal("the session is being closed", "busy");
|
|
17519
17760
|
const ids = how.attachments ?? [];
|
|
17520
17761
|
const refs = [];
|
|
@@ -17848,7 +18089,7 @@ async function startServer(opts) {
|
|
|
17848
18089
|
let scanning = null;
|
|
17849
18090
|
function sharedScan() {
|
|
17850
18091
|
if (scanning) return scanning;
|
|
17851
|
-
const home2 = opts.home ??
|
|
18092
|
+
const home2 = opts.home ?? homedir14();
|
|
17852
18093
|
const extra = extraRoots(home2, [...store.recents().map((r) => r.cwd), ...store.all().filter((s) => s.state !== "exited").map((s) => s.cwd)]);
|
|
17853
18094
|
const walk = (opts.scan ?? scanRepos)({ home: home2, extra }).finally(() => {
|
|
17854
18095
|
if (scanning === walk) scanning = null;
|
|
@@ -18127,6 +18368,7 @@ async function startServer(opts) {
|
|
|
18127
18368
|
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
18369
|
}
|
|
18129
18370
|
if (!s?.tmuxSession) return;
|
|
18371
|
+
if (isOmpSeat2(s)) return refuse(ompSeatReadOnly(s), msg.id);
|
|
18130
18372
|
await tmux.sendKeys(s.tmuxSession, adapterFor(s.tool).interruptKeys());
|
|
18131
18373
|
return;
|
|
18132
18374
|
}
|
|
@@ -18357,7 +18599,7 @@ async function startServer(opts) {
|
|
|
18357
18599
|
relay,
|
|
18358
18600
|
launchChat: (thread, qid, rows) => {
|
|
18359
18601
|
const c = thread === "desktop" ? threads.desktop : threads.device(thread.slice("device:".length));
|
|
18360
|
-
const userHome = resolve12(opts.home ??
|
|
18602
|
+
const userHome = resolve12(opts.home ?? homedir14());
|
|
18361
18603
|
return launchFromChat({
|
|
18362
18604
|
chat: c,
|
|
18363
18605
|
emit: threads.emitterOf(thread) ?? (() => {
|
|
@@ -18501,6 +18743,7 @@ async function startServer(opts) {
|
|
|
18501
18743
|
close: () => new Promise((res) => {
|
|
18502
18744
|
clearInterval(sweepTimer);
|
|
18503
18745
|
clearInterval(seatTimer);
|
|
18746
|
+
ompSeats.stop();
|
|
18504
18747
|
human.stop();
|
|
18505
18748
|
if (goalsTimer) clearTimeout(goalsTimer);
|
|
18506
18749
|
void reducer.close();
|
|
@@ -18520,7 +18763,7 @@ async function startServer(opts) {
|
|
|
18520
18763
|
})
|
|
18521
18764
|
};
|
|
18522
18765
|
}
|
|
18523
|
-
var CONTINUE_ARGS, RESUME_ARGS, MULTI_DIR_ARGS, MODEL_ARGS, EFFORT_ARGS, NoteNotTyped, DELETED_TTL_MS, RESPAWN_HOOKS_MS, ALLOWED_ORIGINS;
|
|
18766
|
+
var SEAT_TOOLS, CONTINUE_ARGS, RESUME_ARGS, MULTI_DIR_ARGS, MODEL_ARGS, EFFORT_ARGS, NoteNotTyped, DELETED_TTL_MS, RESPAWN_HOOKS_MS, ALLOWED_ORIGINS;
|
|
18524
18767
|
var init_server = __esm({
|
|
18525
18768
|
"src/daemon/server.ts"() {
|
|
18526
18769
|
"use strict";
|
|
@@ -18540,6 +18783,7 @@ var init_server = __esm({
|
|
|
18540
18783
|
init_spawn();
|
|
18541
18784
|
init_host();
|
|
18542
18785
|
init_catalog();
|
|
18786
|
+
init_seats();
|
|
18543
18787
|
init_repos2();
|
|
18544
18788
|
init_scan2();
|
|
18545
18789
|
init_folders();
|
|
@@ -18578,6 +18822,7 @@ var init_server = __esm({
|
|
|
18578
18822
|
init_challenges();
|
|
18579
18823
|
init_webauthn();
|
|
18580
18824
|
init_frames();
|
|
18825
|
+
SEAT_TOOLS = { "claude-code": "claude", codex: "codex", omp: "omp" };
|
|
18581
18826
|
CONTINUE_ARGS = { claude: ["--continue"], codex: ["resume", "--last"], cursor: ["--continue"] };
|
|
18582
18827
|
RESUME_ARGS = { claude: (id) => ["--resume", id], codex: (id) => ["resume", id], cursor: (id) => ["--resume", id] };
|
|
18583
18828
|
MULTI_DIR_ARGS = { claude: (dirs) => ["--add-dir", ...dirs] };
|
|
@@ -18606,7 +18851,7 @@ var init_server = __esm({
|
|
|
18606
18851
|
// src/tmux.ts
|
|
18607
18852
|
import { execFile as execFile7 } from "child_process";
|
|
18608
18853
|
import { existsSync as existsSync3 } from "fs";
|
|
18609
|
-
import { join as
|
|
18854
|
+
import { join as join30 } from "path";
|
|
18610
18855
|
function runFile(file, args, env, opts) {
|
|
18611
18856
|
return new Promise((resolve13) => {
|
|
18612
18857
|
execFile7(file, args, { maxBuffer: 4 * 1024 * 1024, env, ...opts?.timeoutMs ? { timeout: opts.timeoutMs, killSignal: "SIGTERM" } : {} }, (err, stdout, stderr) => {
|
|
@@ -18616,7 +18861,7 @@ function runFile(file, args, env, opts) {
|
|
|
18616
18861
|
});
|
|
18617
18862
|
}
|
|
18618
18863
|
function systemdAvailable(env = process.env, exists = existsSync3) {
|
|
18619
|
-
return !!env.XDG_RUNTIME_DIR && exists(
|
|
18864
|
+
return !!env.XDG_RUNTIME_DIR && exists(join30(env.XDG_RUNTIME_DIR, "systemd", "private"));
|
|
18620
18865
|
}
|
|
18621
18866
|
function newSessionCommand(tmuxArgs, scoped, unit) {
|
|
18622
18867
|
if (!scoped) return { file: "tmux", args: tmuxArgs };
|
|
@@ -18749,10 +18994,10 @@ __export(setup_exports, {
|
|
|
18749
18994
|
systemdUnit: () => systemdUnit,
|
|
18750
18995
|
writeBin: () => writeBin
|
|
18751
18996
|
});
|
|
18752
|
-
import { readFile as
|
|
18997
|
+
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";
|
|
18753
18998
|
import { execFile as execFile8 } from "child_process";
|
|
18754
|
-
import { homedir as
|
|
18755
|
-
import { join as
|
|
18999
|
+
import { homedir as homedir15, userInfo } from "os";
|
|
19000
|
+
import { join as join31, dirname as dirname10 } from "path";
|
|
18756
19001
|
function mergeHooks(settings, events, command, file) {
|
|
18757
19002
|
if (settings === void 0) settings = {};
|
|
18758
19003
|
if (!settings || typeof settings !== "object" || Array.isArray(settings)) throw new Error(`${file} root is not an object, not modified`);
|
|
@@ -18800,7 +19045,7 @@ function mergeCursorHooks(config, command = "ledge hook cursor") {
|
|
|
18800
19045
|
async function mergeJsonFile(path3, merge, name) {
|
|
18801
19046
|
let raw = null;
|
|
18802
19047
|
try {
|
|
18803
|
-
raw = await
|
|
19048
|
+
raw = await readFile13(path3, "utf8");
|
|
18804
19049
|
} catch (e) {
|
|
18805
19050
|
if (e.code !== "ENOENT") throw e;
|
|
18806
19051
|
}
|
|
@@ -18836,7 +19081,7 @@ WantedBy=default.target
|
|
|
18836
19081
|
async function writeIfChanged(path3, content3) {
|
|
18837
19082
|
let current2 = null;
|
|
18838
19083
|
try {
|
|
18839
|
-
current2 = await
|
|
19084
|
+
current2 = await readFile13(path3, "utf8");
|
|
18840
19085
|
} catch (e) {
|
|
18841
19086
|
if (e.code !== "ENOENT") throw e;
|
|
18842
19087
|
}
|
|
@@ -18850,7 +19095,7 @@ async function writeIfChanged(path3, content3) {
|
|
|
18850
19095
|
async function stopDetachedDaemon(pidFile) {
|
|
18851
19096
|
let pid;
|
|
18852
19097
|
try {
|
|
18853
|
-
pid = Number((await
|
|
19098
|
+
pid = Number((await readFile13(pidFile, "utf8")).trim());
|
|
18854
19099
|
} catch {
|
|
18855
19100
|
return null;
|
|
18856
19101
|
}
|
|
@@ -18880,10 +19125,10 @@ async function stopDetachedDaemon(pidFile) {
|
|
|
18880
19125
|
async function installService(opts = {}) {
|
|
18881
19126
|
const out = [];
|
|
18882
19127
|
const run4 = opts.run ?? execRunner;
|
|
18883
|
-
const unitDir = opts.unitDir ??
|
|
19128
|
+
const unitDir = opts.unitDir ?? join31(homedir15(), ".config", "systemd", "user");
|
|
18884
19129
|
const node2 = opts.node ?? await realpath2(process.execPath);
|
|
18885
19130
|
const script = opts.script ?? await realpath2(process.argv[1]);
|
|
18886
|
-
const changed = await writeIfChanged(
|
|
19131
|
+
const changed = await writeIfChanged(join31(unitDir, SERVICE), systemdUnit({ node: node2, script }));
|
|
18887
19132
|
const reload = await run4("systemctl", ["--user", "daemon-reload"]);
|
|
18888
19133
|
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
19134
|
const active = (await run4("systemctl", ["--user", "is-active", SERVICE])).code === 0;
|
|
@@ -18912,7 +19157,7 @@ async function installService(opts = {}) {
|
|
|
18912
19157
|
}
|
|
18913
19158
|
async function isDir(path3) {
|
|
18914
19159
|
try {
|
|
18915
|
-
return (await
|
|
19160
|
+
return (await stat7(path3)).isDirectory();
|
|
18916
19161
|
} catch {
|
|
18917
19162
|
return false;
|
|
18918
19163
|
}
|
|
@@ -18921,7 +19166,7 @@ async function writeBin(opts) {
|
|
|
18921
19166
|
const st = await lstat(opts.link).catch(() => null);
|
|
18922
19167
|
const was = st?.isSymbolicLink() ? await readlink(opts.link) : null;
|
|
18923
19168
|
if (was !== null && !was.endsWith("/dist/cli/index.js")) return null;
|
|
18924
|
-
const current2 = st && was === null && st.isFile() ? await
|
|
19169
|
+
const current2 = st && was === null && st.isFile() ? await readFile13(opts.link, "utf8").catch(() => "") : "";
|
|
18925
19170
|
if (st && was === null && !current2.startsWith(BIN_MARK)) return null;
|
|
18926
19171
|
const script = `${BIN_MARK}: the hooks run \`ledge\` wherever node is not on the PATH.
|
|
18927
19172
|
exec ${shQuote(opts.node)} ${shQuote(opts.target)} "$@"
|
|
@@ -18937,24 +19182,24 @@ async function runSetup(opts = {}) {
|
|
|
18937
19182
|
const out = [];
|
|
18938
19183
|
await ensureLedgeDir();
|
|
18939
19184
|
out.push("~/.ledge ready (tmux.conf written if missing)");
|
|
18940
|
-
const settingsPath2 = opts.settingsPath ??
|
|
19185
|
+
const settingsPath2 = opts.settingsPath ?? join31(homedir15(), ".claude", "settings.json");
|
|
18941
19186
|
if (await mergeJsonFile(settingsPath2, mergeClaudeHooks, "settings.json")) out.push(`Claude Code hooks added in ${settingsPath2}`);
|
|
18942
19187
|
else out.push("Claude Code hooks already present");
|
|
18943
|
-
const codexDir = opts.codexDir ?? process.env.CODEX_HOME ??
|
|
19188
|
+
const codexDir = opts.codexDir ?? process.env.CODEX_HOME ?? join31(homedir15(), ".codex");
|
|
18944
19189
|
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 ${
|
|
19190
|
+
else if (await mergeJsonFile(join31(codexDir, "hooks.json"), mergeCodexHooks, "hooks.json")) {
|
|
19191
|
+
out.push(`Codex hooks added in ${join31(codexDir, "hooks.json")}; Codex asks to review them on its next start: trust them there or from Ledge`);
|
|
18947
19192
|
} else out.push("Codex hooks already present");
|
|
18948
|
-
const cursorDir = opts.cursorDir ??
|
|
19193
|
+
const cursorDir = opts.cursorDir ?? join31(homedir15(), ".cursor");
|
|
18949
19194
|
if (!await isDir(cursorDir)) out.push(`Cursor Agent not found (${cursorDir} missing), Cursor Agent hooks skipped`);
|
|
18950
|
-
else if (await mergeJsonFile(
|
|
19195
|
+
else if (await mergeJsonFile(join31(cursorDir, "hooks.json"), mergeCursorHooks, "hooks.json")) out.push(`Cursor Agent hooks added in ${join31(cursorDir, "hooks.json")}`);
|
|
18951
19196
|
else out.push("Cursor Agent hooks already present");
|
|
18952
19197
|
if (opts.service ?? true) {
|
|
18953
19198
|
const cli = opts.cli ?? process.argv[1] ?? "";
|
|
18954
19199
|
if (cli.endsWith(".ts")) out.push("systemd service skipped: run setup from the built CLI (pnpm --filter @paradow/ledge build, then ledge setup)");
|
|
18955
19200
|
else {
|
|
18956
19201
|
const script = await realpath2(cli);
|
|
18957
|
-
const line2 = await writeBin({ link: opts.link ??
|
|
19202
|
+
const line2 = await writeBin({ link: opts.link ?? join31(homedir15(), ".local", "bin", "ledge"), node: process.execPath, target: script });
|
|
18958
19203
|
if (line2) out.push(line2);
|
|
18959
19204
|
out.push(...await installService({ script, unitDir: opts.unitDir, run: opts.run }));
|
|
18960
19205
|
}
|
|
@@ -19016,10 +19261,10 @@ var init_setup = __esm({
|
|
|
19016
19261
|
});
|
|
19017
19262
|
|
|
19018
19263
|
// src/update/guard.ts
|
|
19019
|
-
import { readFile as
|
|
19264
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
19020
19265
|
async function tmuxServerAtRisk(opts = {}) {
|
|
19021
19266
|
const runTmux = opts.runTmux ?? run2;
|
|
19022
|
-
const readCgroup = opts.readCgroup ?? ((pid2) =>
|
|
19267
|
+
const readCgroup = opts.readCgroup ?? ((pid2) => readFile14(`/proc/${pid2}/cgroup`, "utf8"));
|
|
19023
19268
|
const r = await runTmux(["-L", opts.socket ?? "ledge", "list-sessions", "-F", "#{pid}"]);
|
|
19024
19269
|
if (r.code !== 0) return false;
|
|
19025
19270
|
const pid = r.stdout.split("\n").map((s) => s.trim()).find(Boolean);
|
|
@@ -19042,12 +19287,12 @@ var init_guard = __esm({
|
|
|
19042
19287
|
|
|
19043
19288
|
// src/update/self.ts
|
|
19044
19289
|
import { existsSync as existsSync4 } from "fs";
|
|
19045
|
-
import { readFile as
|
|
19046
|
-
import { homedir as
|
|
19047
|
-
import { dirname as dirname11, join as
|
|
19290
|
+
import { readFile as readFile15, realpath as realpath3 } from "fs/promises";
|
|
19291
|
+
import { homedir as homedir16 } from "os";
|
|
19292
|
+
import { dirname as dirname11, join as join32 } from "path";
|
|
19048
19293
|
function gitRoot(script, exists = existsSync4) {
|
|
19049
19294
|
for (let dir = dirname11(script); ; dir = dirname11(dir)) {
|
|
19050
|
-
if (exists(
|
|
19295
|
+
if (exists(join32(dir, ".git"))) return dir;
|
|
19051
19296
|
if (dirname11(dir) === dir) return null;
|
|
19052
19297
|
}
|
|
19053
19298
|
}
|
|
@@ -19057,10 +19302,10 @@ function installKind(script, exists = existsSync4) {
|
|
|
19057
19302
|
}
|
|
19058
19303
|
async function daemonInfo() {
|
|
19059
19304
|
const script = await realpath3(process.argv[1] ?? "").catch(() => process.argv[1] ?? "");
|
|
19060
|
-
const cgroup = await
|
|
19305
|
+
const cgroup = await readFile15("/proc/self/cgroup", "utf8").catch(() => "");
|
|
19061
19306
|
const install = installKind(script);
|
|
19062
19307
|
const root2 = install === "git" ? gitRoot(script) : null;
|
|
19063
|
-
return { version: LEDGE_VERSION, install, systemd: underSystemd(process.env, cgroup), home:
|
|
19308
|
+
return { version: LEDGE_VERSION, install, systemd: underSystemd(process.env, cgroup), home: homedir16(), ...root2 ? { manual: manualCommand(root2) } : {} };
|
|
19064
19309
|
}
|
|
19065
19310
|
var underSystemd, manualCommand;
|
|
19066
19311
|
var init_self = __esm({
|
|
@@ -19074,7 +19319,7 @@ var init_self = __esm({
|
|
|
19074
19319
|
});
|
|
19075
19320
|
|
|
19076
19321
|
// src/update/run-update.ts
|
|
19077
|
-
import { dirname as dirname12, join as
|
|
19322
|
+
import { dirname as dirname12, join as join33 } from "path";
|
|
19078
19323
|
function createDaemonUpdater(deps) {
|
|
19079
19324
|
let busy = false;
|
|
19080
19325
|
let restarting = false;
|
|
@@ -19163,7 +19408,7 @@ var init_run_update = __esm({
|
|
|
19163
19408
|
PACKAGE = "@paradow/ledge";
|
|
19164
19409
|
NPM_TIMEOUT_MS = 3 * 6e4;
|
|
19165
19410
|
RESTART_COMMAND = "systemctl --user restart ledge-daemon";
|
|
19166
|
-
npmCommand = (execPath, version) => ({ file:
|
|
19411
|
+
npmCommand = (execPath, version) => ({ file: join33(dirname12(execPath), "npm"), args: ["i", "-g", `${PACKAGE}@${version}`] });
|
|
19167
19412
|
lastLines = (output, n = 3) => output.split("\n").map((l) => l.trimEnd()).filter(Boolean).slice(-n).join("\n");
|
|
19168
19413
|
npmEnv = (execPath, env) => ({ ...env, PATH: `${dirname12(execPath)}:${env.PATH ?? ""}` });
|
|
19169
19414
|
}
|
|
@@ -20677,7 +20922,7 @@ __export(daemon_exports, {
|
|
|
20677
20922
|
startDaemon: () => startDaemon
|
|
20678
20923
|
});
|
|
20679
20924
|
import { writeFile as writeFile15, rm as rm7 } from "fs/promises";
|
|
20680
|
-
import { join as
|
|
20925
|
+
import { join as join34 } from "path";
|
|
20681
20926
|
async function startDaemon(opts = {}) {
|
|
20682
20927
|
await ensureLedgeDir();
|
|
20683
20928
|
const store = new Store();
|
|
@@ -20694,7 +20939,7 @@ async function startDaemon(opts = {}) {
|
|
|
20694
20939
|
}
|
|
20695
20940
|
for (const s of store.all()) {
|
|
20696
20941
|
if (backendOf(s) !== "omp-rpc") continue;
|
|
20697
|
-
await reapOwner(
|
|
20942
|
+
await reapOwner(join34(paths().dir, "omp", s.id)).catch(() => false);
|
|
20698
20943
|
if (s.state === "exited") continue;
|
|
20699
20944
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
20700
20945
|
store.apply(s.id, { name: "StopFailure", at, source: "daemon", text: "Ledge restarted: the OMP process ended with it" });
|
|
@@ -20781,9 +21026,9 @@ __export(uninstall_exports, {
|
|
|
20781
21026
|
runUninstall: () => runUninstall,
|
|
20782
21027
|
uninstallArgs: () => uninstallArgs
|
|
20783
21028
|
});
|
|
20784
|
-
import { readFile as
|
|
20785
|
-
import { homedir as
|
|
20786
|
-
import { join as
|
|
21029
|
+
import { readFile as readFile16, writeFile as writeFile16, rename as rename6, rm as rm8, lstat as lstat2, realpath as realpath4 } from "fs/promises";
|
|
21030
|
+
import { homedir as homedir17 } from "os";
|
|
21031
|
+
import { join as join35 } from "path";
|
|
20787
21032
|
function prune(config, file, keep) {
|
|
20788
21033
|
if (!isObject(config)) throw new Error(`${file} root is not an object, not modified`);
|
|
20789
21034
|
if (config.hooks === void 0) return { settings: config, removed: 0 };
|
|
@@ -20820,7 +21065,7 @@ function removeCursorHooks(config) {
|
|
|
20820
21065
|
async function pruneJsonFile(path3, remove, name) {
|
|
20821
21066
|
let raw;
|
|
20822
21067
|
try {
|
|
20823
|
-
raw = await
|
|
21068
|
+
raw = await readFile16(path3, "utf8");
|
|
20824
21069
|
} catch (e) {
|
|
20825
21070
|
if (e.code === "ENOENT") return null;
|
|
20826
21071
|
throw e;
|
|
@@ -20841,7 +21086,7 @@ async function pruneJsonFile(path3, remove, name) {
|
|
|
20841
21086
|
async function removeService(opts = {}) {
|
|
20842
21087
|
const out = [];
|
|
20843
21088
|
const run4 = opts.run ?? execRunner;
|
|
20844
|
-
const unit =
|
|
21089
|
+
const unit = join35(opts.unitDir ?? join35(homedir17(), ".config", "systemd", "user"), SERVICE);
|
|
20845
21090
|
let present = true;
|
|
20846
21091
|
try {
|
|
20847
21092
|
await lstat2(unit);
|
|
@@ -20866,7 +21111,7 @@ async function runningSessions(run4, socket) {
|
|
|
20866
21111
|
async function removeLink(link3, script) {
|
|
20867
21112
|
try {
|
|
20868
21113
|
const st = await lstat2(link3);
|
|
20869
|
-
if (st.isFile() ? !(await
|
|
21114
|
+
if (st.isFile() ? !(await readFile16(link3, "utf8")).startsWith(BIN_MARK) : !st.isSymbolicLink() || await realpath4(link3) !== await realpath4(script)) return false;
|
|
20870
21115
|
} catch {
|
|
20871
21116
|
return false;
|
|
20872
21117
|
}
|
|
@@ -20889,9 +21134,9 @@ async function runUninstall(opts = {}) {
|
|
|
20889
21134
|
};
|
|
20890
21135
|
const run4 = opts.run ?? execRunner;
|
|
20891
21136
|
const files = [
|
|
20892
|
-
["Claude Code", opts.settingsPath ??
|
|
20893
|
-
["Codex",
|
|
20894
|
-
["Cursor Agent",
|
|
21137
|
+
["Claude Code", opts.settingsPath ?? join35(homedir17(), ".claude", "settings.json"), (c) => removeClaudeHooks(c, "settings.json")],
|
|
21138
|
+
["Codex", join35(opts.codexDir ?? process.env.CODEX_HOME ?? join35(homedir17(), ".codex"), "hooks.json"), (c) => removeClaudeHooks(c, "hooks.json")],
|
|
21139
|
+
["Cursor Agent", join35(opts.cursorDir ?? join35(homedir17(), ".cursor"), "hooks.json"), removeCursorHooks]
|
|
20895
21140
|
];
|
|
20896
21141
|
for (const [agent, path3, remove] of files) {
|
|
20897
21142
|
await step3(async () => {
|
|
@@ -20913,7 +21158,7 @@ async function runUninstall(opts = {}) {
|
|
|
20913
21158
|
});
|
|
20914
21159
|
else lines.push(`${dir} kept (session journal and log): \`ledge uninstall --purge\` removes it`);
|
|
20915
21160
|
const script = opts.script ?? process.argv[1];
|
|
20916
|
-
const link3 = opts.link ??
|
|
21161
|
+
const link3 = opts.link ?? join35(homedir17(), ".local", "bin", "ledge");
|
|
20917
21162
|
if (script) await step3(async () => {
|
|
20918
21163
|
if (await removeLink(link3, script)) lines.push(`${link3} removed`);
|
|
20919
21164
|
});
|
|
@@ -21171,10 +21416,10 @@ function createDaemonClient(opts) {
|
|
|
21171
21416
|
};
|
|
21172
21417
|
const schedule = () => {
|
|
21173
21418
|
if (closed) return;
|
|
21174
|
-
timer2 = setTimeout(
|
|
21419
|
+
timer2 = setTimeout(open6, backoff);
|
|
21175
21420
|
backoff = Math.min(max, backoff * 2);
|
|
21176
21421
|
};
|
|
21177
|
-
function
|
|
21422
|
+
function open6() {
|
|
21178
21423
|
if (closed) return;
|
|
21179
21424
|
setStatus(status === "open" ? "connecting" : status);
|
|
21180
21425
|
if (!token) {
|
|
@@ -21238,7 +21483,7 @@ function createDaemonClient(opts) {
|
|
|
21238
21483
|
ws = null;
|
|
21239
21484
|
old?.close();
|
|
21240
21485
|
setStatus("connecting");
|
|
21241
|
-
|
|
21486
|
+
open6();
|
|
21242
21487
|
},
|
|
21243
21488
|
close: () => {
|
|
21244
21489
|
closed = true;
|
|
@@ -23011,7 +23256,7 @@ __export(launch_exports, {
|
|
|
23011
23256
|
parseLaunchArgs: () => parseLaunchArgs
|
|
23012
23257
|
});
|
|
23013
23258
|
import { spawn as spawn3 } from "child_process";
|
|
23014
|
-
import { readFile as
|
|
23259
|
+
import { readFile as readFile17, rm as rm9 } from "fs/promises";
|
|
23015
23260
|
import { createConnection } from "net";
|
|
23016
23261
|
function parseLaunchArgs(argv) {
|
|
23017
23262
|
const args = [];
|
|
@@ -23124,7 +23369,7 @@ async function readExitCode(id) {
|
|
|
23124
23369
|
const file = exitFile(id);
|
|
23125
23370
|
let raw = null;
|
|
23126
23371
|
try {
|
|
23127
|
-
raw = await
|
|
23372
|
+
raw = await readFile17(file, "utf8");
|
|
23128
23373
|
} catch {
|
|
23129
23374
|
}
|
|
23130
23375
|
await rm9(file, { force: true });
|
|
@@ -23201,6 +23446,7 @@ var init_launch2 = __esm({
|
|
|
23201
23446
|
import { spawn as spawn4 } from "child_process";
|
|
23202
23447
|
function attachCommand(fleet, s) {
|
|
23203
23448
|
if (s.offline) return { why: "offline" };
|
|
23449
|
+
if (isStoppedOmpSeat(s)) return { why: "stopped" };
|
|
23204
23450
|
if (backendOf(s) === "omp-rpc") return { why: "omp" };
|
|
23205
23451
|
if (!s.tmuxSession) return { why: "none" };
|
|
23206
23452
|
const argv = fleet.terminal(s.id);
|
|
@@ -23223,9 +23469,9 @@ var init_attach = __esm({
|
|
|
23223
23469
|
|
|
23224
23470
|
// src/tui/clipboard.ts
|
|
23225
23471
|
import { execFile as execFile9 } from "child_process";
|
|
23226
|
-
import { mkdtemp as mkdtemp2, readFile as
|
|
23472
|
+
import { mkdtemp as mkdtemp2, readFile as readFile18, rm as rm10 } from "fs/promises";
|
|
23227
23473
|
import { tmpdir as tmpdir2 } from "os";
|
|
23228
|
-
import { join as
|
|
23474
|
+
import { join as join36 } from "path";
|
|
23229
23475
|
async function fromWindows(run4) {
|
|
23230
23476
|
const { stdout } = await run4(POWERSHELL, ["-NoProfile", "-STA", "-Command", SCRIPT], { cwd: "/mnt/c", maxBuffer: MAX_BUFFER, encoding: "buffer" });
|
|
23231
23477
|
const line2 = stdout.toString("utf8").trim();
|
|
@@ -23250,15 +23496,15 @@ async function fromLinux(run4) {
|
|
|
23250
23496
|
return null;
|
|
23251
23497
|
}
|
|
23252
23498
|
async function fromMac(run4) {
|
|
23253
|
-
const dir = await mkdtemp2(
|
|
23254
|
-
const file =
|
|
23499
|
+
const dir = await mkdtemp2(join36(tmpdir2(), "ledge-clip-"));
|
|
23500
|
+
const file = join36(dir, "clip.png");
|
|
23255
23501
|
try {
|
|
23256
23502
|
const script = `set f to open for access POSIX file "${file}" with write permission
|
|
23257
23503
|
set eof f to 0
|
|
23258
23504
|
write (the clipboard as \xABclass PNGf\xBB) to f
|
|
23259
23505
|
close access f`;
|
|
23260
23506
|
await run4("osascript", ["-e", script], { maxBuffer: MAX_BUFFER, encoding: "buffer" });
|
|
23261
|
-
return fromBytes(await
|
|
23507
|
+
return fromBytes(await readFile18(file));
|
|
23262
23508
|
} catch {
|
|
23263
23509
|
return null;
|
|
23264
23510
|
} finally {
|
|
@@ -23689,7 +23935,7 @@ var init_fr = __esm({
|
|
|
23689
23935
|
plural = (n, one2, many) => n > 1 ? many : one2;
|
|
23690
23936
|
fr3 = {
|
|
23691
23937
|
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" },
|
|
23938
|
+
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
23939
|
time: {
|
|
23694
23940
|
now: "\xE0 l\u2019instant",
|
|
23695
23941
|
decimal: ",",
|
|
@@ -23712,6 +23958,7 @@ var init_fr = __esm({
|
|
|
23712
23958
|
openImage: "ouvrir l'image",
|
|
23713
23959
|
navigate: "naviguer",
|
|
23714
23960
|
open: "ouvrir",
|
|
23961
|
+
attachSeat: "attacher son terminal",
|
|
23715
23962
|
answer: "r\xE9pondre",
|
|
23716
23963
|
attach: "attacher (tmux)",
|
|
23717
23964
|
ask: "demander \xE0 Ledge",
|
|
@@ -23786,6 +24033,7 @@ var init_fr = __esm({
|
|
|
23786
24033
|
noPreview: "Pas d\u2019aper\xE7u.",
|
|
23787
24034
|
noExchange: "Aucun \xE9change.",
|
|
23788
24035
|
sessions: (n) => `${n} ${plural(n, "session", "sessions")}`,
|
|
24036
|
+
sessionsOf: (live3, all2) => `${live3} ${plural(live3, "session", "sessions")} sur ${all2}`,
|
|
23789
24037
|
outside: "Hors projet",
|
|
23790
24038
|
offline: "hors ligne",
|
|
23791
24039
|
readOnly: "lecture seule",
|
|
@@ -24009,6 +24257,7 @@ var init_fr = __esm({
|
|
|
24009
24257
|
noteTooLong: (max) => `Note trop longue : ${max} caract\xE8res au plus. Elle reste dans le champ.`,
|
|
24010
24258
|
noNote: (n) => `${n} ne prend pas de note ici.`,
|
|
24011
24259
|
noTerminalOmp: (n) => `${n} est pilot\xE9 par Ledge via le protocole d\u2019OMP : pas de terminal \xE0 attacher.`,
|
|
24260
|
+
seatStopped: (n) => `${n} est arr\xEAt\xE9 : son terminal n\u2019existe plus. 6 ouvre OpenRig, o\xF9 est son rig.`,
|
|
24012
24261
|
noTerminal: (n) => `Pas de terminal pour ${n}.`,
|
|
24013
24262
|
attachFailed: (code3) => `tmux s\u2019est arr\xEAt\xE9 avec le code ${code3}.`,
|
|
24014
24263
|
modelSwitch: "Ce daemon ne change pas le mod\xE8le d\u2019une session en cours.",
|
|
@@ -24257,7 +24506,7 @@ var init_en = __esm({
|
|
|
24257
24506
|
plural2 = (n, one2, many) => n > 1 ? many : one2;
|
|
24258
24507
|
en3 = {
|
|
24259
24508
|
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" },
|
|
24509
|
+
state: { approval: "Needs you", question: "A question", starting: "Starting", idle: "Available", working: "Working", done: "Finished", error: "Error", readonly: "Read-only", stopped: "Stopped" },
|
|
24261
24510
|
time: { now: "just now", decimal: ".", s: (n) => `${n}s`, min: (n) => `${n} min`, h: (n) => `${n} h`, d: (n) => `${n} d` },
|
|
24262
24511
|
tabs: { overview: "Overview", session: "Session", ask: "Ask Ledge", launch: "New session", settings: "Settings", openrig: "OpenRig" },
|
|
24263
24512
|
bar: {
|
|
@@ -24273,6 +24522,7 @@ var init_en = __esm({
|
|
|
24273
24522
|
openImage: "open image",
|
|
24274
24523
|
navigate: "move",
|
|
24275
24524
|
open: "open",
|
|
24525
|
+
attachSeat: "attach its terminal",
|
|
24276
24526
|
answer: "answer",
|
|
24277
24527
|
attach: "attach (tmux)",
|
|
24278
24528
|
ask: "ask Ledge",
|
|
@@ -24346,6 +24596,7 @@ var init_en = __esm({
|
|
|
24346
24596
|
noPreview: "No preview.",
|
|
24347
24597
|
noExchange: "No message yet.",
|
|
24348
24598
|
sessions: (n) => `${n} ${plural2(n, "session", "sessions")}`,
|
|
24599
|
+
sessionsOf: (live3, all2) => `${live3} ${live3 === 1 ? "session" : "sessions"} of ${all2}`,
|
|
24349
24600
|
outside: "No project",
|
|
24350
24601
|
offline: "offline",
|
|
24351
24602
|
readOnly: "read-only",
|
|
@@ -24565,6 +24816,7 @@ var init_en = __esm({
|
|
|
24565
24816
|
noteTooLong: (max) => `Note too long: ${max} characters at most. It stays in the field.`,
|
|
24566
24817
|
noNote: (n) => `${n} takes no note here.`,
|
|
24567
24818
|
noTerminalOmp: (n) => `${n} is driven by Ledge over OMP's protocol: there is no terminal to attach to.`,
|
|
24819
|
+
seatStopped: (n) => `${n} is stopped: its terminal is gone. 6 opens OpenRig, where its rig is.`,
|
|
24568
24820
|
noTerminal: (n) => `No terminal for ${n}.`,
|
|
24569
24821
|
attachFailed: (code3) => `tmux stopped with code ${code3}.`,
|
|
24570
24822
|
modelSwitch: "This daemon cannot change a running session\u2019s model.",
|
|
@@ -24829,15 +25081,19 @@ function overview(sessions, projects) {
|
|
|
24829
25081
|
const known2 = new Map((projects?.projects ?? []).map((p) => [p.id, p]));
|
|
24830
25082
|
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
25083
|
const rigNodes = /* @__PURE__ */ new Map();
|
|
25084
|
+
const rigNodeOf = (rig) => {
|
|
25085
|
+
const kind = rig === "kernel" ? "kernel" : "rig";
|
|
25086
|
+
const key = kind === "kernel" ? "g:kernel" : `g:rig:${rig}`;
|
|
25087
|
+
let node2 = rigNodes.get(key);
|
|
25088
|
+
if (!node2) {
|
|
25089
|
+
node2 = { key, label: kind === "kernel" ? "Kernel" : rig, kind, branches: [{ branch: "", sessions: [] }], count: 0 };
|
|
25090
|
+
rigNodes.set(key, node2);
|
|
25091
|
+
}
|
|
25092
|
+
return node2;
|
|
25093
|
+
};
|
|
24832
25094
|
for (const s of live3) {
|
|
24833
25095
|
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
|
-
}
|
|
25096
|
+
const node3 = rigNodeOf(s.rig.name);
|
|
24841
25097
|
node3.branches[0].sessions.push(s);
|
|
24842
25098
|
node3.count += 1;
|
|
24843
25099
|
continue;
|
|
@@ -24858,6 +25114,12 @@ function overview(sessions, projects) {
|
|
|
24858
25114
|
row3.sessions.push(s);
|
|
24859
25115
|
node2.count += 1;
|
|
24860
25116
|
}
|
|
25117
|
+
for (const s of sessions) {
|
|
25118
|
+
if (!isStoppedOmpSeat(s) || !s.rig) continue;
|
|
25119
|
+
const node2 = rigNodeOf(s.rig.name);
|
|
25120
|
+
node2.branches[0].sessions.push(s);
|
|
25121
|
+
node2.shown = (node2.shown ?? node2.count) + 1;
|
|
25122
|
+
}
|
|
24861
25123
|
const groups = [...rigNodes.values()].sort((a, b3) => a.kind === b3.kind ? a.label.localeCompare(b3.label) : a.kind === "kernel" ? 1 : -1);
|
|
24862
25124
|
const tree2 = [...[...nodes.values()].filter((n) => n.count > 0), ...groups];
|
|
24863
25125
|
const order2 = tree2.flatMap((n) => n.branches.flatMap((b3) => b3.sessions.map((s) => s.id)));
|
|
@@ -25118,7 +25380,7 @@ var init_theme = __esm({
|
|
|
25118
25380
|
|
|
25119
25381
|
// src/tui/model/cells.ts
|
|
25120
25382
|
import stringWidth from "string-width";
|
|
25121
|
-
function
|
|
25383
|
+
function join37(line2, a) {
|
|
25122
25384
|
const out = [];
|
|
25123
25385
|
for (const c of a) {
|
|
25124
25386
|
const last = out[out.length - 1];
|
|
@@ -25133,7 +25395,7 @@ function cut2(line2, width, ellipsis = "\u2026") {
|
|
|
25133
25395
|
const a = split2(line2), room = width - cells(ellipsis);
|
|
25134
25396
|
let n = 0, i = 0;
|
|
25135
25397
|
while (i < a.length && n + a[i].w <= room) n += a[i++].w;
|
|
25136
|
-
const kept =
|
|
25398
|
+
const kept = join37(line2, a.slice(0, i));
|
|
25137
25399
|
const color2 = line2[a[Math.max(0, i - 1)]?.seg ?? 0]?.color;
|
|
25138
25400
|
return [...kept, { text: ellipsis, ...color2 ? { color: color2 } : {} }];
|
|
25139
25401
|
}
|
|
@@ -25152,7 +25414,7 @@ function wrapSegs(line2, width, first = width) {
|
|
|
25152
25414
|
}
|
|
25153
25415
|
w = Math.max(4, width);
|
|
25154
25416
|
if (j >= a.length || a[j].g === "\n") {
|
|
25155
|
-
out.push(
|
|
25417
|
+
out.push(join37(line2, a.slice(i, j)));
|
|
25156
25418
|
i = j + 1;
|
|
25157
25419
|
if (j >= a.length) break;
|
|
25158
25420
|
if (i >= a.length) out.push([]);
|
|
@@ -25161,7 +25423,7 @@ function wrapSegs(line2, width, first = width) {
|
|
|
25161
25423
|
const end2 = a[j].g === " " ? j : space > i ? space : Math.max(j, i + 1);
|
|
25162
25424
|
let k = end2;
|
|
25163
25425
|
while (k > i && a[k - 1].g === " ") k--;
|
|
25164
|
-
out.push(
|
|
25426
|
+
out.push(join37(line2, a.slice(i, k)));
|
|
25165
25427
|
i = end2;
|
|
25166
25428
|
while (i < a.length && a[i].g === " ") i++;
|
|
25167
25429
|
if (a[i]?.g === "\n") {
|
|
@@ -30374,16 +30636,16 @@ function resolveAllAttention(events, context) {
|
|
|
30374
30636
|
let nextEvents;
|
|
30375
30637
|
while (++index2 < events.length) {
|
|
30376
30638
|
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[
|
|
30639
|
+
let open6 = index2;
|
|
30640
|
+
while (open6--) {
|
|
30641
|
+
if (events[open6][0] === "exit" && events[open6][1].type === "attentionSequence" && events[open6][1]._open && // If the markers are the same:
|
|
30642
|
+
context.sliceSerialize(events[open6][1]).charCodeAt(0) === context.sliceSerialize(events[index2][1]).charCodeAt(0)) {
|
|
30643
|
+
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
30644
|
continue;
|
|
30383
30645
|
}
|
|
30384
|
-
const use = events[
|
|
30646
|
+
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
30647
|
const start = {
|
|
30386
|
-
...events[
|
|
30648
|
+
...events[open6][1].end
|
|
30387
30649
|
};
|
|
30388
30650
|
const end2 = {
|
|
30389
30651
|
...events[index2][1].start
|
|
@@ -30394,7 +30656,7 @@ function resolveAllAttention(events, context) {
|
|
|
30394
30656
|
type: use > 1 ? "strongSequence" : "emphasisSequence",
|
|
30395
30657
|
start,
|
|
30396
30658
|
end: {
|
|
30397
|
-
...events[
|
|
30659
|
+
...events[open6][1].end
|
|
30398
30660
|
}
|
|
30399
30661
|
};
|
|
30400
30662
|
const closingSequence = {
|
|
@@ -30407,7 +30669,7 @@ function resolveAllAttention(events, context) {
|
|
|
30407
30669
|
const text7 = {
|
|
30408
30670
|
type: use > 1 ? "strongText" : "emphasisText",
|
|
30409
30671
|
start: {
|
|
30410
|
-
...events[
|
|
30672
|
+
...events[open6][1].end
|
|
30411
30673
|
},
|
|
30412
30674
|
end: {
|
|
30413
30675
|
...events[index2][1].start
|
|
@@ -30422,26 +30684,26 @@ function resolveAllAttention(events, context) {
|
|
|
30422
30684
|
...closingSequence.end
|
|
30423
30685
|
}
|
|
30424
30686
|
};
|
|
30425
|
-
events[
|
|
30687
|
+
events[open6][1].end = {
|
|
30426
30688
|
...openingSequence.start
|
|
30427
30689
|
};
|
|
30428
30690
|
events[index2][1].start = {
|
|
30429
30691
|
...closingSequence.end
|
|
30430
30692
|
};
|
|
30431
30693
|
nextEvents = [];
|
|
30432
|
-
if (events[
|
|
30433
|
-
nextEvents = push(nextEvents, [["enter", events[
|
|
30694
|
+
if (events[open6][1].end.offset - events[open6][1].start.offset) {
|
|
30695
|
+
nextEvents = push(nextEvents, [["enter", events[open6][1], context], ["exit", events[open6][1], context]]);
|
|
30434
30696
|
}
|
|
30435
30697
|
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(
|
|
30698
|
+
nextEvents = push(nextEvents, resolveAll(context.parser.constructs.insideSpan.null, events.slice(open6 + 1, index2), context));
|
|
30437
30699
|
nextEvents = push(nextEvents, [["exit", text7, context], ["enter", closingSequence, context], ["exit", closingSequence, context], ["exit", group, context]]);
|
|
30438
30700
|
let offset = 0;
|
|
30439
30701
|
if (events[index2][1].end.offset - events[index2][1].start.offset) {
|
|
30440
30702
|
offset = 2;
|
|
30441
30703
|
nextEvents = push(nextEvents, [["enter", events[index2][1], context], ["exit", events[index2][1], context]]);
|
|
30442
30704
|
}
|
|
30443
|
-
splice(events,
|
|
30444
|
-
index2 =
|
|
30705
|
+
splice(events, open6 - 1, index2 - open6 + 3, nextEvents);
|
|
30706
|
+
index2 = open6 + nextEvents.length - offset - 2;
|
|
30445
30707
|
break;
|
|
30446
30708
|
}
|
|
30447
30709
|
}
|
|
@@ -30473,10 +30735,10 @@ function tokenizeAttention(effects, ok4) {
|
|
|
30473
30735
|
}
|
|
30474
30736
|
const token = effects.exit("attentionSequence");
|
|
30475
30737
|
const after2 = classifyCharacter(code3);
|
|
30476
|
-
const
|
|
30738
|
+
const open6 = !after2 || after2 === 2 && before2 || attentionMarkers2.includes(code3) && code3 !== 42 && code3 !== 95;
|
|
30477
30739
|
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 || !
|
|
30740
|
+
token._open = Boolean(marker === 42 ? open6 : open6 && (before2 || !close));
|
|
30741
|
+
token._close = Boolean(marker === 42 ? close : close && (after2 || !open6));
|
|
30480
30742
|
return ok4(code3);
|
|
30481
30743
|
}
|
|
30482
30744
|
}
|
|
@@ -30510,9 +30772,9 @@ function tokenizeAutolink(effects, ok4, nok) {
|
|
|
30510
30772
|
effects.consume(code3);
|
|
30511
30773
|
effects.exit("autolinkMarker");
|
|
30512
30774
|
effects.enter("autolinkProtocol");
|
|
30513
|
-
return
|
|
30775
|
+
return open6;
|
|
30514
30776
|
}
|
|
30515
|
-
function
|
|
30777
|
+
function open6(code3) {
|
|
30516
30778
|
if (asciiAlpha(code3)) {
|
|
30517
30779
|
effects.consume(code3);
|
|
30518
30780
|
return schemeOrEmailAtext;
|
|
@@ -30793,9 +31055,9 @@ function tokenizeCharacterReference(effects, ok4, nok) {
|
|
|
30793
31055
|
effects.enter("characterReferenceMarker");
|
|
30794
31056
|
effects.consume(code3);
|
|
30795
31057
|
effects.exit("characterReferenceMarker");
|
|
30796
|
-
return
|
|
31058
|
+
return open6;
|
|
30797
31059
|
}
|
|
30798
|
-
function
|
|
31060
|
+
function open6(code3) {
|
|
30799
31061
|
if (code3 === 35) {
|
|
30800
31062
|
effects.enter("characterReferenceMarkerNumeric");
|
|
30801
31063
|
effects.consume(code3);
|
|
@@ -32196,9 +32458,9 @@ function tokenizeHtmlFlow(effects, ok4, nok) {
|
|
|
32196
32458
|
effects.enter("htmlFlow");
|
|
32197
32459
|
effects.enter("htmlFlowData");
|
|
32198
32460
|
effects.consume(code3);
|
|
32199
|
-
return
|
|
32461
|
+
return open6;
|
|
32200
32462
|
}
|
|
32201
|
-
function
|
|
32463
|
+
function open6(code3) {
|
|
32202
32464
|
if (code3 === 33) {
|
|
32203
32465
|
effects.consume(code3);
|
|
32204
32466
|
return declarationOpen;
|
|
@@ -32546,9 +32808,9 @@ function tokenizeHtmlText(effects, ok4, nok) {
|
|
|
32546
32808
|
effects.enter("htmlText");
|
|
32547
32809
|
effects.enter("htmlTextData");
|
|
32548
32810
|
effects.consume(code3);
|
|
32549
|
-
return
|
|
32811
|
+
return open6;
|
|
32550
32812
|
}
|
|
32551
|
-
function
|
|
32813
|
+
function open6(code3) {
|
|
32552
32814
|
if (code3 === 33) {
|
|
32553
32815
|
effects.consume(code3);
|
|
32554
32816
|
return declarationOpen;
|
|
@@ -32870,12 +33132,12 @@ function resolveAllLabelEnd(events) {
|
|
|
32870
33132
|
function resolveToLabelEnd(events, context) {
|
|
32871
33133
|
let index2 = events.length;
|
|
32872
33134
|
let offset = 0;
|
|
32873
|
-
let
|
|
33135
|
+
let open6;
|
|
32874
33136
|
let close;
|
|
32875
33137
|
let media;
|
|
32876
33138
|
while (index2--) {
|
|
32877
33139
|
const token = events[index2][1];
|
|
32878
|
-
if (
|
|
33140
|
+
if (open6) {
|
|
32879
33141
|
if (token.type === "link" || token.type === "labelLink" && token._inactive) {
|
|
32880
33142
|
break;
|
|
32881
33143
|
}
|
|
@@ -32884,7 +33146,7 @@ function resolveToLabelEnd(events, context) {
|
|
|
32884
33146
|
}
|
|
32885
33147
|
} else if (close) {
|
|
32886
33148
|
if (events[index2][0] === "enter" && (token.type === "labelImage" || token.type === "labelLink") && !token._balanced) {
|
|
32887
|
-
|
|
33149
|
+
open6 = index2;
|
|
32888
33150
|
if (token.type !== "labelLink") {
|
|
32889
33151
|
offset = 2;
|
|
32890
33152
|
break;
|
|
@@ -32895,9 +33157,9 @@ function resolveToLabelEnd(events, context) {
|
|
|
32895
33157
|
}
|
|
32896
33158
|
}
|
|
32897
33159
|
const group = {
|
|
32898
|
-
type: events[
|
|
33160
|
+
type: events[open6][1].type === "labelLink" ? "link" : "image",
|
|
32899
33161
|
start: {
|
|
32900
|
-
...events[
|
|
33162
|
+
...events[open6][1].start
|
|
32901
33163
|
},
|
|
32902
33164
|
end: {
|
|
32903
33165
|
...events[events.length - 1][1].end
|
|
@@ -32906,7 +33168,7 @@ function resolveToLabelEnd(events, context) {
|
|
|
32906
33168
|
const label2 = {
|
|
32907
33169
|
type: "label",
|
|
32908
33170
|
start: {
|
|
32909
|
-
...events[
|
|
33171
|
+
...events[open6][1].start
|
|
32910
33172
|
},
|
|
32911
33173
|
end: {
|
|
32912
33174
|
...events[close][1].end
|
|
@@ -32915,20 +33177,20 @@ function resolveToLabelEnd(events, context) {
|
|
|
32915
33177
|
const text7 = {
|
|
32916
33178
|
type: "labelText",
|
|
32917
33179
|
start: {
|
|
32918
|
-
...events[
|
|
33180
|
+
...events[open6 + offset + 2][1].end
|
|
32919
33181
|
},
|
|
32920
33182
|
end: {
|
|
32921
33183
|
...events[close - 2][1].start
|
|
32922
33184
|
}
|
|
32923
33185
|
};
|
|
32924
33186
|
media = [["enter", group, context], ["enter", label2, context]];
|
|
32925
|
-
media = push(media, events.slice(
|
|
33187
|
+
media = push(media, events.slice(open6 + 1, open6 + offset + 3));
|
|
32926
33188
|
media = push(media, [["enter", text7, context]]);
|
|
32927
|
-
media = push(media, resolveAll(context.parser.constructs.insideSpan.null, events.slice(
|
|
33189
|
+
media = push(media, resolveAll(context.parser.constructs.insideSpan.null, events.slice(open6 + offset + 4, close - 3), context));
|
|
32928
33190
|
media = push(media, [["exit", text7, context], events[close - 2], events[close - 1], ["exit", label2, context]]);
|
|
32929
33191
|
media = push(media, events.slice(close + 1));
|
|
32930
33192
|
media = push(media, [["exit", group, context]]);
|
|
32931
|
-
splice(events,
|
|
33193
|
+
splice(events, open6, events.length, media);
|
|
32932
33194
|
return events;
|
|
32933
33195
|
}
|
|
32934
33196
|
function tokenizeLabelEnd(effects, ok4, nok) {
|
|
@@ -33099,9 +33361,9 @@ function tokenizeLabelStartImage(effects, ok4, nok) {
|
|
|
33099
33361
|
effects.enter("labelImageMarker");
|
|
33100
33362
|
effects.consume(code3);
|
|
33101
33363
|
effects.exit("labelImageMarker");
|
|
33102
|
-
return
|
|
33364
|
+
return open6;
|
|
33103
33365
|
}
|
|
33104
|
-
function
|
|
33366
|
+
function open6(code3) {
|
|
33105
33367
|
if (code3 === 91) {
|
|
33106
33368
|
effects.enter("labelMarker");
|
|
33107
33369
|
effects.consume(code3);
|
|
@@ -33933,30 +34195,30 @@ function gfmStrikethrough(options) {
|
|
|
33933
34195
|
let index2 = -1;
|
|
33934
34196
|
while (++index2 < events.length) {
|
|
33935
34197
|
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[
|
|
34198
|
+
let open6 = index2;
|
|
34199
|
+
while (open6--) {
|
|
34200
|
+
if (events[open6][0] === "exit" && events[open6][1].type === "strikethroughSequenceTemporary" && events[open6][1]._open && // If the sizes are the same:
|
|
34201
|
+
events[index2][1].end.offset - events[index2][1].start.offset === events[open6][1].end.offset - events[open6][1].start.offset) {
|
|
33940
34202
|
events[index2][1].type = "strikethroughSequence";
|
|
33941
|
-
events[
|
|
34203
|
+
events[open6][1].type = "strikethroughSequence";
|
|
33942
34204
|
const strikethrough = {
|
|
33943
34205
|
type: "strikethrough",
|
|
33944
|
-
start: Object.assign({}, events[
|
|
34206
|
+
start: Object.assign({}, events[open6][1].start),
|
|
33945
34207
|
end: Object.assign({}, events[index2][1].end)
|
|
33946
34208
|
};
|
|
33947
34209
|
const text7 = {
|
|
33948
34210
|
type: "strikethroughText",
|
|
33949
|
-
start: Object.assign({}, events[
|
|
34211
|
+
start: Object.assign({}, events[open6][1].end),
|
|
33950
34212
|
end: Object.assign({}, events[index2][1].start)
|
|
33951
34213
|
};
|
|
33952
|
-
const nextEvents = [["enter", strikethrough, context], ["enter", events[
|
|
34214
|
+
const nextEvents = [["enter", strikethrough, context], ["enter", events[open6][1], context], ["exit", events[open6][1], context], ["enter", text7, context]];
|
|
33953
34215
|
const insideSpan2 = context.parser.constructs.insideSpan.null;
|
|
33954
34216
|
if (insideSpan2) {
|
|
33955
|
-
splice(nextEvents, nextEvents.length, 0, resolveAll(insideSpan2, events.slice(
|
|
34217
|
+
splice(nextEvents, nextEvents.length, 0, resolveAll(insideSpan2, events.slice(open6 + 1, index2), context));
|
|
33956
34218
|
}
|
|
33957
34219
|
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 =
|
|
34220
|
+
splice(events, open6 - 1, index2 - open6 + 3, nextEvents);
|
|
34221
|
+
index2 = open6 + nextEvents.length - 2;
|
|
33960
34222
|
break;
|
|
33961
34223
|
}
|
|
33962
34224
|
}
|
|
@@ -34558,8 +34820,8 @@ function gfmTaskListItem() {
|
|
|
34558
34820
|
}
|
|
34559
34821
|
function tokenizeTasklistCheck(effects, ok4, nok) {
|
|
34560
34822
|
const self = this;
|
|
34561
|
-
return
|
|
34562
|
-
function
|
|
34823
|
+
return open6;
|
|
34824
|
+
function open6(code3) {
|
|
34563
34825
|
if (
|
|
34564
34826
|
// Exit if there’s stuff before.
|
|
34565
34827
|
self.previous !== null || // Exit if not in the first content that is the first child of a list
|
|
@@ -36048,8 +36310,8 @@ function compiler(options) {
|
|
|
36048
36310
|
events[start][1]._spread = listSpread;
|
|
36049
36311
|
}
|
|
36050
36312
|
function opener(create, and) {
|
|
36051
|
-
return
|
|
36052
|
-
function
|
|
36313
|
+
return open6;
|
|
36314
|
+
function open6(token) {
|
|
36053
36315
|
enter.call(this, create(token), token);
|
|
36054
36316
|
if (and) and.call(this, token);
|
|
36055
36317
|
}
|
|
@@ -36081,19 +36343,19 @@ function compiler(options) {
|
|
|
36081
36343
|
}
|
|
36082
36344
|
function exit3(token, onExitError) {
|
|
36083
36345
|
const node2 = this.stack.pop();
|
|
36084
|
-
const
|
|
36085
|
-
if (!
|
|
36346
|
+
const open6 = this.tokenStack.pop();
|
|
36347
|
+
if (!open6) {
|
|
36086
36348
|
throw new Error("Cannot close `" + token.type + "` (" + stringifyPosition({
|
|
36087
36349
|
start: token.start,
|
|
36088
36350
|
end: token.end
|
|
36089
36351
|
}) + "): it\u2019s not open");
|
|
36090
36352
|
}
|
|
36091
|
-
if (
|
|
36353
|
+
if (open6[0].type !== token.type) {
|
|
36092
36354
|
if (onExitError) {
|
|
36093
|
-
onExitError.call(this, token,
|
|
36355
|
+
onExitError.call(this, token, open6[0]);
|
|
36094
36356
|
} else {
|
|
36095
|
-
const handler =
|
|
36096
|
-
handler.call(this, token,
|
|
36357
|
+
const handler = open6[1] || defaultOnError;
|
|
36358
|
+
handler.call(this, token, open6[0]);
|
|
36097
36359
|
}
|
|
36098
36360
|
}
|
|
36099
36361
|
node2.position.end = point2(token.end);
|
|
@@ -39513,7 +39775,8 @@ function Overview() {
|
|
|
39513
39775
|
return true;
|
|
39514
39776
|
}
|
|
39515
39777
|
if (key.return) {
|
|
39516
|
-
if (selected)
|
|
39778
|
+
if (selected && isOmpSeat(selected)) attach2(selected);
|
|
39779
|
+
else if (selected) go("session");
|
|
39517
39780
|
return true;
|
|
39518
39781
|
}
|
|
39519
39782
|
if (!selected) return false;
|
|
@@ -39534,7 +39797,7 @@ function Overview() {
|
|
|
39534
39797
|
return false;
|
|
39535
39798
|
});
|
|
39536
39799
|
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) }] : [],
|
|
39800
|
+
...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
39801
|
...n.branches.flatMap((b3) => b3.sessions.map((s) => ({ kind: "session", key: s.id, s, indent: kind === "rig" ? 2 : 0, group: kind })))
|
|
39539
39802
|
]);
|
|
39540
39803
|
const rigRows = groupRows("rig"), kernelRows = groupRows("kernel");
|
|
@@ -39556,6 +39819,7 @@ function Overview() {
|
|
|
39556
39819
|
const stateW = Math.max(...Object.values(t.state).map((x) => x.length)) + 1;
|
|
39557
39820
|
const topicW = Math.max(6, inner - 6 - nameW - stateW - 3);
|
|
39558
39821
|
const askW = Math.max(6, inner - 2 - nameW - 9 - 7);
|
|
39822
|
+
const roleW = Math.max(11, ...rigRows.map((r) => r.kind === "session" ? [...r.s.rig?.role ?? ""].length + 2 : 0));
|
|
39559
39823
|
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
39824
|
const st = botState(s);
|
|
39561
39825
|
return /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: s.id === sel ? C.sel : void 0, children: [
|
|
@@ -39621,16 +39885,18 @@ function Overview() {
|
|
|
39621
39885
|
] }) : null,
|
|
39622
39886
|
/* @__PURE__ */ jsxs6(Text7, { color: C.faint, children: [
|
|
39623
39887
|
" ",
|
|
39624
|
-
t.overview.sessions(r.count)
|
|
39888
|
+
r.shown !== void 0 ? t.overview.sessionsOf(r.count, r.shown) : t.overview.sessions(r.count)
|
|
39625
39889
|
] })
|
|
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) });
|
|
39890
|
+
] }, 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
39891
|
const rigsBox = groupBox(t.overview.rigsGroup, rigRows, rigsHeight);
|
|
39628
39892
|
const kernelBox = groupBox(t.overview.kernelGroup, kernelRows, kernelHeight);
|
|
39893
|
+
const stopped2 = !!selected && isStoppedOmpSeat(selected);
|
|
39629
39894
|
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]] : [
|
|
39895
|
+
// On an OMP seat ↵ attaches its terminal; on a stopped one, neither ↵ nor a has anything to attach.
|
|
39630
39896
|
["j k", t.keys.navigate],
|
|
39631
|
-
["\u21B5", t.keys.open],
|
|
39897
|
+
...stopped2 ? [] : [["\u21B5", selected && isOmpSeat(selected) ? t.keys.attachSeat : t.keys.open]],
|
|
39632
39898
|
...view.yours.length ? [["tab", t.keys.next], ["y n", t.keys.answer]] : [],
|
|
39633
|
-
["a", t.keys.attach],
|
|
39899
|
+
...stopped2 ? [] : [["a", t.keys.attach]],
|
|
39634
39900
|
...writable2 ? [["m", t.keys.writeProject]] : [],
|
|
39635
39901
|
...decisions.length ? [["D", t.keys.decide]] : [],
|
|
39636
39902
|
...near.length ? [["H", t.keys.handover]] : [],
|
|
@@ -39705,21 +39971,23 @@ function Overview() {
|
|
|
39705
39971
|
) : null
|
|
39706
39972
|
] });
|
|
39707
39973
|
}
|
|
39708
|
-
function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0, group }) {
|
|
39974
|
+
function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0, group, roleW = 11 }) {
|
|
39709
39975
|
const { t, g } = useTui();
|
|
39710
39976
|
const st = botState(s);
|
|
39711
39977
|
const n = helperCount(s);
|
|
39978
|
+
const stopped2 = isStoppedOmpSeat(s);
|
|
39979
|
+
const roleTool = group === "kernel" ? `${(s.rig?.role ?? "").replace(/\.(agent|lead|worker)$/, "").padEnd(11)}${s.tool}` : `${(s.rig?.role ?? "").padEnd(roleW)}${s.tool}`;
|
|
39712
39980
|
return /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: on ? C.sel : void 0, children: [
|
|
39713
39981
|
/* @__PURE__ */ jsx10(Text7, { color: on ? C.coral : C.faint, children: on ? "\u258C" : " " }),
|
|
39714
39982
|
/* @__PURE__ */ jsx10(Text7, { children: " ".repeat(2 + indent2) }),
|
|
39715
|
-
/* @__PURE__ */ jsxs6(Text7, { color: s.offline ? C.faint : STATE_COLOR[st], children: [
|
|
39983
|
+
/* @__PURE__ */ jsxs6(Text7, { color: s.offline || stopped2 ? C.faint : STATE_COLOR[st], children: [
|
|
39716
39984
|
g[st],
|
|
39717
39985
|
" "
|
|
39718
39986
|
] }),
|
|
39719
|
-
/* @__PURE__ */ jsx10(Text7, { color: s.offline ? C.dim : C.fg, bold:
|
|
39720
|
-
group ? /* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
|
|
39987
|
+
/* @__PURE__ */ jsx10(Text7, { color: stopped2 ? C.faint : s.offline ? C.dim : C.fg, bold: !stopped2, children: fit3(s.name, nameW) }),
|
|
39988
|
+
group ? /* @__PURE__ */ jsxs6(Text7, { color: stopped2 ? C.faint : C.dim, children: [
|
|
39721
39989
|
" ",
|
|
39722
|
-
fit3(
|
|
39990
|
+
fit3(roleTool, topicW - 1),
|
|
39723
39991
|
" "
|
|
39724
39992
|
] }) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
39725
39993
|
s.rig ? /* @__PURE__ */ jsxs6(Text7, { color: C.blue, children: [
|
|
@@ -39731,7 +39999,7 @@ function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0, group }
|
|
|
39731
39999
|
" "
|
|
39732
40000
|
] })
|
|
39733
40001
|
] }),
|
|
39734
|
-
/* @__PURE__ */ jsx10(Text7, { color: s.offline ? C.faint : STATE_COLOR[st], children: fit3(s.offline ? t.overview.offline : t.state[st], stateW).trimEnd() }),
|
|
40002
|
+
/* @__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
40003
|
n > 0 ? /* @__PURE__ */ jsxs6(Text7, { color: C.violet, children: [
|
|
39736
40004
|
" +",
|
|
39737
40005
|
n
|
|
@@ -39770,7 +40038,7 @@ var init_Overview = __esm({
|
|
|
39770
40038
|
// src/tui/images.tsx
|
|
39771
40039
|
import { mkdir as mkdir13, writeFile as writeFile17 } from "fs/promises";
|
|
39772
40040
|
import { tmpdir as tmpdir3 } from "os";
|
|
39773
|
-
import { join as
|
|
40041
|
+
import { join as join38 } from "path";
|
|
39774
40042
|
import { useRef as useRef8, useState as useState4 } from "react";
|
|
39775
40043
|
import { Text as Text8 } from "ink";
|
|
39776
40044
|
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
@@ -39873,9 +40141,9 @@ function useOpenImage() {
|
|
|
39873
40141
|
const { ref, route } = item;
|
|
39874
40142
|
void (async () => {
|
|
39875
40143
|
const { mime, data } = await getImage(client, ref.id, route);
|
|
39876
|
-
const dir =
|
|
40144
|
+
const dir = join38(tmpdir3(), "ledge-images");
|
|
39877
40145
|
await mkdir13(dir, { recursive: true, mode: 448 });
|
|
39878
|
-
const file =
|
|
40146
|
+
const file = join38(dir, `${ref.id}.${EXT2[mime]}`);
|
|
39879
40147
|
await writeFile17(file, Buffer.from(data, "base64"), { mode: 384 });
|
|
39880
40148
|
await openFile2(file);
|
|
39881
40149
|
})().catch((e) => toast({ text: t.images.openFailed(e instanceof AttachmentFailure ? e.reason : e.message), tone: "error" }));
|
|
@@ -40660,11 +40928,11 @@ function cardPlan(actions, room) {
|
|
|
40660
40928
|
const raw = rows.map((r) => r.state ?? "proposed");
|
|
40661
40929
|
const stopAt = raw.findIndex((s) => s === "failed" || s === "refused" || s === "stale");
|
|
40662
40930
|
const shown = states.map((s, i) => s === "proposed" && stopAt >= 0 && i > stopAt ? "held" : s);
|
|
40663
|
-
const
|
|
40931
|
+
const open6 = shown.flatMap((s, i) => ["done", "running", "denied", "stale", "cut"].includes(s) ? [] : [i]);
|
|
40664
40932
|
const skip = stopAt >= 0 && raw[stopAt] !== "failed";
|
|
40665
|
-
const rowsAll = skip ?
|
|
40933
|
+
const rowsAll = skip ? open6.filter((i) => i !== stopAt) : stopAt >= 0 && states[stopAt] === "cut" ? [] : open6;
|
|
40666
40934
|
const all2 = { kind: stopAt < 0 ? "all" : skip ? "skip" : "resume", rows: rowsAll, k: stopAt + 1 };
|
|
40667
|
-
return { rows, shown, open:
|
|
40935
|
+
return { rows, shown, open: open6, stopAt, all: all2, cutLines };
|
|
40668
40936
|
}
|
|
40669
40937
|
function firstRow(p) {
|
|
40670
40938
|
const act = p.shown.findIndex((s) => s === "proposed" || s === "held" || s === "failed");
|
|
@@ -42054,8 +42322,8 @@ function listItems2(roles, query, builtinOpen) {
|
|
|
42054
42322
|
const by = (a, b3) => a.name.localeCompare(b3.name) || a.id.localeCompare(b3.id);
|
|
42055
42323
|
const kept = roles.filter((r) => matches(r, query)).sort(by);
|
|
42056
42324
|
const mine = kept.filter((r) => r.source === "user"), builtin = kept.filter((r) => r.source === "builtin");
|
|
42057
|
-
const
|
|
42058
|
-
return { mine, builtin, items: [...mine.map((role) => ({ kind: "role", role })), ...builtin.length ? [{ kind: "builtin" }] : [], ...
|
|
42325
|
+
const open6 = builtinOpen || query.trim() !== "";
|
|
42326
|
+
return { mine, builtin, items: [...mine.map((role) => ({ kind: "role", role })), ...builtin.length ? [{ kind: "builtin" }] : [], ...open6 ? builtin.map((role) => ({ kind: "role", role })) : []] };
|
|
42059
42327
|
}
|
|
42060
42328
|
function listLines(roles, o, t, g) {
|
|
42061
42329
|
const r = t.roles, w = o.width;
|
|
@@ -42087,11 +42355,11 @@ function listLines(roles, o, t, g) {
|
|
|
42087
42355
|
if (mine.length) lines.push([]);
|
|
42088
42356
|
const s = i === o.sel;
|
|
42089
42357
|
if (s) selLine = lines.length;
|
|
42090
|
-
const
|
|
42358
|
+
const open6 = o.builtinOpen || o.query.trim() !== "";
|
|
42091
42359
|
const bg = s ? C.sel : void 0;
|
|
42092
|
-
lines.push([{ text: s ? "\u258C" : " ", color: s && o.focused ? C.coral : C.faint, ...bg ? { bg } : {} }, { text: `${
|
|
42360
|
+
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
42361
|
i++;
|
|
42094
|
-
if (
|
|
42362
|
+
if (open6) builtin.forEach(roleLine);
|
|
42095
42363
|
}
|
|
42096
42364
|
return { lines, selLine };
|
|
42097
42365
|
}
|
|
@@ -42187,7 +42455,7 @@ function param(label2, value, o, t, g, w, narrow, extra = {}) {
|
|
|
42187
42455
|
const usedL = cells(lab.map((x) => x.text).join("")), usedV = cells(val.map((x) => x.text).join(""));
|
|
42188
42456
|
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
42457
|
}
|
|
42190
|
-
function paramRows(d, seat2, files,
|
|
42458
|
+
function paramRows(d, seat2, files, open6, o, t) {
|
|
42191
42459
|
const r = t.roles, w = o.width, g = o.g, n = o.narrow;
|
|
42192
42460
|
const P = (label2, value, origin, extra = {}) => param(label2, value, origin, t, g, w, n, extra);
|
|
42193
42461
|
const out = [];
|
|
@@ -42228,8 +42496,8 @@ function paramRows(d, seat2, files, open5, o, t) {
|
|
|
42228
42496
|
const u = prof.uses;
|
|
42229
42497
|
const loadout = seat2?.live?.skills;
|
|
42230
42498
|
if (loadout) {
|
|
42231
|
-
out.push(P(r.p.skills, v(`${r.skillsProjected(loadout.length)} ${
|
|
42232
|
-
if (
|
|
42499
|
+
out.push(P(r.p.skills, v(`${r.skillsProjected(loadout.length)} ${open6.has("loadout") ? g.unfolded : g.folded}`), { kind: "inherited", from: "launch" }, { fold: "loadout" }));
|
|
42500
|
+
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
42501
|
}
|
|
42234
42502
|
const list3 = (label2, l, what, fold) => {
|
|
42235
42503
|
if (!l.declared) {
|
|
@@ -42243,9 +42511,9 @@ function paramRows(d, seat2, files, open5, o, t) {
|
|
|
42243
42511
|
l.items.forEach((x, i) => {
|
|
42244
42512
|
const na = !!(seat2 && x.runtime && seat2.runtime.value && x.runtime !== seat2.runtime.value);
|
|
42245
42513
|
const key = fold?.(x);
|
|
42246
|
-
const row3 = P(i === 0 ? label2 : "", v(`${what(x)}${na ? ` \xB7 ${r.na(x.runtime)}` : ""}${key ? ` ${
|
|
42514
|
+
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
42515
|
out.push(na ? { ...row3, line: row3.line.map((s) => ({ ...s, color: C.faint })) } : row3);
|
|
42248
|
-
if (key &&
|
|
42516
|
+
if (key && open6.has(key)) {
|
|
42249
42517
|
const body = x.plugin ? x.plugin.skills.join(", ") : (x.absolutePath && files[x.absolutePath]?.content) ?? t.roles.reading;
|
|
42250
42518
|
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
42519
|
}
|
|
@@ -42333,9 +42601,9 @@ function yamlRows(d, seat2, files, o, t) {
|
|
|
42333
42601
|
}
|
|
42334
42602
|
return out;
|
|
42335
42603
|
}
|
|
42336
|
-
function filesFor(d, seat2, tab, at,
|
|
42604
|
+
function filesFor(d, seat2, tab, at, open6) {
|
|
42337
42605
|
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 &&
|
|
42606
|
+
if (tab === 1) return (profileOf(d, seat2)?.uses.runtimeResources.items ?? []).filter((x) => x.absolutePath && open6.has(`rr:${x.id}`)).map((x) => x.absolutePath);
|
|
42339
42607
|
if (tab === 2) {
|
|
42340
42608
|
const p = pieces(d, seat2)[at];
|
|
42341
42609
|
return p?.kind === "entry" && p.entry.absolutePath && !isMissing(p.entry) ? [p.entry.absolutePath] : [];
|
|
@@ -42412,7 +42680,7 @@ function RolesScreen({ onRigs, machineId, onMachine }) {
|
|
|
42412
42680
|
const [scroll, setScroll] = useState14(0);
|
|
42413
42681
|
const [at, setAt] = useState14(0);
|
|
42414
42682
|
const [reading, setReading] = useState14(false);
|
|
42415
|
-
const [
|
|
42683
|
+
const [open6, setOpen] = useState14(/* @__PURE__ */ new Set());
|
|
42416
42684
|
const [files, setFiles] = useState14({});
|
|
42417
42685
|
const { items } = listItems2(roles.roles, query, builtinOpen);
|
|
42418
42686
|
const firstRole = items.find((i) => i.kind === "role");
|
|
@@ -42438,7 +42706,7 @@ function RolesScreen({ onRigs, machineId, onMachine }) {
|
|
|
42438
42706
|
asks.current.reset();
|
|
42439
42707
|
setFiles({});
|
|
42440
42708
|
}, [currentId, machine?.id]);
|
|
42441
|
-
const wanted = (d ? filesFor(d, seat2, shownTab, at,
|
|
42709
|
+
const wanted = (d ? filesFor(d, seat2, shownTab, at, open6) : []).join("\n");
|
|
42442
42710
|
useEffect13(() => {
|
|
42443
42711
|
if (!wanted || !machine || !currentId) return;
|
|
42444
42712
|
const id = currentId, mid = machine.id;
|
|
@@ -42451,7 +42719,7 @@ function RolesScreen({ onRigs, machineId, onMachine }) {
|
|
|
42451
42719
|
const bodyH = Math.max(3, rows - 4);
|
|
42452
42720
|
const head2 = d && summary ? headRows(d, summary, { seat: seat2, tab: shownTab, width: w, narrow: !wide, seats }, t) : [];
|
|
42453
42721
|
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,
|
|
42722
|
+
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
42723
|
const room = Math.max(1, bodyH - head2.length);
|
|
42456
42724
|
const maxScroll = Math.max(0, body.length - room);
|
|
42457
42725
|
const params = shownTab === 1 ? body.map((x, i) => x.rail ? i : -1).filter((i) => i >= 0) : [];
|
|
@@ -42709,7 +42977,7 @@ function RigsView({ initialMachine, onRoles }) {
|
|
|
42709
42977
|
const rigs = listedRigs(state2);
|
|
42710
42978
|
const [at, setAt] = useState15(0);
|
|
42711
42979
|
const selectedId = useRef13(null);
|
|
42712
|
-
const [
|
|
42980
|
+
const [open6, setOpen] = useState15(/* @__PURE__ */ new Set());
|
|
42713
42981
|
const [seatAt, setSeatAt] = useState15(null);
|
|
42714
42982
|
const [confirm, setConfirm] = useState15(null);
|
|
42715
42983
|
const [seats, setSeats] = useState15(null);
|
|
@@ -42719,7 +42987,7 @@ function RigsView({ initialMachine, onRoles }) {
|
|
|
42719
42987
|
}, [ids]);
|
|
42720
42988
|
const current2 = rigs[Math.min(at, rigs.length - 1)];
|
|
42721
42989
|
selectedId.current = current2?.id ?? null;
|
|
42722
|
-
const seatNodes = current2 &&
|
|
42990
|
+
const seatNodes = current2 && open6.has(current2.id) ? current2.nodes : [];
|
|
42723
42991
|
const seatSel = seatAt !== null && seatAt < seatNodes.length ? seatAt : null;
|
|
42724
42992
|
const down = () => {
|
|
42725
42993
|
if (seatSel !== null ? seatSel + 1 < seatNodes.length : seatNodes.length > 0) {
|
|
@@ -42737,7 +43005,7 @@ function RigsView({ initialMachine, onRoles }) {
|
|
|
42737
43005
|
}
|
|
42738
43006
|
const n = step2(at, -1, rigs.length), prev = rigs[n];
|
|
42739
43007
|
setAt(n);
|
|
42740
|
-
setSeatAt(prev &&
|
|
43008
|
+
setSeatAt(prev && open6.has(prev.id) && prev.nodes.length ? prev.nodes.length - 1 : null);
|
|
42741
43009
|
};
|
|
42742
43010
|
const run4 = (a) => {
|
|
42743
43011
|
void rig.act(a).then((d) => {
|
|
@@ -42897,12 +43165,12 @@ function RigsView({ initialMachine, onRoles }) {
|
|
|
42897
43165
|
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
43166
|
const crumb = [state2?.version ? o.version(state2.version) : o.title, machine?.label, t.roles.rigs].filter(Boolean).join(" \xB7 ");
|
|
42899
43167
|
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:
|
|
43168
|
+
!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
43169
|
confirm ? /* @__PURE__ */ jsx22(ConfirmBox, { confirm }) : null,
|
|
42902
43170
|
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
43171
|
] });
|
|
42904
43172
|
}
|
|
42905
|
-
function Body({ state: state2, rigs, at, seatAt, open:
|
|
43173
|
+
function Body({ state: state2, rigs, at, seatAt, open: open6, machineLabel: machineLabel2, roleOf: roleOf4 }) {
|
|
42906
43174
|
const { t } = useTui();
|
|
42907
43175
|
const o = t.openrig;
|
|
42908
43176
|
const up = daemonUp(state2);
|
|
@@ -42940,10 +43208,10 @@ function Body({ state: state2, rigs, at, seatAt, open: open5, machineLabel: mach
|
|
|
42940
43208
|
] }, n.session))
|
|
42941
43209
|
] })
|
|
42942
43210
|
] }),
|
|
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:
|
|
43211
|
+
/* @__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
43212
|
] });
|
|
42945
43213
|
}
|
|
42946
|
-
function RigRow({ rig, selected, seatAt, open:
|
|
43214
|
+
function RigRow({ rig, selected, seatAt, open: open6, roleOf: roleOf4 }) {
|
|
42947
43215
|
const { t } = useTui();
|
|
42948
43216
|
const o = t.openrig;
|
|
42949
43217
|
const look = rigLook(rig), l = LOOK[look];
|
|
@@ -42953,14 +43221,14 @@ function RigRow({ rig, selected, seatAt, open: open5, roleOf: roleOf4 }) {
|
|
|
42953
43221
|
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
42954
43222
|
/* @__PURE__ */ jsx22(Box17, { backgroundColor: selected ? C.sel : void 0, children: /* @__PURE__ */ jsxs17(Text18, { wrap: "truncate", children: [
|
|
42955
43223
|
/* @__PURE__ */ jsx22(Text18, { color: selected ? C.coral : C.faint, children: selected ? "\u258C" : " " }),
|
|
42956
|
-
/* @__PURE__ */ jsx22(Text18, { color: C.dim, children:
|
|
43224
|
+
/* @__PURE__ */ jsx22(Text18, { color: C.dim, children: open6 ? "\u25BE " : "\u25B8 " }),
|
|
42957
43225
|
/* @__PURE__ */ jsx22(Text18, { color: C.fg, bold: true, children: rig.name.padEnd(16) }),
|
|
42958
43226
|
/* @__PURE__ */ jsx22(Text18, { color: l.color(), children: ` ${l.glyph} ${o.look[look]}`.padEnd(18) }),
|
|
42959
43227
|
/* @__PURE__ */ jsx22(Text18, { color: C.fg, children: o.seats(rig.running, rig.seats).padEnd(7) }),
|
|
42960
43228
|
note ? /* @__PURE__ */ jsx22(Text18, { color: note.color, children: note.text.padEnd(34) }) : /* @__PURE__ */ jsx22(Text18, { children: "".padEnd(34) }),
|
|
42961
43229
|
/* @__PURE__ */ jsx22(Text18, { color: C.dim, children: when })
|
|
42962
43230
|
] }) }),
|
|
42963
|
-
|
|
43231
|
+
open6 ? rig.nodes.map((n, i) => {
|
|
42964
43232
|
const s = i === seatAt, bg = s ? C.sel : void 0, role = roleOf4(rig.name, n.logicalId);
|
|
42965
43233
|
return /* @__PURE__ */ jsx22(Box17, { backgroundColor: bg, children: /* @__PURE__ */ jsxs17(Text18, { wrap: "truncate", children: [
|
|
42966
43234
|
/* @__PURE__ */ jsx22(Text18, { color: C.coral, children: s ? "\u258C" : " " }),
|
|
@@ -43472,7 +43740,7 @@ function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeS
|
|
|
43472
43740
|
const attach2 = useCallback3((s) => {
|
|
43473
43741
|
const cmd = attachCommand(fleet, s);
|
|
43474
43742
|
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" });
|
|
43743
|
+
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
43744
|
return;
|
|
43477
43745
|
}
|
|
43478
43746
|
void suspendTerminal(async () => {
|
|
@@ -43772,7 +44040,7 @@ var init_glyphs = __esm({
|
|
|
43772
44040
|
|
|
43773
44041
|
// src/tui/settings.ts
|
|
43774
44042
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync13, renameSync as renameSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
43775
|
-
import { join as
|
|
44043
|
+
import { join as join39 } from "path";
|
|
43776
44044
|
import { z as z32 } from "zod";
|
|
43777
44045
|
function defaultLang(env) {
|
|
43778
44046
|
const locale = env.LC_ALL || env.LC_MESSAGES || env.LANG || "";
|
|
@@ -43798,14 +44066,14 @@ var init_settings = __esm({
|
|
|
43798
44066
|
"use strict";
|
|
43799
44067
|
init_paths();
|
|
43800
44068
|
SettingsSchema = z32.object({ lang: z32.enum(["fr", "en"]).optional(), theme: z32.enum(["auto", "dark", "light"]).optional() }).passthrough();
|
|
43801
|
-
settingsPath = () =>
|
|
44069
|
+
settingsPath = () => join39(ledgeDir(), "tui.json");
|
|
43802
44070
|
}
|
|
43803
44071
|
});
|
|
43804
44072
|
|
|
43805
44073
|
// src/tui/terminalProfile.ts
|
|
43806
44074
|
import { execFile as execFile10, spawn as spawn6 } from "child_process";
|
|
43807
|
-
import { mkdir as mkdir14, readFile as
|
|
43808
|
-
import { dirname as dirname13, join as
|
|
44075
|
+
import { mkdir as mkdir14, readFile as readFile19, realpath as realpath5, writeFile as writeFile18 } from "fs/promises";
|
|
44076
|
+
import { dirname as dirname13, join as join40 } from "path";
|
|
43809
44077
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
43810
44078
|
import { promisify } from "util";
|
|
43811
44079
|
function movesToLedgeTab(env, here, script) {
|
|
@@ -43833,13 +44101,13 @@ function fragment(commandline, iconWin) {
|
|
|
43833
44101
|
}
|
|
43834
44102
|
async function profileLoaded(localAppData) {
|
|
43835
44103
|
for (const rel of SETTINGS) {
|
|
43836
|
-
const text7 = await
|
|
44104
|
+
const text7 = await readFile19(join40(localAppData, rel), "utf8").catch(() => "");
|
|
43837
44105
|
if (new RegExp(`"source"\\s*:\\s*"${PROFILE}"`).test(text7)) return true;
|
|
43838
44106
|
}
|
|
43839
44107
|
return false;
|
|
43840
44108
|
}
|
|
43841
44109
|
async function writeIfChanged2(path3, content3) {
|
|
43842
|
-
const current2 = await
|
|
44110
|
+
const current2 = await readFile19(path3).catch(() => null);
|
|
43843
44111
|
if (current2 && current2.equals(Buffer.from(content3))) return false;
|
|
43844
44112
|
await writeFile18(path3, content3);
|
|
43845
44113
|
return true;
|
|
@@ -43851,9 +44119,9 @@ async function openInLedgeTab(o) {
|
|
|
43851
44119
|
await writeIfChanged2(o.launcher, launcherScript(o.node, o.script, o.ledgeHome));
|
|
43852
44120
|
const dir = fragmentDir(o.localAppData.linux);
|
|
43853
44121
|
await mkdir14(dir, { recursive: true });
|
|
43854
|
-
await writeIfChanged2(
|
|
44122
|
+
await writeIfChanged2(join40(dir, "ledge.ico"), await readFile19(o.icon ?? ICON));
|
|
43855
44123
|
const iconWin = `${o.localAppData.win}\\Microsoft\\Windows Terminal\\Fragments\\${PROFILE}\\ledge.ico`;
|
|
43856
|
-
await writeIfChanged2(
|
|
44124
|
+
await writeIfChanged2(join40(dir, "ledge.json"), fragment(commandline, iconWin));
|
|
43857
44125
|
const deadline = Date.now() + (o.waitMs ?? 15e3);
|
|
43858
44126
|
for (let first = true; !await profileLoaded(o.localAppData.linux); first = false) {
|
|
43859
44127
|
if (Date.now() > deadline) return false;
|
|
@@ -43874,11 +44142,11 @@ async function openTuiInLedgeTab(env, onWait) {
|
|
|
43874
44142
|
distro: env.WSL_DISTRO_NAME,
|
|
43875
44143
|
node: await realpath5(process.execPath),
|
|
43876
44144
|
script: await realpath5(process.argv[1]),
|
|
43877
|
-
launcher:
|
|
44145
|
+
launcher: join40(ledgeDir(), "tui.sh"),
|
|
43878
44146
|
ledgeHome: env.LEDGE_HOME,
|
|
43879
44147
|
onWait,
|
|
43880
44148
|
wt: (args) => new Promise((resolve13, reject) => {
|
|
43881
|
-
const child = spawn6(
|
|
44149
|
+
const child = spawn6(join40(linux, "Microsoft", "WindowsApps", "wt.exe"), args, { cwd: "/mnt/c", stdio: "ignore", detached: true });
|
|
43882
44150
|
child.once("error", reject);
|
|
43883
44151
|
child.once("spawn", () => {
|
|
43884
44152
|
child.unref();
|
|
@@ -43902,7 +44170,7 @@ var init_terminalProfile = __esm({
|
|
|
43902
44170
|
"Packages/Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe/LocalState/settings.json",
|
|
43903
44171
|
"Microsoft/Windows Terminal/settings.json"
|
|
43904
44172
|
];
|
|
43905
|
-
fragmentDir = (localAppData) =>
|
|
44173
|
+
fragmentDir = (localAppData) => join40(localAppData, "Microsoft", "Windows Terminal", "Fragments", PROFILE);
|
|
43906
44174
|
PROFILE_ID = "{d10fee13-0978-595f-a851-b60ec80f11c9}";
|
|
43907
44175
|
shQuote2 = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
43908
44176
|
run3 = promisify(execFile10);
|