agent-yes 1.193.0 → 1.195.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/{SUPPORTED_CLIS-eK4A0Nql.js → SUPPORTED_CLIS-Bdj9qDRm.js} +2 -2
- package/dist/SUPPORTED_CLIS-Daig8XHX.js +8 -0
- package/dist/{agentShare-C2PSaiaa.js → agentShare-M2jaTMlH.js} +2 -2
- package/dist/cli.js +4 -4
- package/dist/index.js +2 -2
- package/dist/{notifyDaemon-pJJzfvcJ.js → notifyDaemon-D5HwUMxH.js} +2 -2
- package/dist/{rustBinary-BEEvKYm_.js → rustBinary-B4pfibDJ.js} +2 -2
- package/dist/{schedule-BuWv8206.js → schedule-1oS-Yxxa.js} +4 -4
- package/dist/{serve-BrCvRObJ.js → serve-Ce708ALB.js} +44 -13
- package/dist/{setup-CL-giduA.js → setup-fTLT9Bf2.js} +2 -2
- package/dist/subcommands-CV5gqRKq.js +9 -0
- package/dist/{subcommands-CYAk7q_c.js → subcommands-CqB9zo8_.js} +305 -12
- package/dist/{ts-Cg0IpitM.js → ts-DDsICrj-.js} +2 -2
- package/dist/{versionChecker-C_ztEPMs.js → versionChecker-B8qEzYDu.js} +2 -2
- package/lab/ui/index.html +348 -4
- package/lab/ui/sw.js +101 -19
- package/package.json +1 -1
- package/ts/messageLog.spec.ts +145 -0
- package/ts/messageLog.ts +147 -0
- package/ts/serve.ts +60 -8
- package/ts/subcommands.ts +270 -5
- package/dist/SUPPORTED_CLIS-wAOAd_rx.js +0 -8
- package/dist/subcommands-B9-HhxcS.js +0 -9
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { mkdtemp, readFile, rm } from "fs/promises";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import {
|
|
6
|
+
mailboxPath,
|
|
7
|
+
partyMatches,
|
|
8
|
+
readMailbox,
|
|
9
|
+
recordInbox,
|
|
10
|
+
recordMessage,
|
|
11
|
+
recordOutbox,
|
|
12
|
+
type MessageRecord,
|
|
13
|
+
} from "./messageLog.ts";
|
|
14
|
+
|
|
15
|
+
function makeRecord(over: Partial<MessageRecord> = {}): MessageRecord {
|
|
16
|
+
return {
|
|
17
|
+
at: 1_000,
|
|
18
|
+
nonce: "abcd",
|
|
19
|
+
from: { pid: 11, cli: "claude", cwd: "/from", agent_id: "agent-A" },
|
|
20
|
+
to: { pid: 22, cli: "codex", cwd: "/to", agent_id: "agent-B" },
|
|
21
|
+
body: "hello",
|
|
22
|
+
confirmed: true,
|
|
23
|
+
wrapped: true,
|
|
24
|
+
...over,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("messageLog", () => {
|
|
29
|
+
let dir: string;
|
|
30
|
+
let prevCwd: string;
|
|
31
|
+
|
|
32
|
+
beforeEach(async () => {
|
|
33
|
+
dir = await mkdtemp(path.join(tmpdir(), "msglog-"));
|
|
34
|
+
prevCwd = process.cwd();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
afterEach(async () => {
|
|
38
|
+
process.chdir(prevCwd);
|
|
39
|
+
await rm(dir, { recursive: true, force: true });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("mailboxPath colocates under <cwd>/.agent-yes", () => {
|
|
43
|
+
expect(mailboxPath("/x", "inbox")).toBe(path.join("/x", ".agent-yes", "inbox.jsonl"));
|
|
44
|
+
expect(mailboxPath("/x", "outbox")).toBe(path.join("/x", ".agent-yes", "outbox.jsonl"));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("records to sender outbox and recipient inbox", async () => {
|
|
48
|
+
const from = path.join(dir, "sender");
|
|
49
|
+
const to = path.join(dir, "recipient");
|
|
50
|
+
const rec = makeRecord({
|
|
51
|
+
from: { pid: 11, cli: "claude", cwd: from, agent_id: "A" },
|
|
52
|
+
to: { pid: 22, cli: "codex", cwd: to, agent_id: "B" },
|
|
53
|
+
});
|
|
54
|
+
await recordMessage(rec);
|
|
55
|
+
|
|
56
|
+
const outbox = await readMailbox(from, "outbox");
|
|
57
|
+
const inbox = await readMailbox(to, "inbox");
|
|
58
|
+
expect(outbox).toHaveLength(1);
|
|
59
|
+
expect(inbox).toHaveLength(1);
|
|
60
|
+
expect(outbox[0]!.body).toBe("hello");
|
|
61
|
+
expect(inbox[0]!.to.agent_id).toBe("B");
|
|
62
|
+
// The sender's inbox and recipient's outbox stay empty.
|
|
63
|
+
expect(await readMailbox(from, "inbox")).toHaveLength(0);
|
|
64
|
+
expect(await readMailbox(to, "outbox")).toHaveLength(0);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("writes a human sender's outbox under process.cwd()", async () => {
|
|
68
|
+
process.chdir(dir);
|
|
69
|
+
const to = path.join(dir, "recipient");
|
|
70
|
+
await recordMessage(makeRecord({ from: null, to: { pid: 22, cli: "codex", cwd: to } }));
|
|
71
|
+
const outbox = await readMailbox(dir, "outbox");
|
|
72
|
+
expect(outbox).toHaveLength(1);
|
|
73
|
+
expect(outbox[0]!.from).toBeNull();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("readMailbox skips corrupt lines and returns empty for a missing file", async () => {
|
|
77
|
+
expect(await readMailbox(dir, "inbox")).toEqual([]);
|
|
78
|
+
const from = path.join(dir, "s");
|
|
79
|
+
await recordMessage(makeRecord({ from: { pid: 1, cli: "c", cwd: from } }));
|
|
80
|
+
// Corrupt the file with a partial line; the good record still parses.
|
|
81
|
+
const p = mailboxPath(from, "outbox");
|
|
82
|
+
const raw = await readFile(p, "utf-8");
|
|
83
|
+
const { appendFile } = await import("fs/promises");
|
|
84
|
+
await appendFile(p, "{ not json\n");
|
|
85
|
+
expect(raw.trim().split("\n")).toHaveLength(1);
|
|
86
|
+
expect(await readMailbox(from, "outbox")).toHaveLength(1);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("recordOutbox writes only the sender's outbox (remote peer's cwd untouched)", async () => {
|
|
90
|
+
const from = path.join(dir, "local");
|
|
91
|
+
const to = path.join(dir, "remote");
|
|
92
|
+
await recordOutbox(
|
|
93
|
+
makeRecord({
|
|
94
|
+
from: { pid: 1, cli: "claude", cwd: from, agent_id: "A" },
|
|
95
|
+
to: { pid: 2, cli: "codex", cwd: to, agent_id: "B" },
|
|
96
|
+
remote: "http://host:8080",
|
|
97
|
+
wrapped: false,
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
100
|
+
expect(await readMailbox(from, "outbox")).toHaveLength(1);
|
|
101
|
+
// The remote peer's cwd is on another host — nothing is written there.
|
|
102
|
+
expect(await readMailbox(to, "inbox")).toHaveLength(0);
|
|
103
|
+
expect((await readMailbox(from, "outbox"))[0]!.remote).toBe("http://host:8080");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("recordInbox writes only the recipient's inbox (remote sender's cwd untouched)", async () => {
|
|
107
|
+
const from = path.join(dir, "remote-sender");
|
|
108
|
+
const to = path.join(dir, "local-recipient");
|
|
109
|
+
await recordInbox(
|
|
110
|
+
makeRecord({
|
|
111
|
+
from: { pid: 1, cli: "claude", cwd: from, agent_id: "A" },
|
|
112
|
+
to: { pid: 2, cli: "codex", cwd: to, agent_id: "B" },
|
|
113
|
+
remote: "wire",
|
|
114
|
+
wrapped: false,
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
expect(await readMailbox(to, "inbox")).toHaveLength(1);
|
|
118
|
+
expect(await readMailbox(from, "outbox")).toHaveLength(0);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("preserves the kind tag for key/select events", async () => {
|
|
122
|
+
const from = path.join(dir, "kfrom");
|
|
123
|
+
const to = path.join(dir, "kto");
|
|
124
|
+
await recordMessage(
|
|
125
|
+
makeRecord({
|
|
126
|
+
from: { pid: 1, cli: "bash", cwd: from, agent_id: "A" },
|
|
127
|
+
to: { pid: 2, cli: "bash", cwd: to, agent_id: "B" },
|
|
128
|
+
kind: "key",
|
|
129
|
+
body: "down down enter",
|
|
130
|
+
wrapped: false,
|
|
131
|
+
}),
|
|
132
|
+
);
|
|
133
|
+
const rec = (await readMailbox(to, "inbox"))[0]!;
|
|
134
|
+
expect(rec.kind).toBe("key");
|
|
135
|
+
expect(rec.body).toBe("down down enter");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("partyMatches prefers agent_id, falls back to pid", () => {
|
|
139
|
+
const party = { pid: 5, cli: "c", cwd: "/x", agent_id: "stable" };
|
|
140
|
+
expect(partyMatches(party, "stable", 999)).toBe(true); // agent_id wins across pid churn
|
|
141
|
+
expect(partyMatches(party, "other", 5)).toBe(true); // pid fallback
|
|
142
|
+
expect(partyMatches(party, "other", 6)).toBe(false);
|
|
143
|
+
expect(partyMatches(null, "stable", 5)).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
});
|
package/ts/messageLog.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable inter-agent message log.
|
|
3
|
+
*
|
|
4
|
+
* Every `ay send` that carries a real body is recorded twice — from the two
|
|
5
|
+
* ends' points of view — as append-only JSONL colocated with each agent's
|
|
6
|
+
* project dir (the same `<cwd>/.agent-yes/` convention the session logs use):
|
|
7
|
+
*
|
|
8
|
+
* - the SENDER's `<from.cwd>/.agent-yes/outbox.jsonl`
|
|
9
|
+
* - the RECIPIENT's `<to.cwd>/.agent-yes/inbox.jsonl`
|
|
10
|
+
*
|
|
11
|
+
* A single cwd may host several agents, so records carry the stable `agent_id`
|
|
12
|
+
* (falling back to `pid`) of each end; `ay msgs` filters a mailbox down to one
|
|
13
|
+
* agent by that key. Reading needs no lock (last line wins isn't relevant — a
|
|
14
|
+
* message log keeps every entry); writing is best-effort and never blocks or
|
|
15
|
+
* fails a send.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { appendFile, mkdir, readFile, writeFile } from "fs/promises";
|
|
19
|
+
import path from "path";
|
|
20
|
+
import { logger } from "./logger.ts";
|
|
21
|
+
|
|
22
|
+
/** One end of a message: enough to attribute and to route a reply. */
|
|
23
|
+
export interface MailParty {
|
|
24
|
+
pid: number;
|
|
25
|
+
cli: string;
|
|
26
|
+
cwd: string;
|
|
27
|
+
agent_id?: string | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** A single delivered inter-agent message, stored verbatim in both mailboxes. */
|
|
31
|
+
export interface MessageRecord {
|
|
32
|
+
/** Epoch ms the send was recorded. */
|
|
33
|
+
at: number;
|
|
34
|
+
/** The per-send nonce from the `[ay-msg …]` wrapper, when the body was wrapped. */
|
|
35
|
+
nonce?: string;
|
|
36
|
+
/** Sender; `null` when an interactive human shell sent it (no agent context). */
|
|
37
|
+
from: MailParty | null;
|
|
38
|
+
/** Recipient agent. */
|
|
39
|
+
to: MailParty;
|
|
40
|
+
/** What kind of stdin write this was. Omitted for a normal `ay send` text
|
|
41
|
+
* message; "key" for raw keystrokes (`ay key`), "select" for a menu pick
|
|
42
|
+
* (`ay select`). For key/select the `body` holds the key names / choice. */
|
|
43
|
+
kind?: "key" | "select";
|
|
44
|
+
/** The message body (without the `[ay-msg …]` wrapper), or — for a key/select
|
|
45
|
+
* record — the keystroke names / chosen option. */
|
|
46
|
+
body: string;
|
|
47
|
+
/** Trailing control code name (e.g. "enter", "ctrl-c") when not a plain submit. */
|
|
48
|
+
code?: string;
|
|
49
|
+
/** Whether `ay send` confirmed the CLI acted on it. */
|
|
50
|
+
confirmed?: boolean;
|
|
51
|
+
/** Whether the body was wrapped in an `[ay-msg …]` attribution block. */
|
|
52
|
+
wrapped: boolean;
|
|
53
|
+
/** The remote url/alias when this message crossed the wire (`ay send <remote>:<kw>`);
|
|
54
|
+
* absent for a same-host send. The two ends record their own mailbox on their
|
|
55
|
+
* own machine, so this marks that the peer's cwd is on another host. */
|
|
56
|
+
remote?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Keep at most this many lines per mailbox; older entries are compacted away. */
|
|
60
|
+
const MAILBOX_MAX_LINES = 2000;
|
|
61
|
+
|
|
62
|
+
export type Mailbox = "inbox" | "outbox";
|
|
63
|
+
|
|
64
|
+
/** Path to a cwd's mailbox file (`<cwd>/.agent-yes/{inbox,outbox}.jsonl`). */
|
|
65
|
+
export function mailboxPath(cwd: string, box: Mailbox): string {
|
|
66
|
+
return path.join(cwd, ".agent-yes", `${box}.jsonl`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Whether a mail party is the agent identified by (agentId, pid). Prefers the
|
|
70
|
+
* stable agent_id (survives restart); falls back to pid for legacy records. */
|
|
71
|
+
export function partyMatches(
|
|
72
|
+
party: MailParty | null,
|
|
73
|
+
agentId: string | null | undefined,
|
|
74
|
+
pid: number | null | undefined,
|
|
75
|
+
): boolean {
|
|
76
|
+
if (!party) return false;
|
|
77
|
+
if (agentId && party.agent_id && party.agent_id === agentId) return true;
|
|
78
|
+
if (typeof pid === "number" && party.pid === pid) return true;
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function appendCapped(filePath: string, record: MessageRecord): Promise<void> {
|
|
83
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
84
|
+
await appendFile(filePath, JSON.stringify(record) + "\n");
|
|
85
|
+
// Opportunistic compaction: keep the file bounded despite append-only writes.
|
|
86
|
+
const raw = await readFile(filePath, "utf-8").catch(() => "");
|
|
87
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
88
|
+
if (lines.length > MAILBOX_MAX_LINES) {
|
|
89
|
+
const kept = lines.slice(lines.length - MAILBOX_MAX_LINES).join("\n");
|
|
90
|
+
await writeFile(filePath, kept + "\n");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Record the SENDER's view in its outbox. Best-effort — a filesystem error is
|
|
96
|
+
* logged and swallowed so persistence never breaks a send. The outbox lives
|
|
97
|
+
* under `from.cwd`; a human sender (from === null) writes under `process.cwd()`.
|
|
98
|
+
*/
|
|
99
|
+
export async function recordOutbox(record: MessageRecord): Promise<void> {
|
|
100
|
+
const outCwd = record.from?.cwd ?? process.cwd();
|
|
101
|
+
try {
|
|
102
|
+
await appendCapped(mailboxPath(outCwd, "outbox"), record);
|
|
103
|
+
} catch (err) {
|
|
104
|
+
logger.debug(`[messageLog] outbox append failed: ${err}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Record the RECIPIENT's view in its inbox (under `to.cwd`). Best-effort. */
|
|
109
|
+
export async function recordInbox(record: MessageRecord): Promise<void> {
|
|
110
|
+
try {
|
|
111
|
+
await appendCapped(mailboxPath(record.to.cwd, "inbox"), record);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
logger.debug(`[messageLog] inbox append failed: ${err}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Record a same-host message in both mailboxes — the sender's outbox and the
|
|
119
|
+
* recipient's inbox both live on this machine. For a message that crossed the
|
|
120
|
+
* wire, each end calls `recordOutbox`/`recordInbox` on its own host instead.
|
|
121
|
+
*/
|
|
122
|
+
export async function recordMessage(record: MessageRecord): Promise<void> {
|
|
123
|
+
await recordOutbox(record);
|
|
124
|
+
await recordInbox(record);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Read and parse a cwd's mailbox, oldest first. Missing/corrupt lines skipped. */
|
|
128
|
+
export async function readMailbox(cwd: string, box: Mailbox): Promise<MessageRecord[]> {
|
|
129
|
+
let raw: string;
|
|
130
|
+
try {
|
|
131
|
+
raw = await readFile(mailboxPath(cwd, box), "utf-8");
|
|
132
|
+
} catch {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
const out: MessageRecord[] = [];
|
|
136
|
+
for (const line of raw.split("\n")) {
|
|
137
|
+
const t = line.trim();
|
|
138
|
+
if (!t) continue;
|
|
139
|
+
try {
|
|
140
|
+
const rec = JSON.parse(t) as MessageRecord;
|
|
141
|
+
if (rec && typeof rec.at === "number" && rec.to) out.push(rec);
|
|
142
|
+
} catch {
|
|
143
|
+
/* skip corrupt/partial line */
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
package/ts/serve.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
listRecords,
|
|
17
17
|
readNotes,
|
|
18
18
|
readPtysize,
|
|
19
|
+
recentMessageEdges,
|
|
19
20
|
recentReadEdges,
|
|
20
21
|
renderLogTailLines,
|
|
21
22
|
renderRawLog,
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
} from "./subcommands.ts";
|
|
27
28
|
import { TYPING_BADGE } from "./badges.ts";
|
|
28
29
|
import { ensureNodeRuntime, liveEnv } from "./nodeRuntime.ts";
|
|
30
|
+
import { type MailParty, recordInbox } from "./messageLog.ts";
|
|
29
31
|
import { updateGlobalPidStatus } from "./globalPidIndex.ts";
|
|
30
32
|
import { spawnRejectionReason } from "./spawnGate.ts";
|
|
31
33
|
import { findSpawnHiddenLauncher } from "./rustBinary.ts";
|
|
@@ -1723,12 +1725,16 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
1723
1725
|
});
|
|
1724
1726
|
}
|
|
1725
1727
|
|
|
1726
|
-
// GET /api/edges — recent inter-agent relationship edges for the /rgui
|
|
1727
|
-
// view.
|
|
1728
|
-
//
|
|
1728
|
+
// GET /api/edges — recent inter-agent relationship edges for the /rgui + /w
|
|
1729
|
+
// wire view. Two directional, ephemeral edge sets:
|
|
1730
|
+
// reads — agent `by` read/tailed agent `target` (from ~/.agent-yes/reads.jsonl)
|
|
1731
|
+
// sends — agent `by` sent `target` a message/key/select (from the per-cwd
|
|
1732
|
+
// outbox logs); carries the send `kind` so the UI can style it.
|
|
1733
|
+
// Both are last-minute windows the client fades out by `at`.
|
|
1729
1734
|
if (req.method === "GET" && p === "/api/edges") {
|
|
1730
1735
|
try {
|
|
1731
|
-
|
|
1736
|
+
const [reads, sends] = await Promise.all([recentReadEdges(), recentMessageEdges()]);
|
|
1737
|
+
return Response.json({ reads, sends });
|
|
1732
1738
|
} catch (e) {
|
|
1733
1739
|
return new Response((e as Error).message, { status: 500 });
|
|
1734
1740
|
}
|
|
@@ -2199,15 +2205,20 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
2199
2205
|
}
|
|
2200
2206
|
}
|
|
2201
2207
|
|
|
2202
|
-
// POST /api/send body: {keyword, msg, code?}
|
|
2208
|
+
// POST /api/send body: {keyword, msg, code?, from?}
|
|
2203
2209
|
if (req.method === "POST" && p === "/api/send") {
|
|
2204
|
-
let body: {
|
|
2210
|
+
let body: {
|
|
2211
|
+
keyword: string;
|
|
2212
|
+
msg: string;
|
|
2213
|
+
code?: string;
|
|
2214
|
+
from?: MailParty | null;
|
|
2215
|
+
};
|
|
2205
2216
|
try {
|
|
2206
2217
|
body = (await req.json()) as typeof body;
|
|
2207
2218
|
} catch {
|
|
2208
2219
|
return new Response("invalid JSON body", { status: 400 });
|
|
2209
2220
|
}
|
|
2210
|
-
const { keyword, msg = "", code = "enter" } = body;
|
|
2221
|
+
const { keyword, msg = "", code = "enter", from = null } = body;
|
|
2211
2222
|
if (!keyword || typeof keyword !== "string") {
|
|
2212
2223
|
return new Response("missing keyword", { status: 400 });
|
|
2213
2224
|
}
|
|
@@ -2228,7 +2239,43 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
2228
2239
|
// over this same wire) is protocol noise, not input — stamp anyDaemonWriteAt
|
|
2229
2240
|
// but not the "meaningful" time, so a resize/redraw can't trip the flash.
|
|
2230
2241
|
await noteStdinWrite(record.pid, record.fifo_file, !isTerminalReply(msg));
|
|
2231
|
-
|
|
2242
|
+
// Record the recipient's inbox on THIS host (the sender records its own
|
|
2243
|
+
// outbox on its host). The sender crossed the wire, so `from.cwd` names a
|
|
2244
|
+
// path on another machine — mark it remote so the peer's cwd isn't misread
|
|
2245
|
+
// as local. Only real message bodies are logged. Best-effort.
|
|
2246
|
+
if (msg) {
|
|
2247
|
+
const senderParty: MailParty | null =
|
|
2248
|
+
from && typeof from === "object" && typeof from.pid === "number"
|
|
2249
|
+
? {
|
|
2250
|
+
pid: from.pid,
|
|
2251
|
+
cli: String(from.cli ?? "?"),
|
|
2252
|
+
cwd: String(from.cwd ?? ""),
|
|
2253
|
+
agent_id: from.agent_id ?? undefined,
|
|
2254
|
+
}
|
|
2255
|
+
: null;
|
|
2256
|
+
await recordInbox({
|
|
2257
|
+
at: Date.now(),
|
|
2258
|
+
from: senderParty,
|
|
2259
|
+
to: {
|
|
2260
|
+
pid: record.pid,
|
|
2261
|
+
cli: record.cli,
|
|
2262
|
+
cwd: record.cwd,
|
|
2263
|
+
agent_id: record.agent_id,
|
|
2264
|
+
},
|
|
2265
|
+
body: msg,
|
|
2266
|
+
code: code.toLowerCase() === "enter" ? undefined : code.toLowerCase(),
|
|
2267
|
+
confirmed: true,
|
|
2268
|
+
wrapped: false,
|
|
2269
|
+
remote: senderParty ? "wire" : undefined,
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
return Response.json({
|
|
2273
|
+
ok: true,
|
|
2274
|
+
pid: record.pid,
|
|
2275
|
+
cli: record.cli,
|
|
2276
|
+
cwd: record.cwd,
|
|
2277
|
+
agentId: record.agent_id ?? undefined,
|
|
2278
|
+
});
|
|
2232
2279
|
} catch (e) {
|
|
2233
2280
|
return new Response((e as Error).message, { status: 404 });
|
|
2234
2281
|
}
|
|
@@ -2887,6 +2934,11 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
2887
2934
|
return serveUiFile("e2e.js", "text/javascript; charset=utf-8");
|
|
2888
2935
|
if (req.method === "GET" && p === "/qrcode.js")
|
|
2889
2936
|
return serveUiFile("qrcode.js", "text/javascript; charset=utf-8");
|
|
2937
|
+
// The PWA / preview Service Worker. Without it, --http never registers a SW
|
|
2938
|
+
// (so the in-console P2P preview can't proxy). Served at the console root so
|
|
2939
|
+
// its scope covers /p/<src>/<port>/*.
|
|
2940
|
+
if (req.method === "GET" && p === "/sw.js")
|
|
2941
|
+
return serveUiFile("sw.js", "text/javascript; charset=utf-8");
|
|
2890
2942
|
if (req.method === "GET" && p === "/manifest.webmanifest")
|
|
2891
2943
|
return serveUiFile("manifest.webmanifest", "application/manifest+json");
|
|
2892
2944
|
if (req.method === "GET" && p === "/icon.svg")
|