@mneme-ai/core 1.91.0 → 1.92.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.
@@ -0,0 +1,79 @@
1
+ /**
2
+ * v1.92.0 -- RAINBOW: BOOMERANG (return-pad → MCP-watched inbox).
3
+ *
4
+ * Closes the HOMUNCULUS loop without requiring the Web AI to call MCP.
5
+ *
6
+ * Editor AI (Mneme MCP) --soul--> Web AI (chatgpt/gemini)
7
+ * ^ |
8
+ * | inbox.jsonl | emits HOMUNCULUS RETURN
9
+ * | v
10
+ * Mneme MCP watcher <-- POST /return -- return-pad (page)
11
+ *
12
+ * The page (SAME-SHELL or mobile) has a textarea + Send button. User
13
+ * pastes the Web AI's full reply (must contain HOMUNCULUS RETURN). The
14
+ * page POSTs the body to /return. The HTTP server validates + appends
15
+ * one line to `.mneme/inbox/homunculus-return.jsonl`. Mneme MCP daemon
16
+ * tails this file and surfaces new entries through the supersonic
17
+ * pulse so the editor AI sees "[BOOMERANG] new return -- ingest?".
18
+ *
19
+ * Zero credentials. Zero webhook. Zero new daemon process. Just append
20
+ * + tail of a local file that MCP already watches.
21
+ */
22
+ import { type HomunculusReturn } from "../abyss/homunculus.js";
23
+ export interface BoomerangEntry {
24
+ /** Stable per-entry id (12-hex). */
25
+ id: string;
26
+ /** Wall-clock when ingested. */
27
+ receivedAt: number;
28
+ /** Where the return was posted from (lan/same-shell/tunnel). */
29
+ source: "lan" | "same-shell" | "tunnel" | "manual";
30
+ /** Raw POST body. */
31
+ raw: string;
32
+ /** SHA-256 of raw (dedup signal). */
33
+ rawSha256: string;
34
+ /** Parsed HOMUNCULUS RETURN block (null if invalid). */
35
+ parsed: HomunculusReturn | null;
36
+ /** Whether the editor AI has acknowledged this entry. */
37
+ ingested: boolean;
38
+ /** Free-form note (filled when ingested=true). */
39
+ ingestNote?: string;
40
+ }
41
+ export interface IngestResult {
42
+ ok: boolean;
43
+ id: string;
44
+ parsed: HomunculusReturn | null;
45
+ /** Reason on failure (validation/io). */
46
+ error?: string;
47
+ }
48
+ export interface BoomerangInbox {
49
+ path: string;
50
+ ingest(args: {
51
+ raw: string;
52
+ source?: BoomerangEntry["source"];
53
+ }): IngestResult;
54
+ list(): BoomerangEntry[];
55
+ pending(): BoomerangEntry[];
56
+ markIngested(ids: string[], note?: string): number;
57
+ }
58
+ /** Open or create a BOOMERANG inbox at the given JSONL path. */
59
+ export declare function openBoomerangInbox(path: string): BoomerangInbox;
60
+ /** Render a one-line summary of a pending boomerang entry, for the
61
+ * supersonic pulse. */
62
+ export declare function formatPulseLine(entry: BoomerangEntry): string;
63
+ /** Server-side helper: validate + handle a /return POST body and
64
+ * produce a JSON response payload. Caller wires this into their
65
+ * HTTP framework. */
66
+ export interface ReturnPostResult {
67
+ status: number;
68
+ body: {
69
+ ok: boolean;
70
+ id?: string;
71
+ error?: string;
72
+ };
73
+ }
74
+ export declare function handleReturnPost(args: {
75
+ inbox: BoomerangInbox;
76
+ body: string;
77
+ source: BoomerangEntry["source"];
78
+ }): ReturnPostResult;
79
+ //# sourceMappingURL=boomerang.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boomerang.d.ts","sourceRoot":"","sources":["../../src/rainbow/boomerang.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAEtF,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,MAAM,EAAE,KAAK,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACnD,qBAAqB;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAChC,yDAAyD;IACzD,QAAQ,EAAE,OAAO,CAAC;IAClB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAChC,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAA;KAAE,GAAG,YAAY,CAAC;IAC/E,IAAI,IAAI,cAAc,EAAE,CAAC;IACzB,OAAO,IAAI,cAAc,EAAE,CAAC;IAC5B,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACpD;AAUD,gEAAgE;AAChE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAyE/D;AAED;wBACwB;AACxB,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAU7D;AAED;;sBAEsB;AACtB,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACpD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAA;CAAE,GAAG,gBAAgB,CAMlI"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * v1.92.0 -- RAINBOW: BOOMERANG (return-pad → MCP-watched inbox).
3
+ *
4
+ * Closes the HOMUNCULUS loop without requiring the Web AI to call MCP.
5
+ *
6
+ * Editor AI (Mneme MCP) --soul--> Web AI (chatgpt/gemini)
7
+ * ^ |
8
+ * | inbox.jsonl | emits HOMUNCULUS RETURN
9
+ * | v
10
+ * Mneme MCP watcher <-- POST /return -- return-pad (page)
11
+ *
12
+ * The page (SAME-SHELL or mobile) has a textarea + Send button. User
13
+ * pastes the Web AI's full reply (must contain HOMUNCULUS RETURN). The
14
+ * page POSTs the body to /return. The HTTP server validates + appends
15
+ * one line to `.mneme/inbox/homunculus-return.jsonl`. Mneme MCP daemon
16
+ * tails this file and surfaces new entries through the supersonic
17
+ * pulse so the editor AI sees "[BOOMERANG] new return -- ingest?".
18
+ *
19
+ * Zero credentials. Zero webhook. Zero new daemon process. Just append
20
+ * + tail of a local file that MCP already watches.
21
+ */
22
+ import { appendFileSync, mkdirSync, existsSync, readFileSync, writeFileSync } from "node:fs";
23
+ import { dirname } from "node:path";
24
+ import { createHash, randomBytes } from "node:crypto";
25
+ import { parseHomunculusReturn } from "../abyss/homunculus.js";
26
+ function sha256(s) {
27
+ return createHash("sha256").update(s).digest("hex");
28
+ }
29
+ function randomId() {
30
+ return randomBytes(6).toString("hex");
31
+ }
32
+ /** Open or create a BOOMERANG inbox at the given JSONL path. */
33
+ export function openBoomerangInbox(path) {
34
+ const dir = dirname(path);
35
+ if (!existsSync(dir))
36
+ mkdirSync(dir, { recursive: true });
37
+ if (!existsSync(path))
38
+ writeFileSync(path, "", "utf8");
39
+ function readAll() {
40
+ const text = readFileSync(path, "utf8");
41
+ const out = [];
42
+ for (const line of text.split(/\r?\n/)) {
43
+ if (!line.trim())
44
+ continue;
45
+ try {
46
+ const obj = JSON.parse(line);
47
+ out.push(obj);
48
+ }
49
+ catch {
50
+ // skip malformed lines (forward-compat)
51
+ }
52
+ }
53
+ return out;
54
+ }
55
+ function writeAll(entries) {
56
+ const buf = entries.map((e) => JSON.stringify(e)).join("\n") + (entries.length > 0 ? "\n" : "");
57
+ writeFileSync(path, buf, "utf8");
58
+ }
59
+ return {
60
+ path,
61
+ ingest({ raw, source }) {
62
+ if (typeof raw !== "string" || raw.length === 0) {
63
+ return { ok: false, id: "", parsed: null, error: "empty body" };
64
+ }
65
+ if (raw.length > 256 * 1024) {
66
+ return { ok: false, id: "", parsed: null, error: "body too large (>256KB)" };
67
+ }
68
+ const parsed = parseHomunculusReturn(raw);
69
+ if (!parsed) {
70
+ return { ok: false, id: "", parsed: null, error: "no HOMUNCULUS RETURN block detected" };
71
+ }
72
+ const rawSha256 = sha256(raw);
73
+ // Dedup: if exact sha already in the inbox, return existing id without re-appending.
74
+ const existing = readAll().find((e) => e.rawSha256 === rawSha256);
75
+ if (existing) {
76
+ return { ok: true, id: existing.id, parsed: existing.parsed };
77
+ }
78
+ const entry = {
79
+ id: randomId(),
80
+ receivedAt: Date.now(),
81
+ source: source ?? "manual",
82
+ raw,
83
+ rawSha256,
84
+ parsed,
85
+ ingested: false,
86
+ };
87
+ appendFileSync(path, JSON.stringify(entry) + "\n", "utf8");
88
+ return { ok: true, id: entry.id, parsed };
89
+ },
90
+ list: readAll,
91
+ pending: () => readAll().filter((e) => !e.ingested),
92
+ markIngested(ids, note) {
93
+ const all = readAll();
94
+ const idSet = new Set(ids);
95
+ let count = 0;
96
+ for (const e of all) {
97
+ if (idSet.has(e.id) && !e.ingested) {
98
+ e.ingested = true;
99
+ if (note)
100
+ e.ingestNote = note;
101
+ count++;
102
+ }
103
+ }
104
+ writeAll(all);
105
+ return count;
106
+ },
107
+ };
108
+ }
109
+ /** Render a one-line summary of a pending boomerang entry, for the
110
+ * supersonic pulse. */
111
+ export function formatPulseLine(entry) {
112
+ if (!entry.parsed)
113
+ return `[BOOMERANG ${entry.id}] (unparsed body, ${entry.raw.length}B)`;
114
+ const p = entry.parsed;
115
+ const counts = [
116
+ p.decisions.length > 0 ? `d:${p.decisions.length}` : null,
117
+ p.reasoning.length > 0 ? `r:${p.reasoning.length}` : null,
118
+ p.vaccines.length > 0 ? `v:${p.vaccines.length}` : null,
119
+ p.nextActions.length > 0 ? `n:${p.nextActions.length}` : null,
120
+ ].filter(Boolean).join(" ");
121
+ return `[BOOMERANG ${entry.id}] from ${p.returningFrom} -> ${p.originator} (${counts})`;
122
+ }
123
+ export function handleReturnPost(args) {
124
+ const r = args.inbox.ingest({ raw: args.body, source: args.source });
125
+ if (!r.ok) {
126
+ return { status: 400, body: { ok: false, error: r.error ?? "validation failed" } };
127
+ }
128
+ return { status: 200, body: { ok: true, id: r.id } };
129
+ }
130
+ //# sourceMappingURL=boomerang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boomerang.js","sourceRoot":"","sources":["../../src/rainbow/boomerang.ts"],"names":[],"mappings":"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"}
@@ -16,4 +16,7 @@ export * from "./tunnel.js";
16
16
  export * from "./multi_paste.js";
17
17
  export * from "./resource_hints.js";
18
18
  export * from "./page_renderer.js";
19
+ export * from "./same_shell.js";
20
+ export * from "./phoenix.js";
21
+ export * from "./boomerang.js";
19
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rainbow/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rainbow/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
@@ -16,4 +16,7 @@ export * from "./tunnel.js";
16
16
  export * from "./multi_paste.js";
17
17
  export * from "./resource_hints.js";
18
18
  export * from "./page_renderer.js";
19
+ export * from "./same_shell.js";
20
+ export * from "./phoenix.js";
21
+ export * from "./boomerang.js";
19
22
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rainbow/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rainbow/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"page_renderer.d.ts","sourceRoot":"","sources":["../../src/rainbow/page_renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,4DAA4D;IAC5D,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACjC,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC3B;AAmBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAuG/D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAkFvD"}
1
+ {"version":3,"file":"page_renderer.d.ts","sourceRoot":"","sources":["../../src/rainbow/page_renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,4DAA4D;IAC5D,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACjC,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC3B;AAmBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAuG/D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CA0FvD"}
@@ -170,6 +170,8 @@ h2{font-size:32px;text-align:center}
170
170
  .howto ol{padding-left:28px}
171
171
  .howto li{margin:8px 0}
172
172
  .warn-soft{background:rgba(241,196,15,0.2);border:1px solid rgba(241,196,15,0.4);padding:18px;border-radius:12px;font-size:17px;line-height:1.5;text-align:center;max-width:600px}
173
+ .stop-help{background:rgba(0,0,0,0.35);padding:16px;border-radius:12px;font-size:14px;line-height:1.6;max-width:600px}
174
+ .stop-help h4{font-size:16px;margin-bottom:8px}
173
175
  footer{font-size:14px;opacity:0.7;text-align:center;line-height:1.6;max-width:700px;padding:16px;background:rgba(0,0,0,0.2);border-radius:10px}
174
176
  </style></head><body>
175
177
  <button class="lang-btn" id="lang">ไทย</button>
@@ -196,6 +198,12 @@ footer{font-size:14px;opacity:0.7;text-align:center;line-height:1.6;max-width:70
196
198
  <div class="warn-soft">
197
199
  <span data-en="⚠ Honest truth: paste gives the AI READ-ONLY memory. For LIVE Mneme tool execution that AI must have Mneme installed (Cursor / Claude Code / Codex / VS Code MCP). Web AIs (chatgpt.com, gemini.com) read but can NOT call MCP. They CAN suggest next actions back via HOMUNCULUS RETURN block — paste it back to your editor AI to execute." data-th="⚠ ความจริง: paste ให้ AI เห็น MEMORY แบบอ่านอย่างเดียว. ถ้าจะให้ AI เรียก Mneme tools จริง AI ตัวนั้นต้องติดตั้ง Mneme (Cursor / Claude Code / Codex / VS Code MCP). Web AIs อ่านได้แต่เรียก MCP ไม่ได้. แต่มัน suggest next actions กลับมาเป็น HOMUNCULUS RETURN block ได้ — paste กลับไป editor AI เพื่อ execute"></span>
198
200
  </div>
201
+ <div class="stop-help">
202
+ <h4 data-en="🛑 What the STOP button does" data-th="🛑 ปุ่ม STOP ทำอะไร"></h4>
203
+ <div data-en="Press STOP when you're DONE. It shuts down: (1) the local LAN server on this PC, (2) the public tunnel (if any), and (3) makes this page stop working. The QR will 404." data-th="กด STOP ตอนเสร็จแล้ว — มันจะปิด: (1) LAN server บน PC, (2) public tunnel (ถ้ามี), (3) หน้านี้จะใช้ไม่ได้แล้ว และ QR จะ 404"></div>
204
+ <div style="margin-top:8px" data-en="If you DON'T press STOP: server keeps running until you close this terminal or reboot. That's fine — it's local only. Public quick-tunnels self-expire after ~30 min of idle anyway." data-th="ถ้าไม่กด STOP: server จะรันไปเรื่อยๆ จนกว่าจะปิด terminal หรือ reboot. ไม่อันตราย เพราะเป็น local only. ส่วน quick-tunnel จะตายเองหลัง idle ~30 นาที"></div>
205
+ <div style="margin-top:8px" data-en="Closed the browser by accident? Just say to your editor AI: 'show handoff again'. The page regenerates with a fresh URL." data-th="เผลอปิด browser ไปแล้ว? บอก editor AI ของคุณ: 'show handoff again' หน้าจะ generate ใหม่พร้อม URL ใหม่"></div>
206
+ </div>
199
207
  <footer>
200
208
  <span data-en="Soul ${input.soulTokens} tokens · ${tunnelMention}LAN :7741${pasteMention}" data-th="สมอง ${input.soulTokens} โทเค็น · ${tunnelMention}LAN :7741${pasteMention}"></span><br>
201
209
  <span data-en="Done? Click the red STOP button at top-right." data-th="เสร็จแล้ว? กดปุ่ม STOP สีแดงมุมขวาบน"></span>
@@ -1 +1 @@
1
- {"version":3,"file":"page_renderer.js","sourceRoot":"","sources":["../../src/rainbow/page_renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAuBH,SAAS,MAAM,CAAC,CAAS;IACvB,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,UAAU,CAAC,OAAe,EAAE,IAAI,GAAG,GAAG;IAC7C,OAAO,oDAAoD,IAAI,IAAI,IAAI,8BAA8B,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;AACrI,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAsB;IACrD,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC;IACvC,OAAO,8BAA8B,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eA6D5B,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;aACxB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAsCT,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC;IACvC,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACzC,MAAM,aAAa,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,CAAC;IACpE,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC;IAE/D,OAAO,8BAA8B,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAiCX,OAAO,cAAc,OAAO;;kCAE1B,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;qBAC1D,GAAG;+BACO,MAAM,cAAc,MAAM;;;;;;;;;;;;;;;wBAejC,KAAK,CAAC,UAAU,aAAa,aAAa,YAAY,YAAY,mBAAmB,KAAK,CAAC,UAAU,aAAa,aAAa,YAAY,YAAY;;;;;aAKlK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;;;;;;;;;;sBAUX,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;;;;eAI/B,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"page_renderer.js","sourceRoot":"","sources":["../../src/rainbow/page_renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAuBH,SAAS,MAAM,CAAC,CAAS;IACvB,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,UAAU,CAAC,OAAe,EAAE,IAAI,GAAG,GAAG;IAC7C,OAAO,oDAAoD,IAAI,IAAI,IAAI,8BAA8B,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;AACrI,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAsB;IACrD,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC;IACvC,OAAO,8BAA8B,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eA6D5B,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;aACxB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAsCT,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC;IACvC,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACzC,MAAM,aAAa,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,CAAC;IACpE,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC;IAE/D,OAAO,8BAA8B,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAmCX,OAAO,cAAc,OAAO;;kCAE1B,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;qBAC1D,GAAG;+BACO,MAAM,cAAc,MAAM;;;;;;;;;;;;;;;;;;;;;wBAqBjC,KAAK,CAAC,UAAU,aAAa,aAAa,YAAY,YAAY,mBAAmB,KAAK,CAAC,UAAU,aAAa,aAAa,YAAY,YAAY;;;;;aAKlK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;;;;;;;;;;sBAUX,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;;;;eAI/B,CAAC;AAChB,CAAC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * v1.92.0 -- RAINBOW: PHOENIX (tunnel watchdog + URL push).
3
+ *
4
+ * Quick tunnels (`cloudflared tunnel --url http://localhost:PORT`) die.
5
+ * They die when the process exits, when idle ~30 min, or randomly when
6
+ * the Cloudflare edge garbage-collects them. The user scans a QR five
7
+ * minutes later and gets HTTP 404.
8
+ *
9
+ * PHOENIX:
10
+ * 1. Periodically probes the tunnel URL with HEAD / GET.
11
+ * 2. If the probe fails N consecutive times -> respawn cloudflared.
12
+ * 3. Calls every onUrlChange listener with the new URL.
13
+ * 4. Exposes a /events SSE endpoint so the served PC + mobile pages
14
+ * can subscribe and re-render their QR LIVE when the URL changes
15
+ * -- without the user reopening the page.
16
+ *
17
+ * The page on the phone never has to be reloaded. The wizard never has
18
+ * to confess "the URL died."
19
+ */
20
+ export interface TunnelProbeResult {
21
+ url: string;
22
+ ok: boolean;
23
+ status: number | null;
24
+ elapsedMs: number;
25
+ /** Reason for failure (network, http status, etc). */
26
+ reason?: string;
27
+ }
28
+ export interface PhoenixOptions {
29
+ /** Initial tunnel URL (already-spawned tunnel from startQuickTunnel). */
30
+ initialUrl: string;
31
+ /** Probe interval in ms. Default 30_000. */
32
+ probeIntervalMs?: number;
33
+ /** Consecutive failures before respawn. Default 2. */
34
+ failuresBeforeRespawn?: number;
35
+ /** Function to probe the URL. Default: fetch HEAD. */
36
+ probeFn?: (url: string) => Promise<TunnelProbeResult>;
37
+ /** Function that respawns the tunnel + returns the new URL. */
38
+ respawnFn: () => Promise<string | null>;
39
+ /** Optional logger. */
40
+ log?: (msg: string) => void;
41
+ }
42
+ export interface PhoenixHandle {
43
+ getUrl(): string;
44
+ /** Subscribe to URL changes. Returns an unsubscribe function. */
45
+ onUrlChange(cb: (newUrl: string, oldUrl: string) => void): () => void;
46
+ /** History of every probe + respawn (newest last). */
47
+ getHistory(): Array<{
48
+ when: number;
49
+ kind: "probe" | "respawn";
50
+ ok: boolean;
51
+ url: string;
52
+ note?: string;
53
+ }>;
54
+ /** Stop the watchdog. Tunnel itself is NOT killed -- caller owns it. */
55
+ stop(): void;
56
+ }
57
+ export declare function createPhoenix(opts: PhoenixOptions): PhoenixHandle;
58
+ /** Inline JS for the served page so it can SUBSCRIBE to URL changes via
59
+ * Server-Sent Events. The page polls /events; when the server emits a
60
+ * url-change event, the page replaces its QR <img> src + URL text.
61
+ *
62
+ * Server side: open SSE response, send `event: url-change\ndata: NEW_URL\n\n`
63
+ * each time PHOENIX fires onUrlChange. */
64
+ export declare function renderPhoenixSubscriberScript(input: {
65
+ eventsUrl: string;
66
+ qrImgId: string;
67
+ urlTextId: string;
68
+ }): string;
69
+ /** Format the SSE wire payload for a single url-change event. Server
70
+ * writes this byte sequence to the response body. */
71
+ export declare function formatUrlChangeSseFrame(newUrl: string): string;
72
+ //# sourceMappingURL=phoenix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phoenix.d.ts","sourceRoot":"","sources":["../../src/rainbow/phoenix.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sDAAsD;IACtD,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACtD,+DAA+D;IAC/D,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACxC,uBAAuB;IACvB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,IAAI,MAAM,CAAC;IACjB,iEAAiE;IACjE,WAAW,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACtE,sDAAsD;IACtD,UAAU,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1G,wEAAwE;IACxE,IAAI,IAAI,IAAI,CAAC;CACd;AA8BD,wBAAgB,aAAa,CAAC,IAAI,EAAE,cAAc,GAAG,aAAa,CAsEjE;AAED;;;;;2CAK2C;AAC3C,wBAAgB,6BAA6B,CAAC,KAAK,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAgBtH;AAED;sDACsD;AACtD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAK9D"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * v1.92.0 -- RAINBOW: PHOENIX (tunnel watchdog + URL push).
3
+ *
4
+ * Quick tunnels (`cloudflared tunnel --url http://localhost:PORT`) die.
5
+ * They die when the process exits, when idle ~30 min, or randomly when
6
+ * the Cloudflare edge garbage-collects them. The user scans a QR five
7
+ * minutes later and gets HTTP 404.
8
+ *
9
+ * PHOENIX:
10
+ * 1. Periodically probes the tunnel URL with HEAD / GET.
11
+ * 2. If the probe fails N consecutive times -> respawn cloudflared.
12
+ * 3. Calls every onUrlChange listener with the new URL.
13
+ * 4. Exposes a /events SSE endpoint so the served PC + mobile pages
14
+ * can subscribe and re-render their QR LIVE when the URL changes
15
+ * -- without the user reopening the page.
16
+ *
17
+ * The page on the phone never has to be reloaded. The wizard never has
18
+ * to confess "the URL died."
19
+ */
20
+ import { setTimeout as nodeSetTimeout, clearTimeout as nodeClearTimeout } from "node:timers";
21
+ const HISTORY_MAX = 50;
22
+ /** Default probe: HEAD request with 8s timeout. A 5xx still counts as
23
+ * "tunnel alive" because the LAN server is reachable through it.
24
+ * A 4xx from cloudflared edge (specifically 530/521/404) means the
25
+ * tunnel itself is dead. */
26
+ async function defaultProbe(url) {
27
+ const t0 = Date.now();
28
+ const controller = new AbortController();
29
+ const timer = nodeSetTimeout(() => controller.abort(), 8000);
30
+ try {
31
+ const r = await fetch(url, { method: "HEAD", signal: controller.signal });
32
+ nodeClearTimeout(timer);
33
+ const elapsedMs = Date.now() - t0;
34
+ const isDead = r.status === 404 || r.status === 530 || r.status === 521 || r.status === 502;
35
+ return {
36
+ url,
37
+ ok: !isDead,
38
+ status: r.status,
39
+ elapsedMs,
40
+ reason: isDead ? `tunnel edge returned ${r.status}` : undefined,
41
+ };
42
+ }
43
+ catch (e) {
44
+ nodeClearTimeout(timer);
45
+ return { url, ok: false, status: null, elapsedMs: Date.now() - t0, reason: e.message };
46
+ }
47
+ }
48
+ export function createPhoenix(opts) {
49
+ let url = opts.initialUrl;
50
+ const probeIntervalMs = opts.probeIntervalMs ?? 30_000;
51
+ const failuresBeforeRespawn = opts.failuresBeforeRespawn ?? 2;
52
+ const probe = opts.probeFn ?? defaultProbe;
53
+ const log = opts.log ?? (() => undefined);
54
+ const listeners = [];
55
+ const history = [];
56
+ let consecutiveFailures = 0;
57
+ let stopped = false;
58
+ let timer = null;
59
+ function record(entry) {
60
+ history.push(entry);
61
+ if (history.length > HISTORY_MAX)
62
+ history.shift();
63
+ }
64
+ async function tick() {
65
+ if (stopped)
66
+ return;
67
+ try {
68
+ const r = await probe(url);
69
+ record({ when: Date.now(), kind: "probe", ok: r.ok, url, note: r.reason });
70
+ log(`probe ${url} ok=${r.ok} status=${r.status} elapsed=${r.elapsedMs}ms`);
71
+ if (r.ok) {
72
+ consecutiveFailures = 0;
73
+ }
74
+ else {
75
+ consecutiveFailures++;
76
+ if (consecutiveFailures >= failuresBeforeRespawn) {
77
+ consecutiveFailures = 0;
78
+ log(`tunnel dead (${r.reason ?? "unknown"}) -- respawning`);
79
+ const newUrl = await opts.respawnFn();
80
+ if (newUrl && newUrl !== url) {
81
+ const oldUrl = url;
82
+ url = newUrl;
83
+ record({ when: Date.now(), kind: "respawn", ok: true, url: newUrl, note: `replaced ${oldUrl}` });
84
+ log(`tunnel respawned: ${oldUrl} -> ${newUrl}`);
85
+ for (const cb of [...listeners]) {
86
+ try {
87
+ cb(newUrl, oldUrl);
88
+ }
89
+ catch { /* swallow */ }
90
+ }
91
+ }
92
+ else {
93
+ record({ when: Date.now(), kind: "respawn", ok: false, url, note: "respawnFn returned null" });
94
+ log(`respawn failed -- staying on ${url}`);
95
+ }
96
+ }
97
+ }
98
+ }
99
+ catch (e) {
100
+ record({ when: Date.now(), kind: "probe", ok: false, url, note: e.message });
101
+ }
102
+ if (!stopped)
103
+ timer = nodeSetTimeout(tick, probeIntervalMs);
104
+ }
105
+ // First probe happens after one interval, not immediately, so the
106
+ // initial tunnel has time to settle.
107
+ timer = nodeSetTimeout(tick, probeIntervalMs);
108
+ return {
109
+ getUrl: () => url,
110
+ onUrlChange: (cb) => {
111
+ listeners.push(cb);
112
+ return () => {
113
+ const i = listeners.indexOf(cb);
114
+ if (i >= 0)
115
+ listeners.splice(i, 1);
116
+ };
117
+ },
118
+ getHistory: () => history.slice(),
119
+ stop: () => {
120
+ stopped = true;
121
+ if (timer) {
122
+ nodeClearTimeout(timer);
123
+ timer = null;
124
+ }
125
+ },
126
+ };
127
+ }
128
+ /** Inline JS for the served page so it can SUBSCRIBE to URL changes via
129
+ * Server-Sent Events. The page polls /events; when the server emits a
130
+ * url-change event, the page replaces its QR <img> src + URL text.
131
+ *
132
+ * Server side: open SSE response, send `event: url-change\ndata: NEW_URL\n\n`
133
+ * each time PHOENIX fires onUrlChange. */
134
+ export function renderPhoenixSubscriberScript(input) {
135
+ const eventsUrl = JSON.stringify(input.eventsUrl);
136
+ const qrId = JSON.stringify(input.qrImgId);
137
+ const urlId = JSON.stringify(input.urlTextId);
138
+ return `(function(){
139
+ if (typeof EventSource === "undefined") return;
140
+ var es = new EventSource(${eventsUrl});
141
+ es.addEventListener("url-change", function(ev) {
142
+ var newUrl = ev.data;
143
+ var img = document.getElementById(${qrId});
144
+ if (img) img.src = "https://api.qrserver.com/v1/create-qr-code/?size=360x360&format=svg&margin=10&data=" + encodeURIComponent(newUrl);
145
+ var txt = document.getElementById(${urlId});
146
+ if (txt) txt.textContent = newUrl;
147
+ });
148
+ es.addEventListener("error", function() { /* auto-reconnects */ });
149
+ })();`;
150
+ }
151
+ /** Format the SSE wire payload for a single url-change event. Server
152
+ * writes this byte sequence to the response body. */
153
+ export function formatUrlChangeSseFrame(newUrl) {
154
+ // Strip newlines from URL just in case (URL spec doesn't allow them
155
+ // but be defensive).
156
+ const safe = String(newUrl).replace(/\r?\n/g, "");
157
+ return `event: url-change\ndata: ${safe}\n\n`;
158
+ }
159
+ //# sourceMappingURL=phoenix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phoenix.js","sourceRoot":"","sources":["../../src/rainbow/phoenix.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * v1.92.0 -- RAINBOW: SAME-SHELL (same-machine 1-click clone).
3
+ *
4
+ * The forgotten case: when the user is on ONE machine running editor AI
5
+ * (Cursor / Claude Code) AND wants to continue with Web AI (ChatGPT.com,
6
+ * Gemini.com) in a browser ON THE SAME machine, the existing RAINBOW
7
+ * path forces them through QR → tunnel → phone scan → page → copy →
8
+ * back to PC → paste. Eight steps for a zero-network handoff.
9
+ *
10
+ * SAME-SHELL collapses this to two steps:
11
+ *
12
+ * 1. Editor AI: mneme.rainbow.show_local()
13
+ * → opens default browser at http://localhost:PORT
14
+ * → page auto-copies soul to clipboard on load
15
+ *
16
+ * 2. User opens chatgpt.com/gemini.com/claude.ai (link on page),
17
+ * Ctrl+V, done.
18
+ *
19
+ * Zero QR. Zero tunnel. Zero dpaste. Zero risk of 404 because no
20
+ * external network is involved at all. THIS IS THE FASTEST PATH AND
21
+ * THE LEAST FRAGILE -- but we never told the user it existed.
22
+ */
23
+ export interface SameShellPageInput {
24
+ soulText: string;
25
+ /** Port the local HTTP server is on. Used in the URL displayed for context. */
26
+ port: number;
27
+ defaultLang?: "en" | "th";
28
+ /** If set, the page shows a return-pad textarea posting to `${returnEndpoint}`. */
29
+ returnEndpoint?: string;
30
+ }
31
+ /** Render the same-machine page. Single big COPY button + auto-clipboard
32
+ * on load + four AI deep-links. */
33
+ export declare function renderSameShellPage(input: SameShellPageInput): string;
34
+ //# sourceMappingURL=same_shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"same_shell.d.ts","sourceRoot":"","sources":["../../src/rainbow/same_shell.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAeD;oCACoC;AACpC,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAmIrE"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * v1.92.0 -- RAINBOW: SAME-SHELL (same-machine 1-click clone).
3
+ *
4
+ * The forgotten case: when the user is on ONE machine running editor AI
5
+ * (Cursor / Claude Code) AND wants to continue with Web AI (ChatGPT.com,
6
+ * Gemini.com) in a browser ON THE SAME machine, the existing RAINBOW
7
+ * path forces them through QR → tunnel → phone scan → page → copy →
8
+ * back to PC → paste. Eight steps for a zero-network handoff.
9
+ *
10
+ * SAME-SHELL collapses this to two steps:
11
+ *
12
+ * 1. Editor AI: mneme.rainbow.show_local()
13
+ * → opens default browser at http://localhost:PORT
14
+ * → page auto-copies soul to clipboard on load
15
+ *
16
+ * 2. User opens chatgpt.com/gemini.com/claude.ai (link on page),
17
+ * Ctrl+V, done.
18
+ *
19
+ * Zero QR. Zero tunnel. Zero dpaste. Zero risk of 404 because no
20
+ * external network is involved at all. THIS IS THE FASTEST PATH AND
21
+ * THE LEAST FRAGILE -- but we never told the user it existed.
22
+ */
23
+ function jsSafe(s) {
24
+ return JSON.stringify(s).replace(/<\/(script)/gi, "<\\/$1");
25
+ }
26
+ function escapeHtml(s) {
27
+ return s
28
+ .replace(/&/g, "&amp;")
29
+ .replace(/</g, "&lt;")
30
+ .replace(/>/g, "&gt;")
31
+ .replace(/"/g, "&quot;")
32
+ .replace(/'/g, "&#39;");
33
+ }
34
+ /** Render the same-machine page. Single big COPY button + auto-clipboard
35
+ * on load + four AI deep-links. */
36
+ export function renderSameShellPage(input) {
37
+ const lang = input.defaultLang ?? "en";
38
+ const port = Math.max(1, Math.floor(input.port));
39
+ const returnEndpoint = input.returnEndpoint ?? null;
40
+ return `<!doctype html><html lang="${lang}"><head>
41
+ <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
42
+ <title>Mneme — Clone to another AI on this PC</title>
43
+ <style>
44
+ *{box-sizing:border-box;margin:0;padding:0}
45
+ body{font-family:-apple-system,"Segoe UI",sans-serif;background:linear-gradient(135deg,#16a085 0%,#2c3e50 100%);color:#fff;min-height:100vh;padding:32px 24px}
46
+ .wrap{max-width:760px;margin:0 auto;display:flex;flex-direction:column;gap:22px}
47
+ .lang-btn{position:fixed;top:14px;right:14px;background:rgba(0,0,0,0.4);border:0;color:#fff;padding:10px 18px;border-radius:8px;font-weight:700;cursor:pointer}
48
+ h1{font-size:38px;text-align:center;font-weight:800}
49
+ .tagline{font-size:19px;opacity:0.92;text-align:center;line-height:1.5}
50
+ .copy-btn{background:#fff;color:#16a085;border:0;padding:28px;border-radius:18px;font-weight:800;font-size:24px;cursor:pointer;box-shadow:0 14px 30px rgba(0,0,0,0.4);width:100%}
51
+ .copy-btn:active{transform:scale(0.98)}
52
+ .copy-btn.done{background:#27ae60;color:#fff}
53
+ .grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
54
+ .grid a{display:flex;flex-direction:column;align-items:center;gap:6px;background:rgba(255,255,255,0.18);padding:22px 14px;border-radius:14px;text-decoration:none;color:#fff;font-weight:700;font-size:17px;border:2px solid rgba(255,255,255,0.1)}
55
+ .grid a:hover{background:rgba(255,255,255,0.28);border-color:rgba(255,255,255,0.4)}
56
+ .grid a span.hint{font-size:12px;opacity:0.7;font-weight:500}
57
+ .matrix{background:rgba(0,0,0,0.3);padding:22px;border-radius:14px;font-size:15px;line-height:1.6}
58
+ .matrix h3{font-size:19px;margin-bottom:12px}
59
+ .matrix table{width:100%;border-collapse:collapse;font-size:14px}
60
+ .matrix td{padding:8px 4px;border-bottom:1px solid rgba(255,255,255,0.15)}
61
+ .matrix td:last-child{text-align:center;font-weight:700;width:42px}
62
+ .return-pad{background:rgba(0,0,0,0.4);padding:22px;border-radius:14px;display:flex;flex-direction:column;gap:14px}
63
+ .return-pad h3{font-size:20px}
64
+ .return-pad textarea{width:100%;min-height:140px;padding:14px;border-radius:10px;border:0;font-family:monospace;font-size:13px;background:rgba(255,255,255,0.95);color:#222}
65
+ .return-pad button{background:#9b59b6;color:#fff;border:0;padding:18px;border-radius:12px;font-weight:800;font-size:18px;cursor:pointer}
66
+ .return-pad button:active{transform:scale(0.98)}
67
+ .return-pad .status{font-size:14px;opacity:0.9;text-align:center}
68
+ footer{font-size:13px;opacity:0.7;text-align:center;line-height:1.6}
69
+ </style></head><body>
70
+ <button class="lang-btn" id="lang">ไทย</button>
71
+ <div class="wrap">
72
+ <h1 data-en="🧬 Clone to another AI on this PC" data-th="🧬 Clone ไป AI ตัวอื่นบนเครื่องนี้"></h1>
73
+ <div class="tagline" data-en="Brain already copied to clipboard. Click an AI below, paste (Ctrl+V), continue." data-th="ความจำก๊อปลง clipboard แล้ว. กด AI ข้างล่าง, paste (Ctrl+V), คุยต่อได้เลย"></div>
74
+ <button class="copy-btn" id="c" data-en="📋 Copy brain again" data-th="📋 ก๊อปสมองอีกครั้ง"></button>
75
+ <div class="grid">
76
+ <a href="https://chatgpt.com/" target="_blank" id="lk-cg">🟢 <span>ChatGPT</span><span class="hint" data-en="opens in new tab" data-th="เปิด tab ใหม่"></span></a>
77
+ <a href="https://gemini.google.com/app" target="_blank" id="lk-gm">🔵 <span>Gemini</span><span class="hint" data-en="opens in new tab" data-th="เปิด tab ใหม่"></span></a>
78
+ <a href="https://claude.ai/new" target="_blank" id="lk-cl">🟣 <span>Claude</span><span class="hint" data-en="opens in new tab" data-th="เปิด tab ใหม่"></span></a>
79
+ <a href="https://www.perplexity.ai/" target="_blank" id="lk-pp">⚪ <span>Perplexity</span><span class="hint" data-en="opens in new tab" data-th="เปิด tab ใหม่"></span></a>
80
+ </div>
81
+ <div class="matrix">
82
+ <h3 data-en="🎯 Paste-only — what works / what doesn't" data-th="🎯 Paste-only ใช้ทำอะไรได้/ไม่ได้"></h3>
83
+ <table>
84
+ <tr><td data-en="On a train, only have your phone" data-th="อยู่บนรถไฟ มีแค่มือถือ"></td><td>✅</td></tr>
85
+ <tr><td data-en="Switch models for a second opinion" data-th="อยากเปลี่ยน model ดู second opinion"></td><td>✅</td></tr>
86
+ <tr><td data-en="Share with a teammate (PM, designer)" data-th="ส่งให้เพื่อน/PM/designer คุยต่อ"></td><td>✅</td></tr>
87
+ <tr><td data-en="Backup the whole conversation" data-th="Backup บทสนทนา"></td><td>✅</td></tr>
88
+ <tr><td data-en="Brainstorm with the best model per task" data-th="Brainstorm กับ model ที่เก่งสุดต่อ task"></td><td>✅</td></tr>
89
+ <tr><td data-en="Call Mneme tools (apoptosis / scan / audit)" data-th="เรียก Mneme tools (apoptosis / scan / audit)"></td><td>❌</td></tr>
90
+ <tr><td data-en="Read .mneme/ files on your PC" data-th="อ่าน .mneme/ บน PC"></td><td>❌</td></tr>
91
+ <tr><td data-en="Upgrade Mneme / install" data-th="อัปเกรด Mneme / install"></td><td>❌</td></tr>
92
+ <tr><td data-en="Modify your code" data-th="แก้ code ของคุณ"></td><td>❌</td></tr>
93
+ </table>
94
+ <div style="margin-top:14px;padding:12px;background:rgba(255,255,255,0.1);border-radius:8px;font-size:13px;line-height:1.6">
95
+ <span data-en="✱ The Web AI can SUGGEST next actions back via HOMUNCULUS RETURN block. Paste that back below, your editor AI executes it. Web AI = brain. Editor AI = hands. You = courier (2 paste ops)." data-th="✱ Web AI สามารถ SUGGEST next actions กลับมาเป็น HOMUNCULUS RETURN block ได้. paste กลับช่องล่าง editor AI ของคุณจะ execute. Web AI = สมอง. Editor AI = มือ. คุณ = courier (paste 2 ครั้ง)"></span>
96
+ </div>
97
+ </div>
98
+ ${returnEndpoint ? `<div class="return-pad">
99
+ <h3 data-en="🪃 BOOMERANG — paste the Web AI reply back here" data-th="🪃 BOOMERANG — paste คำตอบ Web AI กลับมาตรงนี้"></h3>
100
+ <div style="font-size:14px;opacity:0.92;line-height:1.5" data-en="Paste the Web AI's full reply (must include the HOMUNCULUS RETURN block). Your editor AI on this PC will pick it up via Mneme MCP and surface next_actions for execution." data-th="paste คำตอบ Web AI ทั้งหมด (ต้องมี HOMUNCULUS RETURN block). editor AI บนเครื่องนี้จะรับผ่าน Mneme MCP แล้วโชว์ next_actions ให้ execute"></div>
101
+ <textarea id="rp" placeholder="# HOMUNCULUS RETURN&#10;originator: claude-opus-4-7&#10;returning_from: gemini-2.5-pro&#10;..."></textarea>
102
+ <button id="rb" data-en="🪃 Send back to editor AI" data-th="🪃 ส่งกลับ editor AI"></button>
103
+ <div class="status" id="rs"></div>
104
+ </div>` : ""}
105
+ <footer>
106
+ <span data-en="localhost:${port} · same-machine handoff · no QR, no tunnel, no network needed" data-th="localhost:${port} · ส่งผ่านเครื่องเดียวกัน · ไม่ต้องใช้ QR / tunnel / network"></span>
107
+ </footer>
108
+ </div>
109
+ <script>
110
+ const SOUL = ${jsSafe(input.soulText)};
111
+ let lang = ${JSON.stringify(lang)};
112
+ function applyLang() {
113
+ document.querySelectorAll("[data-en]").forEach(el => { el.textContent = el.dataset[lang] || el.dataset.en; });
114
+ document.getElementById("lang").textContent = lang === "en" ? "ไทย" : "EN";
115
+ document.documentElement.lang = lang;
116
+ }
117
+ applyLang();
118
+ document.getElementById("lang").onclick = () => { lang = lang === "en" ? "th" : "en"; applyLang(); };
119
+
120
+ async function copySoul(showToast) {
121
+ try {
122
+ if (navigator.clipboard && window.isSecureContext) {
123
+ await navigator.clipboard.writeText(SOUL);
124
+ } else { throw new Error("no clipboard api"); }
125
+ const c = document.getElementById("c");
126
+ c.classList.add("done");
127
+ c.textContent = lang === "th" ? "✓ ก๊อปแล้ว — paste ได้เลย" : "✓ Copied — ready to paste";
128
+ if (showToast === false) return;
129
+ setTimeout(() => {
130
+ c.classList.remove("done");
131
+ c.textContent = lang === "th" ? "📋 ก๊อปสมองอีกครั้ง" : "📋 Copy brain again";
132
+ }, 3500);
133
+ } catch (e) {
134
+ const c = document.getElementById("c");
135
+ c.textContent = lang === "th" ? "❌ ก๊อปไม่ได้ — กดที่ textarea ข้างล่าง → Select All → Copy" : "❌ Copy failed — use the textarea below";
136
+ }
137
+ }
138
+ copySoul(false);
139
+ document.getElementById("c").onclick = () => copySoul(true);
140
+ ${["cg", "gm", "cl", "pp"].map(k => `document.getElementById("lk-${k}").addEventListener("click", () => copySoul(false));`).join("\n")}
141
+ ${returnEndpoint ? `
142
+ const RETURN_URL = ${jsSafe(returnEndpoint)};
143
+ document.getElementById("rb").onclick = async () => {
144
+ const body = document.getElementById("rp").value;
145
+ if (!body.trim()) {
146
+ document.getElementById("rs").textContent = lang === "th" ? "⚠ ใส่ HOMUNCULUS RETURN block ก่อน" : "⚠ paste a HOMUNCULUS RETURN block first";
147
+ return;
148
+ }
149
+ document.getElementById("rs").textContent = lang === "th" ? "⏳ กำลังส่ง..." : "⏳ sending...";
150
+ try {
151
+ const r = await fetch(RETURN_URL, { method: "POST", headers: { "content-type": "text/plain;charset=utf-8" }, body });
152
+ const j = await r.json().catch(() => ({}));
153
+ if (r.ok && j.ok) {
154
+ document.getElementById("rs").textContent = lang === "th" ? ("✓ ส่งถึง editor AI แล้ว — id=" + j.id) : ("✓ delivered to editor AI — id=" + j.id);
155
+ document.getElementById("rp").value = "";
156
+ } else {
157
+ document.getElementById("rs").textContent = lang === "th" ? ("✗ ไม่สำเร็จ: " + (j.error || r.status)) : ("✗ failed: " + (j.error || r.status));
158
+ }
159
+ } catch (e) {
160
+ document.getElementById("rs").textContent = lang === "th" ? ("✗ network: " + e.message) : ("✗ network: " + e.message);
161
+ }
162
+ };
163
+ ` : ""}
164
+ </script>
165
+ </body></html>`;
166
+ }
167
+ //# sourceMappingURL=same_shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"same_shell.js","sourceRoot":"","sources":["../../src/rainbow/same_shell.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAWH,SAAS,MAAM,CAAC,CAAS;IACvB,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED;oCACoC;AACpC,MAAM,UAAU,mBAAmB,CAAC,KAAyB;IAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,IAAI,IAAI,CAAC;IAEpD,OAAO,8BAA8B,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0DzC,cAAc,CAAC,CAAC,CAAC;;;;;;OAMZ,CAAC,CAAC,CAAC,EAAE;;6BAEiB,IAAI,qFAAqF,IAAI;;;;eAI3G,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;aACxB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6B/B,CAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,+BAA+B,CAAC,sDAAsD,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;EACjI,cAAc,CAAC,CAAC,CAAC;qBACE,MAAM,CAAC,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;;CAqB1C,CAAC,CAAC,CAAC,EAAE;;eAES,CAAC;AAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=v1_92.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v1_92.test.d.ts","sourceRoot":"","sources":["../../src/rainbow/v1_92.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,295 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { mkdtempSync, readFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { renderSameShellPage } from "./same_shell.js";
6
+ import { createPhoenix, renderPhoenixSubscriberScript, formatUrlChangeSseFrame, } from "./phoenix.js";
7
+ import { openBoomerangInbox, handleReturnPost, formatPulseLine, } from "./boomerang.js";
8
+ function freshInboxPath() {
9
+ const dir = mkdtempSync(join(tmpdir(), "mneme-boomerang-"));
10
+ return join(dir, "homunculus-return.jsonl");
11
+ }
12
+ const VALID_RETURN = `
13
+ some preamble text from the Web AI
14
+
15
+ # HOMUNCULUS RETURN
16
+ originator: claude-opus-4-7
17
+ returning_from: gemini-2.5-pro
18
+ decisions: |
19
+ - Use Postgres for v1
20
+ - Skip Redis for now
21
+ reasoning: |
22
+ - Postgres native JSONB handles the schema-less case
23
+ next_actions: |
24
+ - bench typeorm vs prisma
25
+ - settle on one ORM by Friday
26
+ `.trim();
27
+ // ============================== SAME-SHELL ==================================
28
+ describe("v1.92 RAINBOW · SAME-SHELL same-machine page", () => {
29
+ it("emits a complete HTML document", () => {
30
+ const html = renderSameShellPage({ soulText: "# SOUL\nbody", port: 7741 });
31
+ expect(html.startsWith("<!doctype html>")).toBe(true);
32
+ expect(html).toContain("</html>");
33
+ expect(html).toContain("Clone to another AI on this PC");
34
+ });
35
+ it("auto-copies soul to clipboard on load (no user click required)", () => {
36
+ const html = renderSameShellPage({ soulText: "x", port: 7741 });
37
+ // copySoul(false) called from top-level script -> auto-copy on load
38
+ expect(html).toContain("copySoul(false)");
39
+ expect(html).toContain("navigator.clipboard");
40
+ });
41
+ it("includes the full TH+EN capability matrix (paste-only truth)", () => {
42
+ const html = renderSameShellPage({ soulText: "x", port: 7741 });
43
+ // EN side
44
+ expect(html).toContain("On a train, only have your phone");
45
+ expect(html).toContain("Switch models for a second opinion");
46
+ expect(html).toContain("Backup the whole conversation");
47
+ expect(html).toContain("Call Mneme tools");
48
+ expect(html).toContain("Modify your code");
49
+ // TH side
50
+ expect(html).toContain("อยู่บนรถไฟ มีแค่มือถือ");
51
+ expect(html).toContain("Backup บทสนทนา");
52
+ expect(html).toContain("เรียก Mneme tools");
53
+ expect(html).toContain("แก้ code ของคุณ");
54
+ });
55
+ it("includes 4 AI deep-links that also re-copy clipboard on click", () => {
56
+ const html = renderSameShellPage({ soulText: "x", port: 7741 });
57
+ expect(html).toContain("chatgpt.com");
58
+ expect(html).toContain("gemini.google.com");
59
+ expect(html).toContain("claude.ai");
60
+ expect(html).toContain("perplexity.ai");
61
+ // Click handlers re-copy to defend against tab-switch clipboard wipe
62
+ expect(html).toContain('document.getElementById("lk-cg").addEventListener("click"');
63
+ });
64
+ it("XSS-safe: </script> in soul is escaped", () => {
65
+ const html = renderSameShellPage({ soulText: 'evil </script><img onerror=1>', port: 7741 });
66
+ expect(html).toContain("<\\/script>");
67
+ const start = html.indexOf("const SOUL = ");
68
+ const end = html.indexOf(";", start);
69
+ expect(html.slice(start, end)).not.toContain("</script>");
70
+ });
71
+ it("includes BOOMERANG return-pad when returnEndpoint is provided", () => {
72
+ const html = renderSameShellPage({ soulText: "x", port: 7741, returnEndpoint: "http://localhost:7741/return" });
73
+ expect(html).toContain("🪃 BOOMERANG");
74
+ expect(html).toContain("textarea");
75
+ expect(html).toContain('id="rp"');
76
+ expect(html).toContain('id="rb"');
77
+ expect(html).toContain("/return");
78
+ // Submit handler does POST with body
79
+ expect(html).toContain('method: "POST"');
80
+ });
81
+ it("omits BOOMERANG return-pad when returnEndpoint absent", () => {
82
+ const html = renderSameShellPage({ soulText: "x", port: 7741 });
83
+ expect(html).not.toContain("🪃 BOOMERANG");
84
+ expect(html).not.toContain('id="rp"');
85
+ });
86
+ it("footer states 'no QR / no tunnel / no network' truth", () => {
87
+ const html = renderSameShellPage({ soulText: "x", port: 7741 });
88
+ expect(html).toContain("no QR, no tunnel, no network needed");
89
+ expect(html).toContain("ไม่ต้องใช้ QR / tunnel / network");
90
+ });
91
+ });
92
+ // ============================== PHOENIX =====================================
93
+ describe("v1.92 RAINBOW · PHOENIX tunnel watchdog", () => {
94
+ function probeFnSeq(seq) {
95
+ let i = 0;
96
+ return async (url) => {
97
+ const r = seq[Math.min(i, seq.length - 1)];
98
+ i++;
99
+ return { url, ok: r.ok, status: r.status, elapsedMs: 0, reason: r.reason };
100
+ };
101
+ }
102
+ it("getUrl returns initial URL", () => {
103
+ const h = createPhoenix({
104
+ initialUrl: "https://a.trycloudflare.com",
105
+ probeIntervalMs: 9_999_999,
106
+ probeFn: probeFnSeq([{ ok: true, status: 200 }]),
107
+ respawnFn: async () => null,
108
+ });
109
+ expect(h.getUrl()).toBe("https://a.trycloudflare.com");
110
+ h.stop();
111
+ });
112
+ it("respawns + fires onUrlChange after N consecutive failures", async () => {
113
+ let respawnCount = 0;
114
+ const events = [];
115
+ const h = createPhoenix({
116
+ initialUrl: "https://dead.trycloudflare.com",
117
+ probeIntervalMs: 5,
118
+ failuresBeforeRespawn: 2,
119
+ probeFn: probeFnSeq([
120
+ { ok: false, status: 404, reason: "tunnel edge returned 404" },
121
+ { ok: false, status: 404, reason: "tunnel edge returned 404" },
122
+ { ok: true, status: 200 },
123
+ { ok: true, status: 200 },
124
+ ]),
125
+ respawnFn: async () => {
126
+ respawnCount++;
127
+ return "https://alive.trycloudflare.com";
128
+ },
129
+ });
130
+ h.onUrlChange((n, o) => events.push([o, n]));
131
+ // Wait for two probe failures + respawn.
132
+ await new Promise((res) => setTimeout(res, 200));
133
+ h.stop();
134
+ expect(respawnCount).toBeGreaterThanOrEqual(1);
135
+ expect(h.getUrl()).toBe("https://alive.trycloudflare.com");
136
+ expect(events.length).toBeGreaterThanOrEqual(1);
137
+ expect(events[0][0]).toBe("https://dead.trycloudflare.com");
138
+ expect(events[0][1]).toBe("https://alive.trycloudflare.com");
139
+ });
140
+ it("does not respawn on single transient failure", async () => {
141
+ let respawnCount = 0;
142
+ const h = createPhoenix({
143
+ initialUrl: "https://x.trycloudflare.com",
144
+ probeIntervalMs: 5,
145
+ failuresBeforeRespawn: 3,
146
+ probeFn: probeFnSeq([
147
+ { ok: false, status: 404 },
148
+ { ok: true, status: 200 },
149
+ { ok: true, status: 200 },
150
+ { ok: true, status: 200 },
151
+ ]),
152
+ respawnFn: async () => { respawnCount++; return "x"; },
153
+ });
154
+ await new Promise((res) => setTimeout(res, 80));
155
+ h.stop();
156
+ expect(respawnCount).toBe(0);
157
+ });
158
+ it("history records every probe + respawn", async () => {
159
+ const h = createPhoenix({
160
+ initialUrl: "https://q.trycloudflare.com",
161
+ probeIntervalMs: 5,
162
+ failuresBeforeRespawn: 1,
163
+ probeFn: probeFnSeq([
164
+ { ok: false, status: 404, reason: "edge 404" },
165
+ { ok: true, status: 200 },
166
+ ]),
167
+ respawnFn: async () => "https://r.trycloudflare.com",
168
+ });
169
+ await new Promise((res) => setTimeout(res, 80));
170
+ h.stop();
171
+ const hist = h.getHistory();
172
+ expect(hist.length).toBeGreaterThanOrEqual(2);
173
+ expect(hist.some((e) => e.kind === "probe")).toBe(true);
174
+ expect(hist.some((e) => e.kind === "respawn")).toBe(true);
175
+ });
176
+ it("renderPhoenixSubscriberScript emits an EventSource subscription IIFE", () => {
177
+ const s = renderPhoenixSubscriberScript({ eventsUrl: "/events", qrImgId: "qr", urlTextId: "url" });
178
+ expect(s).toContain("EventSource");
179
+ expect(s).toContain('"/events"');
180
+ expect(s).toContain('"qr"');
181
+ expect(s).toContain('"url"');
182
+ expect(s).toContain("url-change");
183
+ expect(s).toContain("api.qrserver.com");
184
+ });
185
+ it("formatUrlChangeSseFrame returns valid SSE wire format", () => {
186
+ const f = formatUrlChangeSseFrame("https://new.trycloudflare.com");
187
+ expect(f).toBe("event: url-change\ndata: https://new.trycloudflare.com\n\n");
188
+ });
189
+ it("formatUrlChangeSseFrame strips newlines from input (defensive)", () => {
190
+ const f = formatUrlChangeSseFrame("https://x.com\ninjected\nstuff");
191
+ expect(f).not.toContain("\ninjected");
192
+ expect(f).toContain("https://x.cominjectedstuff");
193
+ });
194
+ });
195
+ // ============================== BOOMERANG ===================================
196
+ describe("v1.92 RAINBOW · BOOMERANG return-pad inbox", () => {
197
+ it("ingests a valid HOMUNCULUS RETURN block + writes one JSONL line", () => {
198
+ const path = freshInboxPath();
199
+ const inbox = openBoomerangInbox(path);
200
+ const r = inbox.ingest({ raw: VALID_RETURN, source: "same-shell" });
201
+ expect(r.ok).toBe(true);
202
+ expect(r.id).toMatch(/^[0-9a-f]{12}$/);
203
+ expect(r.parsed?.decisions.length).toBe(2);
204
+ expect(r.parsed?.nextActions.length).toBe(2);
205
+ const lines = readFileSync(path, "utf8").trim().split("\n");
206
+ expect(lines.length).toBe(1);
207
+ const entry = JSON.parse(lines[0]);
208
+ expect(entry.id).toBe(r.id);
209
+ expect(entry.source).toBe("same-shell");
210
+ expect(entry.ingested).toBe(false);
211
+ expect(entry.parsed.originator).toBe("claude-opus-4-7");
212
+ });
213
+ it("rejects body without HOMUNCULUS RETURN block", () => {
214
+ const inbox = openBoomerangInbox(freshInboxPath());
215
+ const r = inbox.ingest({ raw: "just a normal AI reply, no return block here" });
216
+ expect(r.ok).toBe(false);
217
+ expect(r.error).toContain("HOMUNCULUS RETURN block");
218
+ });
219
+ it("rejects empty body", () => {
220
+ const inbox = openBoomerangInbox(freshInboxPath());
221
+ const r = inbox.ingest({ raw: "" });
222
+ expect(r.ok).toBe(false);
223
+ expect(r.error).toContain("empty");
224
+ });
225
+ it("rejects body over 256KB", () => {
226
+ const inbox = openBoomerangInbox(freshInboxPath());
227
+ const huge = "x".repeat(300 * 1024);
228
+ const r = inbox.ingest({ raw: huge });
229
+ expect(r.ok).toBe(false);
230
+ expect(r.error).toContain("too large");
231
+ });
232
+ it("dedup: same body twice -> same id, single line in file", () => {
233
+ const path = freshInboxPath();
234
+ const inbox = openBoomerangInbox(path);
235
+ const r1 = inbox.ingest({ raw: VALID_RETURN, source: "same-shell" });
236
+ const r2 = inbox.ingest({ raw: VALID_RETURN, source: "tunnel" });
237
+ expect(r1.id).toBe(r2.id);
238
+ const lines = readFileSync(path, "utf8").trim().split("\n");
239
+ expect(lines.length).toBe(1);
240
+ });
241
+ it("pending() returns un-ingested entries; markIngested moves them", () => {
242
+ const path = freshInboxPath();
243
+ const inbox = openBoomerangInbox(path);
244
+ const a = inbox.ingest({ raw: VALID_RETURN }).id;
245
+ expect(inbox.pending().length).toBe(1);
246
+ const moved = inbox.markIngested([a], "applied via mneme.abyss.homunculus.ingest");
247
+ expect(moved).toBe(1);
248
+ expect(inbox.pending().length).toBe(0);
249
+ const all = inbox.list();
250
+ expect(all[0].ingested).toBe(true);
251
+ expect(all[0].ingestNote).toContain("applied");
252
+ // Second mark is idempotent.
253
+ expect(inbox.markIngested([a])).toBe(0);
254
+ });
255
+ it("handleReturnPost returns 200 + {ok,id} on valid body", () => {
256
+ const inbox = openBoomerangInbox(freshInboxPath());
257
+ const r = handleReturnPost({ inbox, body: VALID_RETURN, source: "same-shell" });
258
+ expect(r.status).toBe(200);
259
+ expect(r.body.ok).toBe(true);
260
+ expect(r.body.id).toMatch(/^[0-9a-f]{12}$/);
261
+ });
262
+ it("handleReturnPost returns 400 + {ok:false,error} on garbage", () => {
263
+ const inbox = openBoomerangInbox(freshInboxPath());
264
+ const r = handleReturnPost({ inbox, body: "not a return block", source: "same-shell" });
265
+ expect(r.status).toBe(400);
266
+ expect(r.body.ok).toBe(false);
267
+ expect(r.body.error).toBeTruthy();
268
+ });
269
+ it("formatPulseLine produces compact one-line summary", () => {
270
+ const inbox = openBoomerangInbox(freshInboxPath());
271
+ const r = inbox.ingest({ raw: VALID_RETURN });
272
+ const entry = inbox.list()[0];
273
+ const line = formatPulseLine(entry);
274
+ expect(line).toContain("[BOOMERANG");
275
+ expect(line).toContain(r.id);
276
+ expect(line).toContain("gemini-2.5-pro");
277
+ expect(line).toContain("d:2"); // 2 decisions
278
+ expect(line).toContain("n:2"); // 2 next_actions
279
+ });
280
+ it("survives corrupt JSONL lines (forward-compat)", () => {
281
+ const path = freshInboxPath();
282
+ const inbox = openBoomerangInbox(path);
283
+ inbox.ingest({ raw: VALID_RETURN });
284
+ // Append a malformed line that future versions might write.
285
+ readFileSync(path, "utf8");
286
+ appendCorruptLine(path);
287
+ expect(() => inbox.list()).not.toThrow();
288
+ expect(inbox.list().length).toBe(1);
289
+ });
290
+ });
291
+ function appendCorruptLine(path) {
292
+ const fs = require("node:fs");
293
+ fs.appendFileSync(path, "{not json}\n", "utf8");
294
+ }
295
+ //# sourceMappingURL=v1_92.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v1_92.test.js","sourceRoot":"","sources":["../../src/rainbow/v1_92.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
- {
1
+ {
2
2
  "name": "@mneme-ai/core",
3
- "version": "1.91.0",
3
+ "version": "1.92.0",
4
4
  "description": "Core indexing, retrieval, and graph engine for Mneme",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",