@paradow/ledge 0.40.0 → 0.42.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 +799 -404
- package/dist/cli/reduceWorker.js +46 -9
- 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"() {
|
|
@@ -359,11 +365,15 @@ function pendingKey(p) {
|
|
|
359
365
|
const s = JSON.stringify([p.kind, p.text, p.body ?? null, p.tool ?? null, p.options ?? null, p.optionDetails ?? null, p.helperId ?? null]);
|
|
360
366
|
return cyrb53(s, 0) + cyrb53(s, 1);
|
|
361
367
|
}
|
|
362
|
-
var DecisionSchema, NOTE_MAX, NOTE_NOT_TYPED, DECISION_OFFERS;
|
|
368
|
+
var DecisionSchema, DecidedBySchema, NOTE_MAX, NOTE_NOT_TYPED, DECISION_OFFERS;
|
|
363
369
|
var init_decisions = __esm({
|
|
364
370
|
"../protocol/src/decisions.ts"() {
|
|
365
371
|
"use strict";
|
|
366
372
|
DecisionSchema = z6.enum(["accept", "deny", "always"]);
|
|
373
|
+
DecidedBySchema = z6.discriminatedUnion("kind", [
|
|
374
|
+
z6.object({ kind: z6.literal("pc") }),
|
|
375
|
+
z6.object({ kind: z6.literal("device"), id: z6.string().min(1).max(100), name: z6.string().min(1).max(60) })
|
|
376
|
+
]);
|
|
367
377
|
NOTE_MAX = 4e3;
|
|
368
378
|
NOTE_NOT_TYPED = "note_not_typed";
|
|
369
379
|
DECISION_OFFERS = {
|
|
@@ -2050,6 +2060,7 @@ var init_thread = __esm({
|
|
|
2050
2060
|
init_tasks();
|
|
2051
2061
|
init_attachments();
|
|
2052
2062
|
init_rigMessages();
|
|
2063
|
+
init_decisions();
|
|
2053
2064
|
ToolStatusSchema = z22.enum(["running", "success", "failed", "unknown"]);
|
|
2054
2065
|
FileChangeSchema = z22.object({ path: z22.string().min(1), from: z22.string().min(1).optional(), change: z22.enum(["add", "edit", "delete", "write"]), diff: z22.string().optional() });
|
|
2055
2066
|
base = { id: z22.string().min(1), at: z22.string().datetime(), turn: z22.number().int().nonnegative(), helperId: z22.string().optional() };
|
|
@@ -2075,8 +2086,8 @@ var init_thread = __esm({
|
|
|
2075
2086
|
imagesDropped: z22.literal(true).optional()
|
|
2076
2087
|
}),
|
|
2077
2088
|
/** `unknown`: answered in the terminal, the daemon only saw the prompt go away; `always`: allowed and not asked again;
|
|
2078
|
-
* `byClose`: denied by Ledge closing the session. */
|
|
2079
|
-
z22.object({ kind: z22.literal("permission"), ...base, tool: z22.string(), text: z22.string(), decision: z22.enum(["accept", "deny", "always", "unknown"]).optional(), byClose: z22.literal(true).optional() }),
|
|
2089
|
+
* `byClose`: denied by Ledge closing the session; `by`: who answered it from Ledge (the PC or a device, its name then). */
|
|
2090
|
+
z22.object({ kind: z22.literal("permission"), ...base, tool: z22.string(), text: z22.string(), decision: z22.enum(["accept", "deny", "always", "unknown"]).optional(), byClose: z22.literal(true).optional(), by: DecidedBySchema.optional() }),
|
|
2080
2091
|
z22.object({ kind: z22.literal("reply"), ...base, text: z22.string() }),
|
|
2081
2092
|
z22.object({ kind: z22.literal("helper"), ...base, task: z22.string(), agentType: z22.string().optional(), state: z22.enum(["working", "exited"]) }),
|
|
2082
2093
|
/** A message this session sent to another agent; `id` is the tool_use_id, `to` the address it used, `peer` the Ledge session id, `received` the paired item's id in the peer's thread, `via` set when Ledge's mailbox carries it. */
|
|
@@ -2135,7 +2146,10 @@ var init_thread = __esm({
|
|
|
2135
2146
|
|
|
2136
2147
|
// ../protocol/src/mobile.ts
|
|
2137
2148
|
import { z as z23 } from "zod";
|
|
2138
|
-
|
|
2149
|
+
function deviceKindOfLabel(label2) {
|
|
2150
|
+
return /\b(iPad|tablet|tablette)\b/i.test(label2) ? "tablet" : "phone";
|
|
2151
|
+
}
|
|
2152
|
+
var DEFAULT_RELAY_URL, RelayUrlSchema, MobileConfigSchema, MobileLinkStateSchema, DeviceKindSchema, DEVICE_NAME_MAX, DeviceNameSchema, MobilePairingSchema, MobileStateSchema;
|
|
2139
2153
|
var init_mobile = __esm({
|
|
2140
2154
|
"../protocol/src/mobile.ts"() {
|
|
2141
2155
|
"use strict";
|
|
@@ -2166,6 +2180,9 @@ var init_mobile = __esm({
|
|
|
2166
2180
|
/** The mobile service did not start (a broken mobile.json, a missing key store): nothing is linked until the daemon restarts. */
|
|
2167
2181
|
z23.object({ state: z23.literal("unavailable"), error: z23.string().max(200) })
|
|
2168
2182
|
]);
|
|
2183
|
+
DeviceKindSchema = z23.enum(["phone", "tablet"]);
|
|
2184
|
+
DEVICE_NAME_MAX = 40;
|
|
2185
|
+
DeviceNameSchema = z23.string().trim().min(1).max(DEVICE_NAME_MAX).regex(/^[^\u0000-\u001f\u007f]+$/);
|
|
2169
2186
|
MobilePairingSchema = z23.object({
|
|
2170
2187
|
id: z23.string(),
|
|
2171
2188
|
label: z23.string(),
|
|
@@ -2181,7 +2198,16 @@ var init_mobile = __esm({
|
|
|
2181
2198
|
/** The last sign-in that did not end with a session; cleared by the next one. */
|
|
2182
2199
|
signinError: z23.enum(["expired", "denied", "not_allowed", "failed"]).nullable(),
|
|
2183
2200
|
link: MobileLinkStateSchema,
|
|
2184
|
-
devices: z23.array(z23.object({
|
|
2201
|
+
devices: z23.array(z23.object({
|
|
2202
|
+
id: z23.string(),
|
|
2203
|
+
label: z23.string(),
|
|
2204
|
+
approvedAt: z23.string(),
|
|
2205
|
+
online: z23.boolean(),
|
|
2206
|
+
lastSeenAt: z23.string().optional(),
|
|
2207
|
+
/** Absent from an older daemon; `name` absent: the user gave none, the front derives one from the label. */
|
|
2208
|
+
kind: DeviceKindSchema.optional(),
|
|
2209
|
+
name: z23.string().optional()
|
|
2210
|
+
})),
|
|
2185
2211
|
pairings: z23.array(MobilePairingSchema),
|
|
2186
2212
|
/** « Taper dans les terminaux depuis le téléphone »: absent from an older daemon, which has no phone terminal. */
|
|
2187
2213
|
terminalInput: z23.boolean().optional()
|
|
@@ -2273,6 +2299,8 @@ var init_messages = __esm({
|
|
|
2273
2299
|
decision: DecisionSchema.optional(),
|
|
2274
2300
|
/** Decided: the request was refused by Ledge closing the session (its interrupt keys), not by the user's answer. */
|
|
2275
2301
|
byClose: z24.literal(true).optional(),
|
|
2302
|
+
/** Decided: who answered, when the daemon typed it (absent: answered in the terminal, or an older daemon). */
|
|
2303
|
+
by: DecidedBySchema.optional(),
|
|
2276
2304
|
/** The agent's own session id when the hook names it, so the daemon can resume that conversation. */
|
|
2277
2305
|
agentSessionId: z24.string().optional()
|
|
2278
2306
|
});
|
|
@@ -2549,7 +2577,9 @@ var init_messages = __esm({
|
|
|
2549
2577
|
note: z24.string().trim().min(1).max(NOTE_MAX).optional(),
|
|
2550
2578
|
/** The request the user saw when deciding: refused (the request changed) unless it is still the pending one. Left out by older fronts and the island.
|
|
2551
2579
|
* `key` (pendingKey) covers its whole content; older fronts send kind and text only. */
|
|
2552
|
-
expect: PendingSchema.pick({ kind: true, text: true }).extend({ key: z24.string().max(64).optional() }).optional()
|
|
2580
|
+
expect: PendingSchema.pick({ kind: true, text: true }).extend({ key: z24.string().max(64).optional() }).optional(),
|
|
2581
|
+
/** Who decides: set by the phone gateway for a device (over anything the device sent); left out by a local front, which means the PC. */
|
|
2582
|
+
by: DecidedBySchema.optional()
|
|
2553
2583
|
}),
|
|
2554
2584
|
z24.object({ type: z24.literal("session.focus"), id: z24.string() }),
|
|
2555
2585
|
z24.object({ type: z24.literal("session.kill"), id: z24.string() }),
|
|
@@ -2603,10 +2633,13 @@ var init_messages = __esm({
|
|
|
2603
2633
|
z24.object({ type: z24.literal("mobile.signout") }),
|
|
2604
2634
|
/** While the link is offline: try again now. */
|
|
2605
2635
|
z24.object({ type: z24.literal("mobile.retry") }),
|
|
2606
|
-
|
|
2636
|
+
/** `replaces`: an approved device this one takes the place of (the same app reinstalled), revoked once this one is stored. */
|
|
2637
|
+
z24.object({ type: z24.literal("mobile.pairing.approve"), id: z24.string().min(1).max(100), replaces: z24.string().min(1).max(100).optional() }),
|
|
2607
2638
|
z24.object({ type: z24.literal("mobile.pairing.deny"), id: z24.string().min(1).max(100) }),
|
|
2608
2639
|
/** `id`: the device's fingerprint. */
|
|
2609
2640
|
z24.object({ type: z24.literal("mobile.device.revoke"), id: z24.string().min(1).max(100) }),
|
|
2641
|
+
/** Names a device (`id`: its fingerprint); the device sees its new name in its next snapshot. */
|
|
2642
|
+
z24.object({ type: z24.literal("mobile.device.rename"), id: z24.string().min(1).max(100), name: DeviceNameSchema }),
|
|
2610
2643
|
/** « Taper dans les terminaux depuis le téléphone »; turned off, every phone loses the hand. */
|
|
2611
2644
|
z24.object({ type: z24.literal("mobile.terminal"), input: z24.boolean() }),
|
|
2612
2645
|
/** « Reprendre la main »: the phone typing into the session's terminal goes back to reading it. */
|
|
@@ -2863,7 +2896,9 @@ var init_push = __esm({
|
|
|
2863
2896
|
});
|
|
2864
2897
|
PushPayloadSchema = z27.object({
|
|
2865
2898
|
v: z27.literal(1),
|
|
2866
|
-
|
|
2899
|
+
/** `resolved`: a request or question answered (anywhere), replacing its notification under the same tag; only sent to a
|
|
2900
|
+
* device whose subscription says its service worker knows it (`push.subscribe` `resolved`). */
|
|
2901
|
+
kind: z27.enum(["request", "question", "finished", "chat", "decision", "resolved"]),
|
|
2867
2902
|
title: z27.string().max(80),
|
|
2868
2903
|
body: z27.string().max(120),
|
|
2869
2904
|
url: z27.string().regex(/^\/#\/[A-Za-z0-9/%._~-]*$/).max(300),
|
|
@@ -2896,6 +2931,7 @@ var init_phone = __esm({
|
|
|
2896
2931
|
init_messages();
|
|
2897
2932
|
init_broadcast();
|
|
2898
2933
|
init_push();
|
|
2934
|
+
init_mobile();
|
|
2899
2935
|
init_models();
|
|
2900
2936
|
init_commands();
|
|
2901
2937
|
init_openrig();
|
|
@@ -3167,8 +3203,11 @@ var init_phone = __esm({
|
|
|
3167
3203
|
z28.object({ type: z28.literal("openrig.row"), qitem: QitemIdSchema }),
|
|
3168
3204
|
/** The phone drops its passkey from the daemon. */
|
|
3169
3205
|
z28.object({ type: z28.literal("device.forget"), op: OperationIdSchema }),
|
|
3170
|
-
/** The
|
|
3171
|
-
z28.object({ type: z28.literal("
|
|
3206
|
+
/** The device names itself (on the PC and in its next snapshot); applied to the sender only. */
|
|
3207
|
+
z28.object({ type: z28.literal("device.rename"), op: OperationIdSchema, name: DeviceNameSchema }),
|
|
3208
|
+
/** The phone's push subscription, with the VAPID key it was made with (so a key change is seen). `resolved`: its service
|
|
3209
|
+
* worker shows a `resolved` push, which replaces a request's notification once it is answered elsewhere. */
|
|
3210
|
+
z28.object({ type: z28.literal("push.subscribe"), op: OperationIdSchema, subscription: PushSubscriptionSchema, key: B64U2.length(87), resolved: z28.literal(true).optional() }),
|
|
3172
3211
|
z28.object({ type: z28.literal("push.unsubscribe"), op: OperationIdSchema }),
|
|
3173
3212
|
/** The phone's own chat with Ledge. */
|
|
3174
3213
|
z28.object({ type: z28.literal("chat.ask"), qid: OperationIdSchema, text: z28.string().trim().max(MESSAGE_MAX), attachments: z28.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional() }),
|
|
@@ -3260,7 +3299,16 @@ var init_phone = __esm({
|
|
|
3260
3299
|
openrig: z28.literal(true).optional(),
|
|
3261
3300
|
/** The daemon takes `session.broadcast` (« Écrire au groupe »). */
|
|
3262
3301
|
broadcast: z28.literal(true).optional()
|
|
3263
|
-
}), device: z28.object({
|
|
3302
|
+
}), device: z28.object({
|
|
3303
|
+
confirm: z28.boolean(),
|
|
3304
|
+
push: z28.enum(["on", "off", "stale", "unavailable"]).optional(),
|
|
3305
|
+
chat: z28.literal(true).optional(),
|
|
3306
|
+
contextNear: ContextPrefsSchema.shape.near.optional(),
|
|
3307
|
+
/** This device's id (its fingerprint, as decisions name it in `by`), its kind, and the name the user gave it (absent: none given). */
|
|
3308
|
+
id: z28.string().optional(),
|
|
3309
|
+
kind: DeviceKindSchema.optional(),
|
|
3310
|
+
name: z28.string().optional()
|
|
3311
|
+
}).optional() }),
|
|
3264
3312
|
/** « À toi » on this PC: the answer to `openrig.asks`, then each change, whole. */
|
|
3265
3313
|
z28.object({ type: z28.literal("openrig.asks"), channel: HumanChannelSchema, asks: z28.array(HumanAskSchema), partial: z28.literal(true).optional(), truncated: z28.literal(true).optional() }),
|
|
3266
3314
|
/** An answer's end, as on the desktop (`openrig.answered`), under the phone's `op`. `rejected`: the device check refused it (`reason`). */
|
|
@@ -3788,11 +3836,11 @@ function stripNote(text7) {
|
|
|
3788
3836
|
}
|
|
3789
3837
|
function formatLedgeMessage(m) {
|
|
3790
3838
|
const from = attr(m.from);
|
|
3791
|
-
const
|
|
3792
|
-
if (!m.id || inline(m.text)) return `${
|
|
3839
|
+
const open6 = `<ledge-message from="${from}" tool="${m.tool}"${m.id ? ` id="${attr(m.id)}"` : ""}>${messageNote(from, m.to)} `;
|
|
3840
|
+
if (!m.id || inline(m.text)) return `${open6}${ledgeBody(m.text)}${LEDGE_CLOSE}`;
|
|
3793
3841
|
const first = m.summary?.trim() || m.text.trim().split(/\r\n|\r|\n/)[0].trim();
|
|
3794
3842
|
const head2 = ledgeBody(first.length > POINTER_HEAD ? `${first.slice(0, POINTER_HEAD)}\u2026` : first);
|
|
3795
|
-
return `${
|
|
3843
|
+
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
3844
|
}
|
|
3797
3845
|
function ledgeMessages(text7) {
|
|
3798
3846
|
const out = [];
|
|
@@ -4947,7 +4995,7 @@ __export(who_exports, {
|
|
|
4947
4995
|
});
|
|
4948
4996
|
import { basename as basename5 } from "path";
|
|
4949
4997
|
function formatWho(sessions, self, projects) {
|
|
4950
|
-
const live3 = sessions.filter((s) => s.state !== "exited" && !isArchived(s));
|
|
4998
|
+
const live3 = sessions.filter((s) => s.state !== "exited" && !isArchived(s) || isStoppedOmpSeat(s));
|
|
4951
4999
|
if (live3.length === 0) return "aucune autre session";
|
|
4952
5000
|
const rows = live3.map((s) => [
|
|
4953
5001
|
s.id === self ? "*" : " ",
|
|
@@ -5195,8 +5243,8 @@ function underCodexDaemon(read = (p) => readFileSync5(p, "utf8")) {
|
|
|
5195
5243
|
let pid = process.pid;
|
|
5196
5244
|
for (let depth = 0; depth < 8; depth++) {
|
|
5197
5245
|
try {
|
|
5198
|
-
const
|
|
5199
|
-
pid = Number(
|
|
5246
|
+
const stat8 = read(`/proc/${pid}/stat`);
|
|
5247
|
+
pid = Number(stat8.slice(stat8.lastIndexOf(")") + 2).split(" ")[1]);
|
|
5200
5248
|
if (!pid || pid <= 1) return false;
|
|
5201
5249
|
if (read(`/proc/${pid}/cmdline`).split("\0").includes("app-server")) return true;
|
|
5202
5250
|
} catch {
|
|
@@ -6557,7 +6605,7 @@ var init_history = __esm({
|
|
|
6557
6605
|
return q ? this.put(id, { ...q, status: "unknown", byClose: true }) : void 0;
|
|
6558
6606
|
}
|
|
6559
6607
|
const perm = [...this.items(id)].reverse().find((i) => i.kind === "permission" && i.decision === void 0);
|
|
6560
|
-
return perm && perm.kind === "permission" ? this.put(id, { ...perm, decision: ev.decision, ...ev.byClose ? { byClose: true } : {} }) : void 0;
|
|
6608
|
+
return perm && perm.kind === "permission" ? this.put(id, { ...perm, decision: ev.decision, ...ev.byClose ? { byClose: true } : {}, ...ev.by ? { by: ev.by } : {} }) : void 0;
|
|
6561
6609
|
}
|
|
6562
6610
|
case "AgentMessage": {
|
|
6563
6611
|
if (!ev.messageId || !ev.text) return void 0;
|
|
@@ -6764,9 +6812,9 @@ ${done.output}` : command;
|
|
|
6764
6812
|
const peer = (m.sender ? this.peerOf(m.sender) : void 0) ?? this.peerOf(m.from) ?? (m.name ? this.peerOf(m.name) : void 0);
|
|
6765
6813
|
if (!peer) return this.put(id, recv);
|
|
6766
6814
|
const head2 = (t) => ledgeBody(t).trim().slice(0, 200);
|
|
6767
|
-
const
|
|
6768
|
-
const byId2 = m.id ?
|
|
6769
|
-
const sent = byId2 ?? [...
|
|
6815
|
+
const open6 = this.items(peer).filter((i) => i.kind === "sent" && i.peer === id && i.status !== "read");
|
|
6816
|
+
const byId2 = m.id ? open6.find((i) => mailId(i.id) === m.id) : void 0;
|
|
6817
|
+
const sent = byId2 ?? [...open6].reverse().find((i) => head2(i.text) === head2(recv.text)) ?? open6.at(-1);
|
|
6770
6818
|
if (sent) this.put(peer, { ...sent, status: "read", received: recv.id });
|
|
6771
6819
|
return this.put(id, { ...recv, ...byId2 ? { text: byId2.text } : {}, peer, ...sent ? { sent: sent.id } : {} });
|
|
6772
6820
|
}
|
|
@@ -7838,6 +7886,203 @@ var init_verify = __esm({
|
|
|
7838
7886
|
}
|
|
7839
7887
|
});
|
|
7840
7888
|
|
|
7889
|
+
// src/daemon/omp/seats.ts
|
|
7890
|
+
import { homedir as homedir5 } from "os";
|
|
7891
|
+
import { join as join10 } from "path";
|
|
7892
|
+
import { open, readFile as readFile7, stat as stat2 } from "fs/promises";
|
|
7893
|
+
function entryState(raw) {
|
|
7894
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
7895
|
+
const e = raw;
|
|
7896
|
+
const at = typeof e.timestamp === "string" && !Number.isNaN(Date.parse(e.timestamp)) ? new Date(e.timestamp).toISOString() : void 0;
|
|
7897
|
+
const of = (state2) => ({ state: state2, ...at ? { at } : {} });
|
|
7898
|
+
if (e.type === "custom_message") return of("working");
|
|
7899
|
+
if (e.type === "custom") return e.customType === "tool_execution_start" ? of("working") : null;
|
|
7900
|
+
if (e.type !== "message" || !e.message || typeof e.message !== "object") return null;
|
|
7901
|
+
const m = e.message;
|
|
7902
|
+
if (m.role === "user" || m.role === "toolResult") return of("working");
|
|
7903
|
+
if (m.role !== "assistant") return null;
|
|
7904
|
+
return typeof m.stopReason === "string" ? of(m.stopReason === "toolUse" ? "working" : "idle") : null;
|
|
7905
|
+
}
|
|
7906
|
+
async function nextNewline(fh, from, size) {
|
|
7907
|
+
const buf2 = Buffer.alloc(BLOCK3);
|
|
7908
|
+
for (let at = from; at < size; ) {
|
|
7909
|
+
const { bytesRead } = await fh.read(buf2, 0, Math.min(BLOCK3, size - at), at);
|
|
7910
|
+
if (bytesRead === 0) return -1;
|
|
7911
|
+
const nl = buf2.subarray(0, bytesRead).indexOf(10);
|
|
7912
|
+
if (nl !== -1) return at + nl;
|
|
7913
|
+
at += bytesRead;
|
|
7914
|
+
}
|
|
7915
|
+
return -1;
|
|
7916
|
+
}
|
|
7917
|
+
function createOmpSeats(deps) {
|
|
7918
|
+
const { store } = deps;
|
|
7919
|
+
const root2 = deps.root ?? ompSeatsRoot();
|
|
7920
|
+
const follows = /* @__PURE__ */ new Map();
|
|
7921
|
+
let running = false, timer2 = null;
|
|
7922
|
+
async function journalOf(seat2) {
|
|
7923
|
+
try {
|
|
7924
|
+
const file = JSON.parse(await readFile7(join10(root2, seat2, "runner-state.json"), "utf8")).sessionFile;
|
|
7925
|
+
return typeof file === "string" && file ? file : null;
|
|
7926
|
+
} catch {
|
|
7927
|
+
return null;
|
|
7928
|
+
}
|
|
7929
|
+
}
|
|
7930
|
+
async function followOne(s) {
|
|
7931
|
+
const file = await journalOf(s.rig.seat);
|
|
7932
|
+
if (!file) return;
|
|
7933
|
+
let size;
|
|
7934
|
+
try {
|
|
7935
|
+
size = (await stat2(file)).size;
|
|
7936
|
+
} catch {
|
|
7937
|
+
return;
|
|
7938
|
+
}
|
|
7939
|
+
let f = follows.get(s.id);
|
|
7940
|
+
if (!f || f.file !== file) {
|
|
7941
|
+
const known2 = await lastState(file, size);
|
|
7942
|
+
f = { file, offset: known2.end };
|
|
7943
|
+
follows.set(s.id, f);
|
|
7944
|
+
if (known2.state) apply(s.id, known2.state);
|
|
7945
|
+
return;
|
|
7946
|
+
}
|
|
7947
|
+
if (size < f.offset) {
|
|
7948
|
+
f.offset = size;
|
|
7949
|
+
return;
|
|
7950
|
+
}
|
|
7951
|
+
if (size === f.offset) return;
|
|
7952
|
+
const fh = await open(file, "r");
|
|
7953
|
+
let chunk, end2;
|
|
7954
|
+
try {
|
|
7955
|
+
const readAt2 = async (want) => {
|
|
7956
|
+
const b3 = Buffer.alloc(want);
|
|
7957
|
+
return b3.subarray(0, (await fh.read(b3, 0, want, f.offset)).bytesRead);
|
|
7958
|
+
};
|
|
7959
|
+
chunk = await readAt2(Math.min(size - f.offset, READ_BYTES));
|
|
7960
|
+
end2 = chunk.lastIndexOf(10);
|
|
7961
|
+
if (end2 === -1 && chunk.length < size - f.offset) {
|
|
7962
|
+
chunk = await readAt2(Math.min(size - f.offset, BACK_BYTES));
|
|
7963
|
+
end2 = chunk.lastIndexOf(10);
|
|
7964
|
+
}
|
|
7965
|
+
if (end2 === -1 && chunk.length === BACK_BYTES) {
|
|
7966
|
+
const next4 = await nextNewline(fh, f.offset + chunk.length, size);
|
|
7967
|
+
if (next4 !== -1) {
|
|
7968
|
+
log(`omp seats: an entry of ${s.rig.seat} over ${BACK_BYTES} bytes was skipped`);
|
|
7969
|
+
f.offset = next4 + 1;
|
|
7970
|
+
}
|
|
7971
|
+
return;
|
|
7972
|
+
}
|
|
7973
|
+
} finally {
|
|
7974
|
+
await fh.close();
|
|
7975
|
+
}
|
|
7976
|
+
if (end2 === -1) return;
|
|
7977
|
+
const lines = chunk.subarray(0, end2).toString("utf8").split("\n");
|
|
7978
|
+
f.offset += end2 + 1;
|
|
7979
|
+
let last = null;
|
|
7980
|
+
for (const line2 of lines) {
|
|
7981
|
+
if (!line2.trim()) continue;
|
|
7982
|
+
let entry2;
|
|
7983
|
+
try {
|
|
7984
|
+
entry2 = JSON.parse(line2);
|
|
7985
|
+
} catch {
|
|
7986
|
+
continue;
|
|
7987
|
+
}
|
|
7988
|
+
last = entryState(entry2) ?? last;
|
|
7989
|
+
}
|
|
7990
|
+
if (last) apply(s.id, last);
|
|
7991
|
+
}
|
|
7992
|
+
function apply(id, last) {
|
|
7993
|
+
const cur = store.get(id);
|
|
7994
|
+
if (!cur || cur.state === "exited") return;
|
|
7995
|
+
const at = last.at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
7996
|
+
if (cur.state !== last.state || at > cur.lastActivityAt) store.upsert({ ...cur, state: last.state, lastActivityAt: at > cur.lastActivityAt ? at : cur.lastActivityAt });
|
|
7997
|
+
}
|
|
7998
|
+
async function lastState(file, size) {
|
|
7999
|
+
const fh = await open(file, "r");
|
|
8000
|
+
try {
|
|
8001
|
+
const newlineBefore = async (before2) => {
|
|
8002
|
+
const buf2 = Buffer.alloc(BLOCK3);
|
|
8003
|
+
for (let to = before2; to > 0; ) {
|
|
8004
|
+
if (size - to > BACK_BYTES) return null;
|
|
8005
|
+
const from = Math.max(0, to - BLOCK3);
|
|
8006
|
+
const { bytesRead } = await fh.read(buf2, 0, to - from, from);
|
|
8007
|
+
const at = buf2.subarray(0, bytesRead).lastIndexOf(10);
|
|
8008
|
+
if (at !== -1) return from + at;
|
|
8009
|
+
to = from;
|
|
8010
|
+
}
|
|
8011
|
+
return -1;
|
|
8012
|
+
};
|
|
8013
|
+
const last = await newlineBefore(size);
|
|
8014
|
+
if (last === null || last === -1) return { state: null, end: last === -1 ? 0 : size };
|
|
8015
|
+
const end2 = last + 1;
|
|
8016
|
+
for (let lineEnd = last; lineEnd > 0; ) {
|
|
8017
|
+
const start = await newlineBefore(lineEnd);
|
|
8018
|
+
if (start === null) break;
|
|
8019
|
+
const from = start + 1;
|
|
8020
|
+
const line2 = Buffer.alloc(lineEnd - from);
|
|
8021
|
+
await fh.read(line2, 0, line2.length, from);
|
|
8022
|
+
let entry2 = null;
|
|
8023
|
+
try {
|
|
8024
|
+
entry2 = JSON.parse(line2.toString("utf8"));
|
|
8025
|
+
} catch {
|
|
8026
|
+
}
|
|
8027
|
+
const state2 = entryState(entry2);
|
|
8028
|
+
if (state2) return { state: state2, end: end2 };
|
|
8029
|
+
if (start === -1) break;
|
|
8030
|
+
lineEnd = start;
|
|
8031
|
+
}
|
|
8032
|
+
return { state: null, end: end2 };
|
|
8033
|
+
} finally {
|
|
8034
|
+
await fh.close();
|
|
8035
|
+
}
|
|
8036
|
+
}
|
|
8037
|
+
async function tick() {
|
|
8038
|
+
if (running) return;
|
|
8039
|
+
running = true;
|
|
8040
|
+
try {
|
|
8041
|
+
const live3 = /* @__PURE__ */ new Set();
|
|
8042
|
+
for (const s of store.all()) {
|
|
8043
|
+
if (!isOmpSeat2(s) || !s.rig || s.state === "exited" || isArchived(s)) continue;
|
|
8044
|
+
live3.add(s.id);
|
|
8045
|
+
try {
|
|
8046
|
+
await followOne(s);
|
|
8047
|
+
} catch (e) {
|
|
8048
|
+
log(`omp seats: journal of ${s.rig.seat} not read: ${e.message}`);
|
|
8049
|
+
}
|
|
8050
|
+
}
|
|
8051
|
+
for (const id of follows.keys()) if (!live3.has(id)) follows.delete(id);
|
|
8052
|
+
} finally {
|
|
8053
|
+
running = false;
|
|
8054
|
+
}
|
|
8055
|
+
}
|
|
8056
|
+
return {
|
|
8057
|
+
tick,
|
|
8058
|
+
start() {
|
|
8059
|
+
if (!timer2) {
|
|
8060
|
+
timer2 = setInterval(() => {
|
|
8061
|
+
void tick();
|
|
8062
|
+
}, deps.intervalMs ?? 1e3);
|
|
8063
|
+
timer2.unref?.();
|
|
8064
|
+
}
|
|
8065
|
+
},
|
|
8066
|
+
stop() {
|
|
8067
|
+
if (timer2) clearInterval(timer2);
|
|
8068
|
+
timer2 = null;
|
|
8069
|
+
}
|
|
8070
|
+
};
|
|
8071
|
+
}
|
|
8072
|
+
var ompSeatsRoot, BACK_BYTES, BLOCK3, READ_BYTES, isOmpSeat2;
|
|
8073
|
+
var init_seats = __esm({
|
|
8074
|
+
"src/daemon/omp/seats.ts"() {
|
|
8075
|
+
"use strict";
|
|
8076
|
+
init_src();
|
|
8077
|
+
init_log();
|
|
8078
|
+
ompSeatsRoot = (env = process.env) => join10(env.OPENRIG_HOME?.trim() || join10(homedir5(), ".openrig"), "state", "omp");
|
|
8079
|
+
BACK_BYTES = 16 * 1024 * 1024;
|
|
8080
|
+
BLOCK3 = 64 * 1024;
|
|
8081
|
+
READ_BYTES = 4 * 1024 * 1024;
|
|
8082
|
+
isOmpSeat2 = (s) => s.source === "rig" && s.tool === "omp" && s.backend === void 0;
|
|
8083
|
+
}
|
|
8084
|
+
});
|
|
8085
|
+
|
|
7841
8086
|
// src/daemon/poller.ts
|
|
7842
8087
|
import { createHash as createHash4 } from "crypto";
|
|
7843
8088
|
function startPoller(opts) {
|
|
@@ -7902,7 +8147,7 @@ function startPoller(opts) {
|
|
|
7902
8147
|
}
|
|
7903
8148
|
if (s.state === "exited") {
|
|
7904
8149
|
forget(s.id);
|
|
7905
|
-
if (!s.archivedAt && s.exitedAt) {
|
|
8150
|
+
if (!s.archivedAt && s.exitedAt && !isOmpSeat2(s)) {
|
|
7906
8151
|
const since = Math.max(Date.parse(s.exitedAt), s.restoredAt ? Date.parse(s.restoredAt) : -Infinity);
|
|
7907
8152
|
if (t - since > retireMs) store.archive(s.id, now2());
|
|
7908
8153
|
}
|
|
@@ -7919,6 +8164,7 @@ function startPoller(opts) {
|
|
|
7919
8164
|
forget(s.id);
|
|
7920
8165
|
return;
|
|
7921
8166
|
}
|
|
8167
|
+
if (isOmpSeat2(s)) return;
|
|
7922
8168
|
const full = await tmux.capture(s.tmuxSession, PREVIEW_CAPTURE_LINES);
|
|
7923
8169
|
if (full === null) return;
|
|
7924
8170
|
const pane = full.split("\n").slice(-DETECT_LINES).join("\n");
|
|
@@ -7983,6 +8229,7 @@ var init_poller = __esm({
|
|
|
7983
8229
|
init_log();
|
|
7984
8230
|
init_git();
|
|
7985
8231
|
init_transcript();
|
|
8232
|
+
init_seats();
|
|
7986
8233
|
init_adapters();
|
|
7987
8234
|
DETECT_LINES = 60;
|
|
7988
8235
|
REST_MS = 2e3;
|
|
@@ -8528,7 +8775,7 @@ var init_project = __esm({
|
|
|
8528
8775
|
|
|
8529
8776
|
// src/daemon/omp/host.ts
|
|
8530
8777
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
8531
|
-
import { basename as basename7, join as
|
|
8778
|
+
import { basename as basename7, join as join11 } from "path";
|
|
8532
8779
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
8533
8780
|
function ompArgs(o) {
|
|
8534
8781
|
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 +8787,7 @@ function createOmpHost(deps) {
|
|
|
8540
8787
|
const live3 = /* @__PURE__ */ new Map();
|
|
8541
8788
|
let generations = 0;
|
|
8542
8789
|
async function startProcess(id, o) {
|
|
8543
|
-
const dir =
|
|
8790
|
+
const dir = join11(deps.dir, id);
|
|
8544
8791
|
await mkdir7(dir, { recursive: true });
|
|
8545
8792
|
if (await reapOwner(dir)) log(`omp ${id}: ended the process a previous daemon left`);
|
|
8546
8793
|
const key = sessionKey(id);
|
|
@@ -8720,15 +8967,15 @@ function createOmpHost(deps) {
|
|
|
8720
8967
|
async answer(id, text7) {
|
|
8721
8968
|
const l = live3.get(id);
|
|
8722
8969
|
if (!l) return "the session is not running";
|
|
8723
|
-
const
|
|
8724
|
-
if (!
|
|
8725
|
-
const a = answerFor(
|
|
8970
|
+
const open6 = l.tracking.requests[0];
|
|
8971
|
+
if (!open6) return "nothing to answer";
|
|
8972
|
+
const a = answerFor(open6, text7);
|
|
8726
8973
|
if ("refused" in a) return a.refused;
|
|
8727
8974
|
l.tracking.requests.shift();
|
|
8728
8975
|
try {
|
|
8729
8976
|
await l.rpc.write(a.frame);
|
|
8730
8977
|
} catch (e) {
|
|
8731
|
-
l.tracking.requests.unshift(
|
|
8978
|
+
l.tracking.requests.unshift(open6);
|
|
8732
8979
|
return e.message;
|
|
8733
8980
|
}
|
|
8734
8981
|
store.apply(id, { name: "Decided", at: now2(), source: "daemon" });
|
|
@@ -8777,15 +9024,15 @@ function createOmpHost(deps) {
|
|
|
8777
9024
|
async answerAsk(id, requestId3, answers, cancel = false) {
|
|
8778
9025
|
const l = live3.get(id);
|
|
8779
9026
|
if (!l) return "the session is not running";
|
|
8780
|
-
const
|
|
8781
|
-
if (!
|
|
8782
|
-
const a = cancel ? { frame: { type: "extension_ui_response", id:
|
|
9027
|
+
const open6 = l.tracking.requests[0];
|
|
9028
|
+
if (!open6 || open6.id !== requestId3) return "the request changed, look again";
|
|
9029
|
+
const a = cancel ? { frame: { type: "extension_ui_response", id: open6.id, cancelled: true } } : askAnswerFor(open6, answers);
|
|
8783
9030
|
if ("refused" in a) return a.refused;
|
|
8784
9031
|
l.tracking.requests.shift();
|
|
8785
9032
|
try {
|
|
8786
9033
|
await l.rpc.write(a.frame);
|
|
8787
9034
|
} catch (e) {
|
|
8788
|
-
l.tracking.requests.unshift(
|
|
9035
|
+
l.tracking.requests.unshift(open6);
|
|
8789
9036
|
return e.message;
|
|
8790
9037
|
}
|
|
8791
9038
|
store.apply(id, { name: "Decided", at: now2(), source: "daemon" });
|
|
@@ -8871,9 +9118,9 @@ var init_catalog = __esm({
|
|
|
8871
9118
|
// src/daemon/repos.ts
|
|
8872
9119
|
import { execFile as execFile2 } from "child_process";
|
|
8873
9120
|
import { constants } from "fs";
|
|
8874
|
-
import { open, stat as
|
|
8875
|
-
import { homedir as
|
|
8876
|
-
import { join as
|
|
9121
|
+
import { open as open2, stat as stat3 } from "fs/promises";
|
|
9122
|
+
import { homedir as homedir6 } from "os";
|
|
9123
|
+
import { join as join12, resolve as resolve2 } from "path";
|
|
8877
9124
|
function runGit(cwd, args) {
|
|
8878
9125
|
return new Promise((res) => {
|
|
8879
9126
|
execFile2("git", ["-C", cwd, ...args], { timeout: GIT_TIMEOUT_MS, windowsHide: true }, (err, out) => res(err ? null : out.trim() || null));
|
|
@@ -8891,7 +9138,7 @@ async function slot(job) {
|
|
|
8891
9138
|
}
|
|
8892
9139
|
}
|
|
8893
9140
|
async function readGitFile(path3, max = 64 * 1024) {
|
|
8894
|
-
const fh = await
|
|
9141
|
+
const fh = await open2(path3, constants.O_RDONLY | (constants.O_NONBLOCK ?? 0));
|
|
8895
9142
|
try {
|
|
8896
9143
|
if (!(await fh.stat()).isFile()) throw new Error(`not a regular file: ${path3}`);
|
|
8897
9144
|
const buf2 = Buffer.alloc(max + 1);
|
|
@@ -8905,7 +9152,7 @@ async function readGitFile(path3, max = 64 * 1024) {
|
|
|
8905
9152
|
async function headBranch(dotGit) {
|
|
8906
9153
|
let head2;
|
|
8907
9154
|
try {
|
|
8908
|
-
head2 = (await readGitFile(
|
|
9155
|
+
head2 = (await readGitFile(join12(dotGit, "HEAD"), MAX_HEAD_BYTES)).trim();
|
|
8909
9156
|
} catch {
|
|
8910
9157
|
return null;
|
|
8911
9158
|
}
|
|
@@ -8914,23 +9161,23 @@ async function headBranch(dotGit) {
|
|
|
8914
9161
|
return /^[0-9a-f]{7,}$/i.test(head2) ? head2.slice(0, 7) : null;
|
|
8915
9162
|
}
|
|
8916
9163
|
async function inspectRepos(paths2, opts = {}) {
|
|
8917
|
-
const home2 = opts.home ??
|
|
9164
|
+
const home2 = opts.home ?? homedir6();
|
|
8918
9165
|
const git = opts.git ?? runGit;
|
|
8919
9166
|
return Promise.all(paths2.map((raw) => slot(async () => {
|
|
8920
9167
|
const input = raw.trim();
|
|
8921
9168
|
const path3 = resolve2(input === "~" ? home2 : input.startsWith("~/") ? resolve2(home2, input.slice(2)) : input);
|
|
8922
9169
|
let isDir2 = false;
|
|
8923
9170
|
try {
|
|
8924
|
-
isDir2 = (await
|
|
9171
|
+
isDir2 = (await stat3(path3)).isDirectory();
|
|
8925
9172
|
} catch {
|
|
8926
9173
|
}
|
|
8927
9174
|
if (!isDir2) return { path: raw, exists: false, branch: null };
|
|
8928
9175
|
let dotDir = false;
|
|
8929
9176
|
try {
|
|
8930
|
-
dotDir = (await
|
|
9177
|
+
dotDir = (await stat3(join12(path3, ".git"))).isDirectory();
|
|
8931
9178
|
} catch {
|
|
8932
9179
|
}
|
|
8933
|
-
if (dotDir) return { path: raw, exists: true, branch: await headBranch(
|
|
9180
|
+
if (dotDir) return { path: raw, exists: true, branch: await headBranch(join12(path3, ".git")) };
|
|
8934
9181
|
const deadline = Date.now() + GIT_TIMEOUT_MS;
|
|
8935
9182
|
const bounded = (args) => new Promise((res) => {
|
|
8936
9183
|
const t = setTimeout(() => res(null), Math.max(0, deadline - Date.now()));
|
|
@@ -8962,7 +9209,7 @@ var init_repos2 = __esm({
|
|
|
8962
9209
|
|
|
8963
9210
|
// src/daemon/scan.ts
|
|
8964
9211
|
import { readdir as fsReaddir } from "fs/promises";
|
|
8965
|
-
import { dirname as dirname4, isAbsolute, join as
|
|
9212
|
+
import { dirname as dirname4, isAbsolute, join as join13, resolve as resolve3, sep as sep2 } from "path";
|
|
8966
9213
|
function extraRoots(home2, cwds) {
|
|
8967
9214
|
const h = resolve3(home2);
|
|
8968
9215
|
const out = [];
|
|
@@ -9008,19 +9255,19 @@ function originUrl(config) {
|
|
|
9008
9255
|
}
|
|
9009
9256
|
return url || void 0;
|
|
9010
9257
|
}
|
|
9011
|
-
async function remoteOf(dir, dotIsFile,
|
|
9258
|
+
async function remoteOf(dir, dotIsFile, readFile20) {
|
|
9012
9259
|
try {
|
|
9013
|
-
let gitDir =
|
|
9260
|
+
let gitDir = join13(dir, ".git");
|
|
9014
9261
|
if (dotIsFile) {
|
|
9015
|
-
const m = /^gitdir:\s*(.+)$/m.exec(await
|
|
9262
|
+
const m = /^gitdir:\s*(.+)$/m.exec(await readFile20(gitDir));
|
|
9016
9263
|
if (!m) return void 0;
|
|
9017
9264
|
gitDir = resolve3(dir, m[1].trim());
|
|
9018
9265
|
try {
|
|
9019
|
-
gitDir = resolve3(gitDir, (await
|
|
9266
|
+
gitDir = resolve3(gitDir, (await readFile20(join13(gitDir, "commondir"))).trim());
|
|
9020
9267
|
} catch {
|
|
9021
9268
|
}
|
|
9022
9269
|
}
|
|
9023
|
-
const url = originUrl(await
|
|
9270
|
+
const url = originUrl(await readFile20(join13(gitDir, "config")));
|
|
9024
9271
|
return url ? repositoryId(url) : void 0;
|
|
9025
9272
|
} catch {
|
|
9026
9273
|
return void 0;
|
|
@@ -9037,18 +9284,18 @@ function manifestName(file, text7) {
|
|
|
9037
9284
|
} else name = /<artifactId>\s*([^<]+?)\s*<\/artifactId>/.exec(text7.replace(/<!--[\s\S]*?-->/g, "").replace(/<parent>[\s\S]*?<\/parent>/g, ""))?.[1];
|
|
9038
9285
|
return typeof name === "string" && name.trim() && name.length <= 200 ? name.trim() : void 0;
|
|
9039
9286
|
}
|
|
9040
|
-
async function describe(dir, dotIsFile, home2,
|
|
9287
|
+
async function describe(dir, dotIsFile, home2, readFile20, manifest) {
|
|
9041
9288
|
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 ?
|
|
9289
|
+
dotIsFile ? inspectRepos([dir], { home: home2 }).then((r) => r[0]?.branch ?? null) : headBranch(join13(dir, ".git")),
|
|
9290
|
+
remoteOf(dir, dotIsFile, readFile20),
|
|
9291
|
+
manifest ? readFile20(join13(dir, manifest)).then((text7) => manifestName(manifest, text7), () => void 0) : void 0
|
|
9045
9292
|
]);
|
|
9046
9293
|
return { path: dir, branch, ...remote ? { remote } : {}, ...named ? { manifest: named } : {} };
|
|
9047
9294
|
}
|
|
9048
9295
|
async function scanRepos(opts) {
|
|
9049
9296
|
const now2 = opts.now ?? Date.now;
|
|
9050
9297
|
const readdir5 = opts.readdir ?? ((p) => fsReaddir(p, { withFileTypes: true }));
|
|
9051
|
-
const
|
|
9298
|
+
const readFile20 = opts.readFile ?? ((p) => readGitFile(p));
|
|
9052
9299
|
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
9300
|
const deadline = now2() + ms;
|
|
9054
9301
|
let timer2;
|
|
@@ -9083,11 +9330,11 @@ async function scanRepos(opts) {
|
|
|
9083
9330
|
if (!entries) return;
|
|
9084
9331
|
const dot = entries.find((e) => e.name === ".git" && (e.isDirectory() || e.isFile()));
|
|
9085
9332
|
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,
|
|
9333
|
+
if (dot) found.push(describe(d.path, dot.isFile(), opts.home, readFile20, manifest));
|
|
9087
9334
|
if (dot && d.path !== home2 || d.depth >= depth) return;
|
|
9088
9335
|
const names = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && !SKIP.has(e.name)).map((e) => e.name).sort();
|
|
9089
9336
|
for (const name of names) {
|
|
9090
|
-
const path3 =
|
|
9337
|
+
const path3 = join13(d.path, name);
|
|
9091
9338
|
if (!seen.has(path3)) {
|
|
9092
9339
|
seen.add(path3);
|
|
9093
9340
|
queue.push({ path: path3, depth: d.depth + 1 });
|
|
@@ -9131,23 +9378,23 @@ var init_scan2 = __esm({
|
|
|
9131
9378
|
});
|
|
9132
9379
|
|
|
9133
9380
|
// 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
|
|
9381
|
+
import { mkdir as mkdir8, readdir as readdir3, stat as stat4 } from "fs/promises";
|
|
9382
|
+
import { homedir as homedir7 } from "os";
|
|
9383
|
+
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join14, resolve as resolve4 } from "path";
|
|
9137
9384
|
async function resolveExact(raw, home2) {
|
|
9138
9385
|
const expanded = raw === "~" ? home2 : raw.startsWith("~/") ? resolve4(home2, raw.slice(2)) : raw;
|
|
9139
9386
|
if (!isAbsolute2(expanded)) throw new Error(`not an absolute path: ${raw}`);
|
|
9140
9387
|
const path3 = resolve4(expanded);
|
|
9141
9388
|
let s;
|
|
9142
9389
|
try {
|
|
9143
|
-
s = await
|
|
9390
|
+
s = await stat4(path3);
|
|
9144
9391
|
} catch {
|
|
9145
9392
|
throw new Error(`no such folder: ${path3}`);
|
|
9146
9393
|
}
|
|
9147
9394
|
if (!s.isDirectory()) throw new Error(`not a folder: ${path3}`);
|
|
9148
9395
|
return path3;
|
|
9149
9396
|
}
|
|
9150
|
-
async function resolveFolder(input, home2 =
|
|
9397
|
+
async function resolveFolder(input, home2 = homedir7()) {
|
|
9151
9398
|
return resolveExact(input.trim(), home2);
|
|
9152
9399
|
}
|
|
9153
9400
|
async function subfolders(path3) {
|
|
@@ -9156,7 +9403,7 @@ async function subfolders(path3) {
|
|
|
9156
9403
|
if (d.isDirectory()) return d.name;
|
|
9157
9404
|
if (!d.isSymbolicLink()) return null;
|
|
9158
9405
|
try {
|
|
9159
|
-
return (await
|
|
9406
|
+
return (await stat4(join14(path3, d.name))).isDirectory() ? d.name : null;
|
|
9160
9407
|
} catch {
|
|
9161
9408
|
return null;
|
|
9162
9409
|
}
|
|
@@ -9164,42 +9411,42 @@ async function subfolders(path3) {
|
|
|
9164
9411
|
return found.filter((n) => n !== null).sort(byName.compare);
|
|
9165
9412
|
}
|
|
9166
9413
|
async function gitBranchOf(dir, git) {
|
|
9167
|
-
const dot =
|
|
9414
|
+
const dot = join14(dir, ".git");
|
|
9168
9415
|
let s;
|
|
9169
9416
|
try {
|
|
9170
|
-
s = await
|
|
9417
|
+
s = await stat4(dot);
|
|
9171
9418
|
} catch {
|
|
9172
9419
|
return null;
|
|
9173
9420
|
}
|
|
9174
9421
|
if (s.isFile()) return (await inspectRepos([dir], { git }))[0]?.branch ?? null;
|
|
9175
9422
|
return s.isDirectory() ? headBranch(dot) : null;
|
|
9176
9423
|
}
|
|
9177
|
-
async function listFolder(input, home2 =
|
|
9424
|
+
async function listFolder(input, home2 = homedir7(), requestId3, git) {
|
|
9178
9425
|
const tag = requestId3 === void 0 ? {} : { requestId: requestId3 };
|
|
9179
9426
|
const asked2 = input || "~";
|
|
9180
9427
|
try {
|
|
9181
9428
|
const path3 = await resolveExact(asked2, home2);
|
|
9182
9429
|
const parent = dirname5(path3);
|
|
9183
9430
|
const entries = await subfolders(path3);
|
|
9184
|
-
const branches = await Promise.all(entries.map((name) => gitBranchOf(
|
|
9431
|
+
const branches = await Promise.all(entries.map((name) => gitBranchOf(join14(path3, name), git)));
|
|
9185
9432
|
const repos = entries.flatMap((name, i) => branches[i] === null ? [] : [{ name, branch: branches[i] }]);
|
|
9186
9433
|
return { type: "folder.listing", path: path3, parent: parent === path3 ? null : parent, entries, repos, ...tag };
|
|
9187
9434
|
} catch (e) {
|
|
9188
9435
|
return { type: "folder.listing", path: asked2, parent: null, entries: [], error: e.message, ...tag };
|
|
9189
9436
|
}
|
|
9190
9437
|
}
|
|
9191
|
-
async function createFolder(parent, name, home2 =
|
|
9438
|
+
async function createFolder(parent, name, home2 = homedir7(), requestId3) {
|
|
9192
9439
|
const at = await listFolder(parent, home2, requestId3);
|
|
9193
9440
|
if (at.error) return at;
|
|
9194
9441
|
const clean = name.trim();
|
|
9195
9442
|
if (!clean || clean === "." || clean === ".." || /[/\0]/.test(clean)) return { ...at, error: `invalid folder name: ${name}` };
|
|
9196
9443
|
try {
|
|
9197
|
-
await mkdir8(
|
|
9444
|
+
await mkdir8(join14(at.path, clean));
|
|
9198
9445
|
} catch (e) {
|
|
9199
9446
|
const code3 = e.code;
|
|
9200
9447
|
return { ...at, error: code3 === "EEXIST" ? `already exists: ${clean}` : `cannot create ${clean}: ${e.message}` };
|
|
9201
9448
|
}
|
|
9202
|
-
return listFolder(
|
|
9449
|
+
return listFolder(join14(at.path, clean), home2, requestId3);
|
|
9203
9450
|
}
|
|
9204
9451
|
var byName;
|
|
9205
9452
|
var init_folders = __esm({
|
|
@@ -9263,7 +9510,7 @@ function taskOf2(ref, ctx) {
|
|
|
9263
9510
|
return taskClosed(t) ? no("closed", `task ${t.id} is closed`) : t;
|
|
9264
9511
|
}
|
|
9265
9512
|
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;
|
|
9513
|
+
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
9514
|
}
|
|
9268
9515
|
function cardRefusal(row3) {
|
|
9269
9516
|
if (row3.action !== "session.decide" || !row3.detailCut || row3.args.decision !== "accept") return null;
|
|
@@ -9316,6 +9563,7 @@ var init_actions2 = __esm({
|
|
|
9316
9563
|
"src/daemon/actions.ts"() {
|
|
9317
9564
|
"use strict";
|
|
9318
9565
|
init_src();
|
|
9566
|
+
init_seats();
|
|
9319
9567
|
DECIDABLE = ["permission", "diff", "plan"];
|
|
9320
9568
|
KIND = { permission: "permission", diff: "diff", plan: "plan" };
|
|
9321
9569
|
no = (code3, refused) => ({ refused, code: code3 });
|
|
@@ -9435,13 +9683,13 @@ ${p.body}` : p.text) }
|
|
|
9435
9683
|
});
|
|
9436
9684
|
|
|
9437
9685
|
// src/chat/guide.ts
|
|
9438
|
-
import { access, constants as constants2, readFile as
|
|
9439
|
-
import { delimiter, join as
|
|
9686
|
+
import { access, constants as constants2, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
|
|
9687
|
+
import { delimiter, join as join15 } from "path";
|
|
9440
9688
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9441
9689
|
async function readGuide(files = GUIDE_FILES) {
|
|
9442
9690
|
for (const f of files) {
|
|
9443
9691
|
try {
|
|
9444
|
-
return await
|
|
9692
|
+
return await readFile8(f, "utf8");
|
|
9445
9693
|
} catch {
|
|
9446
9694
|
}
|
|
9447
9695
|
}
|
|
@@ -9453,7 +9701,7 @@ async function installedAgents(path3) {
|
|
|
9453
9701
|
for (const tool of ToolSchema.options) {
|
|
9454
9702
|
for (const d of dirs) {
|
|
9455
9703
|
try {
|
|
9456
|
-
await access(
|
|
9704
|
+
await access(join15(d, AGENT_BIN2[tool]), constants2.X_OK);
|
|
9457
9705
|
found.push(tool);
|
|
9458
9706
|
break;
|
|
9459
9707
|
} catch {
|
|
@@ -9485,7 +9733,7 @@ function ledgeMd(info) {
|
|
|
9485
9733
|
].join("\n");
|
|
9486
9734
|
}
|
|
9487
9735
|
async function writeLedgeMd(dir, info) {
|
|
9488
|
-
await writeFile8(
|
|
9736
|
+
await writeFile8(join15(dir, LEDGE_MD), ledgeMd(info));
|
|
9489
9737
|
}
|
|
9490
9738
|
var LEDGE_MD, GUIDE_FILES, AGENT_BIN2, RISK;
|
|
9491
9739
|
var init_guide = __esm({
|
|
@@ -9500,8 +9748,8 @@ var init_guide = __esm({
|
|
|
9500
9748
|
});
|
|
9501
9749
|
|
|
9502
9750
|
// src/chat/notes.ts
|
|
9503
|
-
import { open as
|
|
9504
|
-
import { join as
|
|
9751
|
+
import { open as open3, readdir as readdir4, stat as stat5, writeFile as writeFile9 } from "fs/promises";
|
|
9752
|
+
import { join as join16 } from "path";
|
|
9505
9753
|
function scalarValue(raw) {
|
|
9506
9754
|
const v2 = raw.trim();
|
|
9507
9755
|
const q = v2[0];
|
|
@@ -9533,7 +9781,7 @@ function copyName(file, taken) {
|
|
|
9533
9781
|
return name;
|
|
9534
9782
|
}
|
|
9535
9783
|
async function head(path3, max) {
|
|
9536
|
-
const fh = await
|
|
9784
|
+
const fh = await open3(path3, "r");
|
|
9537
9785
|
try {
|
|
9538
9786
|
const buf2 = Buffer.alloc(max);
|
|
9539
9787
|
const { bytesRead } = await fh.read(buf2, 0, max, 0);
|
|
@@ -9552,12 +9800,12 @@ async function writeNotes(dir, from, steps, max = NOTE_MAX2) {
|
|
|
9552
9800
|
if (!files.length) lines.push(`- (none: the user can add Markdown files to \`${from}\`, with an optional front matter \`title\` and \`when\`)`);
|
|
9553
9801
|
const taken = /* @__PURE__ */ new Set();
|
|
9554
9802
|
for (const file of files.slice(0, NOTES_MAX)) {
|
|
9555
|
-
const path3 =
|
|
9803
|
+
const path3 = join16(from, file);
|
|
9556
9804
|
let text7;
|
|
9557
9805
|
let size;
|
|
9558
9806
|
let mtime;
|
|
9559
9807
|
try {
|
|
9560
|
-
const st = await
|
|
9808
|
+
const st = await stat5(path3);
|
|
9561
9809
|
size = st.size;
|
|
9562
9810
|
mtime = st.mtime.toISOString();
|
|
9563
9811
|
text7 = await head(path3, max);
|
|
@@ -9574,15 +9822,15 @@ async function writeNotes(dir, from, steps, max = NOTE_MAX2) {
|
|
|
9574
9822
|
const when = fields.when?.trim() ? `when: ${line(fields.when)}` : `first line: ${line(body.split("\n").find((l) => l.trim()) ?? "") || "(empty)"}`;
|
|
9575
9823
|
const truncated = size > max;
|
|
9576
9824
|
const name = copyName(file, taken);
|
|
9577
|
-
await writeFile9(
|
|
9825
|
+
await writeFile9(join16(dir, name), truncated ? `${text7}
|
|
9578
9826
|
|
|
9579
9827
|
[truncated by Ledge: the first ${max} of ${size} bytes]
|
|
9580
9828
|
` : text7);
|
|
9581
|
-
steps.set(name, { kind: "repo", name: file, path:
|
|
9829
|
+
steps.set(name, { kind: "repo", name: file, path: join16(from, file) });
|
|
9582
9830
|
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
9831
|
}
|
|
9584
9832
|
if (files.length > NOTES_MAX) lines.push(`- (${files.length - NOTES_MAX} more notes not copied: Ledge copies ${NOTES_MAX} at most)`);
|
|
9585
|
-
await writeFile9(
|
|
9833
|
+
await writeFile9(join16(dir, NOTES_INDEX), [...lines, ""].join("\n"));
|
|
9586
9834
|
}
|
|
9587
9835
|
var NOTE_MAX2, NOTES_MAX, NOTES_INDEX, unquote, line;
|
|
9588
9836
|
var init_notes = __esm({
|
|
@@ -9601,8 +9849,8 @@ var init_notes = __esm({
|
|
|
9601
9849
|
|
|
9602
9850
|
// src/chat/context.ts
|
|
9603
9851
|
import { mkdir as mkdir9, rm as rm2, writeFile as writeFile10 } from "fs/promises";
|
|
9604
|
-
import { homedir as
|
|
9605
|
-
import { basename as basename8, join as
|
|
9852
|
+
import { homedir as homedir8 } from "os";
|
|
9853
|
+
import { basename as basename8, join as join17, resolve as resolve5 } from "path";
|
|
9606
9854
|
async function writeContext(dir, input) {
|
|
9607
9855
|
await rm2(dir, { recursive: true, force: true });
|
|
9608
9856
|
await mkdir9(dir, { recursive: true });
|
|
@@ -9631,29 +9879,29 @@ async function writeContext(dir, input) {
|
|
|
9631
9879
|
const pane = await input.capture(s).catch(() => null);
|
|
9632
9880
|
if (pane?.length) {
|
|
9633
9881
|
const f = `pane-${s.id}.txt`;
|
|
9634
|
-
await writeFile10(
|
|
9882
|
+
await writeFile10(join17(dir, f), pane.join("\n"));
|
|
9635
9883
|
steps.set(f, { kind: "pane", id: s.id, name: s.name });
|
|
9636
9884
|
lines.push(`- terminal, last lines: ${f}`);
|
|
9637
9885
|
}
|
|
9638
9886
|
if (s.lastReply?.text.trim()) {
|
|
9639
9887
|
const f = `reply-${s.id}.md`;
|
|
9640
|
-
await writeFile10(
|
|
9888
|
+
await writeFile10(join17(dir, f), s.lastReply.text);
|
|
9641
9889
|
steps.set(f, { kind: "reply", id: s.id, name: s.name });
|
|
9642
9890
|
lines.push(`- last reply (${s.lastReply.at}): ${f}`);
|
|
9643
9891
|
}
|
|
9644
9892
|
const g = await input.git(s.cwd).catch(() => null);
|
|
9645
9893
|
if (g) {
|
|
9646
9894
|
const f = `git-${s.id}.txt`;
|
|
9647
|
-
await writeFile10(
|
|
9895
|
+
await writeFile10(join17(dir, f), g);
|
|
9648
9896
|
steps.set(f, { kind: "git", id: s.id, name: s.name });
|
|
9649
9897
|
lines.push(`- git status, log and diff stat: ${f}`);
|
|
9650
9898
|
}
|
|
9651
9899
|
lines.push("");
|
|
9652
9900
|
}
|
|
9653
|
-
await writeFile10(
|
|
9901
|
+
await writeFile10(join17(dir, "sessions.md"), lines.join("\n"));
|
|
9654
9902
|
steps.set("sessions.md", { kind: "sessions" });
|
|
9655
9903
|
const recents = input.recents ?? [];
|
|
9656
|
-
await writeFile10(
|
|
9904
|
+
await writeFile10(join17(dir, "folders.md"), [
|
|
9657
9905
|
"# Recent folders",
|
|
9658
9906
|
"",
|
|
9659
9907
|
"The folders the user started sessions in, most recent first.",
|
|
@@ -9661,7 +9909,7 @@ async function writeContext(dir, input) {
|
|
|
9661
9909
|
...recents.length ? recents.map((r) => `- ${r.project}: ${r.cwd} (last used ${r.lastAt})`) : ["- (none yet)"],
|
|
9662
9910
|
""
|
|
9663
9911
|
].join("\n"));
|
|
9664
|
-
if (withProjects) await writeFile10(
|
|
9912
|
+
if (withProjects) await writeFile10(join17(dir, "projects.md"), [
|
|
9665
9913
|
"# Projects",
|
|
9666
9914
|
"",
|
|
9667
9915
|
...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 +9927,7 @@ async function writeContext(dir, input) {
|
|
|
9679
9927
|
async function writeTasksContext(dir, taskList, steps) {
|
|
9680
9928
|
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
9681
9929
|
const shown = taskList.filter((t) => !taskClosed(t) || t.updatedAt.slice(0, 10) === day);
|
|
9682
|
-
await writeFile10(
|
|
9930
|
+
await writeFile10(join17(dir, "tasks.md"), [
|
|
9683
9931
|
"# Tasks",
|
|
9684
9932
|
"",
|
|
9685
9933
|
"Open tasks and those closed today. A giver `Ledge` is the user, through you.",
|
|
@@ -9691,7 +9939,7 @@ async function writeTasksContext(dir, taskList, steps) {
|
|
|
9691
9939
|
}
|
|
9692
9940
|
function coversHome(path3) {
|
|
9693
9941
|
const p = resolve5(path3);
|
|
9694
|
-
const home2 = resolve5(
|
|
9942
|
+
const home2 = resolve5(homedir8());
|
|
9695
9943
|
return p === home2 || home2.startsWith(p.endsWith("/") ? p : `${p}/`);
|
|
9696
9944
|
}
|
|
9697
9945
|
function stepFor(path3, ctx) {
|
|
@@ -9730,14 +9978,14 @@ var LEDGE_VERSION;
|
|
|
9730
9978
|
var init_version = __esm({
|
|
9731
9979
|
"src/version.ts"() {
|
|
9732
9980
|
"use strict";
|
|
9733
|
-
LEDGE_VERSION = true ? "0.
|
|
9981
|
+
LEDGE_VERSION = true ? "0.42.0" : "0.0.0-dev";
|
|
9734
9982
|
}
|
|
9735
9983
|
});
|
|
9736
9984
|
|
|
9737
9985
|
// src/chat/forge.ts
|
|
9738
9986
|
import { execFile as execFile4 } from "child_process";
|
|
9739
9987
|
import { writeFile as writeFile11 } from "fs/promises";
|
|
9740
|
-
import { join as
|
|
9988
|
+
import { join as join18 } from "path";
|
|
9741
9989
|
function forgeOf(remote) {
|
|
9742
9990
|
const id = repositoryId(remote);
|
|
9743
9991
|
if (!id) return null;
|
|
@@ -9802,12 +10050,12 @@ function createForge(deps = {}) {
|
|
|
9802
10050
|
return;
|
|
9803
10051
|
}
|
|
9804
10052
|
const parts2 = await Promise.all(local.map((r) => section(r.path)));
|
|
9805
|
-
await writeFile11(
|
|
10053
|
+
await writeFile11(join18(ctx.dir, file), parts2.flatMap((s) => s.lines).join("\n"));
|
|
9806
10054
|
const sources = parts2.flatMap((s) => s.source ? [s.source] : []);
|
|
9807
10055
|
ctx.steps.set(file, { kind: "repo", name, path: sources.join(", ") || file });
|
|
9808
10056
|
index2.push(`- ${name}: ${file}`);
|
|
9809
10057
|
}));
|
|
9810
|
-
await writeFile11(
|
|
10058
|
+
await writeFile11(join18(ctx.dir, "forge.md"), [...index2, ""].join("\n"));
|
|
9811
10059
|
}
|
|
9812
10060
|
};
|
|
9813
10061
|
}
|
|
@@ -10074,7 +10322,7 @@ var init_codex2 = __esm({
|
|
|
10074
10322
|
// src/chat/cursor.ts
|
|
10075
10323
|
import { spawn as nodeSpawn5 } from "child_process";
|
|
10076
10324
|
import { copyFileSync, writeFileSync } from "fs";
|
|
10077
|
-
import { extname, join as
|
|
10325
|
+
import { extname, join as join19 } from "path";
|
|
10078
10326
|
function cursorArgs(o) {
|
|
10079
10327
|
return [
|
|
10080
10328
|
"-p",
|
|
@@ -10116,10 +10364,10 @@ function parseCursorLine(line2) {
|
|
|
10116
10364
|
}
|
|
10117
10365
|
function runCursor(o, onEvent, spawnImpl = nodeSpawn5) {
|
|
10118
10366
|
const [file = "cursor-agent", ...pre] = o.bin ?? ["cursor-agent"];
|
|
10119
|
-
writeFileSync(
|
|
10367
|
+
writeFileSync(join19(o.cwd, "AGENTS.md"), `${o.persona ?? buildPersona("cursor")}
|
|
10120
10368
|
`);
|
|
10121
10369
|
const copies = (o.images ?? []).map((img, n) => {
|
|
10122
|
-
const to =
|
|
10370
|
+
const to = join19(o.cwd, `image-${n + 1}${extname(img.path)}`);
|
|
10123
10371
|
copyFileSync(img.path, to);
|
|
10124
10372
|
return to;
|
|
10125
10373
|
});
|
|
@@ -10203,9 +10451,9 @@ var init_omp = __esm({
|
|
|
10203
10451
|
|
|
10204
10452
|
// src/chat/service.ts
|
|
10205
10453
|
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
|
|
10454
|
+
import { mkdir as mkdir10, readFile as readFile9, rm as rm3, writeFile as writeFile12 } from "fs/promises";
|
|
10455
|
+
import { homedir as homedir9 } from "os";
|
|
10456
|
+
import { basename as basename9, dirname as dirname6, join as join20, resolve as resolve6 } from "path";
|
|
10209
10457
|
function droppedNote(total, kept, reasons, counted) {
|
|
10210
10458
|
const dropped = total - kept;
|
|
10211
10459
|
if (dropped <= 0) return "";
|
|
@@ -10226,7 +10474,7 @@ function createChat(deps) {
|
|
|
10226
10474
|
const now2 = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
10227
10475
|
const today = deps.today ?? (() => now2().slice(0, 10));
|
|
10228
10476
|
const uuid = deps.uuid ?? randomUUID7;
|
|
10229
|
-
const dayFile =
|
|
10477
|
+
const dayFile = join20(deps.home, "chat.json");
|
|
10230
10478
|
let turns2 = [];
|
|
10231
10479
|
let turnsDay = "";
|
|
10232
10480
|
let busy = false;
|
|
@@ -10249,7 +10497,7 @@ function createChat(deps) {
|
|
|
10249
10497
|
const day = today();
|
|
10250
10498
|
if (!fresh && !freshNext) {
|
|
10251
10499
|
try {
|
|
10252
|
-
const saved = JSON.parse(await
|
|
10500
|
+
const saved = JSON.parse(await readFile9(dayFile, "utf8"));
|
|
10253
10501
|
if (saved.day === day && saved.sessionId && (saved.tool ?? "claude") === tool) return { sessionId: saved.sessionId, resume: true };
|
|
10254
10502
|
} catch {
|
|
10255
10503
|
}
|
|
@@ -10265,7 +10513,7 @@ function createChat(deps) {
|
|
|
10265
10513
|
const p = await deps.tmux.capture(s.tmuxSession, PANE_LINES);
|
|
10266
10514
|
return p === null ? null : p.split("\n");
|
|
10267
10515
|
};
|
|
10268
|
-
const contextDir =
|
|
10516
|
+
const contextDir = join20(deps.home, "chat", "context");
|
|
10269
10517
|
async function proposalFrom(raw, tool, qid) {
|
|
10270
10518
|
const parsed = parseActionBlock("session", raw);
|
|
10271
10519
|
if ("error" in parsed) {
|
|
@@ -10342,7 +10590,7 @@ function createChat(deps) {
|
|
|
10342
10590
|
for (const r of block2.repos) {
|
|
10343
10591
|
try {
|
|
10344
10592
|
const path3 = await resolveFolder(r.path, deps.userHome);
|
|
10345
|
-
const wide = tooWide(path3, resolve6(deps.userHome ??
|
|
10593
|
+
const wide = tooWide(path3, resolve6(deps.userHome ?? homedir9()));
|
|
10346
10594
|
if (wide) {
|
|
10347
10595
|
log(`chat ${qid}: project path dropped: ${wide}: ${path3}`);
|
|
10348
10596
|
continue;
|
|
@@ -10458,7 +10706,7 @@ function createChat(deps) {
|
|
|
10458
10706
|
let resultError = null;
|
|
10459
10707
|
const handle2 = runTool(tool, { question: mine.told.text ? `${mine.told.text}
|
|
10460
10708
|
|
|
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:
|
|
10709
|
+
${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
10710
|
if (e.type === "tool") {
|
|
10463
10711
|
const step3 = e.path ? stepFor(e.path, ctx) : null;
|
|
10464
10712
|
if (step3 && !turn.steps.some((s) => sameStep(s, step3))) {
|
|
@@ -10745,8 +10993,8 @@ var init_service = __esm({
|
|
|
10745
10993
|
});
|
|
10746
10994
|
|
|
10747
10995
|
// src/chat/threads.ts
|
|
10748
|
-
import { readFile as
|
|
10749
|
-
import { join as
|
|
10996
|
+
import { readFile as readFile10, rm as rm4 } from "fs/promises";
|
|
10997
|
+
import { join as join21 } from "path";
|
|
10750
10998
|
function createSlots(max = 3) {
|
|
10751
10999
|
let used = 0;
|
|
10752
11000
|
return {
|
|
@@ -10767,7 +11015,7 @@ function createChatThreads(deps) {
|
|
|
10767
11015
|
const devices = /* @__PURE__ */ new Map();
|
|
10768
11016
|
const emitters = /* @__PURE__ */ new Map();
|
|
10769
11017
|
const dropping = /* @__PURE__ */ new Map();
|
|
10770
|
-
const deviceHome = (id) =>
|
|
11018
|
+
const deviceHome = (id) => join21(deps.home, "mobile", "chat", id);
|
|
10771
11019
|
const device = (id) => {
|
|
10772
11020
|
checkId(id);
|
|
10773
11021
|
if (dropping.has(id)) throw new Error("device thread is being dropped");
|
|
@@ -10796,7 +11044,7 @@ function createChatThreads(deps) {
|
|
|
10796
11044
|
/** The PC's tool of the day file, whatever its day: `claude` without one. */
|
|
10797
11045
|
async deviceTool() {
|
|
10798
11046
|
try {
|
|
10799
|
-
const t = JSON.parse(await
|
|
11047
|
+
const t = JSON.parse(await readFile10(join21(deps.home, "chat.json"), "utf8")).tool;
|
|
10800
11048
|
return t === "claude" || t === "codex" || t === "cursor" || t === "omp" ? t : "claude";
|
|
10801
11049
|
} catch {
|
|
10802
11050
|
return "claude";
|
|
@@ -10963,7 +11211,7 @@ var init_island2 = __esm({
|
|
|
10963
11211
|
|
|
10964
11212
|
// src/daemon/documents.ts
|
|
10965
11213
|
import { constants as constants3 } from "fs";
|
|
10966
|
-
import { open as
|
|
11214
|
+
import { open as open4, realpath } from "fs/promises";
|
|
10967
11215
|
import { isAbsolute as isAbsolute3, dirname as dirname7, extname as extname2, resolve as resolve7 } from "path";
|
|
10968
11216
|
async function readDocument(request2, context) {
|
|
10969
11217
|
const source2 = request2.path;
|
|
@@ -10984,10 +11232,10 @@ async function readDocument(request2, context) {
|
|
|
10984
11232
|
async function readAt(request2, path3) {
|
|
10985
11233
|
let file;
|
|
10986
11234
|
try {
|
|
10987
|
-
file = await
|
|
10988
|
-
const
|
|
10989
|
-
if (!
|
|
10990
|
-
if (
|
|
11235
|
+
file = await open4(path3, constants3.O_RDONLY | constants3.O_NONBLOCK);
|
|
11236
|
+
const stat8 = await file.stat();
|
|
11237
|
+
if (!stat8.isFile()) return failure(request2, "not_file");
|
|
11238
|
+
if (stat8.size > DOCUMENT_MAX_BYTES) return failure(request2, "too_large");
|
|
10991
11239
|
const chunk = Buffer.allocUnsafe(64 * 1024);
|
|
10992
11240
|
const parts2 = [];
|
|
10993
11241
|
let bytes4 = 0;
|
|
@@ -11010,7 +11258,7 @@ async function readAt(request2, path3) {
|
|
|
11010
11258
|
path: await realpath(path3),
|
|
11011
11259
|
text: text7,
|
|
11012
11260
|
bytes: bytes4,
|
|
11013
|
-
modifiedAt:
|
|
11261
|
+
modifiedAt: stat8.mtime.toISOString()
|
|
11014
11262
|
};
|
|
11015
11263
|
} catch (e) {
|
|
11016
11264
|
return failure(request2, errorCode(e));
|
|
@@ -11039,9 +11287,9 @@ var init_documents2 = __esm({
|
|
|
11039
11287
|
|
|
11040
11288
|
// src/daemon/models.ts
|
|
11041
11289
|
import { execFile as execFile5 } from "child_process";
|
|
11042
|
-
import { readFile as
|
|
11043
|
-
import { homedir as
|
|
11044
|
-
import { join as
|
|
11290
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
11291
|
+
import { homedir as homedir10 } from "os";
|
|
11292
|
+
import { join as join22 } from "path";
|
|
11045
11293
|
function claudeModels(settingsModel) {
|
|
11046
11294
|
const efforts = [...CLAUDE_EFFORTS];
|
|
11047
11295
|
const models = CLAUDE_MODELS.map((m) => ({ ...m, efforts, ...m.id === settingsModel ? { isDefault: true } : {} }));
|
|
@@ -11085,15 +11333,15 @@ function codexConfig(toml) {
|
|
|
11085
11333
|
function createModels(deps = {}) {
|
|
11086
11334
|
const env = deps.env ?? (async () => process.env);
|
|
11087
11335
|
const run4 = deps.run ?? execRun2(env);
|
|
11088
|
-
const readText2 = deps.readText ?? ((p) =>
|
|
11336
|
+
const readText2 = deps.readText ?? ((p) => readFile11(p, "utf8").catch(() => ""));
|
|
11089
11337
|
const now2 = deps.now ?? Date.now, ttl = deps.ttlMs ?? MODELS_TTL_MS;
|
|
11090
11338
|
const cache = /* @__PURE__ */ new Map();
|
|
11091
11339
|
const flying = /* @__PURE__ */ new Map();
|
|
11092
11340
|
async function ask3(tool) {
|
|
11093
11341
|
const e = await env();
|
|
11094
|
-
const home2 = e.HOME ??
|
|
11342
|
+
const home2 = e.HOME ?? homedir10();
|
|
11095
11343
|
if (tool === "claude") {
|
|
11096
|
-
const settings = await readText2(
|
|
11344
|
+
const settings = await readText2(join22(e.CLAUDE_CONFIG_DIR ?? join22(home2, ".claude"), "settings.json"));
|
|
11097
11345
|
let model;
|
|
11098
11346
|
try {
|
|
11099
11347
|
model = settings ? JSON.parse(settings).model : void 0;
|
|
@@ -11109,7 +11357,7 @@ function createModels(deps = {}) {
|
|
|
11109
11357
|
if (r.code === null) throw new Error("not installed");
|
|
11110
11358
|
if (r.code !== 0) throw new Error(r.stderr.trim().split("\n").pop() || `exit ${r.code}`);
|
|
11111
11359
|
if (tool === "cursor") return parseCursorModels(r.stdout);
|
|
11112
|
-
return parseCodexModels(r.stdout, codexConfig(await readText2(
|
|
11360
|
+
return parseCodexModels(r.stdout, codexConfig(await readText2(join22(e.CODEX_HOME ?? join22(home2, ".codex"), "config.toml"))));
|
|
11113
11361
|
}
|
|
11114
11362
|
return {
|
|
11115
11363
|
list(tool) {
|
|
@@ -11160,9 +11408,9 @@ var init_models2 = __esm({
|
|
|
11160
11408
|
|
|
11161
11409
|
// src/daemon/commands.ts
|
|
11162
11410
|
import { spawn as nodeSpawn7 } from "child_process";
|
|
11163
|
-
import { stat as
|
|
11164
|
-
import { homedir as
|
|
11165
|
-
import { join as
|
|
11411
|
+
import { stat as stat6 } from "fs/promises";
|
|
11412
|
+
import { homedir as homedir11 } from "os";
|
|
11413
|
+
import { join as join23 } from "path";
|
|
11166
11414
|
function claudeSlashRefusal(text7) {
|
|
11167
11415
|
const m = /^\/(\S+)(\s+\S[\s\S]*)?$/.exec(text7.trim());
|
|
11168
11416
|
if (!m) return null;
|
|
@@ -11271,13 +11519,13 @@ function probeClaude(o, spawnImpl = nodeSpawn7) {
|
|
|
11271
11519
|
function createCommands(deps = {}) {
|
|
11272
11520
|
const env = deps.env ?? (async () => process.env);
|
|
11273
11521
|
const claude = deps.claude ?? ((cwd, e) => probeClaude({ ...deps.claudeBin ? { bin: deps.claudeBin } : {}, cwd, env: e }));
|
|
11274
|
-
const mtime = deps.mtime ?? ((p) =>
|
|
11522
|
+
const mtime = deps.mtime ?? ((p) => stat6(p).then((s) => s.mtimeMs, () => null));
|
|
11275
11523
|
const now2 = deps.now ?? Date.now, ttl = deps.ttlMs ?? COMMANDS_TTL_MS;
|
|
11276
11524
|
const cache = /* @__PURE__ */ new Map();
|
|
11277
11525
|
const flying = /* @__PURE__ */ new Map();
|
|
11278
11526
|
async function mark(cwd, e) {
|
|
11279
|
-
const home2 = e.CLAUDE_CONFIG_DIR ??
|
|
11280
|
-
const paths2 = [
|
|
11527
|
+
const home2 = e.CLAUDE_CONFIG_DIR ?? join23(e.HOME ?? homedir11(), ".claude");
|
|
11528
|
+
const paths2 = [join23(cwd, ".claude", "commands"), join23(cwd, ".claude", "skills"), join23(home2, "commands"), join23(home2, "skills"), join23(home2, "plugins", "installed_plugins.json")];
|
|
11281
11529
|
return (await Promise.all(paths2.map(mtime))).join(",");
|
|
11282
11530
|
}
|
|
11283
11531
|
return {
|
|
@@ -11455,7 +11703,7 @@ var init_rigSpec = __esm({
|
|
|
11455
11703
|
});
|
|
11456
11704
|
|
|
11457
11705
|
// src/daemon/openrigRoles.ts
|
|
11458
|
-
import { basename as basename10, dirname as dirname8, isAbsolute as isAbsolute5, join as
|
|
11706
|
+
import { basename as basename10, dirname as dirname8, isAbsolute as isAbsolute5, join as join24, resolve as resolve9, sep as sep3 } from "path";
|
|
11459
11707
|
import { parse as parseYaml, stringify as stringify2, YAMLParseError } from "yaml";
|
|
11460
11708
|
function readLibrary(listed2) {
|
|
11461
11709
|
const out = [];
|
|
@@ -11487,15 +11735,15 @@ function parseSpec(text7) {
|
|
|
11487
11735
|
}
|
|
11488
11736
|
}
|
|
11489
11737
|
function specPath(p, specDir, home2, openrigHome) {
|
|
11490
|
-
if (p.startsWith("openrig-home:")) return
|
|
11738
|
+
if (p.startsWith("openrig-home:")) return join24(openrigHome, p.slice("openrig-home:".length));
|
|
11491
11739
|
if (p === "~") return home2;
|
|
11492
|
-
if (p.startsWith("~/")) return
|
|
11740
|
+
if (p.startsWith("~/")) return join24(home2, p.slice(2));
|
|
11493
11741
|
return isAbsolute5(p) ? resolve9(p) : resolve9(specDir, p);
|
|
11494
11742
|
}
|
|
11495
11743
|
function importPath(ref, specDir) {
|
|
11496
11744
|
const p = ref.startsWith("local:") ? resolve9(specDir, ref.slice(6)) : ref.startsWith("path:") ? resolve9(ref.slice(5)) : null;
|
|
11497
11745
|
if (!p) return null;
|
|
11498
|
-
return /\.ya?ml$/i.test(p) ? p :
|
|
11746
|
+
return /\.ya?ml$/i.test(p) ? p : join24(p, "agent.yaml");
|
|
11499
11747
|
}
|
|
11500
11748
|
function declared(spec, ctx, ns, origin) {
|
|
11501
11749
|
const out = [];
|
|
@@ -11724,7 +11972,7 @@ function layerOf(ownerRoot, path3, c) {
|
|
|
11724
11972
|
if (c.agentDir && root2 === c.agentDir) return c.profileFiles.has(path3) ? "profile" : "agent";
|
|
11725
11973
|
if (c.rigRoot && root2 === c.rigRoot) return path3 === c.cultureFile ? "culture" : c.memberFiles.has(path3) ? "member" : c.podFiles.has(path3) ? "pod" : "rig";
|
|
11726
11974
|
if (/[/\\]@openrig[/\\]cli[/\\]/.test(root2) || /[/\\]daemon[/\\]assets$/.test(root2)) return "openrig";
|
|
11727
|
-
if (under3(root2,
|
|
11975
|
+
if (under3(root2, join24(c.openrigHome, "state", "seats"))) return "seat";
|
|
11728
11976
|
return "unknown";
|
|
11729
11977
|
}
|
|
11730
11978
|
function layerOrigin(layer, c, path3) {
|
|
@@ -11734,7 +11982,7 @@ function layerOrigin(layer, c, path3) {
|
|
|
11734
11982
|
case "profile":
|
|
11735
11983
|
return { kind: "here", key: "profiles.startup.files" };
|
|
11736
11984
|
case "culture":
|
|
11737
|
-
return { kind: "inherited", from: "rig", ref: c.rig, key: "culture_file", ...c.rigRoot ? { path:
|
|
11985
|
+
return { kind: "inherited", from: "rig", ref: c.rig, key: "culture_file", ...c.rigRoot ? { path: join24(c.rigRoot, path3) } : {} };
|
|
11738
11986
|
case "rig":
|
|
11739
11987
|
return { kind: "inherited", from: "rig", ref: c.rig };
|
|
11740
11988
|
case "pod":
|
|
@@ -11762,12 +12010,12 @@ async function seatView(s, spec, profile, io, openrigHome, plugins) {
|
|
|
11762
12010
|
const cwdV = str10(node2.cwd) ?? str10(shown.cwd);
|
|
11763
12011
|
const cwd = cwdV ? { value: cwdV, origin: memberOrigin("cwd") } : { value: null, origin: { kind: "unknown", why: "not_exposed" } };
|
|
11764
12012
|
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:
|
|
12013
|
+
const cultureFile = sourced(cultureV, { kind: "inherited", from: "rig", ref: s.rig, key: "culture_file", ...cultureV && s.rigRoot ? { path: join24(s.rigRoot, cultureV) } : {} });
|
|
11766
12014
|
const base3 = s.rigRoot ?? (s.rigSpecPath ? dirname8(s.rigSpecPath) : "/");
|
|
11767
12015
|
const memberStartup = writtenStartup(m.startup, "startup", "member", base3, (key) => memberOrigin(key));
|
|
11768
12016
|
const pod = s.written?.pod ?? {};
|
|
11769
12017
|
const effectiveRuntime = str10(node2.runtime) ?? runtime.value ?? void 0;
|
|
11770
|
-
const into = cwdV ?
|
|
12018
|
+
const into = cwdV ? join24(cwdV, guidanceFile(effectiveRuntime, s.rigDoc)) : void 0;
|
|
11771
12019
|
const profileName = str10(m.profile) ?? str10(shown.profile) ?? "default";
|
|
11772
12020
|
const lc = {
|
|
11773
12021
|
agentDir: s.agentDir,
|
|
@@ -12003,10 +12251,14 @@ var init_openrigRoles2 = __esm({
|
|
|
12003
12251
|
import { execFile as execFile6 } from "child_process";
|
|
12004
12252
|
import { get as httpGet } from "http";
|
|
12005
12253
|
import { existsSync as existsSync2, readdirSync as readdirSync2 } from "fs";
|
|
12006
|
-
import { cp, mkdir as mkdir11, open as openFile, readFile as
|
|
12254
|
+
import { cp, mkdir as mkdir11, open as openFile, readFile as readFile12, stat as statFile, writeFile as writeFile13 } from "fs/promises";
|
|
12007
12255
|
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
|
|
12256
|
+
import { homedir as homedir12 } from "os";
|
|
12257
|
+
import { basename as basename11, delimiter as delimiter2, dirname as dirname9, isAbsolute as isAbsolute6, join as join25, resolve as resolve10, sep as sep4 } from "path";
|
|
12258
|
+
function specSource(raw) {
|
|
12259
|
+
const s = str11(raw);
|
|
12260
|
+
return s === "builtin" || s?.startsWith("builtin_") ? "builtin" : "user";
|
|
12261
|
+
}
|
|
12010
12262
|
function toSeat(raw) {
|
|
12011
12263
|
const n = obj9(raw);
|
|
12012
12264
|
const logicalId = str11(n.logicalId), session = str11(n.canonicalSessionName);
|
|
@@ -12079,17 +12331,17 @@ function findRig(env, exists = existsSync2, list3 = (dir) => {
|
|
|
12079
12331
|
return [];
|
|
12080
12332
|
}
|
|
12081
12333
|
}) {
|
|
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) =>
|
|
12334
|
+
for (const dir of (env.PATH ?? "").split(delimiter2)) if (dir && exists(join25(dir, "rig"))) return dir;
|
|
12335
|
+
const home2 = env.HOME ?? homedir12();
|
|
12336
|
+
const managed = (root2, sub) => list3(root2).filter((v2) => /^v?\d/.test(v2)).sort(byVersionDesc).map((v2) => join25(root2, v2, sub));
|
|
12085
12337
|
const candidates = [
|
|
12086
|
-
...managed(
|
|
12087
|
-
...managed(
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12338
|
+
...managed(join25(home2, ".local", "share", "nvm"), "bin"),
|
|
12339
|
+
...managed(join25(env.NVM_DIR ?? join25(home2, ".nvm"), "versions", "node"), "bin"),
|
|
12340
|
+
join25(home2, ".volta", "bin"),
|
|
12341
|
+
join25(home2, ".npm-global", "bin"),
|
|
12342
|
+
join25(home2, ".local", "share", "pnpm")
|
|
12091
12343
|
];
|
|
12092
|
-
return candidates.find((dir) => exists(
|
|
12344
|
+
return candidates.find((dir) => exists(join25(dir, "rig"))) ?? null;
|
|
12093
12345
|
}
|
|
12094
12346
|
function readCheck(output) {
|
|
12095
12347
|
const j = obj9(parse2(output));
|
|
@@ -12118,11 +12370,11 @@ function localGet(port, path3, timeoutMs) {
|
|
|
12118
12370
|
function createOpenRig(deps = {}) {
|
|
12119
12371
|
const run4 = deps.run ?? execRun3(deps.env ?? (async () => process.env));
|
|
12120
12372
|
const files = deps.fs ?? realFs;
|
|
12121
|
-
const home2 = deps.home ??
|
|
12122
|
-
const rigsDir = deps.rigsDir ??
|
|
12373
|
+
const home2 = deps.home ?? homedir12();
|
|
12374
|
+
const rigsDir = deps.rigsDir ?? join25(home2, ".ledge", "rigs");
|
|
12123
12375
|
const now2 = deps.now ?? Date.now;
|
|
12124
12376
|
const roleIo = deps.roleIo ?? realRoleIo;
|
|
12125
|
-
const openrigHome = deps.openrigHome ?? process.env.OPENRIG_HOME ??
|
|
12377
|
+
const openrigHome = deps.openrigHome ?? process.env.OPENRIG_HOME ?? join25(home2, ".openrig");
|
|
12126
12378
|
const http = deps.http ?? (async (path3) => {
|
|
12127
12379
|
const port = (await state2()).daemon.port;
|
|
12128
12380
|
if (!port) return void 0;
|
|
@@ -12206,7 +12458,7 @@ function createOpenRig(deps = {}) {
|
|
|
12206
12458
|
const e = obj9(raw), name = str11(e.name), path3 = str11(e.resolvedSourcePath) ?? str11(e.sourcePath);
|
|
12207
12459
|
if (e.kind !== "rig" || !name || !path3 || name === KERNEL_RIG) continue;
|
|
12208
12460
|
const text7 = await files.read(path3).catch(() => null);
|
|
12209
|
-
const spec = text7 === null ? null : readSpec(text7, { name, source: e.sourceType
|
|
12461
|
+
const spec = text7 === null ? null : readSpec(text7, { name, source: specSource(e.sourceType), path: path3, ...str11(e.summary) ? { summary: str11(e.summary).trim() } : {} });
|
|
12210
12462
|
if (spec) out.push(spec);
|
|
12211
12463
|
}
|
|
12212
12464
|
return { specs: out.sort((a, b3) => a.source === b3.source ? a.name.localeCompare(b3.name) : a.source === "user" ? -1 : 1) };
|
|
@@ -12216,7 +12468,7 @@ function createOpenRig(deps = {}) {
|
|
|
12216
12468
|
if (acting) return { ok: false, plan: plan2, commands: [], error: "busy" };
|
|
12217
12469
|
acting = true;
|
|
12218
12470
|
try {
|
|
12219
|
-
const cwd = l.cwd === "~" ? home2 : l.cwd.startsWith("~/") ?
|
|
12471
|
+
const cwd = l.cwd === "~" ? home2 : l.cwd.startsWith("~/") ? join25(home2, l.cwd.slice(2)) : l.cwd;
|
|
12220
12472
|
if (!isAbsolute6(cwd) || !files.exists(cwd)) return { ok: false, plan: plan2, commands: [], error: "cwd_missing" };
|
|
12221
12473
|
const listed2 = await specs();
|
|
12222
12474
|
if (listed2.error) return { ok: false, plan: plan2, commands: [], error: listed2.error };
|
|
@@ -12228,17 +12480,17 @@ function createOpenRig(deps = {}) {
|
|
|
12228
12480
|
const rename7 = l.name !== spec.name;
|
|
12229
12481
|
if (changesSpec(l) || l.save || rename7 && !can2.name) {
|
|
12230
12482
|
const composed = composeSpec(parseYaml2(text7), dirname9(spec.path), l);
|
|
12231
|
-
dir =
|
|
12483
|
+
dir = join25(rigsDir, l.name);
|
|
12232
12484
|
await files.copyDir(dirname9(spec.path), dir);
|
|
12233
|
-
await files.write(
|
|
12234
|
-
source2 =
|
|
12485
|
+
await files.write(join25(dir, "rig.yaml"), composed.yaml);
|
|
12486
|
+
source2 = join25(dir, "rig.yaml");
|
|
12235
12487
|
yaml = composed.yaml;
|
|
12236
12488
|
seats = composed.seats;
|
|
12237
12489
|
} else {
|
|
12238
12490
|
yaml = text7;
|
|
12239
12491
|
seats = specSeats(spec);
|
|
12240
12492
|
}
|
|
12241
|
-
if (!dir && rename7 && !plan2) await files.write(
|
|
12493
|
+
if (!dir && rename7 && !plan2) await files.write(join25(rigsDir, l.name, "source.json"), JSON.stringify({ spec: spec.name, path: spec.path }));
|
|
12242
12494
|
const commands = [["up", source2, ...!dir && rename7 ? ["--name", l.name] : [], "--cwd", cwd, ...l.nonInterruptive ? ["--non-interruptive"] : [], ...plan2 ? ["--plan"] : ["--yes"], "--json"]];
|
|
12243
12495
|
if (!plan2 && l.save && dir) commands.push(["specs", "add", dir, "--json"]);
|
|
12244
12496
|
const shown = commands.map(showRigCommand);
|
|
@@ -12262,9 +12514,9 @@ function createOpenRig(deps = {}) {
|
|
|
12262
12514
|
}
|
|
12263
12515
|
}
|
|
12264
12516
|
async function rootOf(rig) {
|
|
12265
|
-
const mine =
|
|
12266
|
-
if (files.exists(
|
|
12267
|
-
const marker = await files.read(
|
|
12517
|
+
const mine = join25(rigsDir, rig);
|
|
12518
|
+
if (files.exists(join25(mine, "rig.yaml"))) return mine;
|
|
12519
|
+
const marker = await files.read(join25(mine, "source.json")).catch(() => null);
|
|
12268
12520
|
const path3 = marker === null ? void 0 : str11(obj9(parse2(marker)).path);
|
|
12269
12521
|
if (path3) return dirname9(path3);
|
|
12270
12522
|
const listed2 = await specs();
|
|
@@ -12294,7 +12546,7 @@ function createOpenRig(deps = {}) {
|
|
|
12294
12546
|
const member = memberOf(show, a.pod, a.from.slice(a.pod.length + 1));
|
|
12295
12547
|
const fragment2 = member ? fragmentOf(member, a.id, root2) : null;
|
|
12296
12548
|
if (!fragment2) return { ok: false, commands: [], ...none, error: "rig_root_unknown" };
|
|
12297
|
-
const path3 =
|
|
12549
|
+
const path3 = join25(rigsDir, rigName, "seats", `${a.pod}.${a.id}.json`);
|
|
12298
12550
|
await files.write(path3, JSON.stringify(fragment2, null, 2));
|
|
12299
12551
|
commands.push(["add", rig.id, a.pod, path3, ...root2 ? ["--rig-root", root2] : [], "--json"]);
|
|
12300
12552
|
}
|
|
@@ -12344,7 +12596,7 @@ function createOpenRig(deps = {}) {
|
|
|
12344
12596
|
const r = await run4(["spec", "show", rig.id, "--json"], STATUS_MS);
|
|
12345
12597
|
if (r.code !== 0) continue;
|
|
12346
12598
|
const root2 = rig.name === KERNEL_RIG ? null : await rootOf(rig.name).catch(() => null);
|
|
12347
|
-
const specFile = root2 ?
|
|
12599
|
+
const specFile = root2 ? join25(root2, "rig.yaml") : null;
|
|
12348
12600
|
const text7 = specFile ? await roleIo.read(specFile) : null;
|
|
12349
12601
|
const written = parseSpec(text7);
|
|
12350
12602
|
const shown = obj9(parse2(r.stdout));
|
|
@@ -12508,14 +12760,14 @@ var init_openrig2 = __esm({
|
|
|
12508
12760
|
if (!dir) return { code: null, stdout: "", stderr: "rig: command not found" };
|
|
12509
12761
|
const e = { ...base3, PATH: [dir, base3.PATH].filter(Boolean).join(delimiter2), NO_COLOR: "1", CLICOLOR: "0", FORCE_COLOR: "0" };
|
|
12510
12762
|
return new Promise((res) => {
|
|
12511
|
-
execFile6(
|
|
12763
|
+
execFile6(join25(dir, "rig"), args, { env: e, timeout: timeoutMs, maxBuffer: 8 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
12512
12764
|
const code3 = !err ? 0 : err.code === "ENOENT" ? null : typeof err.code === "number" ? err.code : 1;
|
|
12513
12765
|
res({ code: code3, stdout: String(stdout), stderr: String(stderr) || (err && code3 !== 0 ? err.message : "") });
|
|
12514
12766
|
});
|
|
12515
12767
|
});
|
|
12516
12768
|
};
|
|
12517
12769
|
realRoleIo = {
|
|
12518
|
-
read: (p) =>
|
|
12770
|
+
read: (p) => readFile12(p, "utf8").catch(() => null),
|
|
12519
12771
|
stat: (p) => statFile(p).then((st) => ({ size: st.size, mtimeMs: st.mtimeMs, file: st.isFile() })).catch(() => null),
|
|
12520
12772
|
head: async (p, max) => {
|
|
12521
12773
|
const h = await openFile(p, "r").catch(() => null);
|
|
@@ -12533,7 +12785,7 @@ var init_openrig2 = __esm({
|
|
|
12533
12785
|
FILES_TTL_MS = 5 * 6e4;
|
|
12534
12786
|
HTTP_MS = 3e3;
|
|
12535
12787
|
realFs = {
|
|
12536
|
-
read: (p) =>
|
|
12788
|
+
read: (p) => readFile12(p, "utf8"),
|
|
12537
12789
|
exists: (p) => existsSync2(p),
|
|
12538
12790
|
copyDir: (from, to) => cp(from, to, { recursive: true, force: true, filter: (src) => basename11(src) !== "rig.yaml" }),
|
|
12539
12791
|
write: async (p, text7) => {
|
|
@@ -12553,7 +12805,7 @@ function goalOf(tags) {
|
|
|
12553
12805
|
return t ? t.slice(GOAL_TAG.length + 1) || null : null;
|
|
12554
12806
|
}
|
|
12555
12807
|
function parseTeam(body) {
|
|
12556
|
-
const m =
|
|
12808
|
+
const m = BLOCK4.exec(body);
|
|
12557
12809
|
if (!m) return null;
|
|
12558
12810
|
let raw;
|
|
12559
12811
|
try {
|
|
@@ -12578,13 +12830,13 @@ function parseTeam(body) {
|
|
|
12578
12830
|
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
12831
|
};
|
|
12580
12832
|
}
|
|
12581
|
-
var GOAL_TAG, goalTag,
|
|
12833
|
+
var GOAL_TAG, goalTag, BLOCK4, obj10, text2, strings, roleOf3, withoutTeam;
|
|
12582
12834
|
var init_goalTeam = __esm({
|
|
12583
12835
|
"src/daemon/goalTeam.ts"() {
|
|
12584
12836
|
"use strict";
|
|
12585
12837
|
GOAL_TAG = "ledge-goal";
|
|
12586
12838
|
goalTag = (goalQitemId2) => `${GOAL_TAG}:${goalQitemId2}`;
|
|
12587
|
-
|
|
12839
|
+
BLOCK4 = /```ledge-team[^\n]*\n([\s\S]*?)```/;
|
|
12588
12840
|
obj10 = (v2) => v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : {};
|
|
12589
12841
|
text2 = (v2) => typeof v2 === "string" && v2.trim() ? v2.trim() : null;
|
|
12590
12842
|
strings = (v2) => Array.isArray(v2) ? v2.filter((x) => typeof x === "string" && x.trim() !== "").map((x) => x.trim()) : [];
|
|
@@ -12592,7 +12844,7 @@ var init_goalTeam = __esm({
|
|
|
12592
12844
|
const m = /^([A-Za-z0-9][A-Za-z0-9_]*)-([A-Za-z0-9][A-Za-z0-9_-]*)@/.exec(seat2);
|
|
12593
12845
|
return m ? `${m[1]}.${m[2]}` : null;
|
|
12594
12846
|
};
|
|
12595
|
-
withoutTeam = (body) => body.replace(
|
|
12847
|
+
withoutTeam = (body) => body.replace(BLOCK4, "").trim();
|
|
12596
12848
|
}
|
|
12597
12849
|
});
|
|
12598
12850
|
|
|
@@ -13217,7 +13469,7 @@ function createRelay(d) {
|
|
|
13217
13469
|
const now2 = d.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
13218
13470
|
const typing = d.typing ?? /* @__PURE__ */ new Set();
|
|
13219
13471
|
const sending = /* @__PURE__ */ new Set();
|
|
13220
|
-
const
|
|
13472
|
+
const open6 = (id) => free(d.store.get(id)) && !typing.has(id) && !d.closing?.has(id);
|
|
13221
13473
|
const emitTo = (thread) => (m) => {
|
|
13222
13474
|
try {
|
|
13223
13475
|
d.threads.emitterOf(thread)?.(m);
|
|
@@ -13261,14 +13513,14 @@ function createRelay(d) {
|
|
|
13261
13513
|
}
|
|
13262
13514
|
function kick(id) {
|
|
13263
13515
|
const q = queued().find((x) => x.relay.id === id);
|
|
13264
|
-
if (!q || !
|
|
13516
|
+
if (!q || !open6(id)) return;
|
|
13265
13517
|
void deliverAndKick(q.thread, q.qid, q.relay).catch((e) => log(`relay ${q.qid} failed: ${e.message}`));
|
|
13266
13518
|
}
|
|
13267
13519
|
async function deliverAndKick(thread, qid, r) {
|
|
13268
13520
|
const refused = await deliver(thread, qid, r);
|
|
13269
13521
|
if (refused) {
|
|
13270
13522
|
if (queued().some((q) => q.relay.id === r.id)) kick(r.id);
|
|
13271
|
-
else if (
|
|
13523
|
+
else if (open6(r.id)) d.next?.(r.id);
|
|
13272
13524
|
}
|
|
13273
13525
|
return refused;
|
|
13274
13526
|
}
|
|
@@ -13285,7 +13537,7 @@ function createRelay(d) {
|
|
|
13285
13537
|
}
|
|
13286
13538
|
return;
|
|
13287
13539
|
}
|
|
13288
|
-
if (
|
|
13540
|
+
if (open6(id)) kick(id);
|
|
13289
13541
|
} catch (e) {
|
|
13290
13542
|
log(`relay change handler failed: ${e.message}`);
|
|
13291
13543
|
}
|
|
@@ -13303,7 +13555,7 @@ function createRelay(d) {
|
|
|
13303
13555
|
mark(thread, qid, { error: m, code: STOPPED_CODE });
|
|
13304
13556
|
throw new Error(m);
|
|
13305
13557
|
}
|
|
13306
|
-
if (
|
|
13558
|
+
if (open6(r.id)) {
|
|
13307
13559
|
if (queued().some((q) => q.relay.id === r.id)) {
|
|
13308
13560
|
markQueued(thread, qid);
|
|
13309
13561
|
kick(r.id);
|
|
@@ -13324,7 +13576,7 @@ function createRelay(d) {
|
|
|
13324
13576
|
settle(id) {
|
|
13325
13577
|
try {
|
|
13326
13578
|
if (queued().some((q) => q.relay.id === id)) kick(id);
|
|
13327
|
-
else if (
|
|
13579
|
+
else if (open6(id)) d.next?.(id);
|
|
13328
13580
|
} catch (e) {
|
|
13329
13581
|
log(`relay settle failed: ${e.message}`);
|
|
13330
13582
|
}
|
|
@@ -13553,7 +13805,7 @@ var init_act = __esm({
|
|
|
13553
13805
|
|
|
13554
13806
|
// src/daemon/handover.ts
|
|
13555
13807
|
import { mkdirSync, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
|
|
13556
|
-
import { join as
|
|
13808
|
+
import { join as join26 } from "path";
|
|
13557
13809
|
function reliefOf(get, s) {
|
|
13558
13810
|
let cur = s;
|
|
13559
13811
|
for (let hops = 0; cur && hops < 50; hops++) {
|
|
@@ -13571,7 +13823,7 @@ function createHandovers(d) {
|
|
|
13571
13823
|
const now2 = d.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
13572
13824
|
const briefMs = d.briefMs ?? BRIEF_MS;
|
|
13573
13825
|
const waiting2 = /* @__PURE__ */ new Map();
|
|
13574
|
-
const file = (id) =>
|
|
13826
|
+
const file = (id) => join26(d.dir, `${id}.md`);
|
|
13575
13827
|
const mark = (id, h) => {
|
|
13576
13828
|
const cur = d.store.get(id);
|
|
13577
13829
|
if (cur) d.store.upsert({ ...cur, handover: { ...h, at: now2() } });
|
|
@@ -14025,7 +14277,7 @@ var init_broadcast2 = __esm({
|
|
|
14025
14277
|
});
|
|
14026
14278
|
|
|
14027
14279
|
// src/daemon/context.ts
|
|
14028
|
-
import { open as
|
|
14280
|
+
import { open as open5 } from "fs/promises";
|
|
14029
14281
|
function longWindow(model) {
|
|
14030
14282
|
if (!model) return false;
|
|
14031
14283
|
if (model.includes("[1m]")) return true;
|
|
@@ -14033,7 +14285,7 @@ function longWindow(model) {
|
|
|
14033
14285
|
return !!m && Number(m[1]) >= 5;
|
|
14034
14286
|
}
|
|
14035
14287
|
async function tail(path3) {
|
|
14036
|
-
const f = await
|
|
14288
|
+
const f = await open5(path3, "r");
|
|
14037
14289
|
try {
|
|
14038
14290
|
const { size } = await f.stat();
|
|
14039
14291
|
const start = Math.max(0, size - TAIL_BYTES2);
|
|
@@ -14117,7 +14369,7 @@ var init_context2 = __esm({
|
|
|
14117
14369
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
14118
14370
|
import { mkdtemp, rm as rm5 } from "fs/promises";
|
|
14119
14371
|
import { tmpdir } from "os";
|
|
14120
|
-
import { join as
|
|
14372
|
+
import { join as join27 } from "path";
|
|
14121
14373
|
function parseRoute(answer, context) {
|
|
14122
14374
|
const raw = answer.trim().replace(/^```(?:json)?\s*/, "").replace(/\s*```$/, "");
|
|
14123
14375
|
const result = RouteResultSchema.parse(JSON.parse(raw));
|
|
@@ -14143,7 +14395,7 @@ function createRouter(deps) {
|
|
|
14143
14395
|
let dir;
|
|
14144
14396
|
let timer2;
|
|
14145
14397
|
try {
|
|
14146
|
-
dir = await mkdtemp(
|
|
14398
|
+
dir = await mkdtemp(join27(tmpdir(), "ledge-routing-"));
|
|
14147
14399
|
const env = { ...await deps.env(), LEDGE_CHAT: "1" };
|
|
14148
14400
|
delete env.ANTHROPIC_API_KEY;
|
|
14149
14401
|
delete env.LEDGE_SESSION;
|
|
@@ -15173,15 +15425,15 @@ var init_operations = __esm({
|
|
|
15173
15425
|
});
|
|
15174
15426
|
|
|
15175
15427
|
// src/mobile/risk.ts
|
|
15176
|
-
import { homedir as
|
|
15177
|
-
import { resolve as resolve11, sep as sep5, join as
|
|
15428
|
+
import { homedir as homedir13 } from "os";
|
|
15429
|
+
import { resolve as resolve11, sep as sep5, join as join28 } from "path";
|
|
15178
15430
|
function isSensitive(cwd, p) {
|
|
15179
15431
|
if (p.includes("~")) return true;
|
|
15180
15432
|
const full = resolve11(cwd, p);
|
|
15181
15433
|
const segs = full.split(sep5);
|
|
15182
15434
|
if (segs.includes(".git") || segs.some((s) => s.startsWith(".env"))) return true;
|
|
15183
|
-
const home2 =
|
|
15184
|
-
return [
|
|
15435
|
+
const home2 = homedir13();
|
|
15436
|
+
return [join28(home2, ".ssh"), join28(home2, ".ledge")].some((d) => full === d || full.startsWith(d + sep5));
|
|
15185
15437
|
}
|
|
15186
15438
|
function classifyShell(command, cwd) {
|
|
15187
15439
|
if (PUSH.test(command)) return high("pushes");
|
|
@@ -15394,7 +15646,7 @@ function toPhoneItem(item) {
|
|
|
15394
15646
|
break;
|
|
15395
15647
|
}
|
|
15396
15648
|
case "permission":
|
|
15397
|
-
out = { kind: "permission", ...base3, tool: item.tool, text: text7(item.text), ...pickSet(item, ["decision"]) };
|
|
15649
|
+
out = { kind: "permission", ...base3, tool: item.tool, text: text7(item.text), ...pickSet(item, ["decision", "by"]) };
|
|
15398
15650
|
break;
|
|
15399
15651
|
case "helper":
|
|
15400
15652
|
out = { kind: "helper", ...base3, task: text7(item.task), state: item.state, ...pickSet(item, ["agentType"]) };
|
|
@@ -15752,6 +16004,15 @@ function createPhoneGateway(deps) {
|
|
|
15752
16004
|
if (!p) return "off";
|
|
15753
16005
|
return key !== void 0 && p.key !== key ? "stale" : "on";
|
|
15754
16006
|
};
|
|
16007
|
+
const ownInfo = (deviceId) => {
|
|
16008
|
+
const d = mobile.deviceInfo?.(deviceId);
|
|
16009
|
+
return d ? { kind: d.kind, ...d.name ? { name: d.name } : {} } : {};
|
|
16010
|
+
};
|
|
16011
|
+
const decidedBy = (deviceId) => {
|
|
16012
|
+
const d = mobile.deviceInfo?.(deviceId);
|
|
16013
|
+
const name = (d?.name ?? d?.label ?? "").trim();
|
|
16014
|
+
return { kind: "device", id: deviceId, name: name ? clip(name, 60).text : "Appareil" };
|
|
16015
|
+
};
|
|
15755
16016
|
const snapshot = (deviceId) => {
|
|
15756
16017
|
const push3 = pushStatus(deviceId);
|
|
15757
16018
|
const f = followers.get(deviceId);
|
|
@@ -15776,7 +16037,7 @@ function createPhoneGateway(deps) {
|
|
|
15776
16037
|
...deps.openrig ? { openrig: true } : {},
|
|
15777
16038
|
broadcast: true
|
|
15778
16039
|
},
|
|
15779
|
-
device: { confirm: deps.credential(deviceId) !== null, push: push3, ...deps.chats ? { chat: true } : {}, ...deps.contextNear ? { contextNear: deps.contextNear() } : {} }
|
|
16040
|
+
device: { confirm: deps.credential(deviceId) !== null, push: push3, ...deps.chats ? { chat: true } : {}, ...deps.contextNear ? { contextNear: deps.contextNear() } : {}, id: deviceId, ...ownInfo(deviceId) }
|
|
15780
16041
|
};
|
|
15781
16042
|
return deps.models ? { ...message, daemon: { ...message.daemon, models: true } } : message;
|
|
15782
16043
|
};
|
|
@@ -15906,10 +16167,10 @@ function createPhoneGateway(deps) {
|
|
|
15906
16167
|
return { status: "rejected", reason: s.state === "exited" ? "ended" : "not_launched" };
|
|
15907
16168
|
}
|
|
15908
16169
|
};
|
|
15909
|
-
const local = (msg) => {
|
|
16170
|
+
const local = (deviceId, msg) => {
|
|
15910
16171
|
switch (msg.type) {
|
|
15911
16172
|
case "session.decide":
|
|
15912
|
-
return { type: "session.decide", id: msg.id, decision: msg.decision, ...msg.note !== void 0 ? { note: msg.note } : {}, expect: { kind: msg.expect.kind, text: msg.expect.text, key: msg.expect.key } };
|
|
16173
|
+
return { type: "session.decide", id: msg.id, decision: msg.decision, ...msg.note !== void 0 ? { note: msg.note } : {}, expect: { kind: msg.expect.kind, text: msg.expect.text, key: msg.expect.key }, by: decidedBy(deviceId) };
|
|
15913
16174
|
case "session.reply":
|
|
15914
16175
|
return { type: "session.reply", id: msg.id, text: msg.text, ...msg.attachments?.length ? { attachments: msg.attachments } : {} };
|
|
15915
16176
|
case "session.answer":
|
|
@@ -16285,9 +16546,14 @@ function createPhoneGateway(deps) {
|
|
|
16285
16546
|
}
|
|
16286
16547
|
if (msg.type === "credential.register") return register(deviceId, msg);
|
|
16287
16548
|
if (msg.type === "device.forget") return { status: "applied" };
|
|
16549
|
+
if (msg.type === "device.rename") {
|
|
16550
|
+
if (!mobile.rename) return { status: "rejected", reason: "unavailable" };
|
|
16551
|
+
mobile.rename(deviceId, msg.name);
|
|
16552
|
+
return { status: "applied" };
|
|
16553
|
+
}
|
|
16288
16554
|
if (msg.type === "push.subscribe") {
|
|
16289
16555
|
if (!pushEndpointAllowed(msg.subscription.endpoint)) return { status: "rejected", reason: "invalid" };
|
|
16290
|
-
mobile.setPush(deviceId, { endpoint: msg.subscription.endpoint, p256dh: msg.subscription.keys.p256dh, auth: msg.subscription.keys.auth, key: msg.key });
|
|
16556
|
+
mobile.setPush(deviceId, { endpoint: msg.subscription.endpoint, p256dh: msg.subscription.keys.p256dh, auth: msg.subscription.keys.auth, key: msg.key, ...msg.resolved ? { resolved: true } : {} });
|
|
16291
16557
|
return { status: "applied" };
|
|
16292
16558
|
}
|
|
16293
16559
|
if (msg.type === "push.unsubscribe") {
|
|
@@ -16306,7 +16572,7 @@ function createPhoneGateway(deps) {
|
|
|
16306
16572
|
if (msg.type === "openrig.answer") return rigAnswer(deviceId, msg);
|
|
16307
16573
|
const refused = precheck(deviceId, msg);
|
|
16308
16574
|
if (refused) return refused;
|
|
16309
|
-
const out = local(msg);
|
|
16575
|
+
const out = local(deviceId, msg);
|
|
16310
16576
|
if (out.type === "session.model") return modelChange(out);
|
|
16311
16577
|
let error;
|
|
16312
16578
|
await deps.dispatch(out, (m) => {
|
|
@@ -16332,7 +16598,7 @@ function createPhoneGateway(deps) {
|
|
|
16332
16598
|
}
|
|
16333
16599
|
registry.finish(msg.op, outcome.status, outcome.reason, outcome.how, outcome.report, outcome.detail);
|
|
16334
16600
|
result(deviceId, msg.op, outcome.status, outcome.reason, outcome.how, outcome.report);
|
|
16335
|
-
if (msg.type === "credential.register" && outcome.status === "applied" && approved(deviceId)) push2(deviceId, snapshot(deviceId), "snapshot");
|
|
16601
|
+
if ((msg.type === "credential.register" || msg.type === "device.rename") && outcome.status === "applied" && approved(deviceId)) push2(deviceId, snapshot(deviceId), "snapshot");
|
|
16336
16602
|
if (msg.type === "device.forget" && outcome.status === "applied") {
|
|
16337
16603
|
let timer2;
|
|
16338
16604
|
await Promise.race([outbox.flush(deviceId), new Promise((r) => {
|
|
@@ -16828,6 +17094,12 @@ function pushFor(before2, after2, all2) {
|
|
|
16828
17094
|
}
|
|
16829
17095
|
return null;
|
|
16830
17096
|
}
|
|
17097
|
+
function resolvedFor(before2, after2, all2, by) {
|
|
17098
|
+
if (!before2?.pending || after2.pending) return null;
|
|
17099
|
+
const badge = Math.min(999, all2.filter((s) => s.pending).length);
|
|
17100
|
+
const body = !by ? "" : by.kind === "pc" ? "sur le PC" : cut(`sur ${by.name}`, BODY_MAX2);
|
|
17101
|
+
return { v: 1, kind: "resolved", title: cut(`${after2.name} \xB7 trait\xE9e`, TITLE_MAX), body, url: `/#/thread/${encodeURIComponent(after2.id)}`, badge, tag: safeTag(after2.id) };
|
|
17102
|
+
}
|
|
16831
17103
|
function chatEndPayload(code3, badge) {
|
|
16832
17104
|
return {
|
|
16833
17105
|
v: 1,
|
|
@@ -16884,6 +17156,15 @@ function createNotifier(deps) {
|
|
|
16884
17156
|
const { mobile, log: log2 } = deps;
|
|
16885
17157
|
const now2 = deps.now ?? Date.now;
|
|
16886
17158
|
const lastFinished = /* @__PURE__ */ new Map();
|
|
17159
|
+
const told = /* @__PURE__ */ new Map();
|
|
17160
|
+
const tell = (id, tag) => {
|
|
17161
|
+
let s = told.get(id);
|
|
17162
|
+
if (!s) {
|
|
17163
|
+
s = /* @__PURE__ */ new Set();
|
|
17164
|
+
told.set(id, s);
|
|
17165
|
+
}
|
|
17166
|
+
s.add(tag);
|
|
17167
|
+
};
|
|
16887
17168
|
return {
|
|
16888
17169
|
async notify(payload, sessionId2, only) {
|
|
16889
17170
|
if (!mobile.state().enabled) return;
|
|
@@ -16908,6 +17189,7 @@ function createNotifier(deps) {
|
|
|
16908
17189
|
if (only !== void 0 && d.id !== only) return [];
|
|
16909
17190
|
const sub = mobile.pushOf(d.id);
|
|
16910
17191
|
if (!sub || relayKey !== void 0 && sub.key !== relayKey || deps.presence(d.id)) return [];
|
|
17192
|
+
if (p.kind === "resolved" && (!sub.resolved || !told.get(d.id)?.has(p.tag))) return [];
|
|
16911
17193
|
return [{ id: d.id, online: d.online, sub }];
|
|
16912
17194
|
});
|
|
16913
17195
|
if (targets.length === 0) return;
|
|
@@ -16918,10 +17200,12 @@ function createNotifier(deps) {
|
|
|
16918
17200
|
endpoint: sub.endpoint,
|
|
16919
17201
|
body: Buffer.from(encryptPush(plain4, { p256dh: sub.p256dh, auth: sub.auth }, { padTo: PAD_TO })).toString("base64url"),
|
|
16920
17202
|
ttl: TTL_S,
|
|
16921
|
-
urgency: p.kind === "finished" || p.kind === "chat" ? "normal" : "high",
|
|
17203
|
+
urgency: p.kind === "finished" || p.kind === "chat" || p.kind === "resolved" ? "normal" : "high",
|
|
16922
17204
|
topic: p.tag
|
|
16923
17205
|
};
|
|
16924
17206
|
const r = await mobile.sendPush(msg);
|
|
17207
|
+
if (p.kind === "resolved") told.get(id)?.delete(p.tag);
|
|
17208
|
+
else if ((p.kind === "request" || p.kind === "question") && r.result === "sent") tell(id, p.tag);
|
|
16925
17209
|
if (r.result === "gone") {
|
|
16926
17210
|
log2(`push: subscription of device ${id} is gone, removed`);
|
|
16927
17211
|
mobile.setPush(id, null);
|
|
@@ -16951,9 +17235,13 @@ var init_notifier = __esm({
|
|
|
16951
17235
|
// src/daemon/server.ts
|
|
16952
17236
|
import { createServer } from "http";
|
|
16953
17237
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
16954
|
-
import { homedir as
|
|
16955
|
-
import { basename as basename12, join as
|
|
17238
|
+
import { homedir as homedir14 } from "os";
|
|
17239
|
+
import { basename as basename12, join as join29, resolve as resolve12 } from "path";
|
|
16956
17240
|
import { WebSocketServer, WebSocket as WebSocket3 } from "ws";
|
|
17241
|
+
function withoutHarness(s) {
|
|
17242
|
+
const { pending: _p, lastReply: _r, transcriptPath: _t, agentSessionId: _a, agentSessionFile: _f, model: _m, effort: _e, modelNext: _n, queue: _q, backend: _b, ...rest } = s;
|
|
17243
|
+
return { ...rest, helpers: [] };
|
|
17244
|
+
}
|
|
16957
17245
|
function resumeKey(s) {
|
|
16958
17246
|
if (s.tool === "omp") return s.agentSessionFile;
|
|
16959
17247
|
if (s.agentSessionId) return s.agentSessionId;
|
|
@@ -17003,11 +17291,12 @@ async function startServer(opts) {
|
|
|
17003
17291
|
const agentEnv2 = opts.agentEnv ?? (async () => ({ ...process.env, ...await loginShellEnv() }));
|
|
17004
17292
|
const attachments = opts.attachments ?? new AttachmentStore(paths().attachments);
|
|
17005
17293
|
const reducer = new CaptureReducer();
|
|
17006
|
-
const threads = createChatThreads({ attachments, store, tmux, home: paths().dir, notesDir:
|
|
17294
|
+
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
17295
|
const chat = opts.chat ?? threads.desktop;
|
|
17008
17296
|
const router = createRouter({ env: agentEnv2 });
|
|
17009
|
-
const omp2 = opts.omp ?? createOmpHost({ store, env: agentEnv2, dir:
|
|
17297
|
+
const omp2 = opts.omp ?? createOmpHost({ store, env: agentEnv2, dir: join29(paths().dir, "omp"), now: now2, attachments });
|
|
17010
17298
|
const openrig = opts.openrig ?? createOpenRig({ env: agentEnv2 });
|
|
17299
|
+
const ompSeatReadOnly = (s) => new Refusal(`${s.name} is the OpenRig OMP seat ${s.rig?.seat ?? ""}: Ledge follows its state only`, "read_only");
|
|
17011
17300
|
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
17301
|
const adoptSeat = (id, seat2) => {
|
|
17013
17302
|
const session = store.get(id);
|
|
@@ -17020,9 +17309,22 @@ async function startServer(opts) {
|
|
|
17020
17309
|
const seatId = (seat2) => `rig-${seat2.replace(/[^A-Za-z0-9._-]/g, "-")}`;
|
|
17021
17310
|
async function discoverSeats(st) {
|
|
17022
17311
|
if (st.daemon.state !== "running") return;
|
|
17023
|
-
const followed = new
|
|
17312
|
+
const followed = new Map(store.all().filter((x) => x.rig && x.state !== "exited").map((x) => [x.rig.seat, x.id]));
|
|
17024
17313
|
for (const r of st.rigs) for (const n of r.nodes) {
|
|
17025
|
-
|
|
17314
|
+
const tool = SEAT_TOOLS[n.runtime];
|
|
17315
|
+
if (!tool || n.sessionStatus !== "running") continue;
|
|
17316
|
+
const live3 = followed.get(n.session);
|
|
17317
|
+
if (live3 !== void 0) {
|
|
17318
|
+
const s = store.get(live3);
|
|
17319
|
+
if (s && s.tool !== tool && (s.source === "rig" || s.source === "hook_only")) {
|
|
17320
|
+
if (s.source === "hook_only" && !await tmux.hasSession(n.session).catch(() => false)) continue;
|
|
17321
|
+
const cur = store.get(live3);
|
|
17322
|
+
if (!cur || cur.state === "exited" || cur.tool === tool) continue;
|
|
17323
|
+
store.upsert({ ...withoutHarness(cur), tool, state: "idle", source: "rig", tmuxSession: n.session, lastActivityAt: now2() });
|
|
17324
|
+
log(`openrig: seat ${n.session} now runs ${n.runtime}`);
|
|
17325
|
+
}
|
|
17326
|
+
continue;
|
|
17327
|
+
}
|
|
17026
17328
|
if (!await tmux.hasSession(n.session).catch(() => false)) continue;
|
|
17027
17329
|
const before2 = store.all().filter((x) => x.rig?.seat === n.session).sort((a, b3) => b3.lastActivityAt.localeCompare(a.lastActivityAt))[0];
|
|
17028
17330
|
const id = before2?.id ?? seatId(n.session), old = store.get(id), at = now2();
|
|
@@ -17031,13 +17333,14 @@ async function startServer(opts) {
|
|
|
17031
17333
|
if (old) {
|
|
17032
17334
|
if (old.archivedAt) store.unarchive(id, at);
|
|
17033
17335
|
const { exitedAt: _e, exitCode: _c, pending: _p, ...rest } = store.get(id);
|
|
17034
|
-
|
|
17336
|
+
const same2 = rest.tool === tool ? rest : { ...withoutHarness(rest), tool };
|
|
17337
|
+
store.upsert({ ...same2, state: "idle", tmuxSession: n.session, source: "rig", rig, lastActivityAt: at });
|
|
17035
17338
|
} else {
|
|
17036
17339
|
const name = pickName([...namesInUse(store.all()), ...heldNames(store.all())]);
|
|
17037
17340
|
store.upsert({
|
|
17038
17341
|
id,
|
|
17039
17342
|
name,
|
|
17040
|
-
tool
|
|
17343
|
+
tool,
|
|
17041
17344
|
cwd,
|
|
17042
17345
|
project: basename12(cwd) || r.name,
|
|
17043
17346
|
title: `${name} \xB7 ${n.logicalId}`,
|
|
@@ -17051,10 +17354,21 @@ async function startServer(opts) {
|
|
|
17051
17354
|
});
|
|
17052
17355
|
log(`openrig: seat ${n.session} followed as ${name}`);
|
|
17053
17356
|
}
|
|
17054
|
-
followed.
|
|
17357
|
+
followed.set(n.session, id);
|
|
17358
|
+
}
|
|
17359
|
+
if (!st.rigs.some((r) => r.nodes.length > 0)) return;
|
|
17360
|
+
const listed2 = new Set(st.rigs.flatMap((r) => r.nodes.map((n) => n.session)));
|
|
17361
|
+
const unsure = new Set(st.rigs.filter((r) => r.seats > 0 && r.nodes.length === 0).map((r) => r.name));
|
|
17362
|
+
for (const s of store.all()) {
|
|
17363
|
+
if (isOmpSeat2(s) && s.rig && s.state === "exited" && !isArchived(s) && !listed2.has(s.rig.seat) && !unsure.has(s.rig.name)) {
|
|
17364
|
+
store.archive(s.id, now2());
|
|
17365
|
+
log(`openrig: seat ${s.rig.seat} is gone from OpenRig`);
|
|
17366
|
+
}
|
|
17055
17367
|
}
|
|
17056
17368
|
}
|
|
17057
17369
|
const SEATS_EVERY_MS = 3e4;
|
|
17370
|
+
const ompSeats = opts.ompSeats ?? createOmpSeats({ store });
|
|
17371
|
+
ompSeats.start();
|
|
17058
17372
|
const seatTimer = setInterval(() => {
|
|
17059
17373
|
void openrig.state().then(discoverSeats).catch((e) => log(`openrig: seats not read: ${e.message}`));
|
|
17060
17374
|
}, SEATS_EVERY_MS);
|
|
@@ -17100,7 +17414,7 @@ async function startServer(opts) {
|
|
|
17100
17414
|
}).catch((e) => log(`openrig: seat ${seat2} not read: ${e.message}`));
|
|
17101
17415
|
};
|
|
17102
17416
|
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 ??
|
|
17417
|
+
const models = opts.models ?? createModels({ env: agentEnv2, omp: async () => ompChoices(await omp2.catalog(opts.home ?? homedir14())) });
|
|
17104
17418
|
const token = await ensureDaemonToken();
|
|
17105
17419
|
const httpServer = createServer(unauthorized);
|
|
17106
17420
|
const sockets = /* @__PURE__ */ new Set();
|
|
@@ -17140,7 +17454,7 @@ async function startServer(opts) {
|
|
|
17140
17454
|
const islandFile = opts.islandFile ?? paths().island;
|
|
17141
17455
|
let island = readIsland(islandFile);
|
|
17142
17456
|
const islandMsg = () => ({ type: "island", prefs: island });
|
|
17143
|
-
const contextFile = opts.contextFile ??
|
|
17457
|
+
const contextFile = opts.contextFile ?? join29(paths().dir, "context.json");
|
|
17144
17458
|
let contextPrefs = readContextPrefs(contextFile);
|
|
17145
17459
|
const contextMsg = () => ({ type: "context.prefs", prefs: contextPrefs });
|
|
17146
17460
|
const mobile = opts.mobile;
|
|
@@ -17197,7 +17511,7 @@ async function startServer(opts) {
|
|
|
17197
17511
|
}
|
|
17198
17512
|
});
|
|
17199
17513
|
const terminalHolders = /* @__PURE__ */ new Map();
|
|
17200
|
-
const holdersMsg = () => ({ type: "terminal.holders", holds: [...terminalHolders].map(([id, deviceId]) => ({ id, device: mobile?.state().devices.find((d) => d.id === deviceId)
|
|
17514
|
+
const holdersMsg = () => ({ type: "terminal.holders", holds: [...terminalHolders].map(([id, deviceId]) => ({ id, device: ((d) => d?.name ?? d?.label)(mobile?.state().devices.find((d) => d.id === deviceId)) ?? deviceId })) });
|
|
17201
17515
|
const typing = /* @__PURE__ */ new Set();
|
|
17202
17516
|
const closing = /* @__PURE__ */ new Set();
|
|
17203
17517
|
const cardThreads = opts.chat ? {
|
|
@@ -17239,7 +17553,7 @@ async function startServer(opts) {
|
|
|
17239
17553
|
tasks,
|
|
17240
17554
|
launch: (req) => launch3(req),
|
|
17241
17555
|
close: (s) => closeSession(s),
|
|
17242
|
-
dir:
|
|
17556
|
+
dir: join29(paths().dir, "handovers"),
|
|
17243
17557
|
now: now2,
|
|
17244
17558
|
...opts.handoverBriefMs !== void 0 ? { briefMs: opts.handoverBriefMs } : {}
|
|
17245
17559
|
});
|
|
@@ -17346,9 +17660,9 @@ async function startServer(opts) {
|
|
|
17346
17660
|
if (!a || a.settled || !answeredBy(a.pending, ev) || !cur || pendingKey(cur) !== pendingKey(a.pending)) return;
|
|
17347
17661
|
a.settled = true;
|
|
17348
17662
|
a.byHook = true;
|
|
17349
|
-
store.apply(id, { name: "Decided", at: now2(), source: "daemon", ...a.decision ? { decision: a.decision } : {} });
|
|
17663
|
+
store.apply(id, { name: "Decided", at: now2(), source: "daemon", ...a.decision ? { decision: a.decision } : {}, ...a.by ? { by: a.by } : {} });
|
|
17350
17664
|
}
|
|
17351
|
-
async function injectAndVerify(id, planFor, requirePrompt, decision) {
|
|
17665
|
+
async function injectAndVerify(id, planFor, requirePrompt, decision, by) {
|
|
17352
17666
|
const s = store.get(id);
|
|
17353
17667
|
if (!s) throw new Refusal(`unknown session ${id}`, "unknown_session");
|
|
17354
17668
|
if (inFlight.has(id)) throw new Refusal("a decision is already in progress", "busy");
|
|
@@ -17374,7 +17688,7 @@ async function startServer(opts) {
|
|
|
17374
17688
|
const plan2 = typeof planFor === "function" ? planFor(before2 ?? "") : planFor;
|
|
17375
17689
|
const stillShown = (p) => decided && adapter.promptGone ? !adapter.promptGone(p, decided) : adapter.promptStillVisible(bottomLines(p));
|
|
17376
17690
|
const tool = s.tool === "claude" && decided?.source === "hook" ? pendingTool(decided) : void 0;
|
|
17377
|
-
const answer = tool ? { pending: decided, ...decision ? { decision } : {}, settled: false, byHook: false } : void 0;
|
|
17691
|
+
const answer = tool ? { pending: decided, ...decision ? { decision } : {}, ...by ? { by } : {}, settled: false, byHook: false } : void 0;
|
|
17378
17692
|
const transcript = tool && !decided.helperId && s.transcriptPath ? s.transcriptPath : void 0;
|
|
17379
17693
|
const call = transcript ? claudeOpenCall(transcript, tool) : void 0;
|
|
17380
17694
|
const promptGone3 = async () => {
|
|
@@ -17395,7 +17709,7 @@ async function startServer(opts) {
|
|
|
17395
17709
|
if (e instanceof NoteNotTyped) {
|
|
17396
17710
|
if (await promptGone3()) {
|
|
17397
17711
|
if (answer) answer.settled = true;
|
|
17398
|
-
if (!answer?.byHook && stillTheSame()) store.apply(id, { name: "Decided", at: now2(), source: "daemon", ...decision ? { decision } : {} });
|
|
17712
|
+
if (!answer?.byHook && stillTheSame()) store.apply(id, { name: "Decided", at: now2(), source: "daemon", ...decision ? { decision } : {}, ...by ? { by } : {} });
|
|
17399
17713
|
throw e;
|
|
17400
17714
|
}
|
|
17401
17715
|
e = new Refusal(NOT_TAKEN, "not_taken");
|
|
@@ -17409,7 +17723,7 @@ async function startServer(opts) {
|
|
|
17409
17723
|
if (answer?.byHook) return true;
|
|
17410
17724
|
if (!stillTheSame()) return false;
|
|
17411
17725
|
if (gone) {
|
|
17412
|
-
store.apply(id, { name: "Decided", at: now2(), source: "daemon", ...decision ? { decision } : {} });
|
|
17726
|
+
store.apply(id, { name: "Decided", at: now2(), source: "daemon", ...decision ? { decision } : {}, ...by ? { by } : {} });
|
|
17413
17727
|
return true;
|
|
17414
17728
|
}
|
|
17415
17729
|
store.apply(id, { name: "InjectFailed", at: now2(), source: "daemon", text: NOT_TAKEN });
|
|
@@ -17515,6 +17829,7 @@ async function startServer(opts) {
|
|
|
17515
17829
|
const s = store.get(id);
|
|
17516
17830
|
if (!s) return unknownSession();
|
|
17517
17831
|
if (s.source === "hook_only") return readOnly();
|
|
17832
|
+
if (isOmpSeat2(s)) return ompSeatReadOnly(s);
|
|
17518
17833
|
if (closing.has(id)) return new Refusal("the session is being closed", "busy");
|
|
17519
17834
|
const ids = how.attachments ?? [];
|
|
17520
17835
|
const refs = [];
|
|
@@ -17848,7 +18163,7 @@ async function startServer(opts) {
|
|
|
17848
18163
|
let scanning = null;
|
|
17849
18164
|
function sharedScan() {
|
|
17850
18165
|
if (scanning) return scanning;
|
|
17851
|
-
const home2 = opts.home ??
|
|
18166
|
+
const home2 = opts.home ?? homedir14();
|
|
17852
18167
|
const extra = extraRoots(home2, [...store.recents().map((r) => r.cwd), ...store.all().filter((s) => s.state !== "exited").map((s) => s.cwd)]);
|
|
17853
18168
|
const walk = (opts.scan ?? scanRepos)({ home: home2, extra }).finally(() => {
|
|
17854
18169
|
if (scanning === walk) scanning = null;
|
|
@@ -18103,7 +18418,7 @@ async function startServer(opts) {
|
|
|
18103
18418
|
if (msg.decision === "always" && !offers(s.tool, pending.kind, "always")) return refuse(new Refusal(NOT_OFFERED, "not_offered"), msg.id);
|
|
18104
18419
|
if (msg.note !== void 0 && (msg.decision !== "deny" || !offers(s.tool, pending.kind, "note"))) return refuse(new Refusal(NOT_OFFERED, "not_offered"), msg.id);
|
|
18105
18420
|
const { decision, note } = msg;
|
|
18106
|
-
await injectAndVerify(msg.id, (pane) => adapter.keysFor(decision, pending, pane, note), true, decision);
|
|
18421
|
+
await injectAndVerify(msg.id, (pane) => adapter.keysFor(decision, pending, pane, note), true, decision, msg.by ?? { kind: "pc" });
|
|
18107
18422
|
return;
|
|
18108
18423
|
}
|
|
18109
18424
|
case "session.reply": {
|
|
@@ -18127,6 +18442,7 @@ async function startServer(opts) {
|
|
|
18127
18442
|
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
18443
|
}
|
|
18129
18444
|
if (!s?.tmuxSession) return;
|
|
18445
|
+
if (isOmpSeat2(s)) return refuse(ompSeatReadOnly(s), msg.id);
|
|
18130
18446
|
await tmux.sendKeys(s.tmuxSession, adapterFor(s.tool).interruptKeys());
|
|
18131
18447
|
return;
|
|
18132
18448
|
}
|
|
@@ -18357,7 +18673,7 @@ async function startServer(opts) {
|
|
|
18357
18673
|
relay,
|
|
18358
18674
|
launchChat: (thread, qid, rows) => {
|
|
18359
18675
|
const c = thread === "desktop" ? threads.desktop : threads.device(thread.slice("device:".length));
|
|
18360
|
-
const userHome = resolve12(opts.home ??
|
|
18676
|
+
const userHome = resolve12(opts.home ?? homedir14());
|
|
18361
18677
|
return launchFromChat({
|
|
18362
18678
|
chat: c,
|
|
18363
18679
|
emit: threads.emitterOf(thread) ?? (() => {
|
|
@@ -18438,10 +18754,22 @@ async function startServer(opts) {
|
|
|
18438
18754
|
const badge = store.all().filter((s) => s.pending).length + tasks.all().filter(openDecision).length;
|
|
18439
18755
|
void notifier.notify(decisionPayload(task, badge), void 0).catch((e) => log(`push failed (${e instanceof Error ? e.name : "error"})`));
|
|
18440
18756
|
};
|
|
18441
|
-
|
|
18757
|
+
const answeredBy2 = /* @__PURE__ */ new Map();
|
|
18758
|
+
const unsubscribeDecided = store.onEvent((id, ev) => {
|
|
18759
|
+
if (ev.name === "Decided") answeredBy2.set(id, ev.by ?? null);
|
|
18760
|
+
});
|
|
18761
|
+
const unsubscribeApply = store.onApply((before2, after2) => {
|
|
18762
|
+
const failed = (e) => log(`push failed (${e instanceof Error ? e.name : "error"})`);
|
|
18763
|
+
const r = resolvedFor(before2, after2, store.all(), answeredBy2.get(after2.id));
|
|
18764
|
+
if (before2?.pending && !after2.pending) answeredBy2.delete(after2.id);
|
|
18765
|
+
if (r) void notifier.notify(r, after2.id).catch(failed);
|
|
18442
18766
|
const p = pushFor(before2, after2, store.all());
|
|
18443
|
-
if (p) void notifier.notify(p, after2.id).catch(
|
|
18767
|
+
if (p) void notifier.notify(p, after2.id).catch(failed);
|
|
18444
18768
|
});
|
|
18769
|
+
unsubscribePush = () => {
|
|
18770
|
+
unsubscribeDecided();
|
|
18771
|
+
unsubscribeApply();
|
|
18772
|
+
};
|
|
18445
18773
|
}
|
|
18446
18774
|
wss.on("connection", (ws, req) => {
|
|
18447
18775
|
ws.on("error", (e) => log(`socket error: ${e.message}`));
|
|
@@ -18501,6 +18829,7 @@ async function startServer(opts) {
|
|
|
18501
18829
|
close: () => new Promise((res) => {
|
|
18502
18830
|
clearInterval(sweepTimer);
|
|
18503
18831
|
clearInterval(seatTimer);
|
|
18832
|
+
ompSeats.stop();
|
|
18504
18833
|
human.stop();
|
|
18505
18834
|
if (goalsTimer) clearTimeout(goalsTimer);
|
|
18506
18835
|
void reducer.close();
|
|
@@ -18520,7 +18849,7 @@ async function startServer(opts) {
|
|
|
18520
18849
|
})
|
|
18521
18850
|
};
|
|
18522
18851
|
}
|
|
18523
|
-
var CONTINUE_ARGS, RESUME_ARGS, MULTI_DIR_ARGS, MODEL_ARGS, EFFORT_ARGS, NoteNotTyped, DELETED_TTL_MS, RESPAWN_HOOKS_MS, ALLOWED_ORIGINS;
|
|
18852
|
+
var SEAT_TOOLS, CONTINUE_ARGS, RESUME_ARGS, MULTI_DIR_ARGS, MODEL_ARGS, EFFORT_ARGS, NoteNotTyped, DELETED_TTL_MS, RESPAWN_HOOKS_MS, ALLOWED_ORIGINS;
|
|
18524
18853
|
var init_server = __esm({
|
|
18525
18854
|
"src/daemon/server.ts"() {
|
|
18526
18855
|
"use strict";
|
|
@@ -18540,6 +18869,7 @@ var init_server = __esm({
|
|
|
18540
18869
|
init_spawn();
|
|
18541
18870
|
init_host();
|
|
18542
18871
|
init_catalog();
|
|
18872
|
+
init_seats();
|
|
18543
18873
|
init_repos2();
|
|
18544
18874
|
init_scan2();
|
|
18545
18875
|
init_folders();
|
|
@@ -18578,6 +18908,7 @@ var init_server = __esm({
|
|
|
18578
18908
|
init_challenges();
|
|
18579
18909
|
init_webauthn();
|
|
18580
18910
|
init_frames();
|
|
18911
|
+
SEAT_TOOLS = { "claude-code": "claude", codex: "codex", omp: "omp" };
|
|
18581
18912
|
CONTINUE_ARGS = { claude: ["--continue"], codex: ["resume", "--last"], cursor: ["--continue"] };
|
|
18582
18913
|
RESUME_ARGS = { claude: (id) => ["--resume", id], codex: (id) => ["resume", id], cursor: (id) => ["--resume", id] };
|
|
18583
18914
|
MULTI_DIR_ARGS = { claude: (dirs) => ["--add-dir", ...dirs] };
|
|
@@ -18606,7 +18937,7 @@ var init_server = __esm({
|
|
|
18606
18937
|
// src/tmux.ts
|
|
18607
18938
|
import { execFile as execFile7 } from "child_process";
|
|
18608
18939
|
import { existsSync as existsSync3 } from "fs";
|
|
18609
|
-
import { join as
|
|
18940
|
+
import { join as join30 } from "path";
|
|
18610
18941
|
function runFile(file, args, env, opts) {
|
|
18611
18942
|
return new Promise((resolve13) => {
|
|
18612
18943
|
execFile7(file, args, { maxBuffer: 4 * 1024 * 1024, env, ...opts?.timeoutMs ? { timeout: opts.timeoutMs, killSignal: "SIGTERM" } : {} }, (err, stdout, stderr) => {
|
|
@@ -18616,7 +18947,7 @@ function runFile(file, args, env, opts) {
|
|
|
18616
18947
|
});
|
|
18617
18948
|
}
|
|
18618
18949
|
function systemdAvailable(env = process.env, exists = existsSync3) {
|
|
18619
|
-
return !!env.XDG_RUNTIME_DIR && exists(
|
|
18950
|
+
return !!env.XDG_RUNTIME_DIR && exists(join30(env.XDG_RUNTIME_DIR, "systemd", "private"));
|
|
18620
18951
|
}
|
|
18621
18952
|
function newSessionCommand(tmuxArgs, scoped, unit) {
|
|
18622
18953
|
if (!scoped) return { file: "tmux", args: tmuxArgs };
|
|
@@ -18749,10 +19080,10 @@ __export(setup_exports, {
|
|
|
18749
19080
|
systemdUnit: () => systemdUnit,
|
|
18750
19081
|
writeBin: () => writeBin
|
|
18751
19082
|
});
|
|
18752
|
-
import { readFile as
|
|
19083
|
+
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
19084
|
import { execFile as execFile8 } from "child_process";
|
|
18754
|
-
import { homedir as
|
|
18755
|
-
import { join as
|
|
19085
|
+
import { homedir as homedir15, userInfo } from "os";
|
|
19086
|
+
import { join as join31, dirname as dirname10 } from "path";
|
|
18756
19087
|
function mergeHooks(settings, events, command, file) {
|
|
18757
19088
|
if (settings === void 0) settings = {};
|
|
18758
19089
|
if (!settings || typeof settings !== "object" || Array.isArray(settings)) throw new Error(`${file} root is not an object, not modified`);
|
|
@@ -18800,7 +19131,7 @@ function mergeCursorHooks(config, command = "ledge hook cursor") {
|
|
|
18800
19131
|
async function mergeJsonFile(path3, merge, name) {
|
|
18801
19132
|
let raw = null;
|
|
18802
19133
|
try {
|
|
18803
|
-
raw = await
|
|
19134
|
+
raw = await readFile13(path3, "utf8");
|
|
18804
19135
|
} catch (e) {
|
|
18805
19136
|
if (e.code !== "ENOENT") throw e;
|
|
18806
19137
|
}
|
|
@@ -18836,7 +19167,7 @@ WantedBy=default.target
|
|
|
18836
19167
|
async function writeIfChanged(path3, content3) {
|
|
18837
19168
|
let current2 = null;
|
|
18838
19169
|
try {
|
|
18839
|
-
current2 = await
|
|
19170
|
+
current2 = await readFile13(path3, "utf8");
|
|
18840
19171
|
} catch (e) {
|
|
18841
19172
|
if (e.code !== "ENOENT") throw e;
|
|
18842
19173
|
}
|
|
@@ -18850,7 +19181,7 @@ async function writeIfChanged(path3, content3) {
|
|
|
18850
19181
|
async function stopDetachedDaemon(pidFile) {
|
|
18851
19182
|
let pid;
|
|
18852
19183
|
try {
|
|
18853
|
-
pid = Number((await
|
|
19184
|
+
pid = Number((await readFile13(pidFile, "utf8")).trim());
|
|
18854
19185
|
} catch {
|
|
18855
19186
|
return null;
|
|
18856
19187
|
}
|
|
@@ -18880,10 +19211,10 @@ async function stopDetachedDaemon(pidFile) {
|
|
|
18880
19211
|
async function installService(opts = {}) {
|
|
18881
19212
|
const out = [];
|
|
18882
19213
|
const run4 = opts.run ?? execRunner;
|
|
18883
|
-
const unitDir = opts.unitDir ??
|
|
19214
|
+
const unitDir = opts.unitDir ?? join31(homedir15(), ".config", "systemd", "user");
|
|
18884
19215
|
const node2 = opts.node ?? await realpath2(process.execPath);
|
|
18885
19216
|
const script = opts.script ?? await realpath2(process.argv[1]);
|
|
18886
|
-
const changed = await writeIfChanged(
|
|
19217
|
+
const changed = await writeIfChanged(join31(unitDir, SERVICE), systemdUnit({ node: node2, script }));
|
|
18887
19218
|
const reload = await run4("systemctl", ["--user", "daemon-reload"]);
|
|
18888
19219
|
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
19220
|
const active = (await run4("systemctl", ["--user", "is-active", SERVICE])).code === 0;
|
|
@@ -18912,7 +19243,7 @@ async function installService(opts = {}) {
|
|
|
18912
19243
|
}
|
|
18913
19244
|
async function isDir(path3) {
|
|
18914
19245
|
try {
|
|
18915
|
-
return (await
|
|
19246
|
+
return (await stat7(path3)).isDirectory();
|
|
18916
19247
|
} catch {
|
|
18917
19248
|
return false;
|
|
18918
19249
|
}
|
|
@@ -18921,7 +19252,7 @@ async function writeBin(opts) {
|
|
|
18921
19252
|
const st = await lstat(opts.link).catch(() => null);
|
|
18922
19253
|
const was = st?.isSymbolicLink() ? await readlink(opts.link) : null;
|
|
18923
19254
|
if (was !== null && !was.endsWith("/dist/cli/index.js")) return null;
|
|
18924
|
-
const current2 = st && was === null && st.isFile() ? await
|
|
19255
|
+
const current2 = st && was === null && st.isFile() ? await readFile13(opts.link, "utf8").catch(() => "") : "";
|
|
18925
19256
|
if (st && was === null && !current2.startsWith(BIN_MARK)) return null;
|
|
18926
19257
|
const script = `${BIN_MARK}: the hooks run \`ledge\` wherever node is not on the PATH.
|
|
18927
19258
|
exec ${shQuote(opts.node)} ${shQuote(opts.target)} "$@"
|
|
@@ -18937,24 +19268,24 @@ async function runSetup(opts = {}) {
|
|
|
18937
19268
|
const out = [];
|
|
18938
19269
|
await ensureLedgeDir();
|
|
18939
19270
|
out.push("~/.ledge ready (tmux.conf written if missing)");
|
|
18940
|
-
const settingsPath2 = opts.settingsPath ??
|
|
19271
|
+
const settingsPath2 = opts.settingsPath ?? join31(homedir15(), ".claude", "settings.json");
|
|
18941
19272
|
if (await mergeJsonFile(settingsPath2, mergeClaudeHooks, "settings.json")) out.push(`Claude Code hooks added in ${settingsPath2}`);
|
|
18942
19273
|
else out.push("Claude Code hooks already present");
|
|
18943
|
-
const codexDir = opts.codexDir ?? process.env.CODEX_HOME ??
|
|
19274
|
+
const codexDir = opts.codexDir ?? process.env.CODEX_HOME ?? join31(homedir15(), ".codex");
|
|
18944
19275
|
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 ${
|
|
19276
|
+
else if (await mergeJsonFile(join31(codexDir, "hooks.json"), mergeCodexHooks, "hooks.json")) {
|
|
19277
|
+
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
19278
|
} else out.push("Codex hooks already present");
|
|
18948
|
-
const cursorDir = opts.cursorDir ??
|
|
19279
|
+
const cursorDir = opts.cursorDir ?? join31(homedir15(), ".cursor");
|
|
18949
19280
|
if (!await isDir(cursorDir)) out.push(`Cursor Agent not found (${cursorDir} missing), Cursor Agent hooks skipped`);
|
|
18950
|
-
else if (await mergeJsonFile(
|
|
19281
|
+
else if (await mergeJsonFile(join31(cursorDir, "hooks.json"), mergeCursorHooks, "hooks.json")) out.push(`Cursor Agent hooks added in ${join31(cursorDir, "hooks.json")}`);
|
|
18951
19282
|
else out.push("Cursor Agent hooks already present");
|
|
18952
19283
|
if (opts.service ?? true) {
|
|
18953
19284
|
const cli = opts.cli ?? process.argv[1] ?? "";
|
|
18954
19285
|
if (cli.endsWith(".ts")) out.push("systemd service skipped: run setup from the built CLI (pnpm --filter @paradow/ledge build, then ledge setup)");
|
|
18955
19286
|
else {
|
|
18956
19287
|
const script = await realpath2(cli);
|
|
18957
|
-
const line2 = await writeBin({ link: opts.link ??
|
|
19288
|
+
const line2 = await writeBin({ link: opts.link ?? join31(homedir15(), ".local", "bin", "ledge"), node: process.execPath, target: script });
|
|
18958
19289
|
if (line2) out.push(line2);
|
|
18959
19290
|
out.push(...await installService({ script, unitDir: opts.unitDir, run: opts.run }));
|
|
18960
19291
|
}
|
|
@@ -19016,10 +19347,10 @@ var init_setup = __esm({
|
|
|
19016
19347
|
});
|
|
19017
19348
|
|
|
19018
19349
|
// src/update/guard.ts
|
|
19019
|
-
import { readFile as
|
|
19350
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
19020
19351
|
async function tmuxServerAtRisk(opts = {}) {
|
|
19021
19352
|
const runTmux = opts.runTmux ?? run2;
|
|
19022
|
-
const readCgroup = opts.readCgroup ?? ((pid2) =>
|
|
19353
|
+
const readCgroup = opts.readCgroup ?? ((pid2) => readFile14(`/proc/${pid2}/cgroup`, "utf8"));
|
|
19023
19354
|
const r = await runTmux(["-L", opts.socket ?? "ledge", "list-sessions", "-F", "#{pid}"]);
|
|
19024
19355
|
if (r.code !== 0) return false;
|
|
19025
19356
|
const pid = r.stdout.split("\n").map((s) => s.trim()).find(Boolean);
|
|
@@ -19042,12 +19373,12 @@ var init_guard = __esm({
|
|
|
19042
19373
|
|
|
19043
19374
|
// src/update/self.ts
|
|
19044
19375
|
import { existsSync as existsSync4 } from "fs";
|
|
19045
|
-
import { readFile as
|
|
19046
|
-
import { homedir as
|
|
19047
|
-
import { dirname as dirname11, join as
|
|
19376
|
+
import { readFile as readFile15, realpath as realpath3 } from "fs/promises";
|
|
19377
|
+
import { homedir as homedir16 } from "os";
|
|
19378
|
+
import { dirname as dirname11, join as join32 } from "path";
|
|
19048
19379
|
function gitRoot(script, exists = existsSync4) {
|
|
19049
19380
|
for (let dir = dirname11(script); ; dir = dirname11(dir)) {
|
|
19050
|
-
if (exists(
|
|
19381
|
+
if (exists(join32(dir, ".git"))) return dir;
|
|
19051
19382
|
if (dirname11(dir) === dir) return null;
|
|
19052
19383
|
}
|
|
19053
19384
|
}
|
|
@@ -19057,10 +19388,10 @@ function installKind(script, exists = existsSync4) {
|
|
|
19057
19388
|
}
|
|
19058
19389
|
async function daemonInfo() {
|
|
19059
19390
|
const script = await realpath3(process.argv[1] ?? "").catch(() => process.argv[1] ?? "");
|
|
19060
|
-
const cgroup = await
|
|
19391
|
+
const cgroup = await readFile15("/proc/self/cgroup", "utf8").catch(() => "");
|
|
19061
19392
|
const install = installKind(script);
|
|
19062
19393
|
const root2 = install === "git" ? gitRoot(script) : null;
|
|
19063
|
-
return { version: LEDGE_VERSION, install, systemd: underSystemd(process.env, cgroup), home:
|
|
19394
|
+
return { version: LEDGE_VERSION, install, systemd: underSystemd(process.env, cgroup), home: homedir16(), ...root2 ? { manual: manualCommand(root2) } : {} };
|
|
19064
19395
|
}
|
|
19065
19396
|
var underSystemd, manualCommand;
|
|
19066
19397
|
var init_self = __esm({
|
|
@@ -19074,7 +19405,7 @@ var init_self = __esm({
|
|
|
19074
19405
|
});
|
|
19075
19406
|
|
|
19076
19407
|
// src/update/run-update.ts
|
|
19077
|
-
import { dirname as dirname12, join as
|
|
19408
|
+
import { dirname as dirname12, join as join33 } from "path";
|
|
19078
19409
|
function createDaemonUpdater(deps) {
|
|
19079
19410
|
let busy = false;
|
|
19080
19411
|
let restarting = false;
|
|
@@ -19163,7 +19494,7 @@ var init_run_update = __esm({
|
|
|
19163
19494
|
PACKAGE = "@paradow/ledge";
|
|
19164
19495
|
NPM_TIMEOUT_MS = 3 * 6e4;
|
|
19165
19496
|
RESTART_COMMAND = "systemctl --user restart ledge-daemon";
|
|
19166
|
-
npmCommand = (execPath, version) => ({ file:
|
|
19497
|
+
npmCommand = (execPath, version) => ({ file: join33(dirname12(execPath), "npm"), args: ["i", "-g", `${PACKAGE}@${version}`] });
|
|
19167
19498
|
lastLines = (output, n = 3) => output.split("\n").map((l) => l.trimEnd()).filter(Boolean).slice(-n).join("\n");
|
|
19168
19499
|
npmEnv = (execPath, env) => ({ ...env, PATH: `${dirname12(execPath)}:${env.PATH ?? ""}` });
|
|
19169
19500
|
}
|
|
@@ -19565,7 +19896,7 @@ function readRevocations() {
|
|
|
19565
19896
|
function writeRevocations(list3) {
|
|
19566
19897
|
writePrivate(paths().revocations, JSON.stringify(z31.array(RevocationSchema).parse(list3), null, 2) + "\n");
|
|
19567
19898
|
}
|
|
19568
|
-
var RelaySessionSchema, DeviceCredentialSchema, ApprovedDeviceSchema, DEFAULT_CONFIG, LEGACY_RELAY_URL, RevocationSchema;
|
|
19899
|
+
var RelaySessionSchema, DeviceCredentialSchema, MAX_DEVICE_PEERS, ApprovedDeviceSchema, DEFAULT_CONFIG, LEGACY_RELAY_URL, RevocationSchema;
|
|
19569
19900
|
var init_files2 = __esm({
|
|
19570
19901
|
"src/mobile/files.ts"() {
|
|
19571
19902
|
"use strict";
|
|
@@ -19582,6 +19913,7 @@ var init_files2 = __esm({
|
|
|
19582
19913
|
rpId: z31.string().min(1),
|
|
19583
19914
|
transports: z31.array(z31.string()).optional()
|
|
19584
19915
|
});
|
|
19916
|
+
MAX_DEVICE_PEERS = 8;
|
|
19585
19917
|
ApprovedDeviceSchema = z31.object({
|
|
19586
19918
|
id: z31.string().min(1),
|
|
19587
19919
|
publicKey: z31.string().min(1),
|
|
@@ -19590,8 +19922,14 @@ var init_files2 = __esm({
|
|
|
19590
19922
|
lastPeer: z31.string().optional(),
|
|
19591
19923
|
lastSeenAt: z31.string().optional(),
|
|
19592
19924
|
credential: DeviceCredentialSchema.optional(),
|
|
19593
|
-
/** The phone's push subscription and the relay's VAPID key it was made with. */
|
|
19594
|
-
push: z31.object({ endpoint: z31.string().min(1), p256dh: z31.string().min(1), auth: z31.string().min(1), key: z31.string().min(1) }).optional()
|
|
19925
|
+
/** The phone's push subscription and the relay's VAPID key it was made with; `resolved`: its service worker shows a `resolved` push. */
|
|
19926
|
+
push: z31.object({ endpoint: z31.string().min(1), p256dh: z31.string().min(1), auth: z31.string().min(1), key: z31.string().min(1), resolved: z31.literal(true).optional() }).optional(),
|
|
19927
|
+
/** Phone or tablet, from the pairing label; absent on a device approved before it was kept. */
|
|
19928
|
+
kind: DeviceKindSchema.optional(),
|
|
19929
|
+
/** The name the user gave it; absent means the default name the fronts derive. */
|
|
19930
|
+
name: DeviceNameSchema.optional(),
|
|
19931
|
+
/** Every relay peer that confirmed a channel for it, newest last (at most MAX_DEVICE_PEERS): all are revoked with it. */
|
|
19932
|
+
peers: z31.array(z31.string().min(1)).max(MAX_DEVICE_PEERS).optional()
|
|
19595
19933
|
});
|
|
19596
19934
|
DEFAULT_CONFIG = () => ({ enabled: false, relayUrl: DEFAULT_RELAY_URL });
|
|
19597
19935
|
LEGACY_RELAY_URL = "https://server.ledge.paradow.dev";
|
|
@@ -20235,6 +20573,10 @@ var init_channels = __esm({
|
|
|
20235
20573
|
});
|
|
20236
20574
|
|
|
20237
20575
|
// src/mobile/service.ts
|
|
20576
|
+
function peersOf(d) {
|
|
20577
|
+
const list3 = d.peers ?? [];
|
|
20578
|
+
return d.lastPeer && !list3.includes(d.lastPeer) ? [...list3, d.lastPeer] : list3;
|
|
20579
|
+
}
|
|
20238
20580
|
async function createMobileService(opts = {}) {
|
|
20239
20581
|
const now2 = opts.now ?? Date.now;
|
|
20240
20582
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
@@ -20265,6 +20607,13 @@ async function createMobileService(opts = {}) {
|
|
|
20265
20607
|
const sendFrame = (peer, frame) => {
|
|
20266
20608
|
link3?.send({ type: "send", to: peer, body: JSON.stringify(frame) });
|
|
20267
20609
|
};
|
|
20610
|
+
const withPeer = (d, peer) => [...peersOf(d).filter((p) => p !== peer), peer].slice(-MAX_DEVICE_PEERS);
|
|
20611
|
+
const withoutPeer = (d, peer) => {
|
|
20612
|
+
if (d.lastPeer !== peer && !d.peers?.includes(peer)) return d;
|
|
20613
|
+
const { lastPeer, peers, ...rest } = d;
|
|
20614
|
+
const left2 = (peers ?? []).filter((p) => p !== peer);
|
|
20615
|
+
return { ...rest, ...lastPeer && lastPeer !== peer ? { lastPeer } : {}, ...left2.length > 0 ? { peers: left2 } : {} };
|
|
20616
|
+
};
|
|
20268
20617
|
const saveDevices = (next4) => {
|
|
20269
20618
|
writeDevices(next4);
|
|
20270
20619
|
devices = next4;
|
|
@@ -20274,7 +20623,8 @@ async function createMobileService(opts = {}) {
|
|
|
20274
20623
|
send: sendFrame,
|
|
20275
20624
|
approve(d) {
|
|
20276
20625
|
const old = devices.find((x) => x.id === d.id);
|
|
20277
|
-
|
|
20626
|
+
const kind = deviceKindOfLabel(d.label);
|
|
20627
|
+
saveDevices(old ? devices.map((x) => x.id === d.id ? { ...x, label: d.label, kind } : x) : [...devices, { ...d, kind }]);
|
|
20278
20628
|
},
|
|
20279
20629
|
onChange: changed,
|
|
20280
20630
|
now: now2,
|
|
@@ -20288,7 +20638,7 @@ async function createMobileService(opts = {}) {
|
|
|
20288
20638
|
if (devices.some((d) => d.id === deviceId)) {
|
|
20289
20639
|
const at = new Date(now2()).toISOString();
|
|
20290
20640
|
try {
|
|
20291
|
-
saveDevices(devices.map((d) => d.id === deviceId ? { ...d, lastPeer: peer, lastSeenAt: at } : d));
|
|
20641
|
+
saveDevices(devices.map((d) => d.id === deviceId ? { ...d, lastPeer: peer, lastSeenAt: at, peers: withPeer(d, peer) } : withoutPeer(d, peer)));
|
|
20292
20642
|
} catch (e) {
|
|
20293
20643
|
log(`mobile: devices.json not written (${e.message})`);
|
|
20294
20644
|
}
|
|
@@ -20491,9 +20841,17 @@ async function createMobileService(opts = {}) {
|
|
|
20491
20841
|
saveDevices(devices.filter((x) => x.id !== id));
|
|
20492
20842
|
log(`mobile: revoked device ${id}`);
|
|
20493
20843
|
channels.dropDevice(id);
|
|
20494
|
-
|
|
20844
|
+
const others = new Set(devices.flatMap(peersOf));
|
|
20845
|
+
const peers = peersOf(d).filter((p) => !others.has(p));
|
|
20846
|
+
for (const p of peers) sendFrame(p, { k: "denied" });
|
|
20847
|
+
changed();
|
|
20848
|
+
await Promise.all(peers.map((p) => revokeOnRelay(p)));
|
|
20849
|
+
};
|
|
20850
|
+
const rename7 = (id, raw) => {
|
|
20851
|
+
const name = DeviceNameSchema.parse(raw);
|
|
20852
|
+
if (!devices.some((d) => d.id === id)) throw new Error("no such device");
|
|
20853
|
+
saveDevices(devices.map((d) => d.id === id ? { ...d, name } : d));
|
|
20495
20854
|
changed();
|
|
20496
|
-
if (d.lastPeer) await revokeOnRelay(d.lastPeer);
|
|
20497
20855
|
};
|
|
20498
20856
|
const pairingIn = (id, states) => {
|
|
20499
20857
|
const p = pairing.views().find((v2) => v2.id === id);
|
|
@@ -20515,13 +20873,20 @@ async function createMobileService(opts = {}) {
|
|
|
20515
20873
|
label: d.label,
|
|
20516
20874
|
approvedAt: d.approvedAt,
|
|
20517
20875
|
online: online.has(d.id),
|
|
20518
|
-
...d.lastSeenAt ? { lastSeenAt: d.lastSeenAt } : {}
|
|
20876
|
+
...d.lastSeenAt ? { lastSeenAt: d.lastSeenAt } : {},
|
|
20877
|
+
kind: d.kind ?? deviceKindOfLabel(d.label),
|
|
20878
|
+
...d.name ? { name: d.name } : {}
|
|
20519
20879
|
})),
|
|
20520
20880
|
pairings: pairing.views(),
|
|
20521
20881
|
terminalInput: config.terminalInput === true
|
|
20522
20882
|
};
|
|
20523
20883
|
},
|
|
20524
20884
|
revoke,
|
|
20885
|
+
rename: rename7,
|
|
20886
|
+
deviceInfo(deviceId) {
|
|
20887
|
+
const d = devices.find((x) => x.id === deviceId);
|
|
20888
|
+
return d ? { label: d.label, kind: d.kind ?? deviceKindOfLabel(d.label), ...d.name ? { name: d.name } : {} } : null;
|
|
20889
|
+
},
|
|
20525
20890
|
credential(deviceId) {
|
|
20526
20891
|
const c = devices.find((d) => d.id === deviceId)?.credential;
|
|
20527
20892
|
return c && c.rpId === rpOf(config.relayUrl)?.rpId ? c : null;
|
|
@@ -20622,17 +20987,24 @@ async function createMobileService(opts = {}) {
|
|
|
20622
20987
|
case "mobile.retry":
|
|
20623
20988
|
link3?.retryNow();
|
|
20624
20989
|
return;
|
|
20625
|
-
case "mobile.pairing.approve":
|
|
20990
|
+
case "mobile.pairing.approve": {
|
|
20626
20991
|
pairingIn(msg.id, ["confirm"]);
|
|
20627
20992
|
if (linkState.state !== "online") throw new Error("the relay is offline");
|
|
20993
|
+
const before2 = new Set(devices.map((d) => d.id));
|
|
20628
20994
|
pairing.approve(msg.id);
|
|
20995
|
+
const added = devices.some((d) => !before2.has(d.id));
|
|
20996
|
+
if (msg.replaces !== void 0 && added && before2.has(msg.replaces)) await revoke(msg.replaces);
|
|
20629
20997
|
return;
|
|
20998
|
+
}
|
|
20630
20999
|
case "mobile.pairing.deny":
|
|
20631
21000
|
pairingIn(msg.id, ["confirm", "waiting"]);
|
|
20632
21001
|
pairing.deny(msg.id);
|
|
20633
21002
|
return;
|
|
20634
21003
|
case "mobile.device.revoke":
|
|
20635
21004
|
return revoke(msg.id);
|
|
21005
|
+
case "mobile.device.rename":
|
|
21006
|
+
rename7(msg.id, msg.name);
|
|
21007
|
+
return;
|
|
20636
21008
|
}
|
|
20637
21009
|
},
|
|
20638
21010
|
send: (deviceId, text7) => channels.send(deviceId, text7),
|
|
@@ -20677,7 +21049,7 @@ __export(daemon_exports, {
|
|
|
20677
21049
|
startDaemon: () => startDaemon
|
|
20678
21050
|
});
|
|
20679
21051
|
import { writeFile as writeFile15, rm as rm7 } from "fs/promises";
|
|
20680
|
-
import { join as
|
|
21052
|
+
import { join as join34 } from "path";
|
|
20681
21053
|
async function startDaemon(opts = {}) {
|
|
20682
21054
|
await ensureLedgeDir();
|
|
20683
21055
|
const store = new Store();
|
|
@@ -20694,7 +21066,7 @@ async function startDaemon(opts = {}) {
|
|
|
20694
21066
|
}
|
|
20695
21067
|
for (const s of store.all()) {
|
|
20696
21068
|
if (backendOf(s) !== "omp-rpc") continue;
|
|
20697
|
-
await reapOwner(
|
|
21069
|
+
await reapOwner(join34(paths().dir, "omp", s.id)).catch(() => false);
|
|
20698
21070
|
if (s.state === "exited") continue;
|
|
20699
21071
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
20700
21072
|
store.apply(s.id, { name: "StopFailure", at, source: "daemon", text: "Ledge restarted: the OMP process ended with it" });
|
|
@@ -20781,9 +21153,9 @@ __export(uninstall_exports, {
|
|
|
20781
21153
|
runUninstall: () => runUninstall,
|
|
20782
21154
|
uninstallArgs: () => uninstallArgs
|
|
20783
21155
|
});
|
|
20784
|
-
import { readFile as
|
|
20785
|
-
import { homedir as
|
|
20786
|
-
import { join as
|
|
21156
|
+
import { readFile as readFile16, writeFile as writeFile16, rename as rename6, rm as rm8, lstat as lstat2, realpath as realpath4 } from "fs/promises";
|
|
21157
|
+
import { homedir as homedir17 } from "os";
|
|
21158
|
+
import { join as join35 } from "path";
|
|
20787
21159
|
function prune(config, file, keep) {
|
|
20788
21160
|
if (!isObject(config)) throw new Error(`${file} root is not an object, not modified`);
|
|
20789
21161
|
if (config.hooks === void 0) return { settings: config, removed: 0 };
|
|
@@ -20820,7 +21192,7 @@ function removeCursorHooks(config) {
|
|
|
20820
21192
|
async function pruneJsonFile(path3, remove, name) {
|
|
20821
21193
|
let raw;
|
|
20822
21194
|
try {
|
|
20823
|
-
raw = await
|
|
21195
|
+
raw = await readFile16(path3, "utf8");
|
|
20824
21196
|
} catch (e) {
|
|
20825
21197
|
if (e.code === "ENOENT") return null;
|
|
20826
21198
|
throw e;
|
|
@@ -20841,7 +21213,7 @@ async function pruneJsonFile(path3, remove, name) {
|
|
|
20841
21213
|
async function removeService(opts = {}) {
|
|
20842
21214
|
const out = [];
|
|
20843
21215
|
const run4 = opts.run ?? execRunner;
|
|
20844
|
-
const unit =
|
|
21216
|
+
const unit = join35(opts.unitDir ?? join35(homedir17(), ".config", "systemd", "user"), SERVICE);
|
|
20845
21217
|
let present = true;
|
|
20846
21218
|
try {
|
|
20847
21219
|
await lstat2(unit);
|
|
@@ -20866,7 +21238,7 @@ async function runningSessions(run4, socket) {
|
|
|
20866
21238
|
async function removeLink(link3, script) {
|
|
20867
21239
|
try {
|
|
20868
21240
|
const st = await lstat2(link3);
|
|
20869
|
-
if (st.isFile() ? !(await
|
|
21241
|
+
if (st.isFile() ? !(await readFile16(link3, "utf8")).startsWith(BIN_MARK) : !st.isSymbolicLink() || await realpath4(link3) !== await realpath4(script)) return false;
|
|
20870
21242
|
} catch {
|
|
20871
21243
|
return false;
|
|
20872
21244
|
}
|
|
@@ -20889,9 +21261,9 @@ async function runUninstall(opts = {}) {
|
|
|
20889
21261
|
};
|
|
20890
21262
|
const run4 = opts.run ?? execRunner;
|
|
20891
21263
|
const files = [
|
|
20892
|
-
["Claude Code", opts.settingsPath ??
|
|
20893
|
-
["Codex",
|
|
20894
|
-
["Cursor Agent",
|
|
21264
|
+
["Claude Code", opts.settingsPath ?? join35(homedir17(), ".claude", "settings.json"), (c) => removeClaudeHooks(c, "settings.json")],
|
|
21265
|
+
["Codex", join35(opts.codexDir ?? process.env.CODEX_HOME ?? join35(homedir17(), ".codex"), "hooks.json"), (c) => removeClaudeHooks(c, "hooks.json")],
|
|
21266
|
+
["Cursor Agent", join35(opts.cursorDir ?? join35(homedir17(), ".cursor"), "hooks.json"), removeCursorHooks]
|
|
20895
21267
|
];
|
|
20896
21268
|
for (const [agent, path3, remove] of files) {
|
|
20897
21269
|
await step3(async () => {
|
|
@@ -20913,7 +21285,7 @@ async function runUninstall(opts = {}) {
|
|
|
20913
21285
|
});
|
|
20914
21286
|
else lines.push(`${dir} kept (session journal and log): \`ledge uninstall --purge\` removes it`);
|
|
20915
21287
|
const script = opts.script ?? process.argv[1];
|
|
20916
|
-
const link3 = opts.link ??
|
|
21288
|
+
const link3 = opts.link ?? join35(homedir17(), ".local", "bin", "ledge");
|
|
20917
21289
|
if (script) await step3(async () => {
|
|
20918
21290
|
if (await removeLink(link3, script)) lines.push(`${link3} removed`);
|
|
20919
21291
|
});
|
|
@@ -21171,10 +21543,10 @@ function createDaemonClient(opts) {
|
|
|
21171
21543
|
};
|
|
21172
21544
|
const schedule = () => {
|
|
21173
21545
|
if (closed) return;
|
|
21174
|
-
timer2 = setTimeout(
|
|
21546
|
+
timer2 = setTimeout(open6, backoff);
|
|
21175
21547
|
backoff = Math.min(max, backoff * 2);
|
|
21176
21548
|
};
|
|
21177
|
-
function
|
|
21549
|
+
function open6() {
|
|
21178
21550
|
if (closed) return;
|
|
21179
21551
|
setStatus(status === "open" ? "connecting" : status);
|
|
21180
21552
|
if (!token) {
|
|
@@ -21238,7 +21610,7 @@ function createDaemonClient(opts) {
|
|
|
21238
21610
|
ws = null;
|
|
21239
21611
|
old?.close();
|
|
21240
21612
|
setStatus("connecting");
|
|
21241
|
-
|
|
21613
|
+
open6();
|
|
21242
21614
|
},
|
|
21243
21615
|
close: () => {
|
|
21244
21616
|
closed = true;
|
|
@@ -23011,7 +23383,7 @@ __export(launch_exports, {
|
|
|
23011
23383
|
parseLaunchArgs: () => parseLaunchArgs
|
|
23012
23384
|
});
|
|
23013
23385
|
import { spawn as spawn3 } from "child_process";
|
|
23014
|
-
import { readFile as
|
|
23386
|
+
import { readFile as readFile17, rm as rm9 } from "fs/promises";
|
|
23015
23387
|
import { createConnection } from "net";
|
|
23016
23388
|
function parseLaunchArgs(argv) {
|
|
23017
23389
|
const args = [];
|
|
@@ -23124,7 +23496,7 @@ async function readExitCode(id) {
|
|
|
23124
23496
|
const file = exitFile(id);
|
|
23125
23497
|
let raw = null;
|
|
23126
23498
|
try {
|
|
23127
|
-
raw = await
|
|
23499
|
+
raw = await readFile17(file, "utf8");
|
|
23128
23500
|
} catch {
|
|
23129
23501
|
}
|
|
23130
23502
|
await rm9(file, { force: true });
|
|
@@ -23201,6 +23573,7 @@ var init_launch2 = __esm({
|
|
|
23201
23573
|
import { spawn as spawn4 } from "child_process";
|
|
23202
23574
|
function attachCommand(fleet, s) {
|
|
23203
23575
|
if (s.offline) return { why: "offline" };
|
|
23576
|
+
if (isStoppedOmpSeat(s)) return { why: "stopped" };
|
|
23204
23577
|
if (backendOf(s) === "omp-rpc") return { why: "omp" };
|
|
23205
23578
|
if (!s.tmuxSession) return { why: "none" };
|
|
23206
23579
|
const argv = fleet.terminal(s.id);
|
|
@@ -23223,9 +23596,9 @@ var init_attach = __esm({
|
|
|
23223
23596
|
|
|
23224
23597
|
// src/tui/clipboard.ts
|
|
23225
23598
|
import { execFile as execFile9 } from "child_process";
|
|
23226
|
-
import { mkdtemp as mkdtemp2, readFile as
|
|
23599
|
+
import { mkdtemp as mkdtemp2, readFile as readFile18, rm as rm10 } from "fs/promises";
|
|
23227
23600
|
import { tmpdir as tmpdir2 } from "os";
|
|
23228
|
-
import { join as
|
|
23601
|
+
import { join as join36 } from "path";
|
|
23229
23602
|
async function fromWindows(run4) {
|
|
23230
23603
|
const { stdout } = await run4(POWERSHELL, ["-NoProfile", "-STA", "-Command", SCRIPT], { cwd: "/mnt/c", maxBuffer: MAX_BUFFER, encoding: "buffer" });
|
|
23231
23604
|
const line2 = stdout.toString("utf8").trim();
|
|
@@ -23250,15 +23623,15 @@ async function fromLinux(run4) {
|
|
|
23250
23623
|
return null;
|
|
23251
23624
|
}
|
|
23252
23625
|
async function fromMac(run4) {
|
|
23253
|
-
const dir = await mkdtemp2(
|
|
23254
|
-
const file =
|
|
23626
|
+
const dir = await mkdtemp2(join36(tmpdir2(), "ledge-clip-"));
|
|
23627
|
+
const file = join36(dir, "clip.png");
|
|
23255
23628
|
try {
|
|
23256
23629
|
const script = `set f to open for access POSIX file "${file}" with write permission
|
|
23257
23630
|
set eof f to 0
|
|
23258
23631
|
write (the clipboard as \xABclass PNGf\xBB) to f
|
|
23259
23632
|
close access f`;
|
|
23260
23633
|
await run4("osascript", ["-e", script], { maxBuffer: MAX_BUFFER, encoding: "buffer" });
|
|
23261
|
-
return fromBytes(await
|
|
23634
|
+
return fromBytes(await readFile18(file));
|
|
23262
23635
|
} catch {
|
|
23263
23636
|
return null;
|
|
23264
23637
|
} finally {
|
|
@@ -23689,7 +24062,7 @@ var init_fr = __esm({
|
|
|
23689
24062
|
plural = (n, one2, many) => n > 1 ? many : one2;
|
|
23690
24063
|
fr3 = {
|
|
23691
24064
|
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" },
|
|
24065
|
+
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
24066
|
time: {
|
|
23694
24067
|
now: "\xE0 l\u2019instant",
|
|
23695
24068
|
decimal: ",",
|
|
@@ -23712,6 +24085,7 @@ var init_fr = __esm({
|
|
|
23712
24085
|
openImage: "ouvrir l'image",
|
|
23713
24086
|
navigate: "naviguer",
|
|
23714
24087
|
open: "ouvrir",
|
|
24088
|
+
attachSeat: "attacher son terminal",
|
|
23715
24089
|
answer: "r\xE9pondre",
|
|
23716
24090
|
attach: "attacher (tmux)",
|
|
23717
24091
|
ask: "demander \xE0 Ledge",
|
|
@@ -23786,6 +24160,7 @@ var init_fr = __esm({
|
|
|
23786
24160
|
noPreview: "Pas d\u2019aper\xE7u.",
|
|
23787
24161
|
noExchange: "Aucun \xE9change.",
|
|
23788
24162
|
sessions: (n) => `${n} ${plural(n, "session", "sessions")}`,
|
|
24163
|
+
sessionsOf: (live3, all2) => `${live3} ${plural(live3, "session", "sessions")} sur ${all2}`,
|
|
23789
24164
|
outside: "Hors projet",
|
|
23790
24165
|
offline: "hors ligne",
|
|
23791
24166
|
readOnly: "lecture seule",
|
|
@@ -24009,6 +24384,7 @@ var init_fr = __esm({
|
|
|
24009
24384
|
noteTooLong: (max) => `Note trop longue : ${max} caract\xE8res au plus. Elle reste dans le champ.`,
|
|
24010
24385
|
noNote: (n) => `${n} ne prend pas de note ici.`,
|
|
24011
24386
|
noTerminalOmp: (n) => `${n} est pilot\xE9 par Ledge via le protocole d\u2019OMP : pas de terminal \xE0 attacher.`,
|
|
24387
|
+
seatStopped: (n) => `${n} est arr\xEAt\xE9 : son terminal n\u2019existe plus. 6 ouvre OpenRig, o\xF9 est son rig.`,
|
|
24012
24388
|
noTerminal: (n) => `Pas de terminal pour ${n}.`,
|
|
24013
24389
|
attachFailed: (code3) => `tmux s\u2019est arr\xEAt\xE9 avec le code ${code3}.`,
|
|
24014
24390
|
modelSwitch: "Ce daemon ne change pas le mod\xE8le d\u2019une session en cours.",
|
|
@@ -24257,7 +24633,7 @@ var init_en = __esm({
|
|
|
24257
24633
|
plural2 = (n, one2, many) => n > 1 ? many : one2;
|
|
24258
24634
|
en3 = {
|
|
24259
24635
|
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" },
|
|
24636
|
+
state: { approval: "Needs you", question: "A question", starting: "Starting", idle: "Available", working: "Working", done: "Finished", error: "Error", readonly: "Read-only", stopped: "Stopped" },
|
|
24261
24637
|
time: { now: "just now", decimal: ".", s: (n) => `${n}s`, min: (n) => `${n} min`, h: (n) => `${n} h`, d: (n) => `${n} d` },
|
|
24262
24638
|
tabs: { overview: "Overview", session: "Session", ask: "Ask Ledge", launch: "New session", settings: "Settings", openrig: "OpenRig" },
|
|
24263
24639
|
bar: {
|
|
@@ -24273,6 +24649,7 @@ var init_en = __esm({
|
|
|
24273
24649
|
openImage: "open image",
|
|
24274
24650
|
navigate: "move",
|
|
24275
24651
|
open: "open",
|
|
24652
|
+
attachSeat: "attach its terminal",
|
|
24276
24653
|
answer: "answer",
|
|
24277
24654
|
attach: "attach (tmux)",
|
|
24278
24655
|
ask: "ask Ledge",
|
|
@@ -24346,6 +24723,7 @@ var init_en = __esm({
|
|
|
24346
24723
|
noPreview: "No preview.",
|
|
24347
24724
|
noExchange: "No message yet.",
|
|
24348
24725
|
sessions: (n) => `${n} ${plural2(n, "session", "sessions")}`,
|
|
24726
|
+
sessionsOf: (live3, all2) => `${live3} ${live3 === 1 ? "session" : "sessions"} of ${all2}`,
|
|
24349
24727
|
outside: "No project",
|
|
24350
24728
|
offline: "offline",
|
|
24351
24729
|
readOnly: "read-only",
|
|
@@ -24565,6 +24943,7 @@ var init_en = __esm({
|
|
|
24565
24943
|
noteTooLong: (max) => `Note too long: ${max} characters at most. It stays in the field.`,
|
|
24566
24944
|
noNote: (n) => `${n} takes no note here.`,
|
|
24567
24945
|
noTerminalOmp: (n) => `${n} is driven by Ledge over OMP's protocol: there is no terminal to attach to.`,
|
|
24946
|
+
seatStopped: (n) => `${n} is stopped: its terminal is gone. 6 opens OpenRig, where its rig is.`,
|
|
24568
24947
|
noTerminal: (n) => `No terminal for ${n}.`,
|
|
24569
24948
|
attachFailed: (code3) => `tmux stopped with code ${code3}.`,
|
|
24570
24949
|
modelSwitch: "This daemon cannot change a running session\u2019s model.",
|
|
@@ -24829,15 +25208,19 @@ function overview(sessions, projects) {
|
|
|
24829
25208
|
const known2 = new Map((projects?.projects ?? []).map((p) => [p.id, p]));
|
|
24830
25209
|
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
25210
|
const rigNodes = /* @__PURE__ */ new Map();
|
|
25211
|
+
const rigNodeOf = (rig) => {
|
|
25212
|
+
const kind = rig === "kernel" ? "kernel" : "rig";
|
|
25213
|
+
const key = kind === "kernel" ? "g:kernel" : `g:rig:${rig}`;
|
|
25214
|
+
let node2 = rigNodes.get(key);
|
|
25215
|
+
if (!node2) {
|
|
25216
|
+
node2 = { key, label: kind === "kernel" ? "Kernel" : rig, kind, branches: [{ branch: "", sessions: [] }], count: 0 };
|
|
25217
|
+
rigNodes.set(key, node2);
|
|
25218
|
+
}
|
|
25219
|
+
return node2;
|
|
25220
|
+
};
|
|
24832
25221
|
for (const s of live3) {
|
|
24833
25222
|
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
|
-
}
|
|
25223
|
+
const node3 = rigNodeOf(s.rig.name);
|
|
24841
25224
|
node3.branches[0].sessions.push(s);
|
|
24842
25225
|
node3.count += 1;
|
|
24843
25226
|
continue;
|
|
@@ -24858,6 +25241,12 @@ function overview(sessions, projects) {
|
|
|
24858
25241
|
row3.sessions.push(s);
|
|
24859
25242
|
node2.count += 1;
|
|
24860
25243
|
}
|
|
25244
|
+
for (const s of sessions) {
|
|
25245
|
+
if (!isStoppedOmpSeat(s) || !s.rig) continue;
|
|
25246
|
+
const node2 = rigNodeOf(s.rig.name);
|
|
25247
|
+
node2.branches[0].sessions.push(s);
|
|
25248
|
+
node2.shown = (node2.shown ?? node2.count) + 1;
|
|
25249
|
+
}
|
|
24861
25250
|
const groups = [...rigNodes.values()].sort((a, b3) => a.kind === b3.kind ? a.label.localeCompare(b3.label) : a.kind === "kernel" ? 1 : -1);
|
|
24862
25251
|
const tree2 = [...[...nodes.values()].filter((n) => n.count > 0), ...groups];
|
|
24863
25252
|
const order2 = tree2.flatMap((n) => n.branches.flatMap((b3) => b3.sessions.map((s) => s.id)));
|
|
@@ -25118,7 +25507,7 @@ var init_theme = __esm({
|
|
|
25118
25507
|
|
|
25119
25508
|
// src/tui/model/cells.ts
|
|
25120
25509
|
import stringWidth from "string-width";
|
|
25121
|
-
function
|
|
25510
|
+
function join37(line2, a) {
|
|
25122
25511
|
const out = [];
|
|
25123
25512
|
for (const c of a) {
|
|
25124
25513
|
const last = out[out.length - 1];
|
|
@@ -25133,7 +25522,7 @@ function cut2(line2, width, ellipsis = "\u2026") {
|
|
|
25133
25522
|
const a = split2(line2), room = width - cells(ellipsis);
|
|
25134
25523
|
let n = 0, i = 0;
|
|
25135
25524
|
while (i < a.length && n + a[i].w <= room) n += a[i++].w;
|
|
25136
|
-
const kept =
|
|
25525
|
+
const kept = join37(line2, a.slice(0, i));
|
|
25137
25526
|
const color2 = line2[a[Math.max(0, i - 1)]?.seg ?? 0]?.color;
|
|
25138
25527
|
return [...kept, { text: ellipsis, ...color2 ? { color: color2 } : {} }];
|
|
25139
25528
|
}
|
|
@@ -25152,7 +25541,7 @@ function wrapSegs(line2, width, first = width) {
|
|
|
25152
25541
|
}
|
|
25153
25542
|
w = Math.max(4, width);
|
|
25154
25543
|
if (j >= a.length || a[j].g === "\n") {
|
|
25155
|
-
out.push(
|
|
25544
|
+
out.push(join37(line2, a.slice(i, j)));
|
|
25156
25545
|
i = j + 1;
|
|
25157
25546
|
if (j >= a.length) break;
|
|
25158
25547
|
if (i >= a.length) out.push([]);
|
|
@@ -25161,7 +25550,7 @@ function wrapSegs(line2, width, first = width) {
|
|
|
25161
25550
|
const end2 = a[j].g === " " ? j : space > i ? space : Math.max(j, i + 1);
|
|
25162
25551
|
let k = end2;
|
|
25163
25552
|
while (k > i && a[k - 1].g === " ") k--;
|
|
25164
|
-
out.push(
|
|
25553
|
+
out.push(join37(line2, a.slice(i, k)));
|
|
25165
25554
|
i = end2;
|
|
25166
25555
|
while (i < a.length && a[i].g === " ") i++;
|
|
25167
25556
|
if (a[i]?.g === "\n") {
|
|
@@ -30374,16 +30763,16 @@ function resolveAllAttention(events, context) {
|
|
|
30374
30763
|
let nextEvents;
|
|
30375
30764
|
while (++index2 < events.length) {
|
|
30376
30765
|
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[
|
|
30766
|
+
let open6 = index2;
|
|
30767
|
+
while (open6--) {
|
|
30768
|
+
if (events[open6][0] === "exit" && events[open6][1].type === "attentionSequence" && events[open6][1]._open && // If the markers are the same:
|
|
30769
|
+
context.sliceSerialize(events[open6][1]).charCodeAt(0) === context.sliceSerialize(events[index2][1]).charCodeAt(0)) {
|
|
30770
|
+
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
30771
|
continue;
|
|
30383
30772
|
}
|
|
30384
|
-
const use = events[
|
|
30773
|
+
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
30774
|
const start = {
|
|
30386
|
-
...events[
|
|
30775
|
+
...events[open6][1].end
|
|
30387
30776
|
};
|
|
30388
30777
|
const end2 = {
|
|
30389
30778
|
...events[index2][1].start
|
|
@@ -30394,7 +30783,7 @@ function resolveAllAttention(events, context) {
|
|
|
30394
30783
|
type: use > 1 ? "strongSequence" : "emphasisSequence",
|
|
30395
30784
|
start,
|
|
30396
30785
|
end: {
|
|
30397
|
-
...events[
|
|
30786
|
+
...events[open6][1].end
|
|
30398
30787
|
}
|
|
30399
30788
|
};
|
|
30400
30789
|
const closingSequence = {
|
|
@@ -30407,7 +30796,7 @@ function resolveAllAttention(events, context) {
|
|
|
30407
30796
|
const text7 = {
|
|
30408
30797
|
type: use > 1 ? "strongText" : "emphasisText",
|
|
30409
30798
|
start: {
|
|
30410
|
-
...events[
|
|
30799
|
+
...events[open6][1].end
|
|
30411
30800
|
},
|
|
30412
30801
|
end: {
|
|
30413
30802
|
...events[index2][1].start
|
|
@@ -30422,26 +30811,26 @@ function resolveAllAttention(events, context) {
|
|
|
30422
30811
|
...closingSequence.end
|
|
30423
30812
|
}
|
|
30424
30813
|
};
|
|
30425
|
-
events[
|
|
30814
|
+
events[open6][1].end = {
|
|
30426
30815
|
...openingSequence.start
|
|
30427
30816
|
};
|
|
30428
30817
|
events[index2][1].start = {
|
|
30429
30818
|
...closingSequence.end
|
|
30430
30819
|
};
|
|
30431
30820
|
nextEvents = [];
|
|
30432
|
-
if (events[
|
|
30433
|
-
nextEvents = push(nextEvents, [["enter", events[
|
|
30821
|
+
if (events[open6][1].end.offset - events[open6][1].start.offset) {
|
|
30822
|
+
nextEvents = push(nextEvents, [["enter", events[open6][1], context], ["exit", events[open6][1], context]]);
|
|
30434
30823
|
}
|
|
30435
30824
|
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(
|
|
30825
|
+
nextEvents = push(nextEvents, resolveAll(context.parser.constructs.insideSpan.null, events.slice(open6 + 1, index2), context));
|
|
30437
30826
|
nextEvents = push(nextEvents, [["exit", text7, context], ["enter", closingSequence, context], ["exit", closingSequence, context], ["exit", group, context]]);
|
|
30438
30827
|
let offset = 0;
|
|
30439
30828
|
if (events[index2][1].end.offset - events[index2][1].start.offset) {
|
|
30440
30829
|
offset = 2;
|
|
30441
30830
|
nextEvents = push(nextEvents, [["enter", events[index2][1], context], ["exit", events[index2][1], context]]);
|
|
30442
30831
|
}
|
|
30443
|
-
splice(events,
|
|
30444
|
-
index2 =
|
|
30832
|
+
splice(events, open6 - 1, index2 - open6 + 3, nextEvents);
|
|
30833
|
+
index2 = open6 + nextEvents.length - offset - 2;
|
|
30445
30834
|
break;
|
|
30446
30835
|
}
|
|
30447
30836
|
}
|
|
@@ -30473,10 +30862,10 @@ function tokenizeAttention(effects, ok4) {
|
|
|
30473
30862
|
}
|
|
30474
30863
|
const token = effects.exit("attentionSequence");
|
|
30475
30864
|
const after2 = classifyCharacter(code3);
|
|
30476
|
-
const
|
|
30865
|
+
const open6 = !after2 || after2 === 2 && before2 || attentionMarkers2.includes(code3) && code3 !== 42 && code3 !== 95;
|
|
30477
30866
|
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 || !
|
|
30867
|
+
token._open = Boolean(marker === 42 ? open6 : open6 && (before2 || !close));
|
|
30868
|
+
token._close = Boolean(marker === 42 ? close : close && (after2 || !open6));
|
|
30480
30869
|
return ok4(code3);
|
|
30481
30870
|
}
|
|
30482
30871
|
}
|
|
@@ -30510,9 +30899,9 @@ function tokenizeAutolink(effects, ok4, nok) {
|
|
|
30510
30899
|
effects.consume(code3);
|
|
30511
30900
|
effects.exit("autolinkMarker");
|
|
30512
30901
|
effects.enter("autolinkProtocol");
|
|
30513
|
-
return
|
|
30902
|
+
return open6;
|
|
30514
30903
|
}
|
|
30515
|
-
function
|
|
30904
|
+
function open6(code3) {
|
|
30516
30905
|
if (asciiAlpha(code3)) {
|
|
30517
30906
|
effects.consume(code3);
|
|
30518
30907
|
return schemeOrEmailAtext;
|
|
@@ -30793,9 +31182,9 @@ function tokenizeCharacterReference(effects, ok4, nok) {
|
|
|
30793
31182
|
effects.enter("characterReferenceMarker");
|
|
30794
31183
|
effects.consume(code3);
|
|
30795
31184
|
effects.exit("characterReferenceMarker");
|
|
30796
|
-
return
|
|
31185
|
+
return open6;
|
|
30797
31186
|
}
|
|
30798
|
-
function
|
|
31187
|
+
function open6(code3) {
|
|
30799
31188
|
if (code3 === 35) {
|
|
30800
31189
|
effects.enter("characterReferenceMarkerNumeric");
|
|
30801
31190
|
effects.consume(code3);
|
|
@@ -32196,9 +32585,9 @@ function tokenizeHtmlFlow(effects, ok4, nok) {
|
|
|
32196
32585
|
effects.enter("htmlFlow");
|
|
32197
32586
|
effects.enter("htmlFlowData");
|
|
32198
32587
|
effects.consume(code3);
|
|
32199
|
-
return
|
|
32588
|
+
return open6;
|
|
32200
32589
|
}
|
|
32201
|
-
function
|
|
32590
|
+
function open6(code3) {
|
|
32202
32591
|
if (code3 === 33) {
|
|
32203
32592
|
effects.consume(code3);
|
|
32204
32593
|
return declarationOpen;
|
|
@@ -32546,9 +32935,9 @@ function tokenizeHtmlText(effects, ok4, nok) {
|
|
|
32546
32935
|
effects.enter("htmlText");
|
|
32547
32936
|
effects.enter("htmlTextData");
|
|
32548
32937
|
effects.consume(code3);
|
|
32549
|
-
return
|
|
32938
|
+
return open6;
|
|
32550
32939
|
}
|
|
32551
|
-
function
|
|
32940
|
+
function open6(code3) {
|
|
32552
32941
|
if (code3 === 33) {
|
|
32553
32942
|
effects.consume(code3);
|
|
32554
32943
|
return declarationOpen;
|
|
@@ -32870,12 +33259,12 @@ function resolveAllLabelEnd(events) {
|
|
|
32870
33259
|
function resolveToLabelEnd(events, context) {
|
|
32871
33260
|
let index2 = events.length;
|
|
32872
33261
|
let offset = 0;
|
|
32873
|
-
let
|
|
33262
|
+
let open6;
|
|
32874
33263
|
let close;
|
|
32875
33264
|
let media;
|
|
32876
33265
|
while (index2--) {
|
|
32877
33266
|
const token = events[index2][1];
|
|
32878
|
-
if (
|
|
33267
|
+
if (open6) {
|
|
32879
33268
|
if (token.type === "link" || token.type === "labelLink" && token._inactive) {
|
|
32880
33269
|
break;
|
|
32881
33270
|
}
|
|
@@ -32884,7 +33273,7 @@ function resolveToLabelEnd(events, context) {
|
|
|
32884
33273
|
}
|
|
32885
33274
|
} else if (close) {
|
|
32886
33275
|
if (events[index2][0] === "enter" && (token.type === "labelImage" || token.type === "labelLink") && !token._balanced) {
|
|
32887
|
-
|
|
33276
|
+
open6 = index2;
|
|
32888
33277
|
if (token.type !== "labelLink") {
|
|
32889
33278
|
offset = 2;
|
|
32890
33279
|
break;
|
|
@@ -32895,9 +33284,9 @@ function resolveToLabelEnd(events, context) {
|
|
|
32895
33284
|
}
|
|
32896
33285
|
}
|
|
32897
33286
|
const group = {
|
|
32898
|
-
type: events[
|
|
33287
|
+
type: events[open6][1].type === "labelLink" ? "link" : "image",
|
|
32899
33288
|
start: {
|
|
32900
|
-
...events[
|
|
33289
|
+
...events[open6][1].start
|
|
32901
33290
|
},
|
|
32902
33291
|
end: {
|
|
32903
33292
|
...events[events.length - 1][1].end
|
|
@@ -32906,7 +33295,7 @@ function resolveToLabelEnd(events, context) {
|
|
|
32906
33295
|
const label2 = {
|
|
32907
33296
|
type: "label",
|
|
32908
33297
|
start: {
|
|
32909
|
-
...events[
|
|
33298
|
+
...events[open6][1].start
|
|
32910
33299
|
},
|
|
32911
33300
|
end: {
|
|
32912
33301
|
...events[close][1].end
|
|
@@ -32915,20 +33304,20 @@ function resolveToLabelEnd(events, context) {
|
|
|
32915
33304
|
const text7 = {
|
|
32916
33305
|
type: "labelText",
|
|
32917
33306
|
start: {
|
|
32918
|
-
...events[
|
|
33307
|
+
...events[open6 + offset + 2][1].end
|
|
32919
33308
|
},
|
|
32920
33309
|
end: {
|
|
32921
33310
|
...events[close - 2][1].start
|
|
32922
33311
|
}
|
|
32923
33312
|
};
|
|
32924
33313
|
media = [["enter", group, context], ["enter", label2, context]];
|
|
32925
|
-
media = push(media, events.slice(
|
|
33314
|
+
media = push(media, events.slice(open6 + 1, open6 + offset + 3));
|
|
32926
33315
|
media = push(media, [["enter", text7, context]]);
|
|
32927
|
-
media = push(media, resolveAll(context.parser.constructs.insideSpan.null, events.slice(
|
|
33316
|
+
media = push(media, resolveAll(context.parser.constructs.insideSpan.null, events.slice(open6 + offset + 4, close - 3), context));
|
|
32928
33317
|
media = push(media, [["exit", text7, context], events[close - 2], events[close - 1], ["exit", label2, context]]);
|
|
32929
33318
|
media = push(media, events.slice(close + 1));
|
|
32930
33319
|
media = push(media, [["exit", group, context]]);
|
|
32931
|
-
splice(events,
|
|
33320
|
+
splice(events, open6, events.length, media);
|
|
32932
33321
|
return events;
|
|
32933
33322
|
}
|
|
32934
33323
|
function tokenizeLabelEnd(effects, ok4, nok) {
|
|
@@ -33099,9 +33488,9 @@ function tokenizeLabelStartImage(effects, ok4, nok) {
|
|
|
33099
33488
|
effects.enter("labelImageMarker");
|
|
33100
33489
|
effects.consume(code3);
|
|
33101
33490
|
effects.exit("labelImageMarker");
|
|
33102
|
-
return
|
|
33491
|
+
return open6;
|
|
33103
33492
|
}
|
|
33104
|
-
function
|
|
33493
|
+
function open6(code3) {
|
|
33105
33494
|
if (code3 === 91) {
|
|
33106
33495
|
effects.enter("labelMarker");
|
|
33107
33496
|
effects.consume(code3);
|
|
@@ -33933,30 +34322,30 @@ function gfmStrikethrough(options) {
|
|
|
33933
34322
|
let index2 = -1;
|
|
33934
34323
|
while (++index2 < events.length) {
|
|
33935
34324
|
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[
|
|
34325
|
+
let open6 = index2;
|
|
34326
|
+
while (open6--) {
|
|
34327
|
+
if (events[open6][0] === "exit" && events[open6][1].type === "strikethroughSequenceTemporary" && events[open6][1]._open && // If the sizes are the same:
|
|
34328
|
+
events[index2][1].end.offset - events[index2][1].start.offset === events[open6][1].end.offset - events[open6][1].start.offset) {
|
|
33940
34329
|
events[index2][1].type = "strikethroughSequence";
|
|
33941
|
-
events[
|
|
34330
|
+
events[open6][1].type = "strikethroughSequence";
|
|
33942
34331
|
const strikethrough = {
|
|
33943
34332
|
type: "strikethrough",
|
|
33944
|
-
start: Object.assign({}, events[
|
|
34333
|
+
start: Object.assign({}, events[open6][1].start),
|
|
33945
34334
|
end: Object.assign({}, events[index2][1].end)
|
|
33946
34335
|
};
|
|
33947
34336
|
const text7 = {
|
|
33948
34337
|
type: "strikethroughText",
|
|
33949
|
-
start: Object.assign({}, events[
|
|
34338
|
+
start: Object.assign({}, events[open6][1].end),
|
|
33950
34339
|
end: Object.assign({}, events[index2][1].start)
|
|
33951
34340
|
};
|
|
33952
|
-
const nextEvents = [["enter", strikethrough, context], ["enter", events[
|
|
34341
|
+
const nextEvents = [["enter", strikethrough, context], ["enter", events[open6][1], context], ["exit", events[open6][1], context], ["enter", text7, context]];
|
|
33953
34342
|
const insideSpan2 = context.parser.constructs.insideSpan.null;
|
|
33954
34343
|
if (insideSpan2) {
|
|
33955
|
-
splice(nextEvents, nextEvents.length, 0, resolveAll(insideSpan2, events.slice(
|
|
34344
|
+
splice(nextEvents, nextEvents.length, 0, resolveAll(insideSpan2, events.slice(open6 + 1, index2), context));
|
|
33956
34345
|
}
|
|
33957
34346
|
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 =
|
|
34347
|
+
splice(events, open6 - 1, index2 - open6 + 3, nextEvents);
|
|
34348
|
+
index2 = open6 + nextEvents.length - 2;
|
|
33960
34349
|
break;
|
|
33961
34350
|
}
|
|
33962
34351
|
}
|
|
@@ -34558,8 +34947,8 @@ function gfmTaskListItem() {
|
|
|
34558
34947
|
}
|
|
34559
34948
|
function tokenizeTasklistCheck(effects, ok4, nok) {
|
|
34560
34949
|
const self = this;
|
|
34561
|
-
return
|
|
34562
|
-
function
|
|
34950
|
+
return open6;
|
|
34951
|
+
function open6(code3) {
|
|
34563
34952
|
if (
|
|
34564
34953
|
// Exit if there’s stuff before.
|
|
34565
34954
|
self.previous !== null || // Exit if not in the first content that is the first child of a list
|
|
@@ -36048,8 +36437,8 @@ function compiler(options) {
|
|
|
36048
36437
|
events[start][1]._spread = listSpread;
|
|
36049
36438
|
}
|
|
36050
36439
|
function opener(create, and) {
|
|
36051
|
-
return
|
|
36052
|
-
function
|
|
36440
|
+
return open6;
|
|
36441
|
+
function open6(token) {
|
|
36053
36442
|
enter.call(this, create(token), token);
|
|
36054
36443
|
if (and) and.call(this, token);
|
|
36055
36444
|
}
|
|
@@ -36081,19 +36470,19 @@ function compiler(options) {
|
|
|
36081
36470
|
}
|
|
36082
36471
|
function exit3(token, onExitError) {
|
|
36083
36472
|
const node2 = this.stack.pop();
|
|
36084
|
-
const
|
|
36085
|
-
if (!
|
|
36473
|
+
const open6 = this.tokenStack.pop();
|
|
36474
|
+
if (!open6) {
|
|
36086
36475
|
throw new Error("Cannot close `" + token.type + "` (" + stringifyPosition({
|
|
36087
36476
|
start: token.start,
|
|
36088
36477
|
end: token.end
|
|
36089
36478
|
}) + "): it\u2019s not open");
|
|
36090
36479
|
}
|
|
36091
|
-
if (
|
|
36480
|
+
if (open6[0].type !== token.type) {
|
|
36092
36481
|
if (onExitError) {
|
|
36093
|
-
onExitError.call(this, token,
|
|
36482
|
+
onExitError.call(this, token, open6[0]);
|
|
36094
36483
|
} else {
|
|
36095
|
-
const handler =
|
|
36096
|
-
handler.call(this, token,
|
|
36484
|
+
const handler = open6[1] || defaultOnError;
|
|
36485
|
+
handler.call(this, token, open6[0]);
|
|
36097
36486
|
}
|
|
36098
36487
|
}
|
|
36099
36488
|
node2.position.end = point2(token.end);
|
|
@@ -39513,7 +39902,8 @@ function Overview() {
|
|
|
39513
39902
|
return true;
|
|
39514
39903
|
}
|
|
39515
39904
|
if (key.return) {
|
|
39516
|
-
if (selected)
|
|
39905
|
+
if (selected && isOmpSeat(selected)) attach2(selected);
|
|
39906
|
+
else if (selected) go("session");
|
|
39517
39907
|
return true;
|
|
39518
39908
|
}
|
|
39519
39909
|
if (!selected) return false;
|
|
@@ -39534,7 +39924,7 @@ function Overview() {
|
|
|
39534
39924
|
return false;
|
|
39535
39925
|
});
|
|
39536
39926
|
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) }] : [],
|
|
39927
|
+
...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
39928
|
...n.branches.flatMap((b3) => b3.sessions.map((s) => ({ kind: "session", key: s.id, s, indent: kind === "rig" ? 2 : 0, group: kind })))
|
|
39539
39929
|
]);
|
|
39540
39930
|
const rigRows = groupRows("rig"), kernelRows = groupRows("kernel");
|
|
@@ -39556,6 +39946,7 @@ function Overview() {
|
|
|
39556
39946
|
const stateW = Math.max(...Object.values(t.state).map((x) => x.length)) + 1;
|
|
39557
39947
|
const topicW = Math.max(6, inner - 6 - nameW - stateW - 3);
|
|
39558
39948
|
const askW = Math.max(6, inner - 2 - nameW - 9 - 7);
|
|
39949
|
+
const roleW = Math.max(11, ...rigRows.map((r) => r.kind === "session" ? [...r.s.rig?.role ?? ""].length + 2 : 0));
|
|
39559
39950
|
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
39951
|
const st = botState(s);
|
|
39561
39952
|
return /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: s.id === sel ? C.sel : void 0, children: [
|
|
@@ -39621,16 +40012,18 @@ function Overview() {
|
|
|
39621
40012
|
] }) : null,
|
|
39622
40013
|
/* @__PURE__ */ jsxs6(Text7, { color: C.faint, children: [
|
|
39623
40014
|
" ",
|
|
39624
|
-
t.overview.sessions(r.count)
|
|
40015
|
+
r.shown !== void 0 ? t.overview.sessionsOf(r.count, r.shown) : t.overview.sessions(r.count)
|
|
39625
40016
|
] })
|
|
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) });
|
|
40017
|
+
] }, 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
40018
|
const rigsBox = groupBox(t.overview.rigsGroup, rigRows, rigsHeight);
|
|
39628
40019
|
const kernelBox = groupBox(t.overview.kernelGroup, kernelRows, kernelHeight);
|
|
40020
|
+
const stopped2 = !!selected && isStoppedOmpSeat(selected);
|
|
39629
40021
|
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]] : [
|
|
40022
|
+
// On an OMP seat ↵ attaches its terminal; on a stopped one, neither ↵ nor a has anything to attach.
|
|
39630
40023
|
["j k", t.keys.navigate],
|
|
39631
|
-
["\u21B5", t.keys.open],
|
|
40024
|
+
...stopped2 ? [] : [["\u21B5", selected && isOmpSeat(selected) ? t.keys.attachSeat : t.keys.open]],
|
|
39632
40025
|
...view.yours.length ? [["tab", t.keys.next], ["y n", t.keys.answer]] : [],
|
|
39633
|
-
["a", t.keys.attach],
|
|
40026
|
+
...stopped2 ? [] : [["a", t.keys.attach]],
|
|
39634
40027
|
...writable2 ? [["m", t.keys.writeProject]] : [],
|
|
39635
40028
|
...decisions.length ? [["D", t.keys.decide]] : [],
|
|
39636
40029
|
...near.length ? [["H", t.keys.handover]] : [],
|
|
@@ -39705,21 +40098,23 @@ function Overview() {
|
|
|
39705
40098
|
) : null
|
|
39706
40099
|
] });
|
|
39707
40100
|
}
|
|
39708
|
-
function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0, group }) {
|
|
40101
|
+
function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0, group, roleW = 11 }) {
|
|
39709
40102
|
const { t, g } = useTui();
|
|
39710
40103
|
const st = botState(s);
|
|
39711
40104
|
const n = helperCount(s);
|
|
40105
|
+
const stopped2 = isStoppedOmpSeat(s);
|
|
40106
|
+
const roleTool = group === "kernel" ? `${(s.rig?.role ?? "").replace(/\.(agent|lead|worker)$/, "").padEnd(11)}${s.tool}` : `${(s.rig?.role ?? "").padEnd(roleW)}${s.tool}`;
|
|
39712
40107
|
return /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: on ? C.sel : void 0, children: [
|
|
39713
40108
|
/* @__PURE__ */ jsx10(Text7, { color: on ? C.coral : C.faint, children: on ? "\u258C" : " " }),
|
|
39714
40109
|
/* @__PURE__ */ jsx10(Text7, { children: " ".repeat(2 + indent2) }),
|
|
39715
|
-
/* @__PURE__ */ jsxs6(Text7, { color: s.offline ? C.faint : STATE_COLOR[st], children: [
|
|
40110
|
+
/* @__PURE__ */ jsxs6(Text7, { color: s.offline || stopped2 ? C.faint : STATE_COLOR[st], children: [
|
|
39716
40111
|
g[st],
|
|
39717
40112
|
" "
|
|
39718
40113
|
] }),
|
|
39719
|
-
/* @__PURE__ */ jsx10(Text7, { color: s.offline ? C.dim : C.fg, bold:
|
|
39720
|
-
group ? /* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
|
|
40114
|
+
/* @__PURE__ */ jsx10(Text7, { color: stopped2 ? C.faint : s.offline ? C.dim : C.fg, bold: !stopped2, children: fit3(s.name, nameW) }),
|
|
40115
|
+
group ? /* @__PURE__ */ jsxs6(Text7, { color: stopped2 ? C.faint : C.dim, children: [
|
|
39721
40116
|
" ",
|
|
39722
|
-
fit3(
|
|
40117
|
+
fit3(roleTool, topicW - 1),
|
|
39723
40118
|
" "
|
|
39724
40119
|
] }) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
39725
40120
|
s.rig ? /* @__PURE__ */ jsxs6(Text7, { color: C.blue, children: [
|
|
@@ -39731,7 +40126,7 @@ function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0, group }
|
|
|
39731
40126
|
" "
|
|
39732
40127
|
] })
|
|
39733
40128
|
] }),
|
|
39734
|
-
/* @__PURE__ */ jsx10(Text7, { color: s.offline ? C.faint : STATE_COLOR[st], children: fit3(s.offline ? t.overview.offline : t.state[st], stateW).trimEnd() }),
|
|
40129
|
+
/* @__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
40130
|
n > 0 ? /* @__PURE__ */ jsxs6(Text7, { color: C.violet, children: [
|
|
39736
40131
|
" +",
|
|
39737
40132
|
n
|
|
@@ -39770,7 +40165,7 @@ var init_Overview = __esm({
|
|
|
39770
40165
|
// src/tui/images.tsx
|
|
39771
40166
|
import { mkdir as mkdir13, writeFile as writeFile17 } from "fs/promises";
|
|
39772
40167
|
import { tmpdir as tmpdir3 } from "os";
|
|
39773
|
-
import { join as
|
|
40168
|
+
import { join as join38 } from "path";
|
|
39774
40169
|
import { useRef as useRef8, useState as useState4 } from "react";
|
|
39775
40170
|
import { Text as Text8 } from "ink";
|
|
39776
40171
|
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
@@ -39873,9 +40268,9 @@ function useOpenImage() {
|
|
|
39873
40268
|
const { ref, route } = item;
|
|
39874
40269
|
void (async () => {
|
|
39875
40270
|
const { mime, data } = await getImage(client, ref.id, route);
|
|
39876
|
-
const dir =
|
|
40271
|
+
const dir = join38(tmpdir3(), "ledge-images");
|
|
39877
40272
|
await mkdir13(dir, { recursive: true, mode: 448 });
|
|
39878
|
-
const file =
|
|
40273
|
+
const file = join38(dir, `${ref.id}.${EXT2[mime]}`);
|
|
39879
40274
|
await writeFile17(file, Buffer.from(data, "base64"), { mode: 384 });
|
|
39880
40275
|
await openFile2(file);
|
|
39881
40276
|
})().catch((e) => toast({ text: t.images.openFailed(e instanceof AttachmentFailure ? e.reason : e.message), tone: "error" }));
|
|
@@ -40660,11 +41055,11 @@ function cardPlan(actions, room) {
|
|
|
40660
41055
|
const raw = rows.map((r) => r.state ?? "proposed");
|
|
40661
41056
|
const stopAt = raw.findIndex((s) => s === "failed" || s === "refused" || s === "stale");
|
|
40662
41057
|
const shown = states.map((s, i) => s === "proposed" && stopAt >= 0 && i > stopAt ? "held" : s);
|
|
40663
|
-
const
|
|
41058
|
+
const open6 = shown.flatMap((s, i) => ["done", "running", "denied", "stale", "cut"].includes(s) ? [] : [i]);
|
|
40664
41059
|
const skip = stopAt >= 0 && raw[stopAt] !== "failed";
|
|
40665
|
-
const rowsAll = skip ?
|
|
41060
|
+
const rowsAll = skip ? open6.filter((i) => i !== stopAt) : stopAt >= 0 && states[stopAt] === "cut" ? [] : open6;
|
|
40666
41061
|
const all2 = { kind: stopAt < 0 ? "all" : skip ? "skip" : "resume", rows: rowsAll, k: stopAt + 1 };
|
|
40667
|
-
return { rows, shown, open:
|
|
41062
|
+
return { rows, shown, open: open6, stopAt, all: all2, cutLines };
|
|
40668
41063
|
}
|
|
40669
41064
|
function firstRow(p) {
|
|
40670
41065
|
const act = p.shown.findIndex((s) => s === "proposed" || s === "held" || s === "failed");
|
|
@@ -42054,8 +42449,8 @@ function listItems2(roles, query, builtinOpen) {
|
|
|
42054
42449
|
const by = (a, b3) => a.name.localeCompare(b3.name) || a.id.localeCompare(b3.id);
|
|
42055
42450
|
const kept = roles.filter((r) => matches(r, query)).sort(by);
|
|
42056
42451
|
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" }] : [], ...
|
|
42452
|
+
const open6 = builtinOpen || query.trim() !== "";
|
|
42453
|
+
return { mine, builtin, items: [...mine.map((role) => ({ kind: "role", role })), ...builtin.length ? [{ kind: "builtin" }] : [], ...open6 ? builtin.map((role) => ({ kind: "role", role })) : []] };
|
|
42059
42454
|
}
|
|
42060
42455
|
function listLines(roles, o, t, g) {
|
|
42061
42456
|
const r = t.roles, w = o.width;
|
|
@@ -42087,11 +42482,11 @@ function listLines(roles, o, t, g) {
|
|
|
42087
42482
|
if (mine.length) lines.push([]);
|
|
42088
42483
|
const s = i === o.sel;
|
|
42089
42484
|
if (s) selLine = lines.length;
|
|
42090
|
-
const
|
|
42485
|
+
const open6 = o.builtinOpen || o.query.trim() !== "";
|
|
42091
42486
|
const bg = s ? C.sel : void 0;
|
|
42092
|
-
lines.push([{ text: s ? "\u258C" : " ", color: s && o.focused ? C.coral : C.faint, ...bg ? { bg } : {} }, { text: `${
|
|
42487
|
+
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
42488
|
i++;
|
|
42094
|
-
if (
|
|
42489
|
+
if (open6) builtin.forEach(roleLine);
|
|
42095
42490
|
}
|
|
42096
42491
|
return { lines, selLine };
|
|
42097
42492
|
}
|
|
@@ -42187,7 +42582,7 @@ function param(label2, value, o, t, g, w, narrow, extra = {}) {
|
|
|
42187
42582
|
const usedL = cells(lab.map((x) => x.text).join("")), usedV = cells(val.map((x) => x.text).join(""));
|
|
42188
42583
|
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
42584
|
}
|
|
42190
|
-
function paramRows(d, seat2, files,
|
|
42585
|
+
function paramRows(d, seat2, files, open6, o, t) {
|
|
42191
42586
|
const r = t.roles, w = o.width, g = o.g, n = o.narrow;
|
|
42192
42587
|
const P = (label2, value, origin, extra = {}) => param(label2, value, origin, t, g, w, n, extra);
|
|
42193
42588
|
const out = [];
|
|
@@ -42228,8 +42623,8 @@ function paramRows(d, seat2, files, open5, o, t) {
|
|
|
42228
42623
|
const u = prof.uses;
|
|
42229
42624
|
const loadout = seat2?.live?.skills;
|
|
42230
42625
|
if (loadout) {
|
|
42231
|
-
out.push(P(r.p.skills, v(`${r.skillsProjected(loadout.length)} ${
|
|
42232
|
-
if (
|
|
42626
|
+
out.push(P(r.p.skills, v(`${r.skillsProjected(loadout.length)} ${open6.has("loadout") ? g.unfolded : g.folded}`), { kind: "inherited", from: "launch" }, { fold: "loadout" }));
|
|
42627
|
+
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
42628
|
}
|
|
42234
42629
|
const list3 = (label2, l, what, fold) => {
|
|
42235
42630
|
if (!l.declared) {
|
|
@@ -42243,9 +42638,9 @@ function paramRows(d, seat2, files, open5, o, t) {
|
|
|
42243
42638
|
l.items.forEach((x, i) => {
|
|
42244
42639
|
const na = !!(seat2 && x.runtime && seat2.runtime.value && x.runtime !== seat2.runtime.value);
|
|
42245
42640
|
const key = fold?.(x);
|
|
42246
|
-
const row3 = P(i === 0 ? label2 : "", v(`${what(x)}${na ? ` \xB7 ${r.na(x.runtime)}` : ""}${key ? ` ${
|
|
42641
|
+
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
42642
|
out.push(na ? { ...row3, line: row3.line.map((s) => ({ ...s, color: C.faint })) } : row3);
|
|
42248
|
-
if (key &&
|
|
42643
|
+
if (key && open6.has(key)) {
|
|
42249
42644
|
const body = x.plugin ? x.plugin.skills.join(", ") : (x.absolutePath && files[x.absolutePath]?.content) ?? t.roles.reading;
|
|
42250
42645
|
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
42646
|
}
|
|
@@ -42333,9 +42728,9 @@ function yamlRows(d, seat2, files, o, t) {
|
|
|
42333
42728
|
}
|
|
42334
42729
|
return out;
|
|
42335
42730
|
}
|
|
42336
|
-
function filesFor(d, seat2, tab, at,
|
|
42731
|
+
function filesFor(d, seat2, tab, at, open6) {
|
|
42337
42732
|
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 &&
|
|
42733
|
+
if (tab === 1) return (profileOf(d, seat2)?.uses.runtimeResources.items ?? []).filter((x) => x.absolutePath && open6.has(`rr:${x.id}`)).map((x) => x.absolutePath);
|
|
42339
42734
|
if (tab === 2) {
|
|
42340
42735
|
const p = pieces(d, seat2)[at];
|
|
42341
42736
|
return p?.kind === "entry" && p.entry.absolutePath && !isMissing(p.entry) ? [p.entry.absolutePath] : [];
|
|
@@ -42412,7 +42807,7 @@ function RolesScreen({ onRigs, machineId, onMachine }) {
|
|
|
42412
42807
|
const [scroll, setScroll] = useState14(0);
|
|
42413
42808
|
const [at, setAt] = useState14(0);
|
|
42414
42809
|
const [reading, setReading] = useState14(false);
|
|
42415
|
-
const [
|
|
42810
|
+
const [open6, setOpen] = useState14(/* @__PURE__ */ new Set());
|
|
42416
42811
|
const [files, setFiles] = useState14({});
|
|
42417
42812
|
const { items } = listItems2(roles.roles, query, builtinOpen);
|
|
42418
42813
|
const firstRole = items.find((i) => i.kind === "role");
|
|
@@ -42438,7 +42833,7 @@ function RolesScreen({ onRigs, machineId, onMachine }) {
|
|
|
42438
42833
|
asks.current.reset();
|
|
42439
42834
|
setFiles({});
|
|
42440
42835
|
}, [currentId, machine?.id]);
|
|
42441
|
-
const wanted = (d ? filesFor(d, seat2, shownTab, at,
|
|
42836
|
+
const wanted = (d ? filesFor(d, seat2, shownTab, at, open6) : []).join("\n");
|
|
42442
42837
|
useEffect13(() => {
|
|
42443
42838
|
if (!wanted || !machine || !currentId) return;
|
|
42444
42839
|
const id = currentId, mid = machine.id;
|
|
@@ -42451,7 +42846,7 @@ function RolesScreen({ onRigs, machineId, onMachine }) {
|
|
|
42451
42846
|
const bodyH = Math.max(3, rows - 4);
|
|
42452
42847
|
const head2 = d && summary ? headRows(d, summary, { seat: seat2, tab: shownTab, width: w, narrow: !wide, seats }, t) : [];
|
|
42453
42848
|
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,
|
|
42849
|
+
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
42850
|
const room = Math.max(1, bodyH - head2.length);
|
|
42456
42851
|
const maxScroll = Math.max(0, body.length - room);
|
|
42457
42852
|
const params = shownTab === 1 ? body.map((x, i) => x.rail ? i : -1).filter((i) => i >= 0) : [];
|
|
@@ -42709,7 +43104,7 @@ function RigsView({ initialMachine, onRoles }) {
|
|
|
42709
43104
|
const rigs = listedRigs(state2);
|
|
42710
43105
|
const [at, setAt] = useState15(0);
|
|
42711
43106
|
const selectedId = useRef13(null);
|
|
42712
|
-
const [
|
|
43107
|
+
const [open6, setOpen] = useState15(/* @__PURE__ */ new Set());
|
|
42713
43108
|
const [seatAt, setSeatAt] = useState15(null);
|
|
42714
43109
|
const [confirm, setConfirm] = useState15(null);
|
|
42715
43110
|
const [seats, setSeats] = useState15(null);
|
|
@@ -42719,7 +43114,7 @@ function RigsView({ initialMachine, onRoles }) {
|
|
|
42719
43114
|
}, [ids]);
|
|
42720
43115
|
const current2 = rigs[Math.min(at, rigs.length - 1)];
|
|
42721
43116
|
selectedId.current = current2?.id ?? null;
|
|
42722
|
-
const seatNodes = current2 &&
|
|
43117
|
+
const seatNodes = current2 && open6.has(current2.id) ? current2.nodes : [];
|
|
42723
43118
|
const seatSel = seatAt !== null && seatAt < seatNodes.length ? seatAt : null;
|
|
42724
43119
|
const down = () => {
|
|
42725
43120
|
if (seatSel !== null ? seatSel + 1 < seatNodes.length : seatNodes.length > 0) {
|
|
@@ -42737,7 +43132,7 @@ function RigsView({ initialMachine, onRoles }) {
|
|
|
42737
43132
|
}
|
|
42738
43133
|
const n = step2(at, -1, rigs.length), prev = rigs[n];
|
|
42739
43134
|
setAt(n);
|
|
42740
|
-
setSeatAt(prev &&
|
|
43135
|
+
setSeatAt(prev && open6.has(prev.id) && prev.nodes.length ? prev.nodes.length - 1 : null);
|
|
42741
43136
|
};
|
|
42742
43137
|
const run4 = (a) => {
|
|
42743
43138
|
void rig.act(a).then((d) => {
|
|
@@ -42897,12 +43292,12 @@ function RigsView({ initialMachine, onRoles }) {
|
|
|
42897
43292
|
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
43293
|
const crumb = [state2?.version ? o.version(state2.version) : o.title, machine?.label, t.roles.rigs].filter(Boolean).join(" \xB7 ");
|
|
42899
43294
|
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:
|
|
43295
|
+
!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
43296
|
confirm ? /* @__PURE__ */ jsx22(ConfirmBox, { confirm }) : null,
|
|
42902
43297
|
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
43298
|
] });
|
|
42904
43299
|
}
|
|
42905
|
-
function Body({ state: state2, rigs, at, seatAt, open:
|
|
43300
|
+
function Body({ state: state2, rigs, at, seatAt, open: open6, machineLabel: machineLabel2, roleOf: roleOf4 }) {
|
|
42906
43301
|
const { t } = useTui();
|
|
42907
43302
|
const o = t.openrig;
|
|
42908
43303
|
const up = daemonUp(state2);
|
|
@@ -42940,10 +43335,10 @@ function Body({ state: state2, rigs, at, seatAt, open: open5, machineLabel: mach
|
|
|
42940
43335
|
] }, n.session))
|
|
42941
43336
|
] })
|
|
42942
43337
|
] }),
|
|
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:
|
|
43338
|
+
/* @__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
43339
|
] });
|
|
42945
43340
|
}
|
|
42946
|
-
function RigRow({ rig, selected, seatAt, open:
|
|
43341
|
+
function RigRow({ rig, selected, seatAt, open: open6, roleOf: roleOf4 }) {
|
|
42947
43342
|
const { t } = useTui();
|
|
42948
43343
|
const o = t.openrig;
|
|
42949
43344
|
const look = rigLook(rig), l = LOOK[look];
|
|
@@ -42953,14 +43348,14 @@ function RigRow({ rig, selected, seatAt, open: open5, roleOf: roleOf4 }) {
|
|
|
42953
43348
|
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
42954
43349
|
/* @__PURE__ */ jsx22(Box17, { backgroundColor: selected ? C.sel : void 0, children: /* @__PURE__ */ jsxs17(Text18, { wrap: "truncate", children: [
|
|
42955
43350
|
/* @__PURE__ */ jsx22(Text18, { color: selected ? C.coral : C.faint, children: selected ? "\u258C" : " " }),
|
|
42956
|
-
/* @__PURE__ */ jsx22(Text18, { color: C.dim, children:
|
|
43351
|
+
/* @__PURE__ */ jsx22(Text18, { color: C.dim, children: open6 ? "\u25BE " : "\u25B8 " }),
|
|
42957
43352
|
/* @__PURE__ */ jsx22(Text18, { color: C.fg, bold: true, children: rig.name.padEnd(16) }),
|
|
42958
43353
|
/* @__PURE__ */ jsx22(Text18, { color: l.color(), children: ` ${l.glyph} ${o.look[look]}`.padEnd(18) }),
|
|
42959
43354
|
/* @__PURE__ */ jsx22(Text18, { color: C.fg, children: o.seats(rig.running, rig.seats).padEnd(7) }),
|
|
42960
43355
|
note ? /* @__PURE__ */ jsx22(Text18, { color: note.color, children: note.text.padEnd(34) }) : /* @__PURE__ */ jsx22(Text18, { children: "".padEnd(34) }),
|
|
42961
43356
|
/* @__PURE__ */ jsx22(Text18, { color: C.dim, children: when })
|
|
42962
43357
|
] }) }),
|
|
42963
|
-
|
|
43358
|
+
open6 ? rig.nodes.map((n, i) => {
|
|
42964
43359
|
const s = i === seatAt, bg = s ? C.sel : void 0, role = roleOf4(rig.name, n.logicalId);
|
|
42965
43360
|
return /* @__PURE__ */ jsx22(Box17, { backgroundColor: bg, children: /* @__PURE__ */ jsxs17(Text18, { wrap: "truncate", children: [
|
|
42966
43361
|
/* @__PURE__ */ jsx22(Text18, { color: C.coral, children: s ? "\u258C" : " " }),
|
|
@@ -43472,7 +43867,7 @@ function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeS
|
|
|
43472
43867
|
const attach2 = useCallback3((s) => {
|
|
43473
43868
|
const cmd = attachCommand(fleet, s);
|
|
43474
43869
|
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" });
|
|
43870
|
+
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
43871
|
return;
|
|
43477
43872
|
}
|
|
43478
43873
|
void suspendTerminal(async () => {
|
|
@@ -43772,7 +44167,7 @@ var init_glyphs = __esm({
|
|
|
43772
44167
|
|
|
43773
44168
|
// src/tui/settings.ts
|
|
43774
44169
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync13, renameSync as renameSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
43775
|
-
import { join as
|
|
44170
|
+
import { join as join39 } from "path";
|
|
43776
44171
|
import { z as z32 } from "zod";
|
|
43777
44172
|
function defaultLang(env) {
|
|
43778
44173
|
const locale = env.LC_ALL || env.LC_MESSAGES || env.LANG || "";
|
|
@@ -43798,14 +44193,14 @@ var init_settings = __esm({
|
|
|
43798
44193
|
"use strict";
|
|
43799
44194
|
init_paths();
|
|
43800
44195
|
SettingsSchema = z32.object({ lang: z32.enum(["fr", "en"]).optional(), theme: z32.enum(["auto", "dark", "light"]).optional() }).passthrough();
|
|
43801
|
-
settingsPath = () =>
|
|
44196
|
+
settingsPath = () => join39(ledgeDir(), "tui.json");
|
|
43802
44197
|
}
|
|
43803
44198
|
});
|
|
43804
44199
|
|
|
43805
44200
|
// src/tui/terminalProfile.ts
|
|
43806
44201
|
import { execFile as execFile10, spawn as spawn6 } from "child_process";
|
|
43807
|
-
import { mkdir as mkdir14, readFile as
|
|
43808
|
-
import { dirname as dirname13, join as
|
|
44202
|
+
import { mkdir as mkdir14, readFile as readFile19, realpath as realpath5, writeFile as writeFile18 } from "fs/promises";
|
|
44203
|
+
import { dirname as dirname13, join as join40 } from "path";
|
|
43809
44204
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
43810
44205
|
import { promisify } from "util";
|
|
43811
44206
|
function movesToLedgeTab(env, here, script) {
|
|
@@ -43833,13 +44228,13 @@ function fragment(commandline, iconWin) {
|
|
|
43833
44228
|
}
|
|
43834
44229
|
async function profileLoaded(localAppData) {
|
|
43835
44230
|
for (const rel of SETTINGS) {
|
|
43836
|
-
const text7 = await
|
|
44231
|
+
const text7 = await readFile19(join40(localAppData, rel), "utf8").catch(() => "");
|
|
43837
44232
|
if (new RegExp(`"source"\\s*:\\s*"${PROFILE}"`).test(text7)) return true;
|
|
43838
44233
|
}
|
|
43839
44234
|
return false;
|
|
43840
44235
|
}
|
|
43841
44236
|
async function writeIfChanged2(path3, content3) {
|
|
43842
|
-
const current2 = await
|
|
44237
|
+
const current2 = await readFile19(path3).catch(() => null);
|
|
43843
44238
|
if (current2 && current2.equals(Buffer.from(content3))) return false;
|
|
43844
44239
|
await writeFile18(path3, content3);
|
|
43845
44240
|
return true;
|
|
@@ -43851,9 +44246,9 @@ async function openInLedgeTab(o) {
|
|
|
43851
44246
|
await writeIfChanged2(o.launcher, launcherScript(o.node, o.script, o.ledgeHome));
|
|
43852
44247
|
const dir = fragmentDir(o.localAppData.linux);
|
|
43853
44248
|
await mkdir14(dir, { recursive: true });
|
|
43854
|
-
await writeIfChanged2(
|
|
44249
|
+
await writeIfChanged2(join40(dir, "ledge.ico"), await readFile19(o.icon ?? ICON));
|
|
43855
44250
|
const iconWin = `${o.localAppData.win}\\Microsoft\\Windows Terminal\\Fragments\\${PROFILE}\\ledge.ico`;
|
|
43856
|
-
await writeIfChanged2(
|
|
44251
|
+
await writeIfChanged2(join40(dir, "ledge.json"), fragment(commandline, iconWin));
|
|
43857
44252
|
const deadline = Date.now() + (o.waitMs ?? 15e3);
|
|
43858
44253
|
for (let first = true; !await profileLoaded(o.localAppData.linux); first = false) {
|
|
43859
44254
|
if (Date.now() > deadline) return false;
|
|
@@ -43874,11 +44269,11 @@ async function openTuiInLedgeTab(env, onWait) {
|
|
|
43874
44269
|
distro: env.WSL_DISTRO_NAME,
|
|
43875
44270
|
node: await realpath5(process.execPath),
|
|
43876
44271
|
script: await realpath5(process.argv[1]),
|
|
43877
|
-
launcher:
|
|
44272
|
+
launcher: join40(ledgeDir(), "tui.sh"),
|
|
43878
44273
|
ledgeHome: env.LEDGE_HOME,
|
|
43879
44274
|
onWait,
|
|
43880
44275
|
wt: (args) => new Promise((resolve13, reject) => {
|
|
43881
|
-
const child = spawn6(
|
|
44276
|
+
const child = spawn6(join40(linux, "Microsoft", "WindowsApps", "wt.exe"), args, { cwd: "/mnt/c", stdio: "ignore", detached: true });
|
|
43882
44277
|
child.once("error", reject);
|
|
43883
44278
|
child.once("spawn", () => {
|
|
43884
44279
|
child.unref();
|
|
@@ -43902,7 +44297,7 @@ var init_terminalProfile = __esm({
|
|
|
43902
44297
|
"Packages/Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe/LocalState/settings.json",
|
|
43903
44298
|
"Microsoft/Windows Terminal/settings.json"
|
|
43904
44299
|
];
|
|
43905
|
-
fragmentDir = (localAppData) =>
|
|
44300
|
+
fragmentDir = (localAppData) => join40(localAppData, "Microsoft", "Windows Terminal", "Fragments", PROFILE);
|
|
43906
44301
|
PROFILE_ID = "{d10fee13-0978-595f-a851-b60ec80f11c9}";
|
|
43907
44302
|
shQuote2 = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
43908
44303
|
run3 = promisify(execFile10);
|