@paradow/ledge 0.3.3 → 0.4.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/README.md +1 -1
- package/dist/cli/{attach-YBGTDBQI.js → attach-IKK5XQGC.js} +5 -5
- package/dist/cli/{chunk-W5XAZ5IS.js → chunk-34DHZSGH.js} +1 -1
- package/dist/cli/{chunk-ZPF5I3LS.js → chunk-5OJZV5MV.js} +1 -1
- package/dist/cli/{chunk-OUIDBDIH.js → chunk-B73JDPSW.js} +1 -1
- package/dist/cli/{chunk-7ICDC2LK.js → chunk-DMV5VW4Q.js} +1 -1
- package/dist/cli/chunk-Y74YLRHP.js +6 -0
- package/dist/cli/{chunk-6GMDFIGB.js → chunk-YT4T7DL2.js} +63 -5
- package/dist/cli/{chunk-Y44XZY6I.js → chunk-ZPR4LCDM.js} +4 -4
- package/dist/cli/{daemon-D5YDWUHB.js → daemon-2KHF4XUC.js} +238 -36
- package/dist/cli/{hook-HP23ZISG.js → hook-QH44D3JC.js} +4 -4
- package/dist/cli/index.js +6 -6
- package/dist/cli/{launch-DFB54ANN.js → launch-CPNM5RX7.js} +5 -5
- package/dist/cli/{status-IW2FESH4.js → status-E2VZMTAF.js} +2 -2
- package/dist/cli/{version-3DLAAFBS.js → version-J6BW646U.js} +1 -1
- package/package.json +3 -3
- package/dist/cli/chunk-HDZVQH6J.js +0 -6
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @paradow/ledge (WSL side)
|
|
2
2
|
|
|
3
|
-
The Ledge daemon and CLI: follow and answer your WSL agent sessions (Claude Code, Codex, Cursor Agent) from the Ledge island on Windows. See the [repository README](https://github.com/
|
|
3
|
+
The Ledge daemon and CLI: follow and answer your WSL agent sessions (Claude Code, Codex, Cursor Agent) from the Ledge island on Windows. See the [repository README](https://github.com/Lyssaen/ledge#readme) for the whole picture.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
attachEnv
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZPR4LCDM.js";
|
|
4
4
|
import {
|
|
5
5
|
realTmux
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-DMV5VW4Q.js";
|
|
7
|
+
import "./chunk-B73JDPSW.js";
|
|
8
8
|
import "./chunk-6ZTCETRG.js";
|
|
9
9
|
import {
|
|
10
10
|
connect
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-34DHZSGH.js";
|
|
12
12
|
import {
|
|
13
13
|
PROTOCOL_PORT
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-YT4T7DL2.js";
|
|
15
15
|
|
|
16
16
|
// src/cli/attach.ts
|
|
17
17
|
import { spawn } from "child_process";
|
|
@@ -64,6 +64,23 @@ var ChatStepSchema = z2.object({
|
|
|
64
64
|
name: z2.string().optional(),
|
|
65
65
|
path: z2.string().optional()
|
|
66
66
|
});
|
|
67
|
+
var ChatProposalSchema = z2.object({
|
|
68
|
+
cwd: z2.string().min(1),
|
|
69
|
+
project: z2.string(),
|
|
70
|
+
prompt: z2.string().optional(),
|
|
71
|
+
tool: ToolSchema,
|
|
72
|
+
launchedId: z2.string().optional(),
|
|
73
|
+
error: z2.string().optional()
|
|
74
|
+
});
|
|
75
|
+
var ChatRelaySchema = z2.object({
|
|
76
|
+
id: z2.string().min(1),
|
|
77
|
+
name: z2.string(),
|
|
78
|
+
tool: ToolSchema,
|
|
79
|
+
text: z2.string().min(1),
|
|
80
|
+
status: z2.enum(["queued", "sent"]).optional(),
|
|
81
|
+
sentAt: z2.string().datetime().optional(),
|
|
82
|
+
error: z2.string().optional()
|
|
83
|
+
});
|
|
67
84
|
var ChatTurnSchema = z2.object({
|
|
68
85
|
qid: z2.string().min(1),
|
|
69
86
|
question: z2.string(),
|
|
@@ -71,7 +88,9 @@ var ChatTurnSchema = z2.object({
|
|
|
71
88
|
steps: z2.array(ChatStepSchema),
|
|
72
89
|
answer: z2.string().optional(),
|
|
73
90
|
answeredAt: z2.string().datetime().optional(),
|
|
74
|
-
error: z2.string().optional()
|
|
91
|
+
error: z2.string().optional(),
|
|
92
|
+
proposal: ChatProposalSchema.optional(),
|
|
93
|
+
relay: ChatRelaySchema.optional()
|
|
75
94
|
});
|
|
76
95
|
|
|
77
96
|
// ../protocol/src/documents.ts
|
|
@@ -154,7 +173,7 @@ var ServerMessageSchema = z4.discriminatedUnion("type", [
|
|
|
154
173
|
/** The chat (spec 2026-10-11 §5): what Ledge reads, its words as they come, the final text, or why it stopped; the day's turns after each snapshot. */
|
|
155
174
|
z4.object({ type: z4.literal("chat.step"), qid: z4.string(), step: ChatStepSchema }),
|
|
156
175
|
z4.object({ type: z4.literal("chat.delta"), qid: z4.string(), text: z4.string() }),
|
|
157
|
-
z4.object({ type: z4.literal("chat.done"), qid: z4.string(), text: z4.string(), at: z4.string().datetime() }),
|
|
176
|
+
z4.object({ type: z4.literal("chat.done"), qid: z4.string(), text: z4.string(), at: z4.string().datetime(), proposal: ChatProposalSchema.optional(), relay: ChatRelaySchema.optional() }),
|
|
158
177
|
z4.object({ type: z4.literal("chat.error"), qid: z4.string(), message: z4.string() }),
|
|
159
178
|
z4.object({ type: z4.literal("chat.history"), turns: z4.array(ChatTurnSchema) }),
|
|
160
179
|
/** The daemon updating itself: npm is running, then the service restarts (the socket closes right after). */
|
|
@@ -166,13 +185,18 @@ var ServerMessageSchema = z4.discriminatedUnion("type", [
|
|
|
166
185
|
var ClientMessageSchema = z4.discriminatedUnion("type", [
|
|
167
186
|
DocumentReadSchema,
|
|
168
187
|
/** The overlay starts a session: `cwd` is a WSL path (`~` allowed), `name` a preferred bot name (the daemon picks a
|
|
169
|
-
* free one otherwise), `prompt` the agent's first instruction, `continue` resumes the folder's last conversation.
|
|
170
|
-
|
|
188
|
+
* free one otherwise), `prompt` the agent's first instruction, `continue` resumes the folder's last conversation.
|
|
189
|
+
* With `qid` it is the chat card of that turn: the daemon launches the turn's proposal and ignores the other fields. */
|
|
190
|
+
z4.object({ type: z4.literal("session.launch"), tool: ToolSchema, cwd: z4.string().min(1), name: z4.string().optional(), prompt: z4.string().optional(), continue: z4.boolean().optional(), qid: z4.string().min(1).optional() }),
|
|
171
191
|
/** The launch form's folder browser: list a folder's subfolders (home when no `path`), or make `name` inside `parent`
|
|
172
192
|
* and list it. Both are answered with a `folder.listing`. */
|
|
173
193
|
z4.object({ type: z4.literal("folder.list"), path: z4.string().optional() }),
|
|
174
194
|
z4.object({ type: z4.literal("folder.create"), parent: z4.string().min(1), name: z4.string().min(1) }),
|
|
175
195
|
z4.object({ type: z4.literal("session.reply"), id: z4.string(), text: z4.string().min(1) }),
|
|
196
|
+
/** The chat card's instruction (spec 2026-10-12 relay §4): sent now if the session is free, else queued until it is. */
|
|
197
|
+
z4.object({ type: z4.literal("session.relay"), qid: z4.string().min(1) }),
|
|
198
|
+
/** Takes the card's queued instruction back out of the queue. */
|
|
199
|
+
z4.object({ type: z4.literal("session.relay.cancel"), qid: z4.string().min(1) }),
|
|
176
200
|
z4.object({ type: z4.literal("session.decide"), id: z4.string(), decision: z4.enum(["accept", "deny"]) }),
|
|
177
201
|
z4.object({ type: z4.literal("session.focus"), id: z4.string() }),
|
|
178
202
|
z4.object({ type: z4.literal("session.kill"), id: z4.string() }),
|
|
@@ -282,6 +306,37 @@ function pickName(taken, preferred) {
|
|
|
282
306
|
for (let i = 2; ; i++) if (free(`${NAMES[0]}-${i}`)) return `${NAMES[0]}-${i}`;
|
|
283
307
|
}
|
|
284
308
|
|
|
309
|
+
// ../protocol/src/proposal.ts
|
|
310
|
+
import { z as z5 } from "zod";
|
|
311
|
+
var ProposalBlockSchema = z5.object({ cwd: z5.string().trim().min(1), prompt: z5.string().optional() });
|
|
312
|
+
var RelayBlockSchema = z5.object({ session: z5.string().trim().min(1), text: z5.string().trim().min(1) });
|
|
313
|
+
var BLOCK = /```ledge-(session|relay)[ \t]*\n?([\s\S]*?)(?:```|$)/g;
|
|
314
|
+
function extractProposal(answer) {
|
|
315
|
+
const found = [];
|
|
316
|
+
const text = answer.replace(BLOCK, (_m, kind, body) => {
|
|
317
|
+
found.push({ kind, raw: body.trim() });
|
|
318
|
+
return "";
|
|
319
|
+
});
|
|
320
|
+
const last = found.at(-1);
|
|
321
|
+
return last ? { text: text.replace(/\n{3,}/g, "\n\n").trim(), kind: last.kind, raw: last.raw } : { text: answer, kind: null, raw: null };
|
|
322
|
+
}
|
|
323
|
+
function parseProposalBlock(raw) {
|
|
324
|
+
try {
|
|
325
|
+
const r = ProposalBlockSchema.safeParse(JSON.parse(raw));
|
|
326
|
+
return r.success ? r.data : null;
|
|
327
|
+
} catch {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function parseRelayBlock(raw) {
|
|
332
|
+
try {
|
|
333
|
+
const r = RelayBlockSchema.safeParse(JSON.parse(raw));
|
|
334
|
+
return r.success ? r.data : null;
|
|
335
|
+
} catch {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
285
340
|
// ../protocol/src/topic.ts
|
|
286
341
|
var TOPIC_MAX = 80;
|
|
287
342
|
function cleanTopic(text) {
|
|
@@ -294,13 +349,16 @@ function sessionTitle(s) {
|
|
|
294
349
|
}
|
|
295
350
|
|
|
296
351
|
export {
|
|
297
|
-
DOCUMENT_MAX_BYTES,
|
|
298
352
|
SessionSchema,
|
|
353
|
+
DOCUMENT_MAX_BYTES,
|
|
299
354
|
PROTOCOL_PORT,
|
|
300
355
|
RecentSchema,
|
|
301
356
|
parseClientMessage,
|
|
302
357
|
parseServerMessage,
|
|
303
358
|
pickName,
|
|
359
|
+
extractProposal,
|
|
360
|
+
parseProposalBlock,
|
|
361
|
+
parseRelayBlock,
|
|
304
362
|
cleanTopic,
|
|
305
363
|
sessionTitle
|
|
306
364
|
};
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
realTmux
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DMV5VW4Q.js";
|
|
4
4
|
import {
|
|
5
5
|
exitFile,
|
|
6
6
|
log,
|
|
7
7
|
spawnSession
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-B73JDPSW.js";
|
|
9
9
|
import {
|
|
10
10
|
ensureLedgeDir
|
|
11
11
|
} from "./chunk-6ZTCETRG.js";
|
|
12
12
|
import {
|
|
13
13
|
connect
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-34DHZSGH.js";
|
|
15
15
|
import {
|
|
16
16
|
PROTOCOL_PORT
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-YT4T7DL2.js";
|
|
18
18
|
|
|
19
19
|
// src/cli/launch.ts
|
|
20
20
|
import { spawn } from "child_process";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
reduce
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5OJZV5MV.js";
|
|
4
4
|
import {
|
|
5
5
|
LEDGE_VERSION
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Y74YLRHP.js";
|
|
7
7
|
import {
|
|
8
8
|
SERVICE
|
|
9
9
|
} from "./chunk-XDRYMOVF.js";
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
realTmux,
|
|
12
12
|
run,
|
|
13
13
|
runFile
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-DMV5VW4Q.js";
|
|
15
15
|
import {
|
|
16
16
|
log,
|
|
17
17
|
spawnSession
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-B73JDPSW.js";
|
|
19
19
|
import {
|
|
20
20
|
ensureLedgeDir,
|
|
21
21
|
paths
|
|
@@ -25,9 +25,12 @@ import {
|
|
|
25
25
|
PROTOCOL_PORT,
|
|
26
26
|
RecentSchema,
|
|
27
27
|
SessionSchema,
|
|
28
|
+
extractProposal,
|
|
28
29
|
parseClientMessage,
|
|
30
|
+
parseProposalBlock,
|
|
31
|
+
parseRelayBlock,
|
|
29
32
|
pickName
|
|
30
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-YT4T7DL2.js";
|
|
31
34
|
|
|
32
35
|
// src/daemon/index.ts
|
|
33
36
|
import { writeFile as writeFile4, rm as rm3 } from "fs/promises";
|
|
@@ -154,7 +157,7 @@ var Store = class {
|
|
|
154
157
|
};
|
|
155
158
|
|
|
156
159
|
// src/daemon/server.ts
|
|
157
|
-
import { basename as
|
|
160
|
+
import { basename as basename4 } from "path";
|
|
158
161
|
import { WebSocketServer, WebSocket } from "ws";
|
|
159
162
|
|
|
160
163
|
// src/adapters/claude.ts
|
|
@@ -976,7 +979,7 @@ function loginShellEnv(opts = {}) {
|
|
|
976
979
|
// src/chat/service.ts
|
|
977
980
|
import { randomUUID } from "crypto";
|
|
978
981
|
import { mkdir as mkdir4, readFile as readFile2, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
979
|
-
import { dirname as dirname4, join as join3 } from "path";
|
|
982
|
+
import { basename as basename3, dirname as dirname4, join as join3 } from "path";
|
|
980
983
|
|
|
981
984
|
// src/chat/context.ts
|
|
982
985
|
import { mkdir as mkdir3, rm, writeFile as writeFile2 } from "fs/promises";
|
|
@@ -1005,6 +1008,7 @@ async function writeContext(dir, input) {
|
|
|
1005
1008
|
`- repository: ${s.repo ?? s.project}${s.branch ? `, branch ${s.branch}${s.worktree ? " (linked worktree)" : ""}` : ""}`,
|
|
1006
1009
|
`- started: ${s.startedAt}, last activity: ${s.lastActivityAt}`
|
|
1007
1010
|
);
|
|
1011
|
+
if (s.source === "hook_only") lines.push("- read-only: Ledge cannot send to it");
|
|
1008
1012
|
if (s.pending) lines.push(`- pending ${s.pending.kind}: ${s.pending.text}`);
|
|
1009
1013
|
const live = s.helpers.filter((h) => h.state !== "exited");
|
|
1010
1014
|
if (live.length) lines.push(`- subagents: ${live.map((h) => h.task || h.kind).join("; ")}`);
|
|
@@ -1032,6 +1036,15 @@ async function writeContext(dir, input) {
|
|
|
1032
1036
|
}
|
|
1033
1037
|
await writeFile2(join2(dir, "sessions.md"), lines.join("\n"));
|
|
1034
1038
|
steps.set("sessions.md", { kind: "sessions" });
|
|
1039
|
+
const recents = input.recents ?? [];
|
|
1040
|
+
await writeFile2(join2(dir, "folders.md"), [
|
|
1041
|
+
"# Recent folders",
|
|
1042
|
+
"",
|
|
1043
|
+
"The folders the user started sessions in, most recent first.",
|
|
1044
|
+
"",
|
|
1045
|
+
...recents.length ? recents.map((r) => `- ${r.project}: ${r.cwd} (last used ${r.lastAt})`) : ["- (none yet)"],
|
|
1046
|
+
""
|
|
1047
|
+
].join("\n"));
|
|
1035
1048
|
const repoPaths = [...new Set(input.sessions.map((s) => s.repoPath ?? s.cwd).filter((p) => p && !coversHome(p)).map((p) => resolve3(p)))];
|
|
1036
1049
|
return { dir, steps, repoPaths };
|
|
1037
1050
|
}
|
|
@@ -1091,10 +1104,10 @@ function runJsonl(file, args, o, parse, onEvent, spawnImpl = nodeSpawn) {
|
|
|
1091
1104
|
}
|
|
1092
1105
|
|
|
1093
1106
|
// src/chat/claude.ts
|
|
1094
|
-
var PERSONA = `Tu es Ledge, le compagnon qui veille sur les sessions d'agents (Claude Code, Codex, Cursor Agent) de l'utilisateur. Tu lis, tu ne touches \xE0 rien :
|
|
1095
|
-
Tes sources : le dossier courant contient sessions.md (toutes les sessions, leur \xE9tat et le nom de leurs fichiers), pane-<id>.txt (les derni\xE8res lignes du terminal d'une session), reply-<id>.md (sa derni\xE8re r\xE9ponse)
|
|
1107
|
+
var PERSONA = `Tu es Ledge, le compagnon qui veille sur les sessions d'agents (Claude Code, Codex, Cursor Agent) de l'utilisateur. Tu lis, tu ne touches \xE0 rien : seulement des commandes de lecture (cat, ls, rg\u2026), aucune commande qui modifie quoi que ce soit, pas d'\xE9criture, et tu n'envoies jamais rien toi-m\xEAme \xE0 une session.
|
|
1108
|
+
Tes sources : le dossier courant contient sessions.md (toutes les sessions, leur \xE9tat et le nom de leurs fichiers), pane-<id>.txt (les derni\xE8res lignes du terminal d'une session), reply-<id>.md (sa derni\xE8re r\xE9ponse), git-<id>.txt (status, log et diff --stat de son dossier) et folders.md (les dossiers r\xE9cents o\xF9 l'utilisateur a lanc\xE9 des sessions). Les d\xE9p\xF4ts eux-m\xEAmes sont lisibles, avec les outils de lecture de ton harnais, pour aller plus loin.
|
|
1096
1109
|
Commence toujours par lire sessions.md. Ne lis que ce que la question demande.
|
|
1097
|
-
R\xE9ponds dans la langue de la question, en 3 \xE0 10 lignes, en Markdown simple : paragraphes courts, listes \xE0 puces, \`code\` pour les commandes, fichiers et branches. Nomme chaque bot par son pr\xE9nom en gras (**Nova**) la premi\xE8re fois. Dis d'o\xF9 vient chaque fait et de quand il date quand cela compte. Si une information manque, dis-le au lieu de supposer. Tu
|
|
1110
|
+
R\xE9ponds dans la langue de la question, en 3 \xE0 10 lignes, en Markdown simple : paragraphes courts, listes \xE0 puces, \`code\` pour les commandes, fichiers et branches. Nomme chaque bot par son pr\xE9nom en gras (**Nova**) la premi\xE8re fois. Dis d'o\xF9 vient chaque fait et de quand il date quand cela compte. Si une information manque, dis-le au lieu de supposer. Tu n'agis jamais toi-m\xEAme ; tu peux lui dire quel bot ouvrir. Quand l'utilisateur demande explicitement d'ouvrir ou de lancer une session, et seulement si le dossier est s\xFBr \u2014 un chemin absolu lu dans sessions.md ou folders.md, ou donn\xE9 tel quel par l'utilisateur \u2014, r\xE9ponds en une ou deux lignes puis termine par un bloc \`\`\`ledge-session contenant {"cwd": "<chemin>", "prompt": "<l'instruction, s'il y en a une>"}. Si plusieurs dossiers conviennent ou aucun, ne mets pas de bloc : cite les candidats et demande lequel. Quand l'utilisateur demande explicitement de dire, demander ou envoyer quelque chose \xE0 une session, et seulement si une seule session de sessions.md correspond et qu'elle n'est pas en lecture seule, r\xE9ponds en une ou deux lignes puis termine par un bloc \`\`\`ledge-relay contenant {"session": "<id>", "text": "<l'instruction, formul\xE9e pour l'agent>"}. Si plusieurs sessions conviennent ou aucune, ne mets pas de bloc : cite les candidats et demande laquelle.`;
|
|
1098
1111
|
function claudeArgs(o) {
|
|
1099
1112
|
return [
|
|
1100
1113
|
"-p",
|
|
@@ -1256,10 +1269,10 @@ function createChat(deps) {
|
|
|
1256
1269
|
const CANCELLED = { ok: false, message: "cancelled", resumed: false };
|
|
1257
1270
|
function rollDay() {
|
|
1258
1271
|
const day = today();
|
|
1259
|
-
if (turnsDay
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1272
|
+
if (turnsDay === day) return false;
|
|
1273
|
+
turns = [];
|
|
1274
|
+
turnsDay = day;
|
|
1275
|
+
return true;
|
|
1263
1276
|
}
|
|
1264
1277
|
async function daySession(fresh, tool) {
|
|
1265
1278
|
const day = today();
|
|
@@ -1282,6 +1295,44 @@ function createChat(deps) {
|
|
|
1282
1295
|
return p === null ? null : p.split("\n");
|
|
1283
1296
|
};
|
|
1284
1297
|
const contextDir = join3(deps.home, "chat", "context");
|
|
1298
|
+
async function proposalFrom(raw, tool, qid) {
|
|
1299
|
+
const block = parseProposalBlock(raw);
|
|
1300
|
+
if (!block) {
|
|
1301
|
+
log(`chat ${qid}: proposal ignored, invalid block: ${raw.slice(0, 200)}`);
|
|
1302
|
+
return void 0;
|
|
1303
|
+
}
|
|
1304
|
+
try {
|
|
1305
|
+
const cwd = await resolveFolder(block.cwd, deps.userHome);
|
|
1306
|
+
const prompt = block.prompt?.trim();
|
|
1307
|
+
if (prompt?.startsWith("-")) {
|
|
1308
|
+
log(`chat ${qid}: proposal ignored: prompt starts with "-"`);
|
|
1309
|
+
return void 0;
|
|
1310
|
+
}
|
|
1311
|
+
return { cwd, project: basename3(cwd), tool, ...prompt ? { prompt } : {} };
|
|
1312
|
+
} catch (e) {
|
|
1313
|
+
log(`chat ${qid}: proposal ignored: ${e.message}`);
|
|
1314
|
+
return void 0;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
function relayFrom(raw, qid) {
|
|
1318
|
+
const block = parseRelayBlock(raw);
|
|
1319
|
+
if (!block) {
|
|
1320
|
+
log(`chat ${qid}: relay ignored, invalid block: ${raw.slice(0, 200)}`);
|
|
1321
|
+
return void 0;
|
|
1322
|
+
}
|
|
1323
|
+
if (block.text.trimStart().startsWith("!")) {
|
|
1324
|
+
log(`chat ${qid}: relay ignored: bash-mode instruction ${block.session}`);
|
|
1325
|
+
return void 0;
|
|
1326
|
+
}
|
|
1327
|
+
const s = deps.store.get(block.session);
|
|
1328
|
+
const why = !s ? "unknown session" : s.source === "hook_only" ? "read-only session" : s.state === "exited" ? "session has exited" : null;
|
|
1329
|
+
if (why) {
|
|
1330
|
+
log(`chat ${qid}: relay ignored: ${why} ${block.session}`);
|
|
1331
|
+
return void 0;
|
|
1332
|
+
}
|
|
1333
|
+
return { id: s.id, name: s.name, tool: s.tool, text: block.text };
|
|
1334
|
+
}
|
|
1335
|
+
const turnOf = (qid) => turns.find((t) => t.qid === qid);
|
|
1285
1336
|
async function attempt(turn, emit, fresh, mine, tool) {
|
|
1286
1337
|
try {
|
|
1287
1338
|
return await run3(turn, emit, fresh, mine, tool);
|
|
@@ -1292,7 +1343,7 @@ function createChat(deps) {
|
|
|
1292
1343
|
}
|
|
1293
1344
|
}
|
|
1294
1345
|
async function run3(turn, emit, fresh, mine, tool) {
|
|
1295
|
-
const ctx = await writeContext(contextDir, { sessions: deps.store.all(), now: now(), capture, git: (cwd) => gitSummary(cwd) });
|
|
1346
|
+
const ctx = await writeContext(contextDir, { sessions: deps.store.all(), recents: deps.store.recents(), now: now(), capture, git: (cwd) => gitSummary(cwd) });
|
|
1296
1347
|
if (mine.cancelled) return CANCELLED;
|
|
1297
1348
|
const { sessionId, resume } = await daySession(fresh, tool);
|
|
1298
1349
|
const label2 = TOOL_LABEL[tool];
|
|
@@ -1331,13 +1382,49 @@ function createChat(deps) {
|
|
|
1331
1382
|
if (timedOut) return { ok: false, message: `${label2} did not answer within 3 minutes`, resumed: false };
|
|
1332
1383
|
if (r.code !== 0) return { ok: false, message: r.code === null ? `${label2} could not start: ${r.stderr.trim() || "not found in PATH"}` : r.stderr.trim() || `${label2} exited with code ${r.code}`, resumed: resume };
|
|
1333
1384
|
if (resultError) return { ok: false, message: resultError, resumed: false };
|
|
1334
|
-
|
|
1385
|
+
const { text, kind, raw } = extractProposal(answer);
|
|
1386
|
+
const proposal = kind === "session" ? await proposalFrom(raw, tool, turn.qid) : void 0;
|
|
1387
|
+
const relay = kind === "relay" ? relayFrom(raw, turn.qid) : void 0;
|
|
1388
|
+
turn.answer = text;
|
|
1335
1389
|
turn.answeredAt = now();
|
|
1336
|
-
|
|
1390
|
+
if (proposal) turn.proposal = proposal;
|
|
1391
|
+
if (relay) turn.relay = relay;
|
|
1392
|
+
emit({ type: "chat.done", qid: turn.qid, text, at: turn.answeredAt, ...proposal ? { proposal } : {}, ...relay ? { relay } : {} });
|
|
1337
1393
|
return { ok: true };
|
|
1338
1394
|
}
|
|
1339
1395
|
return {
|
|
1340
1396
|
history: () => turns,
|
|
1397
|
+
proposal: (qid) => turnOf(qid)?.proposal,
|
|
1398
|
+
markLaunched(qid, id, emit) {
|
|
1399
|
+
const t = turnOf(qid);
|
|
1400
|
+
if (!t?.proposal) return;
|
|
1401
|
+
const next = { ...t.proposal, launchedId: id };
|
|
1402
|
+
delete next.error;
|
|
1403
|
+
t.proposal = next;
|
|
1404
|
+
emit({ type: "chat.history", turns });
|
|
1405
|
+
},
|
|
1406
|
+
markFailed(qid, message, emit) {
|
|
1407
|
+
const t = turnOf(qid);
|
|
1408
|
+
if (!t?.proposal) return;
|
|
1409
|
+
t.proposal = { ...t.proposal, error: message };
|
|
1410
|
+
emit({ type: "chat.history", turns });
|
|
1411
|
+
},
|
|
1412
|
+
relay: (qid) => {
|
|
1413
|
+
rollDay();
|
|
1414
|
+
return turnOf(qid)?.relay;
|
|
1415
|
+
},
|
|
1416
|
+
markRelay(qid, patch, emit) {
|
|
1417
|
+
const t = turnOf(qid);
|
|
1418
|
+
if (!t?.relay) return;
|
|
1419
|
+
const { id, name, tool, text } = t.relay;
|
|
1420
|
+
t.relay = { id, name, tool, text, ...patch };
|
|
1421
|
+
emit({ type: "chat.history", turns });
|
|
1422
|
+
},
|
|
1423
|
+
// A roll caught here is silent otherwise: the overlays would keep showing yesterday's queued card.
|
|
1424
|
+
queuedRelays(emit) {
|
|
1425
|
+
if (rollDay()) emit?.({ type: "chat.history", turns });
|
|
1426
|
+
return turns.flatMap((t) => t.relay?.status === "queued" ? [{ qid: t.qid, relay: t.relay }] : []);
|
|
1427
|
+
},
|
|
1341
1428
|
cancel(qid) {
|
|
1342
1429
|
if (current?.qid === qid) {
|
|
1343
1430
|
current.cancelled = true;
|
|
@@ -1447,6 +1534,88 @@ async function readDocument(request, context) {
|
|
|
1447
1534
|
}
|
|
1448
1535
|
}
|
|
1449
1536
|
|
|
1537
|
+
// src/daemon/relay.ts
|
|
1538
|
+
var free = (s) => !!s && (s.state === "idle" || s.state === "error") && !s.pending;
|
|
1539
|
+
var stopped = (name) => `${name} stopped before it got the instruction`;
|
|
1540
|
+
function createRelay(d) {
|
|
1541
|
+
const now = d.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
1542
|
+
const typing = /* @__PURE__ */ new Set();
|
|
1543
|
+
const sending = /* @__PURE__ */ new Set();
|
|
1544
|
+
async function deliver(qid, r) {
|
|
1545
|
+
typing.add(r.id);
|
|
1546
|
+
sending.add(qid);
|
|
1547
|
+
try {
|
|
1548
|
+
let refused;
|
|
1549
|
+
try {
|
|
1550
|
+
refused = await d.replyTo(r.id, r.text);
|
|
1551
|
+
} catch (e) {
|
|
1552
|
+
refused = e.message;
|
|
1553
|
+
}
|
|
1554
|
+
if (refused) {
|
|
1555
|
+
log(`relay ${qid} to ${r.id} failed: ${refused}`);
|
|
1556
|
+
d.chat.markRelay(qid, { error: refused }, d.broadcast);
|
|
1557
|
+
} else d.chat.markRelay(qid, { status: "sent", sentAt: now() }, d.broadcast);
|
|
1558
|
+
return refused;
|
|
1559
|
+
} finally {
|
|
1560
|
+
typing.delete(r.id);
|
|
1561
|
+
sending.delete(qid);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
function kick(id) {
|
|
1565
|
+
const q = d.chat.queuedRelays(d.broadcast).find((x) => x.relay.id === id);
|
|
1566
|
+
if (!q || typing.has(id) || !free(d.store.get(id))) return;
|
|
1567
|
+
void deliverAndKick(q.qid, q.relay).catch((e) => log(`relay ${q.qid} failed: ${e.message}`));
|
|
1568
|
+
}
|
|
1569
|
+
async function deliverAndKick(qid, r) {
|
|
1570
|
+
const refused = await deliver(qid, r);
|
|
1571
|
+
if (refused) kick(r.id);
|
|
1572
|
+
return refused;
|
|
1573
|
+
}
|
|
1574
|
+
const unsubscribe = d.store.onChange((c) => {
|
|
1575
|
+
if (c.type === "recents") return;
|
|
1576
|
+
const id = c.type === "updated" ? c.session.id : c.id;
|
|
1577
|
+
const queued = d.chat.queuedRelays(d.broadcast).filter((q) => q.relay.id === id);
|
|
1578
|
+
if (queued.length === 0) return;
|
|
1579
|
+
const s = c.type === "updated" ? c.session : void 0;
|
|
1580
|
+
if (!s || s.state === "exited") {
|
|
1581
|
+
for (const q of queued) if (!sending.has(q.qid)) d.chat.markRelay(q.qid, { error: stopped(q.relay.name) }, d.broadcast);
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
if (free(s) && !typing.has(id)) kick(id);
|
|
1585
|
+
});
|
|
1586
|
+
return {
|
|
1587
|
+
async request(qid) {
|
|
1588
|
+
const r = d.chat.relay(qid);
|
|
1589
|
+
if (!r) throw new Error("unknown relay");
|
|
1590
|
+
if (r.status === "sent" || sending.has(qid)) throw new Error("already sent");
|
|
1591
|
+
if (r.status === "queued") throw new Error("already queued");
|
|
1592
|
+
const s = d.store.get(r.id);
|
|
1593
|
+
if (!s || s.state === "exited") {
|
|
1594
|
+
const m = stopped(r.name);
|
|
1595
|
+
d.chat.markRelay(qid, { error: m }, d.broadcast);
|
|
1596
|
+
throw new Error(m);
|
|
1597
|
+
}
|
|
1598
|
+
if (free(s) && !typing.has(r.id)) {
|
|
1599
|
+
if (d.chat.queuedRelays().some((q) => q.relay.id === r.id)) {
|
|
1600
|
+
d.chat.markRelay(qid, { status: "queued" }, d.broadcast);
|
|
1601
|
+
kick(r.id);
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
const refused = await deliverAndKick(qid, r);
|
|
1605
|
+
if (refused) throw new Error(refused);
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
d.chat.markRelay(qid, { status: "queued" }, d.broadcast);
|
|
1609
|
+
},
|
|
1610
|
+
cancel(qid) {
|
|
1611
|
+
if (sending.has(qid)) throw new Error("already sent");
|
|
1612
|
+
if (d.chat.relay(qid)?.status !== "queued") throw new Error("nothing queued");
|
|
1613
|
+
d.chat.markRelay(qid, {}, d.broadcast);
|
|
1614
|
+
},
|
|
1615
|
+
close: unsubscribe
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1450
1619
|
// src/daemon/server.ts
|
|
1451
1620
|
var CONTINUE_ARGS = { claude: ["--continue"], codex: ["resume", "--last"], cursor: ["--continue"] };
|
|
1452
1621
|
function launchArgs(msg) {
|
|
@@ -1476,7 +1645,7 @@ async function startServer(opts) {
|
|
|
1476
1645
|
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
1477
1646
|
const launcher = opts.launcher ?? [process.execPath, process.argv[1] ?? "ledge"];
|
|
1478
1647
|
const agentEnv = opts.agentEnv ?? (async () => ({ ...process.env, ...await loginShellEnv() }));
|
|
1479
|
-
const chat = opts.chat ?? createChat({ store, tmux, home: paths().dir, env: agentEnv, bin: opts.claudeBin, now });
|
|
1648
|
+
const chat = opts.chat ?? createChat({ store, tmux, home: paths().dir, userHome: opts.home, env: agentEnv, bin: opts.claudeBin, now });
|
|
1480
1649
|
const wss = new WebSocketServer({
|
|
1481
1650
|
host: opts.host ?? "127.0.0.1",
|
|
1482
1651
|
port: opts.port ?? PROTOCOL_PORT,
|
|
@@ -1497,6 +1666,7 @@ async function startServer(opts) {
|
|
|
1497
1666
|
const broadcast = (msg) => {
|
|
1498
1667
|
for (const c of wss.clients) send(c, msg);
|
|
1499
1668
|
};
|
|
1669
|
+
const relay = createRelay({ chat, store, replyTo, broadcast, now });
|
|
1500
1670
|
const previews = /* @__PURE__ */ new Map();
|
|
1501
1671
|
const titles = /* @__PURE__ */ new Map();
|
|
1502
1672
|
const unsubscribe = store.onChange((c) => {
|
|
@@ -1582,6 +1752,29 @@ async function startServer(opts) {
|
|
|
1582
1752
|
const NO_PROMPT = "no prompt visible in the terminal, open the tab";
|
|
1583
1753
|
const NOT_TAKEN = "the terminal did not take it";
|
|
1584
1754
|
const FREE_TEXT = "free-text answers are not supported yet, answer in the terminal";
|
|
1755
|
+
async function replyTo(id, text) {
|
|
1756
|
+
const s = store.get(id);
|
|
1757
|
+
if (!s) return "unknown session";
|
|
1758
|
+
if (s.source === "hook_only") return READ_ONLY;
|
|
1759
|
+
if (s.pending && s.pending.kind !== "question") return `a ${s.pending.kind} prompt takes accept or deny, not a reply`;
|
|
1760
|
+
if (text.trim() === "") return "empty reply";
|
|
1761
|
+
if (s.pending?.kind === "question" && !/^[1-9]$/.test(text.trim())) return FREE_TEXT;
|
|
1762
|
+
const pending = s.pending;
|
|
1763
|
+
const adapter = adapterFor(s.tool);
|
|
1764
|
+
await injectAndVerify(id, (pane) => adapter.replyPlan(text, pending, pane || void 0), !!pending);
|
|
1765
|
+
return null;
|
|
1766
|
+
}
|
|
1767
|
+
const chatLaunches = /* @__PURE__ */ new Set();
|
|
1768
|
+
async function launch(req) {
|
|
1769
|
+
const cwd = await resolveFolder(req.cwd, opts.home);
|
|
1770
|
+
const live = store.all();
|
|
1771
|
+
const env = await agentEnv();
|
|
1772
|
+
const session = await spawnSession({ tool: req.tool, args: launchArgs(req), name: req.name, cwd, tmux, now, env, taken: { names: live.filter((s) => s.state !== "exited").map((s) => s.name), ids: live.map((s) => s.id) } });
|
|
1773
|
+
store.upsert(session);
|
|
1774
|
+
store.touchRecent(session.cwd, session.project, session.startedAt);
|
|
1775
|
+
log(`launched ${session.name} (${session.tool}) in ${session.cwd} as ${session.id}`);
|
|
1776
|
+
return session;
|
|
1777
|
+
}
|
|
1585
1778
|
async function handle(ws, msg) {
|
|
1586
1779
|
switch (msg.type) {
|
|
1587
1780
|
case "document.read": {
|
|
@@ -1618,14 +1811,23 @@ async function startServer(opts) {
|
|
|
1618
1811
|
return;
|
|
1619
1812
|
}
|
|
1620
1813
|
case "session.launch": {
|
|
1621
|
-
|
|
1622
|
-
const
|
|
1623
|
-
const
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1814
|
+
if (msg.qid === void 0) return send(ws, { type: "session.launched", id: (await launch(msg)).id });
|
|
1815
|
+
const qid = msg.qid;
|
|
1816
|
+
const p = chat.proposal(qid);
|
|
1817
|
+
if (!p) return send(ws, { type: "error", message: "unknown proposal" });
|
|
1818
|
+
if (p.launchedId || chatLaunches.has(qid)) return send(ws, { type: "error", message: "already launched" });
|
|
1819
|
+
chatLaunches.add(qid);
|
|
1820
|
+
try {
|
|
1821
|
+
const session = await launch({ tool: p.tool, cwd: p.cwd, prompt: p.prompt });
|
|
1822
|
+
send(ws, { type: "session.launched", id: session.id });
|
|
1823
|
+
chat.markLaunched(qid, session.id, broadcast);
|
|
1824
|
+
} catch (e) {
|
|
1825
|
+
chat.markFailed(qid, e.message, broadcast);
|
|
1826
|
+
throw e;
|
|
1827
|
+
} finally {
|
|
1828
|
+
chatLaunches.delete(qid);
|
|
1829
|
+
}
|
|
1830
|
+
return;
|
|
1629
1831
|
}
|
|
1630
1832
|
case "folder.list":
|
|
1631
1833
|
return send(ws, await listFolder(msg.path, opts.home));
|
|
@@ -1639,7 +1841,7 @@ async function startServer(opts) {
|
|
|
1639
1841
|
if (!before) {
|
|
1640
1842
|
const taken = store.all().map((s) => s.name);
|
|
1641
1843
|
const cwd = msg.cwd ?? "";
|
|
1642
|
-
const base = { id: msg.id, name: pickName(taken), tool: msg.agent ?? "claude", cwd, project:
|
|
1844
|
+
const base = { id: msg.id, name: pickName(taken), tool: msg.agent ?? "claude", cwd, project: basename4(cwd) || "unknown", title: "untracked session", tmuxSession: null, state: "working", helpers: [], startedAt: msg.event.at, lastActivityAt: msg.event.at, source: "hook_only" };
|
|
1643
1845
|
store.upsert(reduce(base, msg.event));
|
|
1644
1846
|
} else store.apply(msg.id, msg.event);
|
|
1645
1847
|
logDroppedRequest(msg.id, msg.event, before, store.get(msg.id));
|
|
@@ -1663,15 +1865,9 @@ async function startServer(opts) {
|
|
|
1663
1865
|
return injectAndVerify(msg.id, (pane) => adapter.keysFor(msg.decision, pending, pane), true);
|
|
1664
1866
|
}
|
|
1665
1867
|
case "session.reply": {
|
|
1666
|
-
const
|
|
1667
|
-
if (
|
|
1668
|
-
|
|
1669
|
-
if (s.pending && s.pending.kind !== "question") return send(ws, { type: "error", id: msg.id, message: `a ${s.pending.kind} prompt takes accept or deny, not a reply` });
|
|
1670
|
-
if (msg.text.trim() === "") return send(ws, { type: "error", id: msg.id, message: "empty reply" });
|
|
1671
|
-
if (s.pending?.kind === "question" && !/^[1-9]$/.test(msg.text.trim())) return send(ws, { type: "error", id: msg.id, message: FREE_TEXT });
|
|
1672
|
-
const pending = s.pending;
|
|
1673
|
-
const adapter = adapterFor(s.tool);
|
|
1674
|
-
return injectAndVerify(msg.id, (pane) => adapter.replyPlan(msg.text, pending, pane || void 0), !!pending);
|
|
1868
|
+
const refused = await replyTo(msg.id, msg.text);
|
|
1869
|
+
if (refused) send(ws, { type: "error", id: msg.id, message: refused });
|
|
1870
|
+
return;
|
|
1675
1871
|
}
|
|
1676
1872
|
case "session.interrupt": {
|
|
1677
1873
|
const s = store.get(msg.id);
|
|
@@ -1695,6 +1891,11 @@ async function startServer(opts) {
|
|
|
1695
1891
|
store.remove(msg.id);
|
|
1696
1892
|
return;
|
|
1697
1893
|
}
|
|
1894
|
+
// A chat card's instruction: a refusal reaches the requester as an `error`, the card's state everyone through chat.history.
|
|
1895
|
+
case "session.relay":
|
|
1896
|
+
return relay.request(msg.qid);
|
|
1897
|
+
case "session.relay.cancel":
|
|
1898
|
+
return relay.cancel(msg.qid);
|
|
1698
1899
|
// Broadcast: every overlay shows the same progress, and the one that asked may be the one that reconnects.
|
|
1699
1900
|
// A busy refusal goes to the asker only: it must not fail the flow the other overlays follow.
|
|
1700
1901
|
case "update.daemon":
|
|
@@ -1734,6 +1935,7 @@ async function startServer(opts) {
|
|
|
1734
1935
|
preview,
|
|
1735
1936
|
close: () => new Promise((res) => {
|
|
1736
1937
|
unsubscribe();
|
|
1938
|
+
relay.close();
|
|
1737
1939
|
for (const c of wss.clients) c.terminate();
|
|
1738
1940
|
wss.close(() => res());
|
|
1739
1941
|
})
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
QUESTION_TOOLS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5OJZV5MV.js";
|
|
4
4
|
import {
|
|
5
5
|
CODEX_ENV_VAR,
|
|
6
6
|
CODEX_HOOK_VAR,
|
|
7
7
|
log
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-B73JDPSW.js";
|
|
9
9
|
import "./chunk-6ZTCETRG.js";
|
|
10
10
|
import {
|
|
11
11
|
connect
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-34DHZSGH.js";
|
|
13
|
+
import "./chunk-YT4T7DL2.js";
|
|
14
14
|
|
|
15
15
|
// src/cli/hook.ts
|
|
16
16
|
import { readFileSync, openSync, fstatSync, readSync, closeSync } from "fs";
|
package/dist/cli/index.js
CHANGED
|
@@ -15,7 +15,7 @@ async function hookMain(args) {
|
|
|
15
15
|
try {
|
|
16
16
|
const chunks = [];
|
|
17
17
|
for await (const c of process.stdin) chunks.push(c);
|
|
18
|
-
const { runHook } = await import("./hook-
|
|
18
|
+
const { runHook } = await import("./hook-QH44D3JC.js");
|
|
19
19
|
await runHook(event, Buffer.concat(chunks).toString("utf8"), { tool });
|
|
20
20
|
} catch {
|
|
21
21
|
}
|
|
@@ -25,7 +25,7 @@ async function main(argv) {
|
|
|
25
25
|
const [cmd, ...rest] = argv;
|
|
26
26
|
switch (cmd) {
|
|
27
27
|
case "daemon": {
|
|
28
|
-
const { startDaemon } = await import("./daemon-
|
|
28
|
+
const { startDaemon } = await import("./daemon-2KHF4XUC.js");
|
|
29
29
|
await startDaemon();
|
|
30
30
|
await new Promise(() => {
|
|
31
31
|
});
|
|
@@ -33,7 +33,7 @@ async function main(argv) {
|
|
|
33
33
|
}
|
|
34
34
|
case "--version":
|
|
35
35
|
case "version": {
|
|
36
|
-
const { LEDGE_VERSION } = await import("./version-
|
|
36
|
+
const { LEDGE_VERSION } = await import("./version-J6BW646U.js");
|
|
37
37
|
console.log(LEDGE_VERSION);
|
|
38
38
|
return 0;
|
|
39
39
|
}
|
|
@@ -49,18 +49,18 @@ async function main(argv) {
|
|
|
49
49
|
return r.ok ? 0 : 1;
|
|
50
50
|
}
|
|
51
51
|
case "status": {
|
|
52
|
-
const { printStatus } = await import("./status-
|
|
52
|
+
const { printStatus } = await import("./status-E2VZMTAF.js");
|
|
53
53
|
console.log(await printStatus());
|
|
54
54
|
return 0;
|
|
55
55
|
}
|
|
56
56
|
case "claude":
|
|
57
57
|
case "codex":
|
|
58
58
|
case "cursor": {
|
|
59
|
-
const { launch } = await import("./launch-
|
|
59
|
+
const { launch } = await import("./launch-CPNM5RX7.js");
|
|
60
60
|
return launch(cmd, rest);
|
|
61
61
|
}
|
|
62
62
|
case "attach": {
|
|
63
|
-
const { attach } = await import("./attach-
|
|
63
|
+
const { attach } = await import("./attach-IKK5XQGC.js");
|
|
64
64
|
return attach(rest[0] ?? "");
|
|
65
65
|
}
|
|
66
66
|
default:
|
|
@@ -5,15 +5,15 @@ import {
|
|
|
5
5
|
ensureDaemon,
|
|
6
6
|
launch,
|
|
7
7
|
parseLaunchArgs
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-ZPR4LCDM.js";
|
|
9
|
+
import "./chunk-DMV5VW4Q.js";
|
|
10
10
|
import {
|
|
11
11
|
agentEnv,
|
|
12
12
|
exitFile
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-B73JDPSW.js";
|
|
14
14
|
import "./chunk-6ZTCETRG.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-34DHZSGH.js";
|
|
16
|
+
import "./chunk-YT4T7DL2.js";
|
|
17
17
|
export {
|
|
18
18
|
DaemonError,
|
|
19
19
|
agentEnv,
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paradow/ledge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Follow and answer your WSL agent sessions (Claude Code, Codex, Cursor Agent) from the Ledge island on Windows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": { "ledge": "dist/cli/index.js" },
|
|
7
7
|
"files": ["dist"],
|
|
8
8
|
"engines": { "node": ">=22" },
|
|
9
|
-
"repository": { "type": "git", "url": "git+https://github.com/
|
|
10
|
-
"homepage": "https://github.com/
|
|
9
|
+
"repository": { "type": "git", "url": "git+https://github.com/Lyssaen/ledge.git", "directory": "packages/ledge" },
|
|
10
|
+
"homepage": "https://github.com/Lyssaen/ledge#readme",
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
12
|
"publishConfig": { "access": "public", "provenance": true },
|
|
13
13
|
"scripts": {
|