@rubytech/create-maxy-code 0.1.182 → 0.1.184
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/__tests__/bundler-rewrite-platform-lib.test.js +68 -0
- package/dist/bundler-rewrite-platform-lib.js +29 -0
- package/package.json +1 -1
- package/payload/platform/lib/graph-search/dist/index.d.ts +10 -0
- package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-search/dist/index.js +57 -30
- package/payload/platform/lib/graph-search/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-search/src/__tests__/hybrid.test.ts +95 -2
- package/payload/platform/lib/graph-search/src/__tests__/vector-index-coverage.test.ts +198 -0
- package/payload/platform/lib/graph-search/src/index.ts +73 -29
- package/payload/platform/neo4j/schema.cypher +36 -0
- package/payload/platform/plugins/admin/PLUGIN.md +2 -0
- package/payload/platform/plugins/admin/hooks/admin-authoring-observer.sh +155 -0
- package/payload/platform/plugins/admin/skills/access-manager/SKILL.md +7 -5
- package/payload/platform/plugins/admin/skills/access-manager/references/operations.md +27 -55
- package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +178 -0
- package/payload/platform/plugins/admin/skills/specialist-management/SKILL.md +3 -2
- package/payload/platform/plugins/aeo/mcp/dist/index.js +3 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/contacts/PLUGIN.md +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/index.js +3 -0
- package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/docs/references/access-control.md +29 -22
- package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.js +56 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.js.map +1 -0
- package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +6 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +91 -7
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js +30 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/section-types.d.ts +15 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/section-types.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts +10 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js +35 -4
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js +61 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.js +125 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +65 -5
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.d.ts +15 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.js +45 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +24 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +16 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/references/graph-primitives.md +1 -0
- package/payload/platform/plugins/memory/references/schema-base.md +4 -2
- package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +24 -0
- package/payload/platform/plugins/scheduling/PLUGIN.md +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +3 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/work/PLUGIN.md +1 -0
- package/payload/platform/plugins/work/mcp/dist/index.js +4 -0
- package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/workflows/PLUGIN.md +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/index.js +3 -0
- package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -1
- package/payload/platform/scripts/__tests__/agents-md-bootstrap.test.sh +111 -0
- package/payload/platform/scripts/lib/agents-md-bootstrap.sh +49 -0
- package/payload/platform/scripts/setup-account.sh +18 -14
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +8 -0
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +58 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +41 -4
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +172 -53
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/templates/agents/admin/AGENTS.md +18 -7
- package/payload/platform/templates/agents/admin/IDENTITY.md +2 -2
- package/payload/platform/templates/specialists/agents/archive-ingest-operator.md +1 -1
- package/payload/platform/templates/specialists/agents/citation-auditor.md +1 -1
- package/payload/platform/templates/specialists/agents/compiled-truth-rewriter.md +1 -1
- package/payload/platform/templates/specialists/agents/content-producer.md +3 -1
- package/payload/platform/templates/specialists/agents/database-operator.md +2 -2
- package/payload/platform/templates/specialists/agents/librarian.md +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/platform/templates/specialists/agents/project-manager.md +1 -1
- package/payload/platform/templates/specialists/agents/public-session-reviewer.md +40 -0
- package/payload/platform/templates/specialists/agents/research-assistant.md +1 -1
- package/payload/platform/templates/specialists/agents/typed-edge-classifier.md +1 -1
- package/payload/server/public/assets/ChatInput-DzacFNMk.css +1 -0
- package/payload/server/public/assets/{Checkbox-YrQovXpN.js → Checkbox-BJKNkUYu.js} +1 -1
- package/payload/server/public/assets/{admin-DH5jp-1u.js → admin-BNy4N2d6.js} +1 -1
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Do2zYONG.js → architectureDiagram-Q4EWVU46-DZoOzmjE.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DpIX_bRu.js → blockDiagram-DXYQGD6D-DG3yufCR.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Cz335ZSY.js → c4Diagram-AHTNJAMY-BBx5wXuZ.js} +1 -1
- package/payload/server/public/assets/channel-DdtUtt5g.js +1 -0
- package/payload/server/public/assets/{chunk-336JU56O-Dmknn3Wo.js → chunk-336JU56O-DwP7g5ns.js} +2 -2
- package/payload/server/public/assets/{chunk-426QAEUC-ClfGjizN.js → chunk-426QAEUC-DXzGdc22.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-BPKctJ5c.js → chunk-4TB4RGXK-1LDRjk0E.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-BWDCO9zw.js → chunk-5FUZZQ4R-BHjWhwhn.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-BE34_Db0.js → chunk-5PVQY5BW-CwPHRVN8.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-BysA-_6v.js → chunk-EDXVE4YY-7443hrYZ.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-BMVyMsqA.js → chunk-ENJZ2VHE-BTnmmnmX.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-C5KRdLV4.js → chunk-ICPOFSXX-CPnMJplL.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-DVZD-AVc.js → chunk-OYMX7WX6-DCQ5Q4RS.js} +1 -1
- package/payload/server/public/assets/{chunk-U2HBQHQK-DYmQdx8x.js → chunk-U2HBQHQK-BmfZG8e4.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-DrZenIl3.js → chunk-X2U36JSP-Da2yJL2u.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-CDuGbvMq.js → chunk-YZCP3GAM-BdUmMUJ6.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-COztbiu5.js → chunk-ZZ45TVLE-abkGirrZ.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-CKk2DFZe.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E--YvfDQCh.js +1 -0
- package/payload/server/public/assets/clone-Cw6UfPGM.js +1 -0
- package/payload/server/public/assets/{dagre-Cv9Ib2OM.js → dagre-BVVuAEsU.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-CoEd61KZ.js → dagre-KV5264BT-VG2kNj19.js} +1 -1
- package/payload/server/public/assets/data-uBpwaldK.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-WXNzw3uW.js → diagram-5BDNPKRD-DRiVy69K.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-C6IuQ2VV.js → diagram-G4DWMVQ6-DtpiRTYX.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-CdJdir5A.js → diagram-MMDJMWI5-Da3t99fY.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-DxWKQH5T.js → diagram-TYMM5635-BzQmLTJQ.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-C9fvs33K.js → erDiagram-SMLLAGMA-D3Ll-GDw.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BO7Yrbqv.js → flowDiagram-DWJPFMVM-D17bje6d.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-CQJyWBZq.js → ganttDiagram-T4ZO3ILL-BjeQ-Bun.js} +1 -1
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DZfQHrYw.js → gitGraphDiagram-UUTBAWPF-LtX4pvZS.js} +1 -1
- package/payload/server/public/assets/{graph-labels-BMU8LsOD.js → graph-labels-Cn9jAE6I.js} +1 -1
- package/payload/server/public/assets/graph-tvTPvBaz.js +1 -0
- package/payload/server/public/assets/{graphlib-C40H-Q5E.js → graphlib-CzwsfZOw.js} +1 -1
- package/payload/server/public/assets/{infoDiagram-42DDH7IO-CakfV5f9.js → infoDiagram-42DDH7IO-DOgH4i52.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CPuBPyfa.js → ishikawaDiagram-UXIWVN3A-DYqy5BFj.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-zpZm2Mmo.js → journeyDiagram-VCZTEJTY-CsFpIULJ.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DfJduQCo.js → kanban-definition-6JOO6SKY-5uZuArw7.js} +1 -1
- package/payload/server/public/assets/{lib-Bnh57com.js → lib-p4ylk2XS.js} +1 -1
- package/payload/server/public/assets/{line-BpYQqO5R.js → line-BY8PmCSW.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-C5Mwi14D.js → mermaid-parser.core-CJ_9xqwK.js} +1 -1
- package/payload/server/public/assets/{mermaid.core-D8qBtvvT.js → mermaid.core-CXznBkX7.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-Bli9JhIN.js → mindmap-definition-QFDTVHPH-DZbHBud5.js} +1 -1
- package/payload/server/public/assets/{page-0lzgQWnS.js → page-B3VYaBcf.js} +1 -1
- package/payload/server/public/assets/{page-C29S3cBt.js → page-CfN7nw1J.js} +1 -1
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-MsLXTSLg.js → pieDiagram-DEJITSTG-QmOi4DRG.js} +1 -1
- package/payload/server/public/assets/public-Bbew78md.js +7 -0
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-De1sh2K0.js → quadrantDiagram-34T5L4WZ-Cpidq6Xj.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-D5Sfg1t_.js → requirementDiagram-MS252O5E-CBqtfbzR.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DHA3LIbv.js → sankeyDiagram-XADWPNL6-4BiY4dV9.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BE_QlG1B.js → sequenceDiagram-FGHM5R23-DVlL-l2u.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-NIwp3F4u.js → stateDiagram-FHFEXIEX-CAm9dW68.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B2EzXi50.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CPE9jyDF.js → timeline-definition-GMOUNBTQ-CD-tZc4y.js} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CKfoyYmH.js → vennDiagram-DHZGUBPP-Davcm0TF.js} +1 -1
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-B9yJek-o.js → wardleyDiagram-NUSXRM2D-40LBYw_Y.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DuCcSLqX.js → xychartDiagram-5P7HB3ND-DEPT-__v.js} +1 -1
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +8 -8
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +1254 -337
- package/payload/server/public/assets/ChatInput-CJ50oqWt.css +0 -1
- package/payload/server/public/assets/channel-PC4_IjI1.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-B_pMa-SX.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BdBAgF20.js +0 -1
- package/payload/server/public/assets/clone-CkNt9L9r.js +0 -1
- package/payload/server/public/assets/data-CtanHhM2.js +0 -1
- package/payload/server/public/assets/graph-6DPD6be1.js +0 -1
- package/payload/server/public/assets/public-DltgEfA9.js +0 -7
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-pfk2F6st.js +0 -1
- /package/payload/server/public/assets/{ChatInput-CJo_77bp.js → ChatInput-CvRaT05l.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -816,8 +816,8 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
816
816
|
};
|
|
817
817
|
|
|
818
818
|
// server/index.ts
|
|
819
|
-
import { readFileSync as
|
|
820
|
-
import { resolve as
|
|
819
|
+
import { readFileSync as readFileSync21, existsSync as existsSync24, watchFile } from "fs";
|
|
820
|
+
import { resolve as resolve22, join as join15, basename as basename5 } from "path";
|
|
821
821
|
import { homedir as homedir3 } from "os";
|
|
822
822
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
823
823
|
|
|
@@ -1275,7 +1275,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
1275
1275
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
1276
1276
|
console.error(`${TAG2} draining credential save queue\u2026`);
|
|
1277
1277
|
const timer2 = new Promise(
|
|
1278
|
-
(
|
|
1278
|
+
(resolve23) => setTimeout(() => resolve23("timeout"), timeoutMs)
|
|
1279
1279
|
);
|
|
1280
1280
|
const result = await Promise.race([
|
|
1281
1281
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -1403,11 +1403,11 @@ async function createWaSocket(opts) {
|
|
|
1403
1403
|
return sock;
|
|
1404
1404
|
}
|
|
1405
1405
|
async function waitForConnection(sock) {
|
|
1406
|
-
return new Promise((
|
|
1406
|
+
return new Promise((resolve23, reject) => {
|
|
1407
1407
|
const handler = (update) => {
|
|
1408
1408
|
if (update.connection === "open") {
|
|
1409
1409
|
sock.ev.off("connection.update", handler);
|
|
1410
|
-
|
|
1410
|
+
resolve23();
|
|
1411
1411
|
}
|
|
1412
1412
|
if (update.connection === "close") {
|
|
1413
1413
|
sock.ev.off("connection.update", handler);
|
|
@@ -1521,14 +1521,14 @@ ${inspected}`;
|
|
|
1521
1521
|
return inspect2(err, INSPECT_OPTS2);
|
|
1522
1522
|
}
|
|
1523
1523
|
function withTimeout(label, promise, timeoutMs) {
|
|
1524
|
-
return new Promise((
|
|
1524
|
+
return new Promise((resolve23, reject) => {
|
|
1525
1525
|
const timer2 = setTimeout(() => {
|
|
1526
1526
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
1527
1527
|
}, timeoutMs);
|
|
1528
1528
|
promise.then(
|
|
1529
1529
|
(value) => {
|
|
1530
1530
|
clearTimeout(timer2);
|
|
1531
|
-
|
|
1531
|
+
resolve23(value);
|
|
1532
1532
|
},
|
|
1533
1533
|
(err) => {
|
|
1534
1534
|
clearTimeout(timer2);
|
|
@@ -2063,8 +2063,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
2063
2063
|
const { givenName, familyName } = splitName(input.pushName);
|
|
2064
2064
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
2065
2065
|
let release;
|
|
2066
|
-
const mine = new Promise((
|
|
2067
|
-
release =
|
|
2066
|
+
const mine = new Promise((resolve23) => {
|
|
2067
|
+
release = resolve23;
|
|
2068
2068
|
});
|
|
2069
2069
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
2070
2070
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -3100,11 +3100,11 @@ async function connectWithReconnect(conn) {
|
|
|
3100
3100
|
console.error(
|
|
3101
3101
|
`${TAG12} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
3102
3102
|
);
|
|
3103
|
-
await new Promise((
|
|
3104
|
-
const timer2 = setTimeout(
|
|
3103
|
+
await new Promise((resolve23) => {
|
|
3104
|
+
const timer2 = setTimeout(resolve23, delay);
|
|
3105
3105
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
3106
3106
|
clearTimeout(timer2);
|
|
3107
|
-
|
|
3107
|
+
resolve23();
|
|
3108
3108
|
}, { once: true });
|
|
3109
3109
|
});
|
|
3110
3110
|
}
|
|
@@ -3112,16 +3112,16 @@ async function connectWithReconnect(conn) {
|
|
|
3112
3112
|
}
|
|
3113
3113
|
}
|
|
3114
3114
|
function waitForDisconnectEvent(conn) {
|
|
3115
|
-
return new Promise((
|
|
3115
|
+
return new Promise((resolve23) => {
|
|
3116
3116
|
if (!conn.sock) {
|
|
3117
|
-
|
|
3117
|
+
resolve23();
|
|
3118
3118
|
return;
|
|
3119
3119
|
}
|
|
3120
3120
|
const sock = conn.sock;
|
|
3121
3121
|
const handler = (update) => {
|
|
3122
3122
|
if (update.connection === "close") {
|
|
3123
3123
|
sock.ev.off("connection.update", handler);
|
|
3124
|
-
|
|
3124
|
+
resolve23();
|
|
3125
3125
|
}
|
|
3126
3126
|
};
|
|
3127
3127
|
sock.ev.on("connection.update", handler);
|
|
@@ -3383,8 +3383,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
3383
3383
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
3384
3384
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
3385
3385
|
let resolvePending;
|
|
3386
|
-
const sttPending = new Promise((
|
|
3387
|
-
resolvePending =
|
|
3386
|
+
const sttPending = new Promise((resolve23) => {
|
|
3387
|
+
resolvePending = resolve23;
|
|
3388
3388
|
});
|
|
3389
3389
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
3390
3390
|
try {
|
|
@@ -3788,20 +3788,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
3788
3788
|
|
|
3789
3789
|
// server/routes/health.ts
|
|
3790
3790
|
function checkPort(port2, timeoutMs = 500) {
|
|
3791
|
-
return new Promise((
|
|
3791
|
+
return new Promise((resolve23) => {
|
|
3792
3792
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
3793
3793
|
socket.setTimeout(timeoutMs);
|
|
3794
3794
|
socket.once("connect", () => {
|
|
3795
3795
|
socket.destroy();
|
|
3796
|
-
|
|
3796
|
+
resolve23(true);
|
|
3797
3797
|
});
|
|
3798
3798
|
socket.once("error", () => {
|
|
3799
3799
|
socket.destroy();
|
|
3800
|
-
|
|
3800
|
+
resolve23(false);
|
|
3801
3801
|
});
|
|
3802
3802
|
socket.once("timeout", () => {
|
|
3803
3803
|
socket.destroy();
|
|
3804
|
-
|
|
3804
|
+
resolve23(false);
|
|
3805
3805
|
});
|
|
3806
3806
|
});
|
|
3807
3807
|
}
|
|
@@ -4114,7 +4114,9 @@ async function managerSpawn(opts) {
|
|
|
4114
4114
|
aboutOwner: opts.aboutOwner,
|
|
4115
4115
|
dormantPlugins: opts.dormantPlugins,
|
|
4116
4116
|
activePlugins: opts.activePlugins,
|
|
4117
|
-
specialistDomains: opts.specialistDomains
|
|
4117
|
+
specialistDomains: opts.specialistDomains,
|
|
4118
|
+
sliceToken: opts.sliceToken,
|
|
4119
|
+
personId: opts.personId
|
|
4118
4120
|
})
|
|
4119
4121
|
}).catch((err) => ({ __throw: err instanceof Error ? err.message : String(err) }));
|
|
4120
4122
|
if ("__throw" in res) {
|
|
@@ -4145,6 +4147,169 @@ function managerLogFollowUrl(sessionId) {
|
|
|
4145
4147
|
return `${managerBase()}/${sessionId}/log?follow=1`;
|
|
4146
4148
|
}
|
|
4147
4149
|
|
|
4150
|
+
// app/lib/channel-pty-bridge/public-session-end-review.ts
|
|
4151
|
+
var TAG14 = "[public-session-review]";
|
|
4152
|
+
var CONSUMED_CAP = 500;
|
|
4153
|
+
var consumed = /* @__PURE__ */ new Set();
|
|
4154
|
+
function consumeOnce(sessionId) {
|
|
4155
|
+
if (consumed.has(sessionId)) return false;
|
|
4156
|
+
consumed.add(sessionId);
|
|
4157
|
+
if (consumed.size > CONSUMED_CAP) {
|
|
4158
|
+
const oldest = consumed.values().next().value;
|
|
4159
|
+
if (oldest !== void 0) consumed.delete(oldest);
|
|
4160
|
+
}
|
|
4161
|
+
return true;
|
|
4162
|
+
}
|
|
4163
|
+
function managerBase2() {
|
|
4164
|
+
const port2 = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "public-session-end-review:manager" });
|
|
4165
|
+
return `http://127.0.0.1:${port2}`;
|
|
4166
|
+
}
|
|
4167
|
+
function uiBase() {
|
|
4168
|
+
const port2 = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "public-session-end-review:ui" });
|
|
4169
|
+
return `http://127.0.0.1:${port2}`;
|
|
4170
|
+
}
|
|
4171
|
+
async function fetchJsonl(sessionId) {
|
|
4172
|
+
try {
|
|
4173
|
+
const res = await fetch(`${managerBase2()}/${sessionId}/log?download=1`);
|
|
4174
|
+
if (!res.ok) return null;
|
|
4175
|
+
return await res.text();
|
|
4176
|
+
} catch (err) {
|
|
4177
|
+
console.error(
|
|
4178
|
+
`${TAG14} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
|
|
4179
|
+
);
|
|
4180
|
+
return null;
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
4183
|
+
function countOperatorTurns(jsonl) {
|
|
4184
|
+
let count = 0;
|
|
4185
|
+
for (const raw of jsonl.split("\n")) {
|
|
4186
|
+
const line = raw.trim();
|
|
4187
|
+
if (!line) continue;
|
|
4188
|
+
try {
|
|
4189
|
+
const obj = JSON.parse(line);
|
|
4190
|
+
if (obj.type === "user") count += 1;
|
|
4191
|
+
} catch {
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
return count;
|
|
4195
|
+
}
|
|
4196
|
+
async function fetchPriorWrites(sliceToken, accountId) {
|
|
4197
|
+
const url = `${uiBase()}/api/admin/public-session-context?sliceToken=${encodeURIComponent(sliceToken)}&accountId=${encodeURIComponent(accountId)}`;
|
|
4198
|
+
try {
|
|
4199
|
+
const res = await fetch(url);
|
|
4200
|
+
if (!res.ok) {
|
|
4201
|
+
console.error(
|
|
4202
|
+
`${TAG14} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
|
|
4203
|
+
);
|
|
4204
|
+
return [];
|
|
4205
|
+
}
|
|
4206
|
+
const body = await res.json();
|
|
4207
|
+
return Array.isArray(body.writes) ? body.writes : [];
|
|
4208
|
+
} catch (err) {
|
|
4209
|
+
console.error(
|
|
4210
|
+
`${TAG14} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
4211
|
+
);
|
|
4212
|
+
return [];
|
|
4213
|
+
}
|
|
4214
|
+
}
|
|
4215
|
+
function composeInitialMessage(input) {
|
|
4216
|
+
const priorJson = JSON.stringify(input.priorWrites, null, 2);
|
|
4217
|
+
return [
|
|
4218
|
+
`The gated public-agent session for agent "${input.agentSlug}" has just been reaped. Review the transcript and decide what is worth saving to the visitor's per-visitor memory slice. Dispatch \`database-operator\` for each individual write via the Task tool. Do not call memory-write or memory-update directly.`,
|
|
4219
|
+
"",
|
|
4220
|
+
"<slice-token>",
|
|
4221
|
+
input.sliceToken,
|
|
4222
|
+
"</slice-token>",
|
|
4223
|
+
"",
|
|
4224
|
+
"<person-id>",
|
|
4225
|
+
input.personId ?? "(unknown)",
|
|
4226
|
+
"</person-id>",
|
|
4227
|
+
"",
|
|
4228
|
+
"<prior-writes>",
|
|
4229
|
+
priorJson,
|
|
4230
|
+
"</prior-writes>",
|
|
4231
|
+
"",
|
|
4232
|
+
"<transcript>",
|
|
4233
|
+
input.jsonl,
|
|
4234
|
+
"</transcript>"
|
|
4235
|
+
].join("\n");
|
|
4236
|
+
}
|
|
4237
|
+
async function dispatchReviewer(input, initialMessage) {
|
|
4238
|
+
try {
|
|
4239
|
+
const res = await fetch(`${managerBase2()}/spawn`, {
|
|
4240
|
+
method: "POST",
|
|
4241
|
+
headers: { "content-type": "application/json" },
|
|
4242
|
+
body: JSON.stringify({
|
|
4243
|
+
senderId: `public-session-reviewer-${input.sessionId}`,
|
|
4244
|
+
role: "admin",
|
|
4245
|
+
channel: "webchat",
|
|
4246
|
+
accountId: input.accountId,
|
|
4247
|
+
specialist: "public-session-reviewer",
|
|
4248
|
+
hidden: true,
|
|
4249
|
+
initialMessage,
|
|
4250
|
+
sliceToken: input.sliceToken,
|
|
4251
|
+
personId: input.personId ?? void 0
|
|
4252
|
+
})
|
|
4253
|
+
});
|
|
4254
|
+
if (!res.ok) {
|
|
4255
|
+
return { ok: false, reason: `spawn-status-${res.status}` };
|
|
4256
|
+
}
|
|
4257
|
+
const body = await res.json();
|
|
4258
|
+
return { ok: true, managerSessionId: body.sessionId ?? "unknown" };
|
|
4259
|
+
} catch (err) {
|
|
4260
|
+
return { ok: false, reason: `spawn-threw-${err instanceof Error ? err.message : String(err)}` };
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
async function firePublicSessionEndReview(input) {
|
|
4264
|
+
const sliceShort = input.sliceToken.slice(0, 8);
|
|
4265
|
+
const personField = input.personId ?? "none";
|
|
4266
|
+
const dispatchedAt = Date.now();
|
|
4267
|
+
if (consumed.has(input.sessionId)) {
|
|
4268
|
+
console.log(
|
|
4269
|
+
`${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
4270
|
+
);
|
|
4271
|
+
return;
|
|
4272
|
+
}
|
|
4273
|
+
const jsonl = await fetchJsonl(input.sessionId);
|
|
4274
|
+
if (!jsonl) {
|
|
4275
|
+
console.log(
|
|
4276
|
+
`${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
|
|
4277
|
+
);
|
|
4278
|
+
return;
|
|
4279
|
+
}
|
|
4280
|
+
const operatorTurns = countOperatorTurns(jsonl);
|
|
4281
|
+
if (operatorTurns === 0) {
|
|
4282
|
+
console.log(
|
|
4283
|
+
`${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
|
|
4284
|
+
);
|
|
4285
|
+
return;
|
|
4286
|
+
}
|
|
4287
|
+
const priorWrites = await fetchPriorWrites(input.sliceToken, input.accountId);
|
|
4288
|
+
const initialMessage = composeInitialMessage({
|
|
4289
|
+
agentSlug: input.agentSlug,
|
|
4290
|
+
sliceToken: input.sliceToken,
|
|
4291
|
+
personId: input.personId,
|
|
4292
|
+
jsonl,
|
|
4293
|
+
priorWrites
|
|
4294
|
+
});
|
|
4295
|
+
if (!consumeOnce(input.sessionId)) {
|
|
4296
|
+
console.log(
|
|
4297
|
+
`${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
4298
|
+
);
|
|
4299
|
+
return;
|
|
4300
|
+
}
|
|
4301
|
+
const dispatched = await dispatchReviewer(input, initialMessage);
|
|
4302
|
+
if (!dispatched.ok) {
|
|
4303
|
+
console.error(
|
|
4304
|
+
`${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
|
|
4305
|
+
);
|
|
4306
|
+
return;
|
|
4307
|
+
}
|
|
4308
|
+
console.log(
|
|
4309
|
+
`${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
|
|
4310
|
+
);
|
|
4311
|
+
}
|
|
4312
|
+
|
|
4148
4313
|
// app/lib/channel-pty-bridge/follower.ts
|
|
4149
4314
|
function startFollower(opts) {
|
|
4150
4315
|
const abort = new AbortController();
|
|
@@ -4235,12 +4400,15 @@ function publicIdleMs() {
|
|
|
4235
4400
|
return Number(process.env.CHANNEL_PTY_IDLE_MS ?? String(30 * 6e4));
|
|
4236
4401
|
}
|
|
4237
4402
|
var index = /* @__PURE__ */ new Map();
|
|
4238
|
-
function indexKey(accountId, agentSlug, senderId, liveMemory) {
|
|
4239
|
-
|
|
4403
|
+
function indexKey(accountId, agentSlug, senderId, liveMemory, sliceToken = "") {
|
|
4404
|
+
const slice = sliceToken && sliceToken.length > 0 ? sliceToken : "open";
|
|
4405
|
+
return `${accountId}:${agentSlug}:${liveMemory ? "lm1" : "lm0"}:${slice}:${senderId}`;
|
|
4240
4406
|
}
|
|
4241
4407
|
async function ensureEntry(input) {
|
|
4242
4408
|
startReaper();
|
|
4243
|
-
const
|
|
4409
|
+
const sliceTokenValue = input.sliceToken ?? "";
|
|
4410
|
+
const personIdValue = input.personId ?? null;
|
|
4411
|
+
const key = indexKey(input.accountId, input.agentSlug, input.senderId, input.liveMemory, sliceTokenValue);
|
|
4244
4412
|
const existing = index.get(key);
|
|
4245
4413
|
const tag = tagFor(input.channel);
|
|
4246
4414
|
if (existing) {
|
|
@@ -4264,7 +4432,9 @@ async function ensureEntry(input) {
|
|
|
4264
4432
|
accountId: input.accountId,
|
|
4265
4433
|
agentSlug: input.agentSlug,
|
|
4266
4434
|
liveMemory: input.liveMemory,
|
|
4267
|
-
attachmentDir
|
|
4435
|
+
attachmentDir,
|
|
4436
|
+
sliceToken: sliceTokenValue.length > 0 ? sliceTokenValue : void 0,
|
|
4437
|
+
personId: personIdValue ?? void 0
|
|
4268
4438
|
});
|
|
4269
4439
|
if ("error" in spawned) {
|
|
4270
4440
|
if (spawned.error !== "claude-auth-dead") {
|
|
@@ -4287,7 +4457,9 @@ async function ensureEntry(input) {
|
|
|
4287
4457
|
pendingTurnText: "",
|
|
4288
4458
|
followerAbort: null,
|
|
4289
4459
|
followerRunning: false,
|
|
4290
|
-
liveMemory: input.liveMemory
|
|
4460
|
+
liveMemory: input.liveMemory,
|
|
4461
|
+
sliceToken: sliceTokenValue,
|
|
4462
|
+
personId: personIdValue
|
|
4291
4463
|
};
|
|
4292
4464
|
entry.followerAbort = startFollower({
|
|
4293
4465
|
entry,
|
|
@@ -4314,14 +4486,26 @@ async function writeInput(entry, text) {
|
|
|
4314
4486
|
`${tag} session-resolved senderId=${entry.senderId} action=respawn reason=session-gone-${result.status} sessionId=${entry.sessionId.slice(0, 8)}`
|
|
4315
4487
|
);
|
|
4316
4488
|
if (entry.followerAbort) entry.followerAbort.abort();
|
|
4317
|
-
|
|
4489
|
+
if (entry.role === "public" && entry.sliceToken.length > 0) {
|
|
4490
|
+
void firePublicSessionEndReview({
|
|
4491
|
+
sessionId: entry.sessionId,
|
|
4492
|
+
sliceToken: entry.sliceToken,
|
|
4493
|
+
personId: entry.personId,
|
|
4494
|
+
accountId: entry.accountId,
|
|
4495
|
+
agentSlug: entry.agentSlug,
|
|
4496
|
+
dispatchFor: "exit"
|
|
4497
|
+
});
|
|
4498
|
+
}
|
|
4499
|
+
index.delete(indexKey(entry.accountId, entry.agentSlug, entry.senderId, entry.liveMemory, entry.sliceToken));
|
|
4318
4500
|
const respawned = await ensureEntry({
|
|
4319
4501
|
accountId: entry.accountId,
|
|
4320
4502
|
senderId: entry.senderId,
|
|
4321
4503
|
role: entry.role,
|
|
4322
4504
|
channel: entry.channel,
|
|
4323
4505
|
agentSlug: entry.agentSlug,
|
|
4324
|
-
liveMemory: entry.liveMemory
|
|
4506
|
+
liveMemory: entry.liveMemory,
|
|
4507
|
+
sliceToken: entry.sliceToken.length > 0 ? entry.sliceToken : void 0,
|
|
4508
|
+
personId: entry.personId
|
|
4325
4509
|
});
|
|
4326
4510
|
if (!respawned) return false;
|
|
4327
4511
|
for (const cb of entry.subscribers) respawned.subscribers.add(cb);
|
|
@@ -4359,16 +4543,18 @@ async function dispatchOnce(input) {
|
|
|
4359
4543
|
role: input.role,
|
|
4360
4544
|
channel: input.channel,
|
|
4361
4545
|
agentSlug: input.agentSlug,
|
|
4362
|
-
liveMemory: input.liveMemory
|
|
4546
|
+
liveMemory: input.liveMemory,
|
|
4547
|
+
sliceToken: input.sliceToken,
|
|
4548
|
+
personId: input.personId
|
|
4363
4549
|
});
|
|
4364
4550
|
if (!entry) return { error: "spawn-failed" };
|
|
4365
4551
|
entry.lastInboundAt = Date.now();
|
|
4366
|
-
let
|
|
4552
|
+
let resolve23;
|
|
4367
4553
|
const turnPromise = new Promise((r) => {
|
|
4368
|
-
|
|
4554
|
+
resolve23 = r;
|
|
4369
4555
|
});
|
|
4370
4556
|
const listener = (text) => {
|
|
4371
|
-
|
|
4557
|
+
resolve23(text);
|
|
4372
4558
|
};
|
|
4373
4559
|
entry.subscribers.add(listener);
|
|
4374
4560
|
const writeOk = await writeInput(entry, input.text);
|
|
@@ -4418,11 +4604,75 @@ function startReaper() {
|
|
|
4418
4604
|
);
|
|
4419
4605
|
if (entry.followerAbort) entry.followerAbort.abort();
|
|
4420
4606
|
index.delete(key);
|
|
4607
|
+
if (entry.role === "public" && entry.sliceToken.length > 0) {
|
|
4608
|
+
void firePublicSessionEndReview({
|
|
4609
|
+
sessionId: entry.sessionId,
|
|
4610
|
+
sliceToken: entry.sliceToken,
|
|
4611
|
+
personId: entry.personId,
|
|
4612
|
+
accountId: entry.accountId,
|
|
4613
|
+
agentSlug: entry.agentSlug,
|
|
4614
|
+
dispatchFor: "eviction"
|
|
4615
|
+
});
|
|
4616
|
+
}
|
|
4421
4617
|
void managerDelete(entry.sessionId);
|
|
4422
4618
|
}
|
|
4423
4619
|
}, 6e4);
|
|
4424
4620
|
if (reaperHandle.unref) reaperHandle.unref();
|
|
4425
4621
|
}
|
|
4622
|
+
function handlePublicSessionExit(sessionId) {
|
|
4623
|
+
let found = null;
|
|
4624
|
+
for (const [key2, entry2] of index) {
|
|
4625
|
+
if (entry2.sessionId === sessionId) {
|
|
4626
|
+
found = { key: key2, entry: entry2 };
|
|
4627
|
+
break;
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
if (!found) {
|
|
4631
|
+
console.error(`[public-session-exit] sessionId=${sessionId} result=no-entry`);
|
|
4632
|
+
return;
|
|
4633
|
+
}
|
|
4634
|
+
const { key, entry } = found;
|
|
4635
|
+
if (entry.role !== "public" || entry.sliceToken.length === 0) {
|
|
4636
|
+
return;
|
|
4637
|
+
}
|
|
4638
|
+
if (entry.followerAbort) entry.followerAbort.abort();
|
|
4639
|
+
index.delete(key);
|
|
4640
|
+
void firePublicSessionEndReview({
|
|
4641
|
+
sessionId: entry.sessionId,
|
|
4642
|
+
sliceToken: entry.sliceToken,
|
|
4643
|
+
personId: entry.personId,
|
|
4644
|
+
accountId: entry.accountId,
|
|
4645
|
+
agentSlug: entry.agentSlug,
|
|
4646
|
+
dispatchFor: "exit"
|
|
4647
|
+
});
|
|
4648
|
+
}
|
|
4649
|
+
|
|
4650
|
+
// app/lib/access-session-store.ts
|
|
4651
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4652
|
+
var sessions = /* @__PURE__ */ new Map();
|
|
4653
|
+
function createAccessSession(input) {
|
|
4654
|
+
const sessionId = randomUUID4();
|
|
4655
|
+
const session = {
|
|
4656
|
+
...input,
|
|
4657
|
+
sessionId,
|
|
4658
|
+
createdAt: Date.now()
|
|
4659
|
+
};
|
|
4660
|
+
sessions.set(sessionId, session);
|
|
4661
|
+
return session;
|
|
4662
|
+
}
|
|
4663
|
+
function getAccessSession(sessionId) {
|
|
4664
|
+
return sessions.get(sessionId) ?? null;
|
|
4665
|
+
}
|
|
4666
|
+
function evictAccessSessionsByGrant(grantId) {
|
|
4667
|
+
let dropped = 0;
|
|
4668
|
+
for (const [sessionId, session] of sessions) {
|
|
4669
|
+
if (session.grantId === grantId) {
|
|
4670
|
+
sessions.delete(sessionId);
|
|
4671
|
+
dropped += 1;
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
return dropped;
|
|
4675
|
+
}
|
|
4426
4676
|
|
|
4427
4677
|
// server/routes/chat.ts
|
|
4428
4678
|
var WEBCHAT_TAG = "[webchat-adaptor]";
|
|
@@ -4545,13 +4795,37 @@ ${lines}]`;
|
|
|
4545
4795
|
return c.json({ error: "No default agent configured" }, 500);
|
|
4546
4796
|
}
|
|
4547
4797
|
let liveMemory = false;
|
|
4798
|
+
let accessMode = "open";
|
|
4548
4799
|
try {
|
|
4549
|
-
|
|
4800
|
+
const config = resolveAgentConfig(account.accountDir, agentSlug);
|
|
4801
|
+
liveMemory = config.liveMemory;
|
|
4802
|
+
accessMode = config.accessMode;
|
|
4550
4803
|
} catch (err) {
|
|
4551
4804
|
const m = err instanceof Error ? err.message : String(err);
|
|
4552
4805
|
console.error(`${WEBCHAT_TAG} reject reason=agent-config-failed agentSlug=${agentSlug} message=${m}`);
|
|
4553
4806
|
return c.json({ error: "Agent config invalid" }, 500);
|
|
4554
4807
|
}
|
|
4808
|
+
let sliceToken;
|
|
4809
|
+
let personId = null;
|
|
4810
|
+
if (accessMode === "gated" || accessMode === "paid") {
|
|
4811
|
+
const cookieHeader = c.req.header("cookie") ?? "";
|
|
4812
|
+
const accessSessionId = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="))?.slice("__access_session=".length);
|
|
4813
|
+
const session = accessSessionId ? getAccessSession(accessSessionId) : null;
|
|
4814
|
+
if (!session) {
|
|
4815
|
+
console.error(
|
|
4816
|
+
`${WEBCHAT_TAG} reject reason=gate-required agentSlug=${agentSlug} senderId=${session_key.slice(0, 8)} cookie=${accessSessionId ? "unknown-id" : "absent"}`
|
|
4817
|
+
);
|
|
4818
|
+
return c.json({ error: "gate-required", agentSlug }, 401);
|
|
4819
|
+
}
|
|
4820
|
+
if (session.agentSlug !== agentSlug) {
|
|
4821
|
+
console.error(
|
|
4822
|
+
`${WEBCHAT_TAG} reject reason=gate-agent-mismatch agentSlug=${agentSlug} cookieAgent=${session.agentSlug}`
|
|
4823
|
+
);
|
|
4824
|
+
return c.json({ error: "gate-required", agentSlug }, 401);
|
|
4825
|
+
}
|
|
4826
|
+
sliceToken = session.sliceToken;
|
|
4827
|
+
personId = session.personId;
|
|
4828
|
+
}
|
|
4555
4829
|
const sk = session_key.slice(0, 8);
|
|
4556
4830
|
console.error(
|
|
4557
4831
|
`${WEBCHAT_TAG} inbound from=${sk} bytes=${Buffer.byteLength(fullMessage, "utf8")} attachments=${attachmentCount} accountId=${account.accountId}`
|
|
@@ -4566,6 +4840,8 @@ ${lines}]`;
|
|
|
4566
4840
|
channel: "webchat",
|
|
4567
4841
|
agentSlug,
|
|
4568
4842
|
liveMemory,
|
|
4843
|
+
sliceToken,
|
|
4844
|
+
personId,
|
|
4569
4845
|
text: fullMessage,
|
|
4570
4846
|
timeoutMs: webchatTurnTimeoutMs()
|
|
4571
4847
|
});
|
|
@@ -4617,15 +4893,15 @@ import { readFile as readFile2, stat as stat3 } from "fs/promises";
|
|
|
4617
4893
|
import { realpathSync as realpathSync2, readdirSync as readdirSync2, readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
|
|
4618
4894
|
|
|
4619
4895
|
// app/lib/whatsapp/login.ts
|
|
4620
|
-
import { randomUUID as
|
|
4621
|
-
var
|
|
4896
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
4897
|
+
var TAG15 = "[whatsapp:login]";
|
|
4622
4898
|
var ACTIVE_LOGIN_TTL_MS = 3 * 6e4;
|
|
4623
4899
|
var activeLogins = /* @__PURE__ */ new Map();
|
|
4624
4900
|
function closeSocket(sock) {
|
|
4625
4901
|
try {
|
|
4626
4902
|
sock.ws?.close?.();
|
|
4627
4903
|
} catch (err) {
|
|
4628
|
-
console.warn(`${
|
|
4904
|
+
console.warn(`${TAG15} socket close error during cleanup: ${String(err)}`);
|
|
4629
4905
|
}
|
|
4630
4906
|
}
|
|
4631
4907
|
function resetActiveLogin(accountId) {
|
|
@@ -4648,7 +4924,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
4648
4924
|
const current = activeLogins.get(accountId);
|
|
4649
4925
|
if (current?.id === login.id) {
|
|
4650
4926
|
current.connected = true;
|
|
4651
|
-
console.error(`${
|
|
4927
|
+
console.error(`${TAG15} loginConnectionLoop: connected account=${accountId} attempt=${attempt}`);
|
|
4652
4928
|
}
|
|
4653
4929
|
return;
|
|
4654
4930
|
} catch (err) {
|
|
@@ -4658,7 +4934,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
4658
4934
|
if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
|
|
4659
4935
|
if (attempt >= LOGIN_MAX_RECONNECTS) {
|
|
4660
4936
|
console.error(
|
|
4661
|
-
`${
|
|
4937
|
+
`${TAG15} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
|
|
4662
4938
|
);
|
|
4663
4939
|
current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
|
|
4664
4940
|
} else {
|
|
@@ -4670,7 +4946,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
4670
4946
|
attempt++;
|
|
4671
4947
|
const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
|
|
4672
4948
|
console.error(
|
|
4673
|
-
`${
|
|
4949
|
+
`${TAG15} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
|
|
4674
4950
|
);
|
|
4675
4951
|
closeSocket(current.sock);
|
|
4676
4952
|
await new Promise((r) => setTimeout(r, delay));
|
|
@@ -4681,7 +4957,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
4681
4957
|
current.sock = newSock;
|
|
4682
4958
|
} catch (sockErr) {
|
|
4683
4959
|
console.error(
|
|
4684
|
-
`${
|
|
4960
|
+
`${TAG15} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
|
|
4685
4961
|
);
|
|
4686
4962
|
current.error = `Reconnection failed: ${String(sockErr)}`;
|
|
4687
4963
|
return;
|
|
@@ -4695,7 +4971,7 @@ async function startLogin(opts) {
|
|
|
4695
4971
|
const hasAuth = await authExists(authDir);
|
|
4696
4972
|
const selfId = readSelfId(authDir);
|
|
4697
4973
|
console.error(
|
|
4698
|
-
`${
|
|
4974
|
+
`${TAG15} startLogin account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasQr=${!!existing0.qr}}` : " activeLogin=none")
|
|
4699
4975
|
);
|
|
4700
4976
|
if (hasAuth && !force) {
|
|
4701
4977
|
const who = selfId.e164 ?? selfId.jid ?? "unknown";
|
|
@@ -4707,7 +4983,7 @@ async function startLogin(opts) {
|
|
|
4707
4983
|
await clearAuth(authDir);
|
|
4708
4984
|
const existing = activeLogins.get(accountId);
|
|
4709
4985
|
if (existing && isLoginFresh(existing) && existing.qrDataUrl && !force) {
|
|
4710
|
-
console.error(`${
|
|
4986
|
+
console.error(`${TAG15} startLogin account=${accountId} guard: returning existing QR (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
|
|
4711
4987
|
return {
|
|
4712
4988
|
qrDataUrl: existing.qrDataUrl,
|
|
4713
4989
|
qrRaw: existing.qr,
|
|
@@ -4715,13 +4991,13 @@ async function startLogin(opts) {
|
|
|
4715
4991
|
};
|
|
4716
4992
|
}
|
|
4717
4993
|
if (existing) {
|
|
4718
|
-
console.error(`${
|
|
4994
|
+
console.error(`${TAG15} startLogin account=${accountId} ${force ? "force override" : "stale/no-QR"}, resetting active login`);
|
|
4719
4995
|
}
|
|
4720
4996
|
resetActiveLogin(accountId);
|
|
4721
4997
|
let resolveQr = null;
|
|
4722
4998
|
let rejectQr = null;
|
|
4723
|
-
const qrPromise = new Promise((
|
|
4724
|
-
resolveQr =
|
|
4999
|
+
const qrPromise = new Promise((resolve23, reject) => {
|
|
5000
|
+
resolveQr = resolve23;
|
|
4725
5001
|
rejectQr = reject;
|
|
4726
5002
|
});
|
|
4727
5003
|
const qrTimer = setTimeout(
|
|
@@ -4737,14 +5013,14 @@ async function startLogin(opts) {
|
|
|
4737
5013
|
onQr: (qr2) => {
|
|
4738
5014
|
loginQrCount++;
|
|
4739
5015
|
if (pendingQr) {
|
|
4740
|
-
console.error(`${
|
|
5016
|
+
console.error(`${TAG15} QR rotation #${loginQrCount} received for account=${accountId} \u2014 not forwarded (initial QR already captured)`);
|
|
4741
5017
|
return;
|
|
4742
5018
|
}
|
|
4743
5019
|
pendingQr = qr2;
|
|
4744
5020
|
const current = activeLogins.get(accountId);
|
|
4745
5021
|
if (current && !current.qr) current.qr = qr2;
|
|
4746
5022
|
clearTimeout(qrTimer);
|
|
4747
|
-
console.error(`${
|
|
5023
|
+
console.error(`${TAG15} QR #${loginQrCount} received for account=${accountId} \u2014 forwarding to caller`);
|
|
4748
5024
|
resolveQr?.(qr2);
|
|
4749
5025
|
}
|
|
4750
5026
|
});
|
|
@@ -4756,7 +5032,7 @@ async function startLogin(opts) {
|
|
|
4756
5032
|
const login = {
|
|
4757
5033
|
accountId,
|
|
4758
5034
|
authDir,
|
|
4759
|
-
id:
|
|
5035
|
+
id: randomUUID5(),
|
|
4760
5036
|
sock,
|
|
4761
5037
|
startedAt: Date.now(),
|
|
4762
5038
|
connected: false
|
|
@@ -4764,7 +5040,7 @@ async function startLogin(opts) {
|
|
|
4764
5040
|
activeLogins.set(accountId, login);
|
|
4765
5041
|
if (pendingQr && !login.qr) login.qr = pendingQr;
|
|
4766
5042
|
loginConnectionLoop(accountId, login).catch((err) => {
|
|
4767
|
-
console.error(`${
|
|
5043
|
+
console.error(`${TAG15} loginConnectionLoop unexpected error: ${String(err)}`);
|
|
4768
5044
|
const current = activeLogins.get(accountId);
|
|
4769
5045
|
if (current?.id === login.id) {
|
|
4770
5046
|
current.error = `Unexpected login error: ${String(err)}`;
|
|
@@ -4789,7 +5065,7 @@ async function waitForLogin(opts) {
|
|
|
4789
5065
|
const { accountId, timeoutMs = 6e4 } = opts;
|
|
4790
5066
|
const login = activeLogins.get(accountId);
|
|
4791
5067
|
console.error(
|
|
4792
|
-
`${
|
|
5068
|
+
`${TAG15} waitForLogin account=${accountId} timeout=${timeoutMs}ms` + (login ? ` login={id=${login.id.slice(0, 8)},age=${Math.round((Date.now() - login.startedAt) / 1e3)}s,connected=${login.connected},hasQr=${!!login.qr}}` : " login=none")
|
|
4793
5069
|
);
|
|
4794
5070
|
if (!login) {
|
|
4795
5071
|
return { connected: false, message: "No active WhatsApp login in progress." };
|
|
@@ -4802,7 +5078,7 @@ async function waitForLogin(opts) {
|
|
|
4802
5078
|
while (Date.now() < deadline) {
|
|
4803
5079
|
if (login.connected) {
|
|
4804
5080
|
const selfId = readSelfId(login.authDir);
|
|
4805
|
-
console.error(`${
|
|
5081
|
+
console.error(`${TAG15} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
4806
5082
|
const sock = login.sock;
|
|
4807
5083
|
const authDir = login.authDir;
|
|
4808
5084
|
activeLogins.delete(accountId);
|
|
@@ -4822,7 +5098,7 @@ async function waitForLogin(opts) {
|
|
|
4822
5098
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
4823
5099
|
}
|
|
4824
5100
|
const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
|
|
4825
|
-
console.error(`${
|
|
5101
|
+
console.error(`${TAG15} waitForLogin timeout account=${accountId} elapsed=${elapsed}s \u2014 cleaning up active login`);
|
|
4826
5102
|
resetActiveLogin(accountId);
|
|
4827
5103
|
return { connected: false, message: "Login timed out. Try generating a new QR." };
|
|
4828
5104
|
}
|
|
@@ -4830,7 +5106,7 @@ async function waitForLogin(opts) {
|
|
|
4830
5106
|
// app/lib/whatsapp/config-persist.ts
|
|
4831
5107
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
|
|
4832
5108
|
import { resolve as resolve5, join as join4 } from "path";
|
|
4833
|
-
var
|
|
5109
|
+
var TAG16 = "[whatsapp:config]";
|
|
4834
5110
|
function configPath(accountDir) {
|
|
4835
5111
|
return resolve5(accountDir, "account.json");
|
|
4836
5112
|
}
|
|
@@ -4847,9 +5123,9 @@ function reloadManagerConfig(accountDir) {
|
|
|
4847
5123
|
try {
|
|
4848
5124
|
const config = readConfig(accountDir);
|
|
4849
5125
|
reloadConfig(config);
|
|
4850
|
-
console.error(`${
|
|
5126
|
+
console.error(`${TAG16} reloaded manager config`);
|
|
4851
5127
|
} catch (err) {
|
|
4852
|
-
console.error(`${
|
|
5128
|
+
console.error(`${TAG16} manager config reload failed: ${String(err)}`);
|
|
4853
5129
|
}
|
|
4854
5130
|
}
|
|
4855
5131
|
var E164_PATTERN = /^\+\d{7,15}$/;
|
|
@@ -4875,25 +5151,25 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
|
|
|
4875
5151
|
const adminPhones = wa.adminPhones;
|
|
4876
5152
|
if (!adminPhones.includes(normalized)) {
|
|
4877
5153
|
adminPhones.push(normalized);
|
|
4878
|
-
console.error(`${
|
|
5154
|
+
console.error(`${TAG16} added selfPhone=${normalized} to adminPhones`);
|
|
4879
5155
|
}
|
|
4880
5156
|
} else {
|
|
4881
|
-
console.error(`${
|
|
5157
|
+
console.error(`${TAG16} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
|
|
4882
5158
|
}
|
|
4883
5159
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
4884
5160
|
if (!parsed.success) {
|
|
4885
5161
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
4886
|
-
console.error(`${
|
|
5162
|
+
console.error(`${TAG16} validation failed after pairing: ${msg}`);
|
|
4887
5163
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
4888
5164
|
}
|
|
4889
5165
|
config.whatsapp = parsed.data;
|
|
4890
5166
|
writeConfig(accountDir, config);
|
|
4891
|
-
console.error(`${
|
|
5167
|
+
console.error(`${TAG16} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
|
|
4892
5168
|
reloadManagerConfig(accountDir);
|
|
4893
5169
|
return { ok: true };
|
|
4894
5170
|
} catch (err) {
|
|
4895
5171
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4896
|
-
console.error(`${
|
|
5172
|
+
console.error(`${TAG16} persist failed account=${accountId}: ${msg}`);
|
|
4897
5173
|
return { ok: false, error: msg };
|
|
4898
5174
|
}
|
|
4899
5175
|
}
|
|
@@ -4923,12 +5199,12 @@ function addAdminPhone(accountDir, phone) {
|
|
|
4923
5199
|
}
|
|
4924
5200
|
config.whatsapp = parsed.data;
|
|
4925
5201
|
writeConfig(accountDir, config);
|
|
4926
|
-
console.error(`${
|
|
5202
|
+
console.error(`${TAG16} added admin phone=${normalized}`);
|
|
4927
5203
|
reloadManagerConfig(accountDir);
|
|
4928
5204
|
return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
|
|
4929
5205
|
} catch (err) {
|
|
4930
5206
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4931
|
-
console.error(`${
|
|
5207
|
+
console.error(`${TAG16} addAdminPhone failed: ${msg}`);
|
|
4932
5208
|
return { ok: false, error: msg };
|
|
4933
5209
|
}
|
|
4934
5210
|
}
|
|
@@ -4956,12 +5232,12 @@ function removeAdminPhone(accountDir, phone) {
|
|
|
4956
5232
|
}
|
|
4957
5233
|
config.whatsapp = parsed.data;
|
|
4958
5234
|
writeConfig(accountDir, config);
|
|
4959
|
-
console.error(`${
|
|
5235
|
+
console.error(`${TAG16} removed admin phone=${normalized}`);
|
|
4960
5236
|
reloadManagerConfig(accountDir);
|
|
4961
5237
|
return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
|
|
4962
5238
|
} catch (err) {
|
|
4963
5239
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4964
|
-
console.error(`${
|
|
5240
|
+
console.error(`${TAG16} removeAdminPhone failed: ${msg}`);
|
|
4965
5241
|
return { ok: false, error: msg };
|
|
4966
5242
|
}
|
|
4967
5243
|
}
|
|
@@ -4999,12 +5275,12 @@ function setPublicAgent(accountDir, slug) {
|
|
|
4999
5275
|
}
|
|
5000
5276
|
config.whatsapp = parsed.data;
|
|
5001
5277
|
writeConfig(accountDir, config);
|
|
5002
|
-
console.error(`${
|
|
5278
|
+
console.error(`${TAG16} publicAgent set to ${trimmed}`);
|
|
5003
5279
|
reloadManagerConfig(accountDir);
|
|
5004
5280
|
return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
|
|
5005
5281
|
} catch (err) {
|
|
5006
5282
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5007
|
-
console.error(`${
|
|
5283
|
+
console.error(`${TAG16} setPublicAgent failed: ${msg}`);
|
|
5008
5284
|
return { ok: false, error: msg };
|
|
5009
5285
|
}
|
|
5010
5286
|
}
|
|
@@ -5068,12 +5344,12 @@ function setGroupPublicAgent(accountDir, accountId, groupJid, slug) {
|
|
|
5068
5344
|
}
|
|
5069
5345
|
config.whatsapp = parsed.data;
|
|
5070
5346
|
writeConfig(accountDir, config);
|
|
5071
|
-
console.error(`${
|
|
5347
|
+
console.error(`${TAG16} setGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup} slug=${trimmedSlug}`);
|
|
5072
5348
|
reloadManagerConfig(accountDir);
|
|
5073
5349
|
return { ok: true, message: `Per-group public agent set to "${trimmedSlug}" for group ${trimmedGroup}.` };
|
|
5074
5350
|
} catch (err) {
|
|
5075
5351
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5076
|
-
console.error(`${
|
|
5352
|
+
console.error(`${TAG16} setGroupPublicAgent failed: ${msg}`);
|
|
5077
5353
|
return { ok: false, error: msg };
|
|
5078
5354
|
}
|
|
5079
5355
|
}
|
|
@@ -5100,12 +5376,12 @@ function unsetGroupPublicAgent(accountDir, accountId, groupJid) {
|
|
|
5100
5376
|
}
|
|
5101
5377
|
config.whatsapp = parsed.data;
|
|
5102
5378
|
writeConfig(accountDir, config);
|
|
5103
|
-
console.error(`${
|
|
5379
|
+
console.error(`${TAG16} unsetGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup}`);
|
|
5104
5380
|
reloadManagerConfig(accountDir);
|
|
5105
5381
|
return { ok: true, message: `Per-group public agent override removed for group ${trimmedGroup}.` };
|
|
5106
5382
|
} catch (err) {
|
|
5107
5383
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5108
|
-
console.error(`${
|
|
5384
|
+
console.error(`${TAG16} unsetGroupPublicAgent failed: ${msg}`);
|
|
5109
5385
|
return { ok: false, error: msg };
|
|
5110
5386
|
}
|
|
5111
5387
|
}
|
|
@@ -5126,17 +5402,17 @@ function updateConfig(accountDir, fields) {
|
|
|
5126
5402
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
5127
5403
|
if (!parsed.success) {
|
|
5128
5404
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
5129
|
-
console.error(`${
|
|
5405
|
+
console.error(`${TAG16} update validation failed: ${msg}`);
|
|
5130
5406
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
5131
5407
|
}
|
|
5132
5408
|
config.whatsapp = parsed.data;
|
|
5133
5409
|
writeConfig(accountDir, config);
|
|
5134
|
-
console.error(`${
|
|
5410
|
+
console.error(`${TAG16} updated fields=[${fieldNames.join(",")}]`);
|
|
5135
5411
|
reloadManagerConfig(accountDir);
|
|
5136
5412
|
return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
|
|
5137
5413
|
} catch (err) {
|
|
5138
5414
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5139
|
-
console.error(`${
|
|
5415
|
+
console.error(`${TAG16} updateConfig failed: ${msg}`);
|
|
5140
5416
|
return { ok: false, error: msg };
|
|
5141
5417
|
}
|
|
5142
5418
|
}
|
|
@@ -5262,17 +5538,17 @@ function serializeWhatsAppSchema() {
|
|
|
5262
5538
|
}
|
|
5263
5539
|
|
|
5264
5540
|
// server/routes/whatsapp.ts
|
|
5265
|
-
var
|
|
5541
|
+
var TAG17 = "[whatsapp:api]";
|
|
5266
5542
|
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
5267
5543
|
var app3 = new Hono();
|
|
5268
5544
|
app3.get("/status", (c) => {
|
|
5269
5545
|
try {
|
|
5270
5546
|
const status = getStatus();
|
|
5271
5547
|
const summary = status.map((a) => `${a.accountId}:${a.connected ? "up" : "down"}`).join(", ");
|
|
5272
|
-
console.error(`${
|
|
5548
|
+
console.error(`${TAG17} status accounts=${status.length} [${summary}]`);
|
|
5273
5549
|
return c.json({ accounts: status });
|
|
5274
5550
|
} catch (err) {
|
|
5275
|
-
console.error(`${
|
|
5551
|
+
console.error(`${TAG17} status error: ${String(err)}`);
|
|
5276
5552
|
return c.json({ error: String(err) }, 500);
|
|
5277
5553
|
}
|
|
5278
5554
|
});
|
|
@@ -5283,10 +5559,10 @@ app3.post("/login/start", async (c) => {
|
|
|
5283
5559
|
const force = body.force ?? false;
|
|
5284
5560
|
const authDir = join5(MAXY_DIR, "credentials", "whatsapp", accountId);
|
|
5285
5561
|
const result = await startLogin({ accountId, authDir, force });
|
|
5286
|
-
console.error(`${
|
|
5562
|
+
console.error(`${TAG17} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
|
|
5287
5563
|
return c.json(result);
|
|
5288
5564
|
} catch (err) {
|
|
5289
|
-
console.error(`${
|
|
5565
|
+
console.error(`${TAG17} login/start error: ${String(err)}`);
|
|
5290
5566
|
return c.json({ error: String(err) }, 500);
|
|
5291
5567
|
}
|
|
5292
5568
|
});
|
|
@@ -5301,7 +5577,7 @@ app3.post("/login/wait", async (c) => {
|
|
|
5301
5577
|
try {
|
|
5302
5578
|
await registerLoginSocket(accountId, result.sock, result.authDir);
|
|
5303
5579
|
} catch (regErr) {
|
|
5304
|
-
console.error(`${
|
|
5580
|
+
console.error(`${TAG17} registerLoginSocket failed account=${accountId}: ${String(regErr)}`);
|
|
5305
5581
|
}
|
|
5306
5582
|
try {
|
|
5307
5583
|
const account = resolveAccount();
|
|
@@ -5309,16 +5585,16 @@ app3.post("/login/wait", async (c) => {
|
|
|
5309
5585
|
const persistResult = persistAfterPairing(account.accountDir, accountId, result.selfPhone ?? null);
|
|
5310
5586
|
configPersisted = persistResult.ok;
|
|
5311
5587
|
if (!persistResult.ok) {
|
|
5312
|
-
console.error(`${
|
|
5588
|
+
console.error(`${TAG17} config persist failed account=${accountId}: ${persistResult.error}`);
|
|
5313
5589
|
}
|
|
5314
5590
|
} else {
|
|
5315
|
-
console.error(`${
|
|
5591
|
+
console.error(`${TAG17} config persist skipped \u2014 no account resolved`);
|
|
5316
5592
|
}
|
|
5317
5593
|
} catch (persistErr) {
|
|
5318
|
-
console.error(`${
|
|
5594
|
+
console.error(`${TAG17} config persist error account=${accountId}: ${String(persistErr)}`);
|
|
5319
5595
|
}
|
|
5320
5596
|
}
|
|
5321
|
-
console.error(`${
|
|
5597
|
+
console.error(`${TAG17} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${configPersisted}`);
|
|
5322
5598
|
return c.json({
|
|
5323
5599
|
connected: result.connected,
|
|
5324
5600
|
message: result.message,
|
|
@@ -5326,7 +5602,7 @@ app3.post("/login/wait", async (c) => {
|
|
|
5326
5602
|
configPersisted
|
|
5327
5603
|
});
|
|
5328
5604
|
} catch (err) {
|
|
5329
|
-
console.error(`${
|
|
5605
|
+
console.error(`${TAG17} login/wait error: ${String(err)}`);
|
|
5330
5606
|
return c.json({ error: String(err) }, 500);
|
|
5331
5607
|
}
|
|
5332
5608
|
});
|
|
@@ -5337,7 +5613,7 @@ app3.post("/disconnect", async (c) => {
|
|
|
5337
5613
|
await stopConnection(accountId);
|
|
5338
5614
|
return c.json({ disconnected: true, accountId });
|
|
5339
5615
|
} catch (err) {
|
|
5340
|
-
console.error(`${
|
|
5616
|
+
console.error(`${TAG17} disconnect error: ${String(err)}`);
|
|
5341
5617
|
return c.json({ error: String(err) }, 500);
|
|
5342
5618
|
}
|
|
5343
5619
|
});
|
|
@@ -5348,7 +5624,7 @@ app3.post("/reconnect", async (c) => {
|
|
|
5348
5624
|
await startConnection(accountId);
|
|
5349
5625
|
return c.json({ reconnecting: true, accountId });
|
|
5350
5626
|
} catch (err) {
|
|
5351
|
-
console.error(`${
|
|
5627
|
+
console.error(`${TAG17} reconnect error: ${String(err)}`);
|
|
5352
5628
|
return c.json({ error: String(err) }, 500);
|
|
5353
5629
|
}
|
|
5354
5630
|
});
|
|
@@ -5367,7 +5643,7 @@ app3.post("/send", async (c) => {
|
|
|
5367
5643
|
const result = await sendTextMessage(sock, to, text, { accountId });
|
|
5368
5644
|
return c.json(result);
|
|
5369
5645
|
} catch (err) {
|
|
5370
|
-
console.error(`${
|
|
5646
|
+
console.error(`${TAG17} send error: ${String(err)}`);
|
|
5371
5647
|
return c.json({ error: String(err) }, 500);
|
|
5372
5648
|
}
|
|
5373
5649
|
});
|
|
@@ -5388,7 +5664,7 @@ app3.post("/config", async (c) => {
|
|
|
5388
5664
|
return c.json({ ok: false, error: 'Missing required field "phone" (E.164 format, e.g. +441234567890).' }, 400);
|
|
5389
5665
|
}
|
|
5390
5666
|
const result = addAdminPhone(account.accountDir, phone);
|
|
5391
|
-
console.error(`${
|
|
5667
|
+
console.error(`${TAG17} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
|
|
5392
5668
|
return c.json(result, result.ok ? 200 : 400);
|
|
5393
5669
|
}
|
|
5394
5670
|
case "remove-admin-phone": {
|
|
@@ -5396,12 +5672,12 @@ app3.post("/config", async (c) => {
|
|
|
5396
5672
|
return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
|
|
5397
5673
|
}
|
|
5398
5674
|
const result = removeAdminPhone(account.accountDir, phone);
|
|
5399
|
-
console.error(`${
|
|
5675
|
+
console.error(`${TAG17} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
|
|
5400
5676
|
return c.json(result, result.ok ? 200 : 400);
|
|
5401
5677
|
}
|
|
5402
5678
|
case "list-admin-phones": {
|
|
5403
5679
|
const phones = readAdminPhones(account.accountDir);
|
|
5404
|
-
console.error(`${
|
|
5680
|
+
console.error(`${TAG17} config action=list-admin-phones count=${phones.length}`);
|
|
5405
5681
|
return c.json({ ok: true, phones });
|
|
5406
5682
|
}
|
|
5407
5683
|
case "set-public-agent": {
|
|
@@ -5409,14 +5685,14 @@ app3.post("/config", async (c) => {
|
|
|
5409
5685
|
return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
|
|
5410
5686
|
}
|
|
5411
5687
|
const result = setPublicAgent(account.accountDir, slug);
|
|
5412
|
-
console.error(`${
|
|
5688
|
+
console.error(`${TAG17} config action=set-public-agent slug=${slug} ok=${result.ok}`);
|
|
5413
5689
|
return c.json(result, result.ok ? 200 : 400);
|
|
5414
5690
|
}
|
|
5415
5691
|
case "get-public-agent": {
|
|
5416
5692
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
5417
5693
|
const targetGroup = typeof groupJid === "string" && groupJid.trim() ? groupJid.trim() : void 0;
|
|
5418
5694
|
const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount, groupJid: targetGroup });
|
|
5419
|
-
console.error(`${
|
|
5695
|
+
console.error(`${TAG17} config action=get-public-agent accountId=${targetAccount} groupJid=${targetGroup ?? "none"} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
|
|
5420
5696
|
return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
|
|
5421
5697
|
}
|
|
5422
5698
|
case "set-group-public-agent": {
|
|
@@ -5428,7 +5704,7 @@ app3.post("/config", async (c) => {
|
|
|
5428
5704
|
}
|
|
5429
5705
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
5430
5706
|
const result = setGroupPublicAgent(account.accountDir, targetAccount, groupJid, slug);
|
|
5431
|
-
console.error(`${
|
|
5707
|
+
console.error(`${TAG17} config action=set-group-public-agent accountId=${targetAccount} groupJid=${groupJid} slug=${slug} ok=${result.ok}`);
|
|
5432
5708
|
return c.json(result, result.ok ? 200 : 400);
|
|
5433
5709
|
}
|
|
5434
5710
|
case "unset-group-public-agent": {
|
|
@@ -5437,7 +5713,7 @@ app3.post("/config", async (c) => {
|
|
|
5437
5713
|
}
|
|
5438
5714
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
5439
5715
|
const result = unsetGroupPublicAgent(account.accountDir, targetAccount, groupJid);
|
|
5440
|
-
console.error(`${
|
|
5716
|
+
console.error(`${TAG17} config action=unset-group-public-agent accountId=${targetAccount} groupJid=${groupJid} ok=${result.ok}`);
|
|
5441
5717
|
return c.json(result, result.ok ? 200 : 400);
|
|
5442
5718
|
}
|
|
5443
5719
|
case "list-public-agents": {
|
|
@@ -5454,26 +5730,26 @@ app3.post("/config", async (c) => {
|
|
|
5454
5730
|
const config = JSON.parse(readFileSync6(configPath2, "utf-8"));
|
|
5455
5731
|
agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
|
|
5456
5732
|
} catch {
|
|
5457
|
-
console.error(`${
|
|
5733
|
+
console.error(`${TAG17} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
|
|
5458
5734
|
}
|
|
5459
5735
|
}
|
|
5460
5736
|
} catch (err) {
|
|
5461
|
-
console.error(`${
|
|
5737
|
+
console.error(`${TAG17} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
|
|
5462
5738
|
}
|
|
5463
5739
|
}
|
|
5464
|
-
console.error(`${
|
|
5740
|
+
console.error(`${TAG17} config action=list-public-agents count=${agents.length}`);
|
|
5465
5741
|
return c.json({ ok: true, agents });
|
|
5466
5742
|
}
|
|
5467
5743
|
case "schema": {
|
|
5468
5744
|
const text = serializeWhatsAppSchema();
|
|
5469
|
-
console.error(`${
|
|
5745
|
+
console.error(`${TAG17} config action=schema`);
|
|
5470
5746
|
return c.json({ ok: true, text });
|
|
5471
5747
|
}
|
|
5472
5748
|
case "list-groups": {
|
|
5473
5749
|
const groupAccountId = accountId ?? "default";
|
|
5474
5750
|
const sock = getSocket(groupAccountId);
|
|
5475
5751
|
if (!sock) {
|
|
5476
|
-
console.error(`${
|
|
5752
|
+
console.error(`${TAG17} config action=list-groups error="not connected" accountId=${groupAccountId}`);
|
|
5477
5753
|
return c.json({ ok: false, error: `WhatsApp account "${groupAccountId}" is not connected. Connect first, then retry.` });
|
|
5478
5754
|
}
|
|
5479
5755
|
try {
|
|
@@ -5483,10 +5759,10 @@ app3.post("/config", async (c) => {
|
|
|
5483
5759
|
name: g.subject ?? g.id,
|
|
5484
5760
|
participantCount: Array.isArray(g.participants) ? g.participants.length : 0
|
|
5485
5761
|
}));
|
|
5486
|
-
console.error(`${
|
|
5762
|
+
console.error(`${TAG17} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
|
|
5487
5763
|
return c.json({ ok: true, groups });
|
|
5488
5764
|
} catch (err) {
|
|
5489
|
-
console.error(`${
|
|
5765
|
+
console.error(`${TAG17} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
|
|
5490
5766
|
return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
|
|
5491
5767
|
}
|
|
5492
5768
|
}
|
|
@@ -5496,12 +5772,12 @@ app3.post("/config", async (c) => {
|
|
|
5496
5772
|
}
|
|
5497
5773
|
const result = updateConfig(account.accountDir, fields);
|
|
5498
5774
|
const fieldNames = Object.keys(fields);
|
|
5499
|
-
console.error(`${
|
|
5775
|
+
console.error(`${TAG17} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
|
|
5500
5776
|
return c.json(result, result.ok ? 200 : 400);
|
|
5501
5777
|
}
|
|
5502
5778
|
case "get-config": {
|
|
5503
5779
|
const waConfig = getConfig(account.accountDir);
|
|
5504
|
-
console.error(`${
|
|
5780
|
+
console.error(`${TAG17} config action=get-config`);
|
|
5505
5781
|
return c.json({ ok: true, config: waConfig });
|
|
5506
5782
|
}
|
|
5507
5783
|
default:
|
|
@@ -5511,7 +5787,7 @@ app3.post("/config", async (c) => {
|
|
|
5511
5787
|
);
|
|
5512
5788
|
}
|
|
5513
5789
|
} catch (err) {
|
|
5514
|
-
console.error(`${
|
|
5790
|
+
console.error(`${TAG17} config error: ${String(err)}`);
|
|
5515
5791
|
return c.json({ ok: false, error: String(err) }, 500);
|
|
5516
5792
|
}
|
|
5517
5793
|
});
|
|
@@ -5533,16 +5809,16 @@ app3.post("/send-document", async (c) => {
|
|
|
5533
5809
|
const accountResolved = realpathSync2(accountDir);
|
|
5534
5810
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
5535
5811
|
const sanitised = filePath.replace(accountDir, "<account>/");
|
|
5536
|
-
console.error(`${
|
|
5812
|
+
console.error(`${TAG17} send-document REJECTED path=${sanitised} reason=outside_account_directory`);
|
|
5537
5813
|
return c.json({ error: "Access denied: file is outside the account directory" }, 403);
|
|
5538
5814
|
}
|
|
5539
5815
|
} catch (err) {
|
|
5540
5816
|
const code = err.code;
|
|
5541
5817
|
if (code === "ENOENT") {
|
|
5542
|
-
console.error(`${
|
|
5818
|
+
console.error(`${TAG17} send-document ENOENT path=${filePath}`);
|
|
5543
5819
|
return c.json({ error: `File not found: ${filePath}` }, 404);
|
|
5544
5820
|
}
|
|
5545
|
-
console.error(`${
|
|
5821
|
+
console.error(`${TAG17} send-document path error: ${String(err)}`);
|
|
5546
5822
|
return c.json({ error: String(err) }, 500);
|
|
5547
5823
|
}
|
|
5548
5824
|
const fileStat = await stat3(resolvedPath);
|
|
@@ -5564,11 +5840,11 @@ app3.post("/send-document", async (c) => {
|
|
|
5564
5840
|
caption
|
|
5565
5841
|
}, { accountId });
|
|
5566
5842
|
console.error(
|
|
5567
|
-
`${
|
|
5843
|
+
`${TAG17} send-document to=${to} size=${fileStat.size} mime=${mimetype} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
|
|
5568
5844
|
);
|
|
5569
5845
|
return c.json(result);
|
|
5570
5846
|
} catch (err) {
|
|
5571
|
-
console.error(`${
|
|
5847
|
+
console.error(`${TAG17} send-document error: ${String(err)}`);
|
|
5572
5848
|
return c.json({ error: String(err) }, 500);
|
|
5573
5849
|
}
|
|
5574
5850
|
});
|
|
@@ -5578,11 +5854,11 @@ app3.get("/activity", (c) => {
|
|
|
5578
5854
|
const result = getChannelActivity(accountId);
|
|
5579
5855
|
const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
|
|
5580
5856
|
console.error(
|
|
5581
|
-
`${
|
|
5857
|
+
`${TAG17} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
|
|
5582
5858
|
);
|
|
5583
5859
|
return c.json(result);
|
|
5584
5860
|
} catch (err) {
|
|
5585
|
-
console.error(`${
|
|
5861
|
+
console.error(`${TAG17} activity error: ${String(err)}`);
|
|
5586
5862
|
return c.json({ error: String(err) }, 500);
|
|
5587
5863
|
}
|
|
5588
5864
|
});
|
|
@@ -5601,10 +5877,10 @@ app3.get("/conversations", (c) => {
|
|
|
5601
5877
|
};
|
|
5602
5878
|
});
|
|
5603
5879
|
conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
|
|
5604
|
-
console.error(`${
|
|
5880
|
+
console.error(`${TAG17} conversations account=${accountId} count=${conversations.length}`);
|
|
5605
5881
|
return c.json({ conversations });
|
|
5606
5882
|
} catch (err) {
|
|
5607
|
-
console.error(`${
|
|
5883
|
+
console.error(`${TAG17} conversations error: ${String(err)}`);
|
|
5608
5884
|
return c.json({ error: String(err) }, 500);
|
|
5609
5885
|
}
|
|
5610
5886
|
});
|
|
@@ -5619,10 +5895,10 @@ app3.get("/messages", (c) => {
|
|
|
5619
5895
|
const limit = limitParam ? parseInt(limitParam, 10) : void 0;
|
|
5620
5896
|
const effectiveLimit = limit && !Number.isNaN(limit) && limit > 0 ? limit : void 0;
|
|
5621
5897
|
const messages = getMessages(accountId, jid, effectiveLimit);
|
|
5622
|
-
console.error(`${
|
|
5898
|
+
console.error(`${TAG17} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
|
|
5623
5899
|
return c.json({ messages });
|
|
5624
5900
|
} catch (err) {
|
|
5625
|
-
console.error(`${
|
|
5901
|
+
console.error(`${TAG17} messages error: ${String(err)}`);
|
|
5626
5902
|
return c.json({ error: String(err) }, 500);
|
|
5627
5903
|
}
|
|
5628
5904
|
});
|
|
@@ -5703,7 +5979,7 @@ app3.get("/conversation-graph-state", async (c) => {
|
|
|
5703
5979
|
}
|
|
5704
5980
|
} catch (err) {
|
|
5705
5981
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5706
|
-
console.error(`${
|
|
5982
|
+
console.error(`${TAG17} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
|
|
5707
5983
|
return c.json({ error: `Graph query failed: ${msg}`, cacheKey, cypher: cypher.trim() }, 500);
|
|
5708
5984
|
}
|
|
5709
5985
|
const ms = Date.now() - t0;
|
|
@@ -5716,7 +5992,7 @@ app3.get("/conversation-graph-state", async (c) => {
|
|
|
5716
5992
|
ms
|
|
5717
5993
|
});
|
|
5718
5994
|
} catch (err) {
|
|
5719
|
-
console.error(`${
|
|
5995
|
+
console.error(`${TAG17} conversation-graph-state error: ${String(err)}`);
|
|
5720
5996
|
return c.json({ error: String(err) }, 500);
|
|
5721
5997
|
}
|
|
5722
5998
|
});
|
|
@@ -5728,12 +6004,12 @@ app3.get("/group-info", async (c) => {
|
|
|
5728
6004
|
return c.json({ error: "Missing required parameter: jid" }, 400);
|
|
5729
6005
|
}
|
|
5730
6006
|
if (!isGroupJid(jid)) {
|
|
5731
|
-
console.error(`${
|
|
6007
|
+
console.error(`${TAG17} group-info error="not a group JID" jid=${jid} account=${accountId}`);
|
|
5732
6008
|
return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
|
|
5733
6009
|
}
|
|
5734
6010
|
const sock = getSocket(accountId);
|
|
5735
6011
|
if (!sock) {
|
|
5736
|
-
console.error(`${
|
|
6012
|
+
console.error(`${TAG17} group-info error="not connected" account=${accountId}`);
|
|
5737
6013
|
return c.json({ error: `WhatsApp account "${accountId}" is not connected. Connect first, then retry.` }, 400);
|
|
5738
6014
|
}
|
|
5739
6015
|
const meta = await sock.groupMetadata(jid);
|
|
@@ -5746,10 +6022,10 @@ app3.get("/group-info", async (c) => {
|
|
|
5746
6022
|
participantCount: meta.participants.length,
|
|
5747
6023
|
participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
|
|
5748
6024
|
};
|
|
5749
|
-
console.error(`${
|
|
6025
|
+
console.error(`${TAG17} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
|
|
5750
6026
|
return c.json(result);
|
|
5751
6027
|
} catch (err) {
|
|
5752
|
-
console.error(`${
|
|
6028
|
+
console.error(`${TAG17} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
|
|
5753
6029
|
return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
|
|
5754
6030
|
}
|
|
5755
6031
|
});
|
|
@@ -5758,7 +6034,7 @@ var whatsapp_default = app3;
|
|
|
5758
6034
|
// server/routes/onboarding.ts
|
|
5759
6035
|
import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
|
|
5760
6036
|
import { openSync, closeSync, writeFileSync as writeFileSync5, writeSync, existsSync as existsSync6, readFileSync as readFileSync8, unlinkSync } from "fs";
|
|
5761
|
-
import { createHash, randomUUID as
|
|
6037
|
+
import { createHash, randomUUID as randomUUID6 } from "crypto";
|
|
5762
6038
|
|
|
5763
6039
|
// ../lib/admins-write/src/index.ts
|
|
5764
6040
|
import { existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4, renameSync, mkdirSync as mkdirSync2, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
|
|
@@ -6034,7 +6310,7 @@ app4.post("/set-pin", async (c) => {
|
|
|
6034
6310
|
const hash = hashPin(body.pin);
|
|
6035
6311
|
const account = resolveAccount();
|
|
6036
6312
|
const existingOwnerUserId = account?.config.admins?.find((a) => a.role === "owner")?.userId;
|
|
6037
|
-
const userId = existingOwnerUserId ??
|
|
6313
|
+
const userId = existingOwnerUserId ?? randomUUID6();
|
|
6038
6314
|
if (existingOwnerUserId) {
|
|
6039
6315
|
console.log(`[set-pin] reusing existing owner userId=${userId.slice(0, 8)}\u2026 (change-PIN preserves identity)`);
|
|
6040
6316
|
} else {
|
|
@@ -6295,7 +6571,7 @@ var client_error_default = app5;
|
|
|
6295
6571
|
import { readFileSync as readFileSync9, readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync8 } from "fs";
|
|
6296
6572
|
import { join as join8 } from "path";
|
|
6297
6573
|
import { createHash as createHash2 } from "crypto";
|
|
6298
|
-
import { randomUUID as
|
|
6574
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
6299
6575
|
function hashPin2(pin) {
|
|
6300
6576
|
return createHash2("sha256").update(pin).digest("hex");
|
|
6301
6577
|
}
|
|
@@ -6352,7 +6628,7 @@ async function resolveUserIdentity(accountId, userId) {
|
|
|
6352
6628
|
async function createAdminSession(accountId, thinkingView, userId, userName, role, avatar) {
|
|
6353
6629
|
const account = resolveAccount();
|
|
6354
6630
|
const effectiveThinkingView = thinkingView ?? account?.config.thinkingView ?? "default";
|
|
6355
|
-
const signedSessionToken =
|
|
6631
|
+
const signedSessionToken = randomUUID7();
|
|
6356
6632
|
const cacheKey = fingerprintSessionKey(signedSessionToken);
|
|
6357
6633
|
registerSession(cacheKey, "admin", accountId, void 0, userId, userName, role);
|
|
6358
6634
|
if (userId) setWantsPriorConversation(cacheKey);
|
|
@@ -7223,7 +7499,7 @@ app14.get("/", requireAdminSession, async (c) => {
|
|
|
7223
7499
|
if (!userId) return c.json({ error: "User identity required \u2014 authenticate with users.json PIN" }, 401);
|
|
7224
7500
|
try {
|
|
7225
7501
|
const flushed = await listAdminSessions(accountId, userId, 20);
|
|
7226
|
-
const
|
|
7502
|
+
const sessions2 = flushed.map((r) => ({
|
|
7227
7503
|
sessionId: r.sessionId,
|
|
7228
7504
|
cacheKey: null,
|
|
7229
7505
|
name: r.name,
|
|
@@ -7231,15 +7507,15 @@ app14.get("/", requireAdminSession, async (c) => {
|
|
|
7231
7507
|
phase: "flushed",
|
|
7232
7508
|
channel: r.channel
|
|
7233
7509
|
})).sort((a, b) => a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0).slice(0, 20);
|
|
7234
|
-
const channelCounts =
|
|
7510
|
+
const channelCounts = sessions2.reduce((acc, s) => {
|
|
7235
7511
|
const k = s.channel ?? "unknown";
|
|
7236
7512
|
acc[k] = (acc[k] ?? 0) + 1;
|
|
7237
7513
|
return acc;
|
|
7238
7514
|
}, {});
|
|
7239
7515
|
console.error(
|
|
7240
|
-
`[conversations-list] render rows=${
|
|
7516
|
+
`[conversations-list] render rows=${sessions2.length} channels=${JSON.stringify(channelCounts)}`
|
|
7241
7517
|
);
|
|
7242
|
-
return c.json({ sessions });
|
|
7518
|
+
return c.json({ sessions: sessions2 });
|
|
7243
7519
|
} catch (err) {
|
|
7244
7520
|
console.error(`[sessions-list] Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7245
7521
|
return c.json({ error: "Failed to fetch sessions" }, 500);
|
|
@@ -7298,11 +7574,11 @@ app14.delete("/:id", requireAdminSession, async (c) => {
|
|
|
7298
7574
|
const owned = await verifyConversationOwnership(sessionId, accountId);
|
|
7299
7575
|
if (!owned) return c.json({ error: "Conversation not found" }, 404);
|
|
7300
7576
|
const managerPort = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "admin-conversation-delete" });
|
|
7301
|
-
const
|
|
7577
|
+
const managerBase4 = `http://127.0.0.1:${managerPort}`;
|
|
7302
7578
|
const outcome = await cascadeAdminConversationDelete({
|
|
7303
7579
|
sessionId,
|
|
7304
7580
|
accountId,
|
|
7305
|
-
managerBase:
|
|
7581
|
+
managerBase: managerBase4
|
|
7306
7582
|
});
|
|
7307
7583
|
if (outcome.ok) return c.json({ ok: true, claudeSessionIds: outcome.claudeSessionIds });
|
|
7308
7584
|
if (outcome.reason === "pty-still-alive") {
|
|
@@ -7948,7 +8224,7 @@ function resolveTunnelUrl() {
|
|
|
7948
8224
|
if (!state) return null;
|
|
7949
8225
|
return `https://${hostname2}.${state.domain}`;
|
|
7950
8226
|
}
|
|
7951
|
-
var
|
|
8227
|
+
var TAG18 = "[claude-session-manager:wrapper]";
|
|
7952
8228
|
async function refuseIfClaudeAuthDead(c, route, sessionId) {
|
|
7953
8229
|
const auth = await ensureAuth();
|
|
7954
8230
|
if (auth.status !== "dead" && auth.status !== "missing") return null;
|
|
@@ -7960,7 +8236,7 @@ async function refuseIfClaudeAuthDead(c, route, sessionId) {
|
|
|
7960
8236
|
503
|
|
7961
8237
|
);
|
|
7962
8238
|
}
|
|
7963
|
-
function
|
|
8239
|
+
function managerBase3() {
|
|
7964
8240
|
const port2 = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "claude-session-manager:wrapper" });
|
|
7965
8241
|
return `http://127.0.0.1:${port2}`;
|
|
7966
8242
|
}
|
|
@@ -7971,12 +8247,12 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
7971
8247
|
const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
|
|
7972
8248
|
const ownerMs = Date.now() - ownerStart;
|
|
7973
8249
|
const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
|
|
7974
|
-
console.log(`${
|
|
8250
|
+
console.log(`${TAG18} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
|
|
7975
8251
|
const dormantPlugins = computeDormantPlugins(args.senderId);
|
|
7976
8252
|
const activePlugins = computeActivePlugins(args.senderId);
|
|
7977
8253
|
const specialistDomains = computeSpecialistDomains(args.senderId);
|
|
7978
8254
|
const tunnelUrl = resolveTunnelUrl();
|
|
7979
|
-
console.log(`${
|
|
8255
|
+
console.log(`${TAG18} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
|
|
7980
8256
|
const upstreamPayload = JSON.stringify({
|
|
7981
8257
|
senderId: args.senderId,
|
|
7982
8258
|
// Task 205 — pass userId through to the manager so it lands as
|
|
@@ -8003,24 +8279,24 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
8003
8279
|
// unshapely values.
|
|
8004
8280
|
conversationNodeId: args.conversationNodeId
|
|
8005
8281
|
});
|
|
8006
|
-
console.log(`${
|
|
8282
|
+
console.log(`${TAG18} forward-spawn-start managerBase=${managerBase3()} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
|
|
8007
8283
|
const forwardStart = Date.now();
|
|
8008
|
-
const upstream = await fetch(`${
|
|
8284
|
+
const upstream = await fetch(`${managerBase3()}/spawn`, {
|
|
8009
8285
|
method: "POST",
|
|
8010
8286
|
headers: { "content-type": "application/json" },
|
|
8011
8287
|
body: upstreamPayload
|
|
8012
8288
|
}).catch((err) => {
|
|
8013
|
-
console.error(`${
|
|
8289
|
+
console.error(`${TAG18} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
|
|
8014
8290
|
return null;
|
|
8015
8291
|
});
|
|
8016
8292
|
if (!upstream) return {
|
|
8017
8293
|
response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
|
|
8018
8294
|
claudeSessionId: null
|
|
8019
8295
|
};
|
|
8020
|
-
console.log(`${
|
|
8296
|
+
console.log(`${TAG18} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
|
|
8021
8297
|
if (args.initialMessage) {
|
|
8022
8298
|
const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
|
|
8023
|
-
console.log(`${
|
|
8299
|
+
console.log(`${TAG18} initial-message-inlined bytes=${inputBytes}`);
|
|
8024
8300
|
}
|
|
8025
8301
|
const bodyText = await upstream.text().catch(() => "");
|
|
8026
8302
|
let claudeSessionId = null;
|
|
@@ -8069,7 +8345,7 @@ app15.post("/", async (c) => {
|
|
|
8069
8345
|
authSurface = "cookie";
|
|
8070
8346
|
senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
8071
8347
|
if (!senderId) {
|
|
8072
|
-
console.error(`${
|
|
8348
|
+
console.error(`${TAG18} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
|
|
8073
8349
|
return c.json({ error: "admin-account-not-resolved" }, 500);
|
|
8074
8350
|
}
|
|
8075
8351
|
userId = getUserIdForSession(cacheKey) ?? void 0;
|
|
@@ -8080,9 +8356,9 @@ app15.post("/", async (c) => {
|
|
|
8080
8356
|
}
|
|
8081
8357
|
const lookupSessionId = body.adminSessionId;
|
|
8082
8358
|
const operatorMeta = await fetch(
|
|
8083
|
-
`${
|
|
8359
|
+
`${managerBase3()}/${encodeURIComponent(lookupSessionId)}/meta`
|
|
8084
8360
|
).then((r) => r.ok ? r.json() : null).catch((err) => {
|
|
8085
|
-
console.error(`${
|
|
8361
|
+
console.error(`${TAG18} fetch-failed op=operator-meta-lookup message=${err instanceof Error ? err.message : String(err)}`);
|
|
8086
8362
|
return null;
|
|
8087
8363
|
});
|
|
8088
8364
|
const operatorSenderId = operatorMeta && typeof operatorMeta.senderId === "string" && operatorMeta.senderId.length > 0 ? operatorMeta.senderId : null;
|
|
@@ -8097,7 +8373,7 @@ app15.post("/", async (c) => {
|
|
|
8097
8373
|
const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
|
|
8098
8374
|
const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
|
|
8099
8375
|
const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
|
|
8100
|
-
console.log(`${
|
|
8376
|
+
console.log(`${TAG18} spawn-request-in surface=${authSurface} accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
|
|
8101
8377
|
const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
|
|
8102
8378
|
const { response, claudeSessionId } = await performSpawnWithInitialMessage({
|
|
8103
8379
|
senderId,
|
|
@@ -8116,14 +8392,14 @@ app15.post("/", async (c) => {
|
|
|
8116
8392
|
claudeSessionId,
|
|
8117
8393
|
senderId
|
|
8118
8394
|
);
|
|
8119
|
-
console.log(`${
|
|
8395
|
+
console.log(`${TAG18} route-done surface=${authSurface} status=${response.status} route-ms=${Date.now() - routeStart}`);
|
|
8120
8396
|
return response;
|
|
8121
8397
|
});
|
|
8122
8398
|
app15.get("/events", async (c) => {
|
|
8123
|
-
const upstream = await fetch(`${
|
|
8399
|
+
const upstream = await fetch(`${managerBase3()}/events`, {
|
|
8124
8400
|
headers: { accept: "text/event-stream" }
|
|
8125
8401
|
}).catch((err) => {
|
|
8126
|
-
console.error(`${
|
|
8402
|
+
console.error(`${TAG18} fetch-failed op=events message=${err instanceof Error ? err.message : String(err)}`);
|
|
8127
8403
|
return null;
|
|
8128
8404
|
});
|
|
8129
8405
|
if (!upstream || !upstream.body) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8142,9 +8418,9 @@ app15.get("/", async (c) => {
|
|
|
8142
8418
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
8143
8419
|
if (!senderId) return c.json({ error: "admin-account-not-resolved" }, 500);
|
|
8144
8420
|
const upstream = await fetch(
|
|
8145
|
-
`${
|
|
8421
|
+
`${managerBase3()}/list?senderId=${encodeURIComponent(senderId)}`
|
|
8146
8422
|
).catch((err) => {
|
|
8147
|
-
console.error(`${
|
|
8423
|
+
console.error(`${TAG18} fetch-failed op=list message=${err instanceof Error ? err.message : String(err)}`);
|
|
8148
8424
|
return null;
|
|
8149
8425
|
});
|
|
8150
8426
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8173,7 +8449,7 @@ app15.post("/resume", async (c) => {
|
|
|
8173
8449
|
const activePlugins = computeActivePlugins(senderId);
|
|
8174
8450
|
const specialistDomains = computeSpecialistDomains(senderId);
|
|
8175
8451
|
const tunnelUrl = resolveTunnelUrl();
|
|
8176
|
-
const upstream = await fetch(`${
|
|
8452
|
+
const upstream = await fetch(`${managerBase3()}/resume`, {
|
|
8177
8453
|
method: "POST",
|
|
8178
8454
|
headers: { "content-type": "application/json" },
|
|
8179
8455
|
body: JSON.stringify({
|
|
@@ -8198,7 +8474,7 @@ app15.post("/resume", async (c) => {
|
|
|
8198
8474
|
conversationNodeId: getSessionIdForSession(cacheKey)
|
|
8199
8475
|
})
|
|
8200
8476
|
}).catch((err) => {
|
|
8201
|
-
console.error(`${
|
|
8477
|
+
console.error(`${TAG18} fetch-failed op=resume message=${err instanceof Error ? err.message : String(err)}`);
|
|
8202
8478
|
return null;
|
|
8203
8479
|
});
|
|
8204
8480
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8223,10 +8499,10 @@ app15.post("/resume", async (c) => {
|
|
|
8223
8499
|
app15.post("/:sessionId/stop", async (c) => {
|
|
8224
8500
|
const sessionId = c.req.param("sessionId");
|
|
8225
8501
|
const upstream = await fetch(
|
|
8226
|
-
`${
|
|
8502
|
+
`${managerBase3()}/${encodeURIComponent(sessionId)}/stop`,
|
|
8227
8503
|
{ method: "POST" }
|
|
8228
8504
|
).catch((err) => {
|
|
8229
|
-
console.error(`${
|
|
8505
|
+
console.error(`${TAG18} fetch-failed op=stop message=${err instanceof Error ? err.message : String(err)}`);
|
|
8230
8506
|
return null;
|
|
8231
8507
|
});
|
|
8232
8508
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8236,14 +8512,14 @@ app15.post("/:sessionId/rename", async (c) => {
|
|
|
8236
8512
|
const sessionId = c.req.param("sessionId");
|
|
8237
8513
|
const body = await c.req.text();
|
|
8238
8514
|
const upstream = await fetch(
|
|
8239
|
-
`${
|
|
8515
|
+
`${managerBase3()}/${encodeURIComponent(sessionId)}/rename`,
|
|
8240
8516
|
{
|
|
8241
8517
|
method: "POST",
|
|
8242
8518
|
headers: { "content-type": "application/json" },
|
|
8243
8519
|
body
|
|
8244
8520
|
}
|
|
8245
8521
|
).catch((err) => {
|
|
8246
|
-
console.error(`${
|
|
8522
|
+
console.error(`${TAG18} fetch-failed op=rename message=${err instanceof Error ? err.message : String(err)}`);
|
|
8247
8523
|
return null;
|
|
8248
8524
|
});
|
|
8249
8525
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8258,10 +8534,10 @@ app15.delete("/:sessionId", async (c) => {
|
|
|
8258
8534
|
if (!sessionId) {
|
|
8259
8535
|
console.log(`[admin-conversation-delete] reason=no-conversation-mapping claudeSessionId=${claudeSessionId.slice(0, 8)}`);
|
|
8260
8536
|
const upstream = await fetch(
|
|
8261
|
-
`${
|
|
8537
|
+
`${managerBase3()}/${encodeURIComponent(claudeSessionId)}`,
|
|
8262
8538
|
{ method: "DELETE" }
|
|
8263
8539
|
).catch((err) => {
|
|
8264
|
-
console.error(`${
|
|
8540
|
+
console.error(`${TAG18} fetch-failed op=delete message=${err instanceof Error ? err.message : String(err)}`);
|
|
8265
8541
|
return null;
|
|
8266
8542
|
});
|
|
8267
8543
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8277,7 +8553,7 @@ app15.delete("/:sessionId", async (c) => {
|
|
|
8277
8553
|
const outcome = await cascadeAdminConversationDelete({
|
|
8278
8554
|
sessionId,
|
|
8279
8555
|
accountId,
|
|
8280
|
-
managerBase:
|
|
8556
|
+
managerBase: managerBase3(),
|
|
8281
8557
|
claudeSessionIds: all
|
|
8282
8558
|
});
|
|
8283
8559
|
if (outcome.ok) return c.json({ ok: true, claudeSessionIds: outcome.claudeSessionIds });
|
|
@@ -8316,14 +8592,14 @@ app15.post("/:sessionId/archive", async (c) => {
|
|
|
8316
8592
|
const sessionId = c.req.param("sessionId");
|
|
8317
8593
|
const body = await c.req.text();
|
|
8318
8594
|
const upstream = await fetch(
|
|
8319
|
-
`${
|
|
8595
|
+
`${managerBase3()}/${encodeURIComponent(sessionId)}/archive`,
|
|
8320
8596
|
{
|
|
8321
8597
|
method: "POST",
|
|
8322
8598
|
headers: { "content-type": "application/json" },
|
|
8323
8599
|
body
|
|
8324
8600
|
}
|
|
8325
8601
|
).catch((err) => {
|
|
8326
|
-
console.error(`${
|
|
8602
|
+
console.error(`${TAG18} fetch-failed op=archive message=${err instanceof Error ? err.message : String(err)}`);
|
|
8327
8603
|
return null;
|
|
8328
8604
|
});
|
|
8329
8605
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8332,9 +8608,9 @@ app15.post("/:sessionId/archive", async (c) => {
|
|
|
8332
8608
|
app15.get("/:sessionId/meta", async (c) => {
|
|
8333
8609
|
const sessionId = c.req.param("sessionId");
|
|
8334
8610
|
const upstream = await fetch(
|
|
8335
|
-
`${
|
|
8611
|
+
`${managerBase3()}/${encodeURIComponent(sessionId)}/meta`
|
|
8336
8612
|
).catch((err) => {
|
|
8337
|
-
console.error(`${
|
|
8613
|
+
console.error(`${TAG18} fetch-failed op=meta message=${err instanceof Error ? err.message : String(err)}`);
|
|
8338
8614
|
return null;
|
|
8339
8615
|
});
|
|
8340
8616
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8344,14 +8620,14 @@ app15.post("/:sessionId/input", async (c) => {
|
|
|
8344
8620
|
const sessionId = c.req.param("sessionId");
|
|
8345
8621
|
const rawBody = await c.req.text();
|
|
8346
8622
|
const upstream = await fetch(
|
|
8347
|
-
`${
|
|
8623
|
+
`${managerBase3()}/${encodeURIComponent(sessionId)}/input`,
|
|
8348
8624
|
{
|
|
8349
8625
|
method: "POST",
|
|
8350
8626
|
headers: { "content-type": "application/json" },
|
|
8351
8627
|
body: rawBody
|
|
8352
8628
|
}
|
|
8353
8629
|
).catch((err) => {
|
|
8354
|
-
console.error(`${
|
|
8630
|
+
console.error(`${TAG18} fetch-failed op=input message=${err instanceof Error ? err.message : String(err)}`);
|
|
8355
8631
|
return null;
|
|
8356
8632
|
});
|
|
8357
8633
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8364,9 +8640,9 @@ app15.get("/:sessionId/log", async (c) => {
|
|
|
8364
8640
|
if (c.req.query("download") === "1") search.set("download", "1");
|
|
8365
8641
|
const qs = search.toString() ? `?${search.toString()}` : "";
|
|
8366
8642
|
const upstream = await fetch(
|
|
8367
|
-
`${
|
|
8643
|
+
`${managerBase3()}/${encodeURIComponent(sessionId)}/log${qs}`
|
|
8368
8644
|
).catch((err) => {
|
|
8369
|
-
console.error(`${
|
|
8645
|
+
console.error(`${TAG18} fetch-failed op=log message=${err instanceof Error ? err.message : String(err)}`);
|
|
8370
8646
|
return null;
|
|
8371
8647
|
});
|
|
8372
8648
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8375,7 +8651,7 @@ app15.get("/:sessionId/log", async (c) => {
|
|
|
8375
8651
|
var claude_sessions_default = app15;
|
|
8376
8652
|
|
|
8377
8653
|
// server/routes/admin/log-ingest.ts
|
|
8378
|
-
var
|
|
8654
|
+
var TAG19 = "[log-ingest]";
|
|
8379
8655
|
var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
|
|
8380
8656
|
var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
|
|
8381
8657
|
var MAX_LINE_BYTES = 4096;
|
|
@@ -8386,7 +8662,7 @@ function isLoopbackAddr(addr) {
|
|
|
8386
8662
|
app16.post("/", async (c) => {
|
|
8387
8663
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
8388
8664
|
if (!isLoopbackAddr(remoteAddr)) {
|
|
8389
|
-
console.error(`${
|
|
8665
|
+
console.error(`${TAG19} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
8390
8666
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
8391
8667
|
}
|
|
8392
8668
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -8395,7 +8671,7 @@ app16.post("/", async (c) => {
|
|
|
8395
8671
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
8396
8672
|
const offender = tokens.find((t) => !isLoopbackAddr(t));
|
|
8397
8673
|
if (offender !== void 0) {
|
|
8398
|
-
console.error(`${
|
|
8674
|
+
console.error(`${TAG19} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
8399
8675
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
8400
8676
|
}
|
|
8401
8677
|
}
|
|
@@ -8437,18 +8713,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
8437
8713
|
]);
|
|
8438
8714
|
var app17 = new Hono();
|
|
8439
8715
|
app17.post("/", async (c) => {
|
|
8440
|
-
const
|
|
8716
|
+
const TAG28 = "[admin:events]";
|
|
8441
8717
|
let body;
|
|
8442
8718
|
try {
|
|
8443
8719
|
body = await c.req.json();
|
|
8444
8720
|
} catch (err) {
|
|
8445
8721
|
const detail = err instanceof Error ? err.message : String(err);
|
|
8446
|
-
console.error(`${
|
|
8722
|
+
console.error(`${TAG28} reject reason=body-not-json detail=${detail}`);
|
|
8447
8723
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
8448
8724
|
}
|
|
8449
8725
|
const event = typeof body.event === "string" ? body.event : "";
|
|
8450
8726
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
8451
|
-
console.error(`${
|
|
8727
|
+
console.error(`${TAG28} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
8452
8728
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
8453
8729
|
}
|
|
8454
8730
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -9326,6 +9602,27 @@ function pickFulltextIndex(allowedScopes) {
|
|
|
9326
9602
|
function isPublicOnlyScope(allowedScopes) {
|
|
9327
9603
|
return Array.isArray(allowedScopes) && allowedScopes.length === 1 && allowedScopes[0] === "public";
|
|
9328
9604
|
}
|
|
9605
|
+
function buildScopeAndSliceClause(allowedScopes, sliceToken, alias = "node") {
|
|
9606
|
+
const hasSlice = typeof sliceToken === "string" && sliceToken.length > 0;
|
|
9607
|
+
const hasScopes = Array.isArray(allowedScopes) && allowedScopes.length > 0;
|
|
9608
|
+
if (!hasSlice && !hasScopes) return { clause: "", params: {} };
|
|
9609
|
+
if (hasSlice && hasScopes) {
|
|
9610
|
+
return {
|
|
9611
|
+
clause: `AND (${alias}.sliceToken = $sliceToken OR ${alias}.scope IS NULL OR ${alias}.scope IN $allowedScopes)`,
|
|
9612
|
+
params: { sliceToken, allowedScopes }
|
|
9613
|
+
};
|
|
9614
|
+
}
|
|
9615
|
+
if (hasSlice) {
|
|
9616
|
+
return {
|
|
9617
|
+
clause: `AND (${alias}.sliceToken = $sliceToken OR ${alias}.scope IS NULL)`,
|
|
9618
|
+
params: { sliceToken }
|
|
9619
|
+
};
|
|
9620
|
+
}
|
|
9621
|
+
return {
|
|
9622
|
+
clause: `AND (${alias}.scope IS NULL OR ${alias}.scope IN $allowedScopes)`,
|
|
9623
|
+
params: { allowedScopes }
|
|
9624
|
+
};
|
|
9625
|
+
}
|
|
9329
9626
|
function escapeLucene(query) {
|
|
9330
9627
|
return query.replace(/[+\-&|!(){}[\]^"~*?:\\/]/g, "\\$&");
|
|
9331
9628
|
}
|
|
@@ -9361,8 +9658,9 @@ function buildKeywordFilter(keywords, keywordMatch = "any") {
|
|
|
9361
9658
|
};
|
|
9362
9659
|
}
|
|
9363
9660
|
async function bm25Only(session, params) {
|
|
9364
|
-
const { query, accountId, limit, allowedScopes, agentSlug, keywords, keywordMatch, labels } = params;
|
|
9365
|
-
const
|
|
9661
|
+
const { query, accountId, limit, allowedScopes, agentSlug, keywords, keywordMatch, labels, sliceToken } = params;
|
|
9662
|
+
const scope = buildScopeAndSliceClause(allowedScopes, sliceToken);
|
|
9663
|
+
const scopeClause = scope.clause;
|
|
9366
9664
|
const agentClause = agentSlug ? "AND node.agents IS NOT NULL AND $agentSlug IN node.agents" : "";
|
|
9367
9665
|
const labelClause = labels && labels.length > 0 ? "AND any(l IN labels(node) WHERE l IN $labels)" : "";
|
|
9368
9666
|
const keywordFilter = buildKeywordFilter(keywords, keywordMatch);
|
|
@@ -9386,7 +9684,7 @@ async function bm25Only(session, params) {
|
|
|
9386
9684
|
query: escaped,
|
|
9387
9685
|
accountId,
|
|
9388
9686
|
limit: int(limit),
|
|
9389
|
-
...
|
|
9687
|
+
...scope.params,
|
|
9390
9688
|
...agentSlug ? { agentSlug } : {},
|
|
9391
9689
|
...labels && labels.length > 0 ? { labels } : {},
|
|
9392
9690
|
...keywordFilter?.params ?? {}
|
|
@@ -9418,6 +9716,7 @@ async function hybrid(session, embed2, params) {
|
|
|
9418
9716
|
accountId,
|
|
9419
9717
|
limit,
|
|
9420
9718
|
allowedScopes,
|
|
9719
|
+
sliceToken,
|
|
9421
9720
|
keywords,
|
|
9422
9721
|
keywordMatch = "any",
|
|
9423
9722
|
agentSlug,
|
|
@@ -9496,22 +9795,18 @@ async function hybrid(session, embed2, params) {
|
|
|
9496
9795
|
if (effectiveLabels && effectiveLabels.length > 0) {
|
|
9497
9796
|
indexesToQuery = effectiveLabels.map((l) => labelToIndex.get(l)).filter((idx) => idx !== void 0);
|
|
9498
9797
|
if (indexesToQuery.length === 0) {
|
|
9499
|
-
|
|
9500
|
-
|
|
9501
|
-
|
|
9502
|
-
|
|
9503
|
-
|
|
9504
|
-
suppressed: 0,
|
|
9505
|
-
bm25Bypass: 0,
|
|
9506
|
-
threshold: params.vectorThreshold ?? null,
|
|
9507
|
-
stageCounts: emptyStageCounts
|
|
9508
|
-
};
|
|
9798
|
+
const missing = effectiveLabels.filter((l) => !labelToIndex.has(l));
|
|
9799
|
+
process.stderr.write(
|
|
9800
|
+
`[graph-search] labels-without-vector-index=${missing.join(",")} accountId=${(accountId ?? "").slice(0, 8)} \u2014 falling through to BM25-only for these labels
|
|
9801
|
+
`
|
|
9802
|
+
);
|
|
9509
9803
|
}
|
|
9510
9804
|
} else {
|
|
9511
9805
|
indexesToQuery = [...new Set(labelToIndex.values())];
|
|
9512
9806
|
}
|
|
9513
|
-
const
|
|
9514
|
-
const
|
|
9807
|
+
const hybridScope = buildScopeAndSliceClause(allowedScopes, sliceToken);
|
|
9808
|
+
const scopeClause = hybridScope.clause;
|
|
9809
|
+
const scopeParams = hybridScope.params;
|
|
9515
9810
|
const agentClause = agentSlug ? "AND node.agents IS NOT NULL AND $agentSlug IN node.agents" : "";
|
|
9516
9811
|
const agentParams = agentSlug ? { agentSlug } : {};
|
|
9517
9812
|
const keywordFilter = buildKeywordFilter(keywords, keywordMatch);
|
|
@@ -9626,7 +9921,8 @@ async function hybrid(session, embed2, params) {
|
|
|
9626
9921
|
query: kw,
|
|
9627
9922
|
accountId,
|
|
9628
9923
|
limit,
|
|
9629
|
-
allowedScopes
|
|
9924
|
+
allowedScopes,
|
|
9925
|
+
sliceToken
|
|
9630
9926
|
});
|
|
9631
9927
|
if (kwHits.length === 0) continue;
|
|
9632
9928
|
const rawScores = kwHits.map((h) => h.score);
|
|
@@ -9635,21 +9931,21 @@ async function hybrid(session, embed2, params) {
|
|
|
9635
9931
|
mergeBm25Hit(scoreMap, kwHits[i], normalised[i]);
|
|
9636
9932
|
}
|
|
9637
9933
|
}
|
|
9638
|
-
const
|
|
9934
|
+
const propScope = buildScopeAndSliceClause(allowedScopes, sliceToken);
|
|
9639
9935
|
const propResult = await session.run(
|
|
9640
9936
|
`MATCH (node)
|
|
9641
9937
|
WHERE node.accountId = $accountId
|
|
9642
9938
|
AND ${(0, import_dist.notTrashed)("node")}
|
|
9643
9939
|
AND node.keywords IS NOT NULL
|
|
9644
9940
|
AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
|
|
9645
|
-
${
|
|
9941
|
+
${propScope.clause}
|
|
9646
9942
|
RETURN node, labels(node) AS nodeLabels, elementId(node) AS nodeId
|
|
9647
9943
|
LIMIT $limit`,
|
|
9648
9944
|
{
|
|
9649
9945
|
accountId,
|
|
9650
9946
|
kwSubs: keywordSubscriptions,
|
|
9651
9947
|
limit: int(limit),
|
|
9652
|
-
...
|
|
9948
|
+
...propScope.params
|
|
9653
9949
|
}
|
|
9654
9950
|
);
|
|
9655
9951
|
for (const record of propResult.records) {
|
|
@@ -9677,7 +9973,8 @@ async function hybrid(session, embed2, params) {
|
|
|
9677
9973
|
query: kw,
|
|
9678
9974
|
accountId,
|
|
9679
9975
|
limit,
|
|
9680
|
-
allowedScopes
|
|
9976
|
+
allowedScopes,
|
|
9977
|
+
sliceToken
|
|
9681
9978
|
});
|
|
9682
9979
|
if (kwHits.length === 0) continue;
|
|
9683
9980
|
const rawScores = kwHits.map((h) => h.score);
|
|
@@ -9686,21 +9983,21 @@ async function hybrid(session, embed2, params) {
|
|
|
9686
9983
|
mergeBm25Hit(scoreMap, kwHits[i], normalised[i]);
|
|
9687
9984
|
}
|
|
9688
9985
|
}
|
|
9689
|
-
const
|
|
9986
|
+
const propScope = buildScopeAndSliceClause(allowedScopes, sliceToken);
|
|
9690
9987
|
const propResult = await session.run(
|
|
9691
9988
|
`MATCH (node)
|
|
9692
9989
|
WHERE node.accountId = $accountId
|
|
9693
9990
|
AND ${(0, import_dist.notTrashed)("node")}
|
|
9694
9991
|
AND node.keywords IS NOT NULL
|
|
9695
9992
|
AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
|
|
9696
|
-
${
|
|
9993
|
+
${propScope.clause}
|
|
9697
9994
|
RETURN node, labels(node) AS nodeLabels, elementId(node) AS nodeId
|
|
9698
9995
|
LIMIT $limit`,
|
|
9699
9996
|
{
|
|
9700
9997
|
accountId,
|
|
9701
9998
|
kwSubs: keywordSubscriptions,
|
|
9702
9999
|
limit: int(limit),
|
|
9703
|
-
...
|
|
10000
|
+
...propScope.params
|
|
9704
10001
|
}
|
|
9705
10002
|
);
|
|
9706
10003
|
for (const record of propResult.records) {
|
|
@@ -10731,7 +11028,7 @@ app21.post("/", requireAdminSession, async (c) => {
|
|
|
10731
11028
|
return c.json({ error: "AdminConversation node is missing sessionId" }, 500);
|
|
10732
11029
|
}
|
|
10733
11030
|
const managerPort = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "graph-page-cascade" });
|
|
10734
|
-
const
|
|
11031
|
+
const managerBase4 = `http://127.0.0.1:${managerPort}`;
|
|
10735
11032
|
try {
|
|
10736
11033
|
await session.close();
|
|
10737
11034
|
} catch {
|
|
@@ -10739,7 +11036,7 @@ app21.post("/", requireAdminSession, async (c) => {
|
|
|
10739
11036
|
const outcome = await cascadeAdminConversationDelete({
|
|
10740
11037
|
sessionId,
|
|
10741
11038
|
accountId,
|
|
10742
|
-
managerBase:
|
|
11039
|
+
managerBase: managerBase4
|
|
10743
11040
|
});
|
|
10744
11041
|
const elapsed2 = Date.now() - started;
|
|
10745
11042
|
const labelSummary2 = preflightLabels.join(",");
|
|
@@ -11848,7 +12145,7 @@ app31.get("/", async (c) => {
|
|
|
11848
12145
|
var health_default2 = app31;
|
|
11849
12146
|
|
|
11850
12147
|
// server/routes/admin/linkedin-ingest.ts
|
|
11851
|
-
var
|
|
12148
|
+
var TAG20 = "[linkedin-ingest-route]";
|
|
11852
12149
|
var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
11853
12150
|
var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
11854
12151
|
var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
|
|
@@ -11947,25 +12244,25 @@ app32.post("/", requireAdminSession, async (c) => {
|
|
|
11947
12244
|
try {
|
|
11948
12245
|
body = await c.req.json();
|
|
11949
12246
|
} catch {
|
|
11950
|
-
console.error(
|
|
12247
|
+
console.error(TAG20 + " rejected status=400 reason=schema:body-not-json");
|
|
11951
12248
|
return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
|
|
11952
12249
|
}
|
|
11953
12250
|
const v = validate(body);
|
|
11954
12251
|
if (!v.ok) {
|
|
11955
|
-
console.error(
|
|
12252
|
+
console.error(TAG20 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
|
|
11956
12253
|
return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
|
|
11957
12254
|
}
|
|
11958
12255
|
const envelope = v.envelope;
|
|
11959
12256
|
const cacheKey = c.var.cacheKey ?? "";
|
|
11960
12257
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
11961
12258
|
if (!senderId) {
|
|
11962
|
-
console.error(
|
|
12259
|
+
console.error(TAG20 + " rejected status=500 reason=admin-account-not-resolved");
|
|
11963
12260
|
return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
|
|
11964
12261
|
}
|
|
11965
12262
|
const userId = getUserIdForSession(cacheKey) ?? void 0;
|
|
11966
12263
|
const payloadBytes = JSON.stringify(envelope).length;
|
|
11967
12264
|
console.log(
|
|
11968
|
-
|
|
12265
|
+
TAG20 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
|
|
11969
12266
|
);
|
|
11970
12267
|
const initialMessage = buildInitialMessage(envelope);
|
|
11971
12268
|
const spawnStart = Date.now();
|
|
@@ -11987,12 +12284,12 @@ app32.post("/", requireAdminSession, async (c) => {
|
|
|
11987
12284
|
if (!response.ok) {
|
|
11988
12285
|
const detail = await response.text().catch(() => "");
|
|
11989
12286
|
console.error(
|
|
11990
|
-
|
|
12287
|
+
TAG20 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + response.status + " ms=" + (Date.now() - spawnStart) + " detail=" + detail.slice(0, 200)
|
|
11991
12288
|
);
|
|
11992
12289
|
return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: response.status, detail: detail.slice(0, 500) }, 502);
|
|
11993
12290
|
}
|
|
11994
12291
|
console.log(
|
|
11995
|
-
|
|
12292
|
+
TAG20 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + (claudeSessionId ?? "unknown") + " ms=" + (Date.now() - spawnStart)
|
|
11996
12293
|
);
|
|
11997
12294
|
return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: claudeSessionId }, 202);
|
|
11998
12295
|
});
|
|
@@ -12000,7 +12297,7 @@ var linkedin_ingest_default = app32;
|
|
|
12000
12297
|
|
|
12001
12298
|
// server/routes/admin/post-turn-context.ts
|
|
12002
12299
|
import neo4j3 from "neo4j-driver";
|
|
12003
|
-
var
|
|
12300
|
+
var TAG21 = "[post-turn-context]";
|
|
12004
12301
|
var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
|
|
12005
12302
|
"embedding",
|
|
12006
12303
|
"passwordHash",
|
|
@@ -12042,7 +12339,7 @@ var app33 = new Hono();
|
|
|
12042
12339
|
app33.get("/", async (c) => {
|
|
12043
12340
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
12044
12341
|
if (!isLoopbackAddr2(remoteAddr)) {
|
|
12045
|
-
console.error(`${
|
|
12342
|
+
console.error(`${TAG21} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
12046
12343
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
12047
12344
|
}
|
|
12048
12345
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -12051,7 +12348,7 @@ app33.get("/", async (c) => {
|
|
|
12051
12348
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
12052
12349
|
const offender = tokens.find((t) => !isLoopbackAddr2(t));
|
|
12053
12350
|
if (offender !== void 0) {
|
|
12054
|
-
console.error(`${
|
|
12351
|
+
console.error(`${TAG21} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
12055
12352
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
12056
12353
|
}
|
|
12057
12354
|
}
|
|
@@ -12083,7 +12380,7 @@ app33.get("/", async (c) => {
|
|
|
12083
12380
|
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
12084
12381
|
const total = Date.now() - started;
|
|
12085
12382
|
console.log(
|
|
12086
|
-
`${
|
|
12383
|
+
`${TAG21} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
|
|
12087
12384
|
);
|
|
12088
12385
|
return c.json({
|
|
12089
12386
|
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
@@ -12092,7 +12389,7 @@ app33.get("/", async (c) => {
|
|
|
12092
12389
|
const elapsed = Date.now() - started;
|
|
12093
12390
|
const message = err instanceof Error ? err.message : String(err);
|
|
12094
12391
|
console.error(
|
|
12095
|
-
`${
|
|
12392
|
+
`${TAG21} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
|
|
12096
12393
|
);
|
|
12097
12394
|
return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
|
|
12098
12395
|
} finally {
|
|
@@ -12101,41 +12398,659 @@ app33.get("/", async (c) => {
|
|
|
12101
12398
|
});
|
|
12102
12399
|
var post_turn_context_default = app33;
|
|
12103
12400
|
|
|
12104
|
-
// server/routes/admin/
|
|
12401
|
+
// server/routes/admin/public-session-context.ts
|
|
12402
|
+
import neo4j4 from "neo4j-driver";
|
|
12403
|
+
var TAG22 = "[public-session-context]";
|
|
12404
|
+
var STRIPPED_PROPERTIES3 = /* @__PURE__ */ new Set([
|
|
12405
|
+
"embedding",
|
|
12406
|
+
"passwordHash",
|
|
12407
|
+
"magicToken",
|
|
12408
|
+
"otpCode",
|
|
12409
|
+
"cacheKey"
|
|
12410
|
+
]);
|
|
12411
|
+
function isLoopbackAddr3(addr) {
|
|
12412
|
+
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
12413
|
+
}
|
|
12414
|
+
function convertValue2(value) {
|
|
12415
|
+
if (neo4j4.isDateTime(value) || neo4j4.isDate(value) || neo4j4.isLocalDateTime(value) || neo4j4.isLocalTime(value) || neo4j4.isTime(value) || neo4j4.isDuration(value)) {
|
|
12416
|
+
return value.toString();
|
|
12417
|
+
}
|
|
12418
|
+
if (neo4j4.isInt(value)) {
|
|
12419
|
+
return value.inSafeRange() ? value.toNumber() : value.toString();
|
|
12420
|
+
}
|
|
12421
|
+
if (Array.isArray(value)) {
|
|
12422
|
+
return value.map((v) => convertValue2(v));
|
|
12423
|
+
}
|
|
12424
|
+
if (value !== null && typeof value === "object" && value.constructor === Object) {
|
|
12425
|
+
const out = {};
|
|
12426
|
+
for (const [k, v] of Object.entries(value)) {
|
|
12427
|
+
out[k] = convertValue2(v);
|
|
12428
|
+
}
|
|
12429
|
+
return out;
|
|
12430
|
+
}
|
|
12431
|
+
return value;
|
|
12432
|
+
}
|
|
12433
|
+
function pruneProperties2(raw) {
|
|
12434
|
+
const out = {};
|
|
12435
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
12436
|
+
if (STRIPPED_PROPERTIES3.has(k)) continue;
|
|
12437
|
+
out[k] = convertValue2(v);
|
|
12438
|
+
}
|
|
12439
|
+
return out;
|
|
12440
|
+
}
|
|
12105
12441
|
var app34 = new Hono();
|
|
12106
|
-
app34.
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12115
|
-
|
|
12116
|
-
|
|
12117
|
-
|
|
12118
|
-
|
|
12119
|
-
|
|
12120
|
-
|
|
12121
|
-
|
|
12122
|
-
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
|
|
12126
|
-
|
|
12127
|
-
|
|
12128
|
-
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
|
|
12442
|
+
app34.get("/", async (c) => {
|
|
12443
|
+
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
12444
|
+
if (!isLoopbackAddr3(remoteAddr)) {
|
|
12445
|
+
console.error(`${TAG22} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
12446
|
+
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
12447
|
+
}
|
|
12448
|
+
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
12449
|
+
const xffRaw = Array.isArray(xffHeader) ? xffHeader.join(",") : typeof xffHeader === "string" ? xffHeader : "";
|
|
12450
|
+
if (xffRaw.length > 0) {
|
|
12451
|
+
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
12452
|
+
const offender = tokens.find((t) => !isLoopbackAddr3(t));
|
|
12453
|
+
if (offender !== void 0) {
|
|
12454
|
+
console.error(`${TAG22} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
12455
|
+
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
12456
|
+
}
|
|
12457
|
+
}
|
|
12458
|
+
const sliceToken = (c.req.query("sliceToken") ?? "").trim();
|
|
12459
|
+
const accountId = (c.req.query("accountId") ?? "").trim();
|
|
12460
|
+
if (!sliceToken) return c.json({ error: "sliceToken required" }, 400);
|
|
12461
|
+
if (!accountId) return c.json({ error: "accountId required" }, 400);
|
|
12462
|
+
const session = getSession();
|
|
12463
|
+
const started = Date.now();
|
|
12464
|
+
try {
|
|
12465
|
+
const cypher = `
|
|
12466
|
+
MATCH (n)
|
|
12467
|
+
WHERE n.sliceToken = $sliceToken AND n.accountId = $accountId
|
|
12468
|
+
RETURN elementId(n) AS elementId,
|
|
12469
|
+
labels(n) AS labels,
|
|
12470
|
+
properties(n) AS properties,
|
|
12471
|
+
coalesce(n.createdAt, n.observedAt, '') AS sortKey
|
|
12472
|
+
`;
|
|
12473
|
+
const res = await session.executeRead(
|
|
12474
|
+
(tx) => tx.run(cypher, { sliceToken, accountId })
|
|
12475
|
+
);
|
|
12476
|
+
const writes = res.records.map((r) => ({
|
|
12477
|
+
elementId: r.get("elementId"),
|
|
12478
|
+
labels: r.get("labels"),
|
|
12479
|
+
properties: pruneProperties2(r.get("properties")),
|
|
12480
|
+
sortKey: String(r.get("sortKey") ?? "")
|
|
12481
|
+
}));
|
|
12482
|
+
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
12483
|
+
const total = Date.now() - started;
|
|
12484
|
+
console.log(
|
|
12485
|
+
`${TAG22} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
|
|
12486
|
+
);
|
|
12487
|
+
return c.json({
|
|
12488
|
+
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
12489
|
+
});
|
|
12490
|
+
} catch (err) {
|
|
12491
|
+
const elapsed = Date.now() - started;
|
|
12492
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12493
|
+
console.error(
|
|
12494
|
+
`${TAG22} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
|
|
12495
|
+
);
|
|
12496
|
+
return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
|
|
12497
|
+
} finally {
|
|
12498
|
+
await session.close();
|
|
12499
|
+
}
|
|
12500
|
+
});
|
|
12501
|
+
var public_session_context_default = app34;
|
|
12135
12502
|
|
|
12136
|
-
// server/routes/
|
|
12137
|
-
|
|
12503
|
+
// server/routes/admin/public-session-exit.ts
|
|
12504
|
+
var TAG23 = "[public-session-exit-route]";
|
|
12505
|
+
function isLoopbackAddr4(addr) {
|
|
12506
|
+
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
12507
|
+
}
|
|
12508
|
+
var app35 = new Hono();
|
|
12509
|
+
app35.post("/", async (c) => {
|
|
12510
|
+
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
12511
|
+
if (!isLoopbackAddr4(remoteAddr)) {
|
|
12512
|
+
console.error(`${TAG23} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
12513
|
+
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
12514
|
+
}
|
|
12515
|
+
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
12516
|
+
const xffRaw = Array.isArray(xffHeader) ? xffHeader.join(",") : typeof xffHeader === "string" ? xffHeader : "";
|
|
12517
|
+
if (xffRaw.length > 0) {
|
|
12518
|
+
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
12519
|
+
const offender = tokens.find((t) => !isLoopbackAddr4(t));
|
|
12520
|
+
if (offender !== void 0) {
|
|
12521
|
+
console.error(`${TAG23} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
12522
|
+
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
12523
|
+
}
|
|
12524
|
+
}
|
|
12525
|
+
let body;
|
|
12526
|
+
try {
|
|
12527
|
+
body = await c.req.json();
|
|
12528
|
+
} catch {
|
|
12529
|
+
return c.json({ error: "invalid-json" }, 400);
|
|
12530
|
+
}
|
|
12531
|
+
const sessionId = typeof body.sessionId === "string" ? body.sessionId.trim() : "";
|
|
12532
|
+
if (!sessionId) return c.json({ error: "sessionId required" }, 400);
|
|
12533
|
+
handlePublicSessionExit(sessionId);
|
|
12534
|
+
return c.json({ ok: true });
|
|
12535
|
+
});
|
|
12536
|
+
var public_session_exit_default = app35;
|
|
12537
|
+
|
|
12538
|
+
// server/routes/admin/access-session-evict.ts
|
|
12539
|
+
var TAG24 = "[access-session-evict]";
|
|
12540
|
+
function isLoopbackAddr5(addr) {
|
|
12541
|
+
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
12542
|
+
}
|
|
12543
|
+
var app36 = new Hono();
|
|
12544
|
+
app36.post("/", async (c) => {
|
|
12545
|
+
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
12546
|
+
if (!isLoopbackAddr5(remoteAddr)) {
|
|
12547
|
+
console.error(`${TAG24} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
12548
|
+
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
12549
|
+
}
|
|
12550
|
+
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
12551
|
+
const xffRaw = Array.isArray(xffHeader) ? xffHeader.join(",") : typeof xffHeader === "string" ? xffHeader : "";
|
|
12552
|
+
if (xffRaw.length > 0) {
|
|
12553
|
+
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
12554
|
+
const offender = tokens.find((t) => !isLoopbackAddr5(t));
|
|
12555
|
+
if (offender !== void 0) {
|
|
12556
|
+
console.error(`${TAG24} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
12557
|
+
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
12558
|
+
}
|
|
12559
|
+
}
|
|
12560
|
+
let body;
|
|
12561
|
+
try {
|
|
12562
|
+
body = await c.req.json();
|
|
12563
|
+
} catch {
|
|
12564
|
+
return c.json({ error: "invalid-json" }, 400);
|
|
12565
|
+
}
|
|
12566
|
+
const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
|
|
12567
|
+
if (!grantId) return c.json({ error: "grantId required" }, 400);
|
|
12568
|
+
const dropped = evictAccessSessionsByGrant(grantId);
|
|
12569
|
+
console.log(`${TAG24} grantId=${grantId} dropped=${dropped}`);
|
|
12570
|
+
return c.json({ ok: true, dropped });
|
|
12571
|
+
});
|
|
12572
|
+
var access_session_evict_default = app36;
|
|
12573
|
+
|
|
12574
|
+
// server/routes/admin/index.ts
|
|
12575
|
+
var app37 = new Hono();
|
|
12576
|
+
app37.route("/session", session_default);
|
|
12577
|
+
app37.route("/new-session-failure", new_session_failure_default);
|
|
12578
|
+
app37.route("/new-session-submit", new_session_submit_default);
|
|
12579
|
+
app37.route("/logs", logs_default);
|
|
12580
|
+
app37.route("/claude-info", claude_info_default);
|
|
12581
|
+
app37.route("/claude-capabilities", claude_capabilities_default);
|
|
12582
|
+
app37.route("/attachment", attachment_default);
|
|
12583
|
+
app37.route("/agents", agents_default);
|
|
12584
|
+
app37.route("/sessions", sessions_default);
|
|
12585
|
+
app37.route("/claude-sessions", claude_sessions_default);
|
|
12586
|
+
app37.route("/log-ingest", log_ingest_default);
|
|
12587
|
+
app37.route("/events", events_default);
|
|
12588
|
+
app37.route("/files", files_default);
|
|
12589
|
+
app37.route("/graph-search", graph_search_default);
|
|
12590
|
+
app37.route("/graph-subgraph", graph_subgraph_default);
|
|
12591
|
+
app37.route("/graph-delete", graph_delete_default);
|
|
12592
|
+
app37.route("/graph-restore", graph_restore_default);
|
|
12593
|
+
app37.route("/graph-labels-in-graph", graph_labels_in_graph_default);
|
|
12594
|
+
app37.route("/graph-default-view", graph_default_view_default);
|
|
12595
|
+
app37.route("/session-defaults", session_defaults_default);
|
|
12596
|
+
app37.route("/file-attach", file_attach_default);
|
|
12597
|
+
app37.route("/sidebar-artefacts", sidebar_artefacts_default);
|
|
12598
|
+
app37.route("/sidebar-artefact-save", sidebar_artefact_save_default);
|
|
12599
|
+
app37.route("/sidebar-artefact-content", sidebar_artefact_content_default);
|
|
12600
|
+
app37.route("/system-stats", system_stats_default);
|
|
12601
|
+
app37.route("/health-brand", health_default2);
|
|
12602
|
+
app37.route("/linkedin-ingest", linkedin_ingest_default);
|
|
12603
|
+
app37.route("/post-turn-context", post_turn_context_default);
|
|
12604
|
+
app37.route("/public-session-context", public_session_context_default);
|
|
12605
|
+
app37.route("/public-session-exit", public_session_exit_default);
|
|
12606
|
+
app37.route("/access-session-evict", access_session_evict_default);
|
|
12607
|
+
var admin_default = app37;
|
|
12608
|
+
|
|
12609
|
+
// app/lib/access-gate.ts
|
|
12610
|
+
import neo4j5 from "neo4j-driver";
|
|
12611
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
12138
12612
|
import { resolve as resolve18 } from "path";
|
|
12613
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
12614
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
|
|
12615
|
+
var driver = null;
|
|
12616
|
+
function readPassword() {
|
|
12617
|
+
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
12618
|
+
const passwordFile = resolve18(PLATFORM_ROOT7, "config/.neo4j-password");
|
|
12619
|
+
try {
|
|
12620
|
+
return readFileSync16(passwordFile, "utf-8").trim();
|
|
12621
|
+
} catch {
|
|
12622
|
+
throw new Error(
|
|
12623
|
+
`Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
|
|
12624
|
+
);
|
|
12625
|
+
}
|
|
12626
|
+
}
|
|
12627
|
+
function getDriver() {
|
|
12628
|
+
if (!driver) {
|
|
12629
|
+
const uri = process.env.NEO4J_URI;
|
|
12630
|
+
if (!uri) {
|
|
12631
|
+
throw new Error(
|
|
12632
|
+
"[ui/access-gate] NEO4J_URI unset \u2014 refusing to default to bolt://localhost:7687"
|
|
12633
|
+
);
|
|
12634
|
+
}
|
|
12635
|
+
const user = process.env.NEO4J_USER ?? "neo4j";
|
|
12636
|
+
const password = readPassword();
|
|
12637
|
+
console.error(`[ui/access-gate] resolved neo4j_uri=${uri}`);
|
|
12638
|
+
driver = neo4j5.driver(uri, neo4j5.auth.basic(user, password), {
|
|
12639
|
+
maxConnectionPoolSize: 5
|
|
12640
|
+
});
|
|
12641
|
+
}
|
|
12642
|
+
return driver;
|
|
12643
|
+
}
|
|
12644
|
+
function getSession3() {
|
|
12645
|
+
return getDriver().session();
|
|
12646
|
+
}
|
|
12647
|
+
process.on("SIGINT", async () => {
|
|
12648
|
+
if (driver) {
|
|
12649
|
+
await driver.close();
|
|
12650
|
+
driver = null;
|
|
12651
|
+
}
|
|
12652
|
+
});
|
|
12653
|
+
var rateLimitMap = /* @__PURE__ */ new Map();
|
|
12654
|
+
var ACCESS_MAX_ATTEMPTS = 5;
|
|
12655
|
+
var ACCESS_LOCKOUT_MS = 15 * 60 * 1e3;
|
|
12656
|
+
var ACCESS_WINDOW_MS = 15 * 60 * 1e3;
|
|
12657
|
+
function checkAccessRateLimit(ip, agentSlug) {
|
|
12658
|
+
const key = `${ip}:${agentSlug}`;
|
|
12659
|
+
const entry = rateLimitMap.get(key);
|
|
12660
|
+
if (!entry) return null;
|
|
12661
|
+
const now = Date.now();
|
|
12662
|
+
if (entry.lockedUntil && now < entry.lockedUntil) {
|
|
12663
|
+
const remainingMs = entry.lockedUntil - now;
|
|
12664
|
+
const remainingS = Math.ceil(remainingMs / 1e3);
|
|
12665
|
+
return `Too many attempts. Try again in ${remainingS}s`;
|
|
12666
|
+
}
|
|
12667
|
+
if (now - entry.firstAttempt > ACCESS_WINDOW_MS) {
|
|
12668
|
+
rateLimitMap.delete(key);
|
|
12669
|
+
return null;
|
|
12670
|
+
}
|
|
12671
|
+
return null;
|
|
12672
|
+
}
|
|
12673
|
+
function recordAccessFailedAttempt(ip, agentSlug) {
|
|
12674
|
+
const key = `${ip}:${agentSlug}`;
|
|
12675
|
+
const now = Date.now();
|
|
12676
|
+
const entry = rateLimitMap.get(key);
|
|
12677
|
+
if (!entry || now - entry.firstAttempt > ACCESS_WINDOW_MS) {
|
|
12678
|
+
rateLimitMap.set(key, { attempts: 1, firstAttempt: now });
|
|
12679
|
+
return;
|
|
12680
|
+
}
|
|
12681
|
+
entry.attempts++;
|
|
12682
|
+
if (entry.attempts >= ACCESS_MAX_ATTEMPTS) {
|
|
12683
|
+
entry.lockedUntil = now + ACCESS_LOCKOUT_MS;
|
|
12684
|
+
}
|
|
12685
|
+
}
|
|
12686
|
+
function clearAccessRateLimit(ip, agentSlug) {
|
|
12687
|
+
rateLimitMap.delete(`${ip}:${agentSlug}`);
|
|
12688
|
+
}
|
|
12689
|
+
var contactRateMap = /* @__PURE__ */ new Map();
|
|
12690
|
+
var REQUEST_LINK_MAX = 3;
|
|
12691
|
+
var REQUEST_LINK_WINDOW_MS = 60 * 60 * 1e3;
|
|
12692
|
+
function checkRequestLinkRateLimit(contactValue) {
|
|
12693
|
+
const entry = contactRateMap.get(contactValue);
|
|
12694
|
+
if (!entry) return null;
|
|
12695
|
+
const now = Date.now();
|
|
12696
|
+
if (now - entry.windowStart > REQUEST_LINK_WINDOW_MS) {
|
|
12697
|
+
contactRateMap.delete(contactValue);
|
|
12698
|
+
return null;
|
|
12699
|
+
}
|
|
12700
|
+
if (entry.count >= REQUEST_LINK_MAX) {
|
|
12701
|
+
const remainingMs = REQUEST_LINK_WINDOW_MS - (now - entry.windowStart);
|
|
12702
|
+
const remainingM = Math.ceil(remainingMs / 6e4);
|
|
12703
|
+
return `Too many requests. Try again in ${remainingM} minute(s)`;
|
|
12704
|
+
}
|
|
12705
|
+
return null;
|
|
12706
|
+
}
|
|
12707
|
+
function recordRequestLinkAttempt(contactValue) {
|
|
12708
|
+
const now = Date.now();
|
|
12709
|
+
const entry = contactRateMap.get(contactValue);
|
|
12710
|
+
if (!entry || now - entry.windowStart > REQUEST_LINK_WINDOW_MS) {
|
|
12711
|
+
contactRateMap.set(contactValue, { count: 1, windowStart: now });
|
|
12712
|
+
return;
|
|
12713
|
+
}
|
|
12714
|
+
entry.count++;
|
|
12715
|
+
}
|
|
12716
|
+
function maskContact(value) {
|
|
12717
|
+
if (value.includes("@")) {
|
|
12718
|
+
const [local, domain] = value.split("@");
|
|
12719
|
+
return `${local[0]}***@${domain}`;
|
|
12720
|
+
}
|
|
12721
|
+
return "****";
|
|
12722
|
+
}
|
|
12723
|
+
function readGrant(record) {
|
|
12724
|
+
const g = record.get("g").properties;
|
|
12725
|
+
const personId = record.get("personId");
|
|
12726
|
+
return {
|
|
12727
|
+
grantId: record.get("grantId"),
|
|
12728
|
+
agentSlug: String(g.agentSlug),
|
|
12729
|
+
accountId: String(g.accountId),
|
|
12730
|
+
contactValue: String(g.contactValue),
|
|
12731
|
+
displayName: record.get("givenName") ?? null,
|
|
12732
|
+
personId,
|
|
12733
|
+
expiresAt: g.expiresAt ? new Date(String(g.expiresAt)).getTime() : null,
|
|
12734
|
+
status: String(g.status),
|
|
12735
|
+
sliceToken: String(g.sliceToken ?? "")
|
|
12736
|
+
};
|
|
12737
|
+
}
|
|
12738
|
+
async function findGrantByMagicToken(token) {
|
|
12739
|
+
const session = getSession3();
|
|
12740
|
+
try {
|
|
12741
|
+
const result = await session.run(
|
|
12742
|
+
`MATCH (p:Person)-[:HAS_ACCESS]->(g:AccessGrant {magicToken: $token})
|
|
12743
|
+
RETURN g, p.givenName AS givenName,
|
|
12744
|
+
elementId(g) AS grantId,
|
|
12745
|
+
elementId(p) AS personId`,
|
|
12746
|
+
{ token }
|
|
12747
|
+
);
|
|
12748
|
+
if (result.records.length === 0) return null;
|
|
12749
|
+
return readGrant(result.records[0]);
|
|
12750
|
+
} finally {
|
|
12751
|
+
await session.close();
|
|
12752
|
+
}
|
|
12753
|
+
}
|
|
12754
|
+
async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
|
|
12755
|
+
const session = getSession3();
|
|
12756
|
+
try {
|
|
12757
|
+
const result = await session.run(
|
|
12758
|
+
`MATCH (p:Person)-[:HAS_ACCESS]->(g:AccessGrant {
|
|
12759
|
+
contactValue: $contactValue,
|
|
12760
|
+
agentSlug: $agentSlug,
|
|
12761
|
+
accountId: $accountId
|
|
12762
|
+
})
|
|
12763
|
+
WHERE g.status IN ['invited', 'active']
|
|
12764
|
+
RETURN g, p.givenName AS givenName,
|
|
12765
|
+
elementId(g) AS grantId,
|
|
12766
|
+
elementId(p) AS personId`,
|
|
12767
|
+
{ contactValue, agentSlug, accountId }
|
|
12768
|
+
);
|
|
12769
|
+
if (result.records.length === 0) return null;
|
|
12770
|
+
return readGrant(result.records[0]);
|
|
12771
|
+
} finally {
|
|
12772
|
+
await session.close();
|
|
12773
|
+
}
|
|
12774
|
+
}
|
|
12775
|
+
async function consumeMagicTokenAndActivate(grantId) {
|
|
12776
|
+
const session = getSession3();
|
|
12777
|
+
try {
|
|
12778
|
+
const result = await session.run(
|
|
12779
|
+
`MATCH (g:AccessGrant) WHERE elementId(g) = $grantId
|
|
12780
|
+
SET g.magicToken = null,
|
|
12781
|
+
g.magicTokenExpiresAt = null,
|
|
12782
|
+
g.status = "active"
|
|
12783
|
+
RETURN count(g) AS affected`,
|
|
12784
|
+
{ grantId }
|
|
12785
|
+
);
|
|
12786
|
+
const affected = result.records[0]?.get("affected");
|
|
12787
|
+
const count = typeof affected === "number" ? affected : affected?.toNumber?.() ?? 0;
|
|
12788
|
+
if (count === 0) {
|
|
12789
|
+
throw new Error(`[access-gate] consumeMagicTokenAndActivate: no grant matched grantId=${grantId}`);
|
|
12790
|
+
}
|
|
12791
|
+
} finally {
|
|
12792
|
+
await session.close();
|
|
12793
|
+
}
|
|
12794
|
+
}
|
|
12795
|
+
async function generateNewMagicToken(grantId) {
|
|
12796
|
+
const token = randomUUID8();
|
|
12797
|
+
const session = getSession3();
|
|
12798
|
+
try {
|
|
12799
|
+
const result = await session.run(
|
|
12800
|
+
`MATCH (g:AccessGrant) WHERE elementId(g) = $grantId
|
|
12801
|
+
SET g.magicToken = $token,
|
|
12802
|
+
g.magicTokenExpiresAt = datetime() + duration('PT15M')
|
|
12803
|
+
RETURN count(g) AS affected`,
|
|
12804
|
+
{ grantId, token }
|
|
12805
|
+
);
|
|
12806
|
+
const affected = result.records[0]?.get("affected");
|
|
12807
|
+
const count = typeof affected === "number" ? affected : affected?.toNumber?.() ?? 0;
|
|
12808
|
+
if (count === 0) {
|
|
12809
|
+
throw new Error(`[access-gate] generateNewMagicToken: no grant matched grantId=${grantId}`);
|
|
12810
|
+
}
|
|
12811
|
+
} finally {
|
|
12812
|
+
await session.close();
|
|
12813
|
+
}
|
|
12814
|
+
return token;
|
|
12815
|
+
}
|
|
12816
|
+
|
|
12817
|
+
// server/routes/access/verify-token.ts
|
|
12818
|
+
var TAG25 = "[access-verify]";
|
|
12819
|
+
var MINT_TAG = "[access-session-mint]";
|
|
12820
|
+
var COOKIE_NAME = "__access_session";
|
|
12821
|
+
var app38 = new Hono();
|
|
12822
|
+
app38.post("/", async (c) => {
|
|
12823
|
+
const ip = c.var.clientIp || "unknown";
|
|
12824
|
+
let body;
|
|
12825
|
+
try {
|
|
12826
|
+
body = await c.req.json();
|
|
12827
|
+
} catch {
|
|
12828
|
+
return c.json({ error: "invalid-json" }, 400);
|
|
12829
|
+
}
|
|
12830
|
+
const token = typeof body.token === "string" ? body.token.trim() : "";
|
|
12831
|
+
const agentSlug = typeof body.agentSlug === "string" ? body.agentSlug.trim() : "";
|
|
12832
|
+
if (!token || !agentSlug) {
|
|
12833
|
+
return c.json({ error: "token and agentSlug required" }, 400);
|
|
12834
|
+
}
|
|
12835
|
+
const rateMsg = checkAccessRateLimit(ip, agentSlug);
|
|
12836
|
+
if (rateMsg) {
|
|
12837
|
+
console.error(`${TAG25} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
|
|
12838
|
+
return c.json({ error: rateMsg }, 429);
|
|
12839
|
+
}
|
|
12840
|
+
const grant = await findGrantByMagicToken(token);
|
|
12841
|
+
if (!grant) {
|
|
12842
|
+
recordAccessFailedAttempt(ip, agentSlug);
|
|
12843
|
+
console.error(`${TAG25} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
|
|
12844
|
+
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
12845
|
+
}
|
|
12846
|
+
if (grant.agentSlug !== agentSlug) {
|
|
12847
|
+
recordAccessFailedAttempt(ip, agentSlug);
|
|
12848
|
+
console.error(
|
|
12849
|
+
`${TAG25} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
|
|
12850
|
+
);
|
|
12851
|
+
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
12852
|
+
}
|
|
12853
|
+
if (grant.status === "expired" || grant.status === "revoked") {
|
|
12854
|
+
recordAccessFailedAttempt(ip, agentSlug);
|
|
12855
|
+
console.error(
|
|
12856
|
+
`${TAG25} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
|
|
12857
|
+
);
|
|
12858
|
+
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
12859
|
+
}
|
|
12860
|
+
if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
|
|
12861
|
+
recordAccessFailedAttempt(ip, agentSlug);
|
|
12862
|
+
console.error(
|
|
12863
|
+
`${TAG25} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
|
|
12864
|
+
);
|
|
12865
|
+
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
12866
|
+
}
|
|
12867
|
+
if (!grant.sliceToken) {
|
|
12868
|
+
console.error(
|
|
12869
|
+
`${TAG25} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
|
|
12870
|
+
);
|
|
12871
|
+
return c.json({ error: "grant-misconfigured" }, 500);
|
|
12872
|
+
}
|
|
12873
|
+
const session = createAccessSession({
|
|
12874
|
+
grantId: grant.grantId,
|
|
12875
|
+
sliceToken: grant.sliceToken,
|
|
12876
|
+
agentSlug: grant.agentSlug,
|
|
12877
|
+
accountId: grant.accountId,
|
|
12878
|
+
personId: grant.personId,
|
|
12879
|
+
contactValue: grant.contactValue
|
|
12880
|
+
});
|
|
12881
|
+
try {
|
|
12882
|
+
await consumeMagicTokenAndActivate(grant.grantId);
|
|
12883
|
+
} catch (err) {
|
|
12884
|
+
console.error(
|
|
12885
|
+
`${TAG25} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
12886
|
+
);
|
|
12887
|
+
return c.json({ error: "verification-failed" }, 500);
|
|
12888
|
+
}
|
|
12889
|
+
clearAccessRateLimit(ip, agentSlug);
|
|
12890
|
+
console.log(`${TAG25} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
|
|
12891
|
+
console.log(
|
|
12892
|
+
`${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
|
|
12893
|
+
);
|
|
12894
|
+
c.header(
|
|
12895
|
+
"Set-Cookie",
|
|
12896
|
+
`${COOKIE_NAME}=${session.sessionId}; HttpOnly; Secure; SameSite=Lax; Path=/`
|
|
12897
|
+
);
|
|
12898
|
+
return c.json({
|
|
12899
|
+
ok: true,
|
|
12900
|
+
agentSlug,
|
|
12901
|
+
displayName: grant.displayName
|
|
12902
|
+
});
|
|
12903
|
+
});
|
|
12904
|
+
var verify_token_default = app38;
|
|
12905
|
+
|
|
12906
|
+
// app/lib/access-email.ts
|
|
12907
|
+
import { spawn as spawn2 } from "child_process";
|
|
12908
|
+
import { resolve as resolve19 } from "path";
|
|
12909
|
+
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
|
|
12910
|
+
var SEND_SCRIPT = resolve19(
|
|
12911
|
+
PLATFORM_ROOT8,
|
|
12912
|
+
"plugins",
|
|
12913
|
+
"email",
|
|
12914
|
+
"mcp",
|
|
12915
|
+
"dist",
|
|
12916
|
+
"scripts",
|
|
12917
|
+
"send-transactional.js"
|
|
12918
|
+
);
|
|
12919
|
+
async function sendMagicLinkEmail(payload) {
|
|
12920
|
+
return new Promise((resolveResult) => {
|
|
12921
|
+
const child = spawn2("node", [SEND_SCRIPT], {
|
|
12922
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
12923
|
+
env: process.env
|
|
12924
|
+
});
|
|
12925
|
+
let stdout = "";
|
|
12926
|
+
let stderr = "";
|
|
12927
|
+
let settled = false;
|
|
12928
|
+
const timeout = setTimeout(() => {
|
|
12929
|
+
if (settled) return;
|
|
12930
|
+
settled = true;
|
|
12931
|
+
try {
|
|
12932
|
+
child.kill("SIGTERM");
|
|
12933
|
+
} catch {
|
|
12934
|
+
}
|
|
12935
|
+
resolveResult({ ok: false, error: "send-transactional timeout (30s)" });
|
|
12936
|
+
}, 3e4);
|
|
12937
|
+
child.stdout.on("data", (chunk) => {
|
|
12938
|
+
stdout += chunk.toString();
|
|
12939
|
+
});
|
|
12940
|
+
child.stderr.on("data", (chunk) => {
|
|
12941
|
+
stderr += chunk.toString();
|
|
12942
|
+
});
|
|
12943
|
+
child.on("error", (err) => {
|
|
12944
|
+
if (settled) return;
|
|
12945
|
+
settled = true;
|
|
12946
|
+
clearTimeout(timeout);
|
|
12947
|
+
resolveResult({ ok: false, error: `spawn-failed: ${err.message}` });
|
|
12948
|
+
});
|
|
12949
|
+
child.on("exit", (code) => {
|
|
12950
|
+
if (settled) return;
|
|
12951
|
+
settled = true;
|
|
12952
|
+
clearTimeout(timeout);
|
|
12953
|
+
if (code === 0) {
|
|
12954
|
+
const match = stdout.match(/^ok messageId=(.+)$/m);
|
|
12955
|
+
resolveResult({ ok: true, messageId: match?.[1]?.trim() ?? "unknown" });
|
|
12956
|
+
} else {
|
|
12957
|
+
resolveResult({ ok: false, error: stderr.trim() || `exit-${code}` });
|
|
12958
|
+
}
|
|
12959
|
+
});
|
|
12960
|
+
child.stdin.write(JSON.stringify(payload));
|
|
12961
|
+
child.stdin.end();
|
|
12962
|
+
});
|
|
12963
|
+
}
|
|
12964
|
+
|
|
12965
|
+
// server/routes/access/request-magic-link.ts
|
|
12966
|
+
var TAG26 = "[access-request-link]";
|
|
12967
|
+
var app39 = new Hono();
|
|
12968
|
+
var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
|
|
12969
|
+
app39.post("/", async (c) => {
|
|
12970
|
+
let body;
|
|
12971
|
+
try {
|
|
12972
|
+
body = await c.req.json();
|
|
12973
|
+
} catch {
|
|
12974
|
+
return c.json({ error: "invalid-json" }, 400);
|
|
12975
|
+
}
|
|
12976
|
+
const contactValue = typeof body.contactValue === "string" ? body.contactValue.trim().toLowerCase() : "";
|
|
12977
|
+
const agentSlug = typeof body.agentSlug === "string" ? body.agentSlug.trim() : "";
|
|
12978
|
+
if (!contactValue || !agentSlug) {
|
|
12979
|
+
return c.json({ error: "contactValue and agentSlug required" }, 400);
|
|
12980
|
+
}
|
|
12981
|
+
const rateMsg = checkRequestLinkRateLimit(contactValue);
|
|
12982
|
+
if (rateMsg) {
|
|
12983
|
+
console.error(`${TAG26} contactValue=${maskContact(contactValue)} result=rate-limited`);
|
|
12984
|
+
return c.json({ error: rateMsg }, 429);
|
|
12985
|
+
}
|
|
12986
|
+
recordRequestLinkAttempt(contactValue);
|
|
12987
|
+
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
12988
|
+
if (!accountId) {
|
|
12989
|
+
console.error(`${TAG26} contactValue=${maskContact(contactValue)} result=no-account-id`);
|
|
12990
|
+
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
12991
|
+
}
|
|
12992
|
+
const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
|
|
12993
|
+
if (!grant) {
|
|
12994
|
+
console.log(`${TAG26} contactValue=${maskContact(contactValue)} result=notfound`);
|
|
12995
|
+
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
12996
|
+
}
|
|
12997
|
+
let token;
|
|
12998
|
+
try {
|
|
12999
|
+
token = await generateNewMagicToken(grant.grantId);
|
|
13000
|
+
} catch (err) {
|
|
13001
|
+
console.error(
|
|
13002
|
+
`${TAG26} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
13003
|
+
);
|
|
13004
|
+
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
13005
|
+
}
|
|
13006
|
+
const host = c.req.header("host") ?? "";
|
|
13007
|
+
const proto = (c.req.header("x-forwarded-proto") ?? "https").toLowerCase();
|
|
13008
|
+
const magicUrl = `${proto}://${host}/${agentSlug}?token=${token}`;
|
|
13009
|
+
const subject = `Your link to ${grant.agentSlug}`;
|
|
13010
|
+
const bodyText = [
|
|
13011
|
+
grant.displayName ? `Hi ${grant.displayName},` : "Hello,",
|
|
13012
|
+
"",
|
|
13013
|
+
`Here's a fresh link to access ${grant.agentSlug}:`,
|
|
13014
|
+
"",
|
|
13015
|
+
magicUrl,
|
|
13016
|
+
"",
|
|
13017
|
+
"This link expires in 15 minutes."
|
|
13018
|
+
].join("\n");
|
|
13019
|
+
const htmlBody = `
|
|
13020
|
+
<p>${grant.displayName ? `Hi ${grant.displayName},` : "Hello,"}</p>
|
|
13021
|
+
<p>Here's a fresh link to access <strong>${grant.agentSlug}</strong>:</p>
|
|
13022
|
+
<p><a href="${magicUrl}" style="display:inline-block;padding:12px 24px;background:#000;color:#fff;text-decoration:none;border-radius:6px;">Open ${grant.agentSlug}</a></p>
|
|
13023
|
+
<p style="color:#888;font-size:14px;">This link expires in 15 minutes.</p>
|
|
13024
|
+
`;
|
|
13025
|
+
const sendResult = await sendMagicLinkEmail({
|
|
13026
|
+
accountId,
|
|
13027
|
+
to: grant.contactValue,
|
|
13028
|
+
subject,
|
|
13029
|
+
body: bodyText,
|
|
13030
|
+
html: htmlBody
|
|
13031
|
+
});
|
|
13032
|
+
if (!sendResult.ok) {
|
|
13033
|
+
console.error(
|
|
13034
|
+
`${TAG26} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
|
|
13035
|
+
);
|
|
13036
|
+
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
13037
|
+
}
|
|
13038
|
+
console.log(
|
|
13039
|
+
`${TAG26} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
|
|
13040
|
+
);
|
|
13041
|
+
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
13042
|
+
});
|
|
13043
|
+
var request_magic_link_default = app39;
|
|
13044
|
+
|
|
13045
|
+
// server/routes/access/index.ts
|
|
13046
|
+
var app40 = new Hono();
|
|
13047
|
+
app40.route("/verify-token", verify_token_default);
|
|
13048
|
+
app40.route("/request-magic-link", request_magic_link_default);
|
|
13049
|
+
var access_default = app40;
|
|
13050
|
+
|
|
13051
|
+
// server/routes/sites.ts
|
|
13052
|
+
import { existsSync as existsSync21, readFileSync as readFileSync17, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
|
|
13053
|
+
import { resolve as resolve20 } from "path";
|
|
12139
13054
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
12140
13055
|
var MIME = {
|
|
12141
13056
|
".html": "text/html; charset=utf-8",
|
|
@@ -12166,8 +13081,8 @@ function getExt(p) {
|
|
|
12166
13081
|
if (idx < p.lastIndexOf("/")) return "";
|
|
12167
13082
|
return p.slice(idx).toLowerCase();
|
|
12168
13083
|
}
|
|
12169
|
-
var
|
|
12170
|
-
|
|
13084
|
+
var app41 = new Hono();
|
|
13085
|
+
app41.get("/:rel{.*}", (c) => {
|
|
12171
13086
|
const reqPath = c.req.path;
|
|
12172
13087
|
const rawRel = c.req.param("rel") ?? "";
|
|
12173
13088
|
const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
@@ -12192,8 +13107,8 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
12192
13107
|
}
|
|
12193
13108
|
segments.push(seg);
|
|
12194
13109
|
}
|
|
12195
|
-
const rootDir =
|
|
12196
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
13110
|
+
const rootDir = resolve20(account.accountDir, "sites");
|
|
13111
|
+
let filePath = segments.length === 0 ? rootDir : resolve20(rootDir, ...segments);
|
|
12197
13112
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
12198
13113
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
12199
13114
|
return c.text("Forbidden", 403);
|
|
@@ -12213,7 +13128,7 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
12213
13128
|
return c.redirect(target, 301);
|
|
12214
13129
|
}
|
|
12215
13130
|
if (stat7?.isDirectory()) {
|
|
12216
|
-
filePath =
|
|
13131
|
+
filePath = resolve20(filePath, "index.html");
|
|
12217
13132
|
}
|
|
12218
13133
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
12219
13134
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
@@ -12238,7 +13153,7 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
12238
13153
|
}
|
|
12239
13154
|
let body;
|
|
12240
13155
|
try {
|
|
12241
|
-
body =
|
|
13156
|
+
body = readFileSync17(realPath);
|
|
12242
13157
|
} catch (err) {
|
|
12243
13158
|
const code = err?.code;
|
|
12244
13159
|
if (code === "EISDIR") {
|
|
@@ -12270,11 +13185,11 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
12270
13185
|
"X-Content-Type-Options": "nosniff"
|
|
12271
13186
|
});
|
|
12272
13187
|
});
|
|
12273
|
-
var sites_default =
|
|
13188
|
+
var sites_default = app41;
|
|
12274
13189
|
|
|
12275
13190
|
// app/lib/visitor-token.ts
|
|
12276
13191
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
12277
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
13192
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync18, writeFileSync as writeFileSync6 } from "fs";
|
|
12278
13193
|
import { dirname as dirname5 } from "path";
|
|
12279
13194
|
var TOKEN_PREFIX = "v1.";
|
|
12280
13195
|
var SECRET_BYTES = 32;
|
|
@@ -12283,7 +13198,7 @@ var cachedSecret = null;
|
|
|
12283
13198
|
function getSecret() {
|
|
12284
13199
|
if (cachedSecret) return cachedSecret;
|
|
12285
13200
|
try {
|
|
12286
|
-
const hex2 =
|
|
13201
|
+
const hex2 = readFileSync18(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
12287
13202
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
12288
13203
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
12289
13204
|
return cachedSecret;
|
|
@@ -12297,7 +13212,7 @@ function getSecret() {
|
|
|
12297
13212
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
12298
13213
|
} catch {
|
|
12299
13214
|
}
|
|
12300
|
-
const hex =
|
|
13215
|
+
const hex = readFileSync18(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
12301
13216
|
cachedSecret = Buffer.from(hex, "hex");
|
|
12302
13217
|
return cachedSecret;
|
|
12303
13218
|
}
|
|
@@ -12350,7 +13265,7 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
12350
13265
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
12351
13266
|
|
|
12352
13267
|
// app/lib/brand-config.ts
|
|
12353
|
-
import { existsSync as existsSync22, readFileSync as
|
|
13268
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
|
|
12354
13269
|
import { join as join14 } from "path";
|
|
12355
13270
|
var cached2 = null;
|
|
12356
13271
|
var cachedAttempted = false;
|
|
@@ -12362,7 +13277,7 @@ function readBrandConfig() {
|
|
|
12362
13277
|
const brandPath = join14(platformRoot, "config", "brand.json");
|
|
12363
13278
|
if (!existsSync22(brandPath)) return null;
|
|
12364
13279
|
try {
|
|
12365
|
-
cached2 = JSON.parse(
|
|
13280
|
+
cached2 = JSON.parse(readFileSync19(brandPath, "utf-8"));
|
|
12366
13281
|
return cached2;
|
|
12367
13282
|
} catch {
|
|
12368
13283
|
return null;
|
|
@@ -12370,7 +13285,7 @@ function readBrandConfig() {
|
|
|
12370
13285
|
}
|
|
12371
13286
|
|
|
12372
13287
|
// server/routes/visitor-consent.ts
|
|
12373
|
-
var
|
|
13288
|
+
var app42 = new Hono();
|
|
12374
13289
|
var CONSENT_COOKIE_NAME = "mxy_consent";
|
|
12375
13290
|
var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
|
|
12376
13291
|
var DEFAULT_CONSENT_COPY = {
|
|
@@ -12415,17 +13330,17 @@ function siteSlugFromReferer(referer) {
|
|
|
12415
13330
|
return "";
|
|
12416
13331
|
}
|
|
12417
13332
|
}
|
|
12418
|
-
|
|
13333
|
+
app42.options("/consent", (c) => {
|
|
12419
13334
|
const origin = getOrigin(c);
|
|
12420
13335
|
setCorsHeaders(c, origin);
|
|
12421
13336
|
return c.body(null, 204);
|
|
12422
13337
|
});
|
|
12423
|
-
|
|
13338
|
+
app42.options("/brand-config", (c) => {
|
|
12424
13339
|
const origin = getOrigin(c);
|
|
12425
13340
|
setCorsHeaders(c, origin);
|
|
12426
13341
|
return c.body(null, 204);
|
|
12427
13342
|
});
|
|
12428
|
-
|
|
13343
|
+
app42.post("/consent", async (c) => {
|
|
12429
13344
|
const origin = getOrigin(c);
|
|
12430
13345
|
setCorsHeaders(c, origin);
|
|
12431
13346
|
let raw;
|
|
@@ -12465,7 +13380,7 @@ app36.post("/consent", async (c) => {
|
|
|
12465
13380
|
console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
|
|
12466
13381
|
return c.body(null, 204);
|
|
12467
13382
|
});
|
|
12468
|
-
|
|
13383
|
+
app42.get("/brand-config", (c) => {
|
|
12469
13384
|
const origin = getOrigin(c);
|
|
12470
13385
|
setCorsHeaders(c, origin);
|
|
12471
13386
|
const brand = readBrandConfig();
|
|
@@ -12475,7 +13390,7 @@ app36.get("/brand-config", (c) => {
|
|
|
12475
13390
|
c.header("Cache-Control", "public, max-age=300");
|
|
12476
13391
|
return c.json({ consent: { copy, palette } });
|
|
12477
13392
|
});
|
|
12478
|
-
var visitor_consent_default =
|
|
13393
|
+
var visitor_consent_default = app42;
|
|
12479
13394
|
|
|
12480
13395
|
// server/routes/listings.ts
|
|
12481
13396
|
function getCookie(headerValue, name) {
|
|
@@ -12502,8 +13417,8 @@ function appendConsentParams(pageUrl, token) {
|
|
|
12502
13417
|
}
|
|
12503
13418
|
var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
|
|
12504
13419
|
var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
|
|
12505
|
-
var
|
|
12506
|
-
|
|
13420
|
+
var app43 = new Hono();
|
|
13421
|
+
app43.get("/:slug/click", async (c) => {
|
|
12507
13422
|
const slug = c.req.param("slug") ?? "";
|
|
12508
13423
|
const rawSession = c.req.query("session") ?? "";
|
|
12509
13424
|
const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
|
|
@@ -12567,10 +13482,10 @@ app37.get("/:slug/click", async (c) => {
|
|
|
12567
13482
|
console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
12568
13483
|
return c.redirect(redirectUrl, 302);
|
|
12569
13484
|
});
|
|
12570
|
-
var listings_default =
|
|
13485
|
+
var listings_default = app43;
|
|
12571
13486
|
|
|
12572
13487
|
// server/routes/visitor-event.ts
|
|
12573
|
-
var
|
|
13488
|
+
var app44 = new Hono();
|
|
12574
13489
|
var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
|
|
12575
13490
|
var buckets = /* @__PURE__ */ new Map();
|
|
12576
13491
|
var RATE_LIMIT = 60;
|
|
@@ -12637,12 +13552,12 @@ function originAllowed(origin, allowlist) {
|
|
|
12637
13552
|
return false;
|
|
12638
13553
|
}
|
|
12639
13554
|
}
|
|
12640
|
-
|
|
13555
|
+
app44.options("/event", (c) => {
|
|
12641
13556
|
const origin = getOrigin2(c);
|
|
12642
13557
|
setCorsHeaders2(c, origin);
|
|
12643
13558
|
return c.body(null, 204);
|
|
12644
13559
|
});
|
|
12645
|
-
|
|
13560
|
+
app44.post("/event", async (c) => {
|
|
12646
13561
|
const origin = getOrigin2(c);
|
|
12647
13562
|
setCorsHeaders2(c, origin);
|
|
12648
13563
|
const ua = c.req.header("user-agent") ?? "";
|
|
@@ -12847,7 +13762,7 @@ async function writeEvent(opts) {
|
|
|
12847
13762
|
);
|
|
12848
13763
|
}
|
|
12849
13764
|
}
|
|
12850
|
-
var visitor_event_default =
|
|
13765
|
+
var visitor_event_default = app44;
|
|
12851
13766
|
|
|
12852
13767
|
// app/lib/graph-health.ts
|
|
12853
13768
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
@@ -12931,7 +13846,7 @@ function startGraphHealthTimer() {
|
|
|
12931
13846
|
}
|
|
12932
13847
|
|
|
12933
13848
|
// app/lib/whatsapp/inbound/claude-bridge.ts
|
|
12934
|
-
var
|
|
13849
|
+
var TAG27 = "[whatsapp-adaptor]";
|
|
12935
13850
|
function whatsappTurnTimeoutMs() {
|
|
12936
13851
|
return Number(process.env.WHATSAPP_PTY_TURN_TIMEOUT_MS ?? String(5 * 6e4));
|
|
12937
13852
|
}
|
|
@@ -12954,7 +13869,7 @@ async function dispatchToClaude(input) {
|
|
|
12954
13869
|
await input.reply(result.turnText);
|
|
12955
13870
|
} catch (err) {
|
|
12956
13871
|
const m = err instanceof Error ? err.message : String(err);
|
|
12957
|
-
console.error(`${
|
|
13872
|
+
console.error(`${TAG27} reject reason=reply-failed senderId=${input.senderId} message=${m}`);
|
|
12958
13873
|
}
|
|
12959
13874
|
}
|
|
12960
13875
|
function startReaper2() {
|
|
@@ -12991,8 +13906,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
12991
13906
|
|
|
12992
13907
|
// ../lib/entitlement/src/index.ts
|
|
12993
13908
|
import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
|
|
12994
|
-
import { existsSync as existsSync23, readFileSync as
|
|
12995
|
-
import { resolve as
|
|
13909
|
+
import { existsSync as existsSync23, readFileSync as readFileSync20, statSync as statSync8 } from "fs";
|
|
13910
|
+
import { resolve as resolve21 } from "path";
|
|
12996
13911
|
|
|
12997
13912
|
// ../lib/entitlement/src/canonicalize.ts
|
|
12998
13913
|
function canonicalize(value) {
|
|
@@ -13027,7 +13942,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
13027
13942
|
var GRACE_DAYS = 7;
|
|
13028
13943
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
13029
13944
|
function pubkeyPath(brand) {
|
|
13030
|
-
return
|
|
13945
|
+
return resolve21(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
13031
13946
|
}
|
|
13032
13947
|
var memo = null;
|
|
13033
13948
|
function memoKey(mtimeMs, account) {
|
|
@@ -13039,7 +13954,7 @@ function resolveEntitlement(brand, account) {
|
|
|
13039
13954
|
if (brand.commercialMode !== true) {
|
|
13040
13955
|
return logResolved(implicitTrust(account), null);
|
|
13041
13956
|
}
|
|
13042
|
-
const entitlementPath =
|
|
13957
|
+
const entitlementPath = resolve21(brand.configDir, "entitlement.json");
|
|
13043
13958
|
if (!existsSync23(entitlementPath)) {
|
|
13044
13959
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
13045
13960
|
}
|
|
@@ -13055,7 +13970,7 @@ function resolveEntitlement(brand, account) {
|
|
|
13055
13970
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
13056
13971
|
let pubkeyPem;
|
|
13057
13972
|
try {
|
|
13058
|
-
pubkeyPem =
|
|
13973
|
+
pubkeyPem = readFileSync20(pubkeyPath(brand), "utf-8");
|
|
13059
13974
|
} catch (err) {
|
|
13060
13975
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
13061
13976
|
reason: "pubkey-missing"
|
|
@@ -13069,7 +13984,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
13069
13984
|
}
|
|
13070
13985
|
let envelope;
|
|
13071
13986
|
try {
|
|
13072
|
-
envelope = JSON.parse(
|
|
13987
|
+
envelope = JSON.parse(readFileSync20(entitlementPath, "utf-8"));
|
|
13073
13988
|
} catch {
|
|
13074
13989
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
13075
13990
|
}
|
|
@@ -13229,15 +14144,15 @@ function clientFrom(c) {
|
|
|
13229
14144
|
c.req.header("x-forwarded-for")
|
|
13230
14145
|
);
|
|
13231
14146
|
}
|
|
13232
|
-
var
|
|
13233
|
-
var BRAND_JSON_PATH =
|
|
14147
|
+
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
14148
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join15(PLATFORM_ROOT9, "config", "brand.json") : "";
|
|
13234
14149
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
13235
14150
|
if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
|
|
13236
14151
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
13237
14152
|
}
|
|
13238
14153
|
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
13239
14154
|
try {
|
|
13240
|
-
const parsed = JSON.parse(
|
|
14155
|
+
const parsed = JSON.parse(readFileSync21(BRAND_JSON_PATH, "utf-8"));
|
|
13241
14156
|
BRAND = { ...BRAND, ...parsed };
|
|
13242
14157
|
} catch (err) {
|
|
13243
14158
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -13260,7 +14175,7 @@ var ALIAS_DOMAINS_PATH = join15(homedir3(), BRAND.configDir, "alias-domains.json
|
|
|
13260
14175
|
function loadAliasDomains() {
|
|
13261
14176
|
try {
|
|
13262
14177
|
if (!existsSync24(ALIAS_DOMAINS_PATH)) return null;
|
|
13263
|
-
const parsed = JSON.parse(
|
|
14178
|
+
const parsed = JSON.parse(readFileSync21(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
13264
14179
|
if (!Array.isArray(parsed)) {
|
|
13265
14180
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
13266
14181
|
return null;
|
|
@@ -13284,9 +14199,9 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
|
|
|
13284
14199
|
function isPublicHost(host) {
|
|
13285
14200
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
13286
14201
|
}
|
|
13287
|
-
var
|
|
13288
|
-
|
|
13289
|
-
|
|
14202
|
+
var app45 = new Hono();
|
|
14203
|
+
app45.use("*", clientIpMiddleware);
|
|
14204
|
+
app45.use("*", async (c, next) => {
|
|
13290
14205
|
await next();
|
|
13291
14206
|
c.header("X-Content-Type-Options", "nosniff");
|
|
13292
14207
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
@@ -13296,7 +14211,7 @@ app39.use("*", async (c, next) => {
|
|
|
13296
14211
|
);
|
|
13297
14212
|
});
|
|
13298
14213
|
var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
|
|
13299
|
-
|
|
14214
|
+
app45.use("*", async (c, next) => {
|
|
13300
14215
|
if (!HTTP_LOG_PATHS.has(c.req.path)) {
|
|
13301
14216
|
await next();
|
|
13302
14217
|
return;
|
|
@@ -13329,7 +14244,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
|
|
|
13329
14244
|
"/sites/"
|
|
13330
14245
|
];
|
|
13331
14246
|
var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
|
|
13332
|
-
|
|
14247
|
+
app45.use("*", async (c, next) => {
|
|
13333
14248
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13334
14249
|
if (!isPublicHost(host)) {
|
|
13335
14250
|
await next();
|
|
@@ -13369,7 +14284,7 @@ function resolveRemoteAuthOpts() {
|
|
|
13369
14284
|
return brandLoginOpts;
|
|
13370
14285
|
}
|
|
13371
14286
|
var MAX_LOGIN_BODY = 8 * 1024;
|
|
13372
|
-
|
|
14287
|
+
app45.post("/__remote-auth/login", async (c) => {
|
|
13373
14288
|
const client = clientFrom(c);
|
|
13374
14289
|
const clientIp = client.ip || "unknown";
|
|
13375
14290
|
if (!requestIsTlsTerminated(c)) {
|
|
@@ -13414,7 +14329,7 @@ app39.post("/__remote-auth/login", async (c) => {
|
|
|
13414
14329
|
}
|
|
13415
14330
|
});
|
|
13416
14331
|
});
|
|
13417
|
-
|
|
14332
|
+
app45.get("/__remote-auth/logout", (c) => {
|
|
13418
14333
|
const client = clientFrom(c);
|
|
13419
14334
|
const clientIp = client.ip || "unknown";
|
|
13420
14335
|
console.error(`[remote-auth] logout ip=${clientIp}`);
|
|
@@ -13427,7 +14342,7 @@ app39.get("/__remote-auth/logout", (c) => {
|
|
|
13427
14342
|
}
|
|
13428
14343
|
});
|
|
13429
14344
|
});
|
|
13430
|
-
|
|
14345
|
+
app45.post("/__remote-auth/change-password", async (c) => {
|
|
13431
14346
|
const client = clientFrom(c);
|
|
13432
14347
|
const clientIp = client.ip || "unknown";
|
|
13433
14348
|
const rateLimited = checkRateLimit(client);
|
|
@@ -13478,13 +14393,13 @@ app39.post("/__remote-auth/change-password", async (c) => {
|
|
|
13478
14393
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
|
|
13479
14394
|
}
|
|
13480
14395
|
});
|
|
13481
|
-
|
|
14396
|
+
app45.get("/__remote-auth/setup", (c) => {
|
|
13482
14397
|
if (isRemoteAuthConfigured()) {
|
|
13483
14398
|
return c.redirect("/");
|
|
13484
14399
|
}
|
|
13485
14400
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
|
|
13486
14401
|
});
|
|
13487
|
-
|
|
14402
|
+
app45.post("/__remote-auth/set-initial-password", async (c) => {
|
|
13488
14403
|
if (isRemoteAuthConfigured()) {
|
|
13489
14404
|
return c.redirect("/");
|
|
13490
14405
|
}
|
|
@@ -13522,10 +14437,10 @@ app39.post("/__remote-auth/set-initial-password", async (c) => {
|
|
|
13522
14437
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
|
|
13523
14438
|
}
|
|
13524
14439
|
});
|
|
13525
|
-
|
|
14440
|
+
app45.get("/api/remote-auth/status", (c) => {
|
|
13526
14441
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
13527
14442
|
});
|
|
13528
|
-
|
|
14443
|
+
app45.post("/api/remote-auth/set-password", async (c) => {
|
|
13529
14444
|
let body;
|
|
13530
14445
|
try {
|
|
13531
14446
|
body = await c.req.json();
|
|
@@ -13556,9 +14471,9 @@ app39.post("/api/remote-auth/set-password", async (c) => {
|
|
|
13556
14471
|
return c.json({ error: "Failed to save password" }, 500);
|
|
13557
14472
|
}
|
|
13558
14473
|
});
|
|
13559
|
-
|
|
14474
|
+
app45.route("/api/_client-error", client_error_default);
|
|
13560
14475
|
console.log("[client-error-route] mounted");
|
|
13561
|
-
|
|
14476
|
+
app45.use("*", async (c, next) => {
|
|
13562
14477
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13563
14478
|
const path2 = c.req.path;
|
|
13564
14479
|
if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
|
|
@@ -13591,11 +14506,12 @@ app39.use("*", async (c, next) => {
|
|
|
13591
14506
|
console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
|
|
13592
14507
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
|
|
13593
14508
|
});
|
|
13594
|
-
|
|
13595
|
-
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
14509
|
+
app45.route("/api/health", health_default);
|
|
14510
|
+
app45.route("/api/chat", chat_default);
|
|
14511
|
+
app45.route("/api/whatsapp", whatsapp_default);
|
|
14512
|
+
app45.route("/api/onboarding", onboarding_default);
|
|
14513
|
+
app45.route("/api/admin", admin_default);
|
|
14514
|
+
app45.route("/api/access", access_default);
|
|
13599
14515
|
var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
|
|
13600
14516
|
var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
13601
14517
|
var IMAGE_MIME = {
|
|
@@ -13607,7 +14523,7 @@ var IMAGE_MIME = {
|
|
|
13607
14523
|
".svg": "image/svg+xml",
|
|
13608
14524
|
".ico": "image/x-icon"
|
|
13609
14525
|
};
|
|
13610
|
-
|
|
14526
|
+
app45.get("/agent-assets/:slug/:filename", (c) => {
|
|
13611
14527
|
const slug = c.req.param("slug");
|
|
13612
14528
|
const filename = c.req.param("filename");
|
|
13613
14529
|
if (!SAFE_SLUG_RE2.test(slug)) {
|
|
@@ -13623,8 +14539,8 @@ app39.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
13623
14539
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
13624
14540
|
return c.text("Not found", 404);
|
|
13625
14541
|
}
|
|
13626
|
-
const filePath =
|
|
13627
|
-
const expectedDir =
|
|
14542
|
+
const filePath = resolve22(account.accountDir, "agents", slug, "assets", filename);
|
|
14543
|
+
const expectedDir = resolve22(account.accountDir, "agents", slug, "assets");
|
|
13628
14544
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
13629
14545
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
13630
14546
|
return c.text("Forbidden", 403);
|
|
@@ -13636,13 +14552,13 @@ app39.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
13636
14552
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
13637
14553
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
13638
14554
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
13639
|
-
const body =
|
|
14555
|
+
const body = readFileSync21(filePath);
|
|
13640
14556
|
return c.body(body, 200, {
|
|
13641
14557
|
"Content-Type": contentType,
|
|
13642
14558
|
"Cache-Control": "public, max-age=3600"
|
|
13643
14559
|
});
|
|
13644
14560
|
});
|
|
13645
|
-
|
|
14561
|
+
app45.get("/generated/:filename", (c) => {
|
|
13646
14562
|
const filename = c.req.param("filename");
|
|
13647
14563
|
if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
|
|
13648
14564
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
@@ -13653,8 +14569,8 @@ app39.get("/generated/:filename", (c) => {
|
|
|
13653
14569
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
13654
14570
|
return c.text("Not found", 404);
|
|
13655
14571
|
}
|
|
13656
|
-
const filePath =
|
|
13657
|
-
const expectedDir =
|
|
14572
|
+
const filePath = resolve22(account.accountDir, "generated", filename);
|
|
14573
|
+
const expectedDir = resolve22(account.accountDir, "generated");
|
|
13658
14574
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
13659
14575
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
13660
14576
|
return c.text("Forbidden", 403);
|
|
@@ -13666,22 +14582,22 @@ app39.get("/generated/:filename", (c) => {
|
|
|
13666
14582
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
13667
14583
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
13668
14584
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
13669
|
-
const body =
|
|
14585
|
+
const body = readFileSync21(filePath);
|
|
13670
14586
|
return c.body(body, 200, {
|
|
13671
14587
|
"Content-Type": contentType,
|
|
13672
14588
|
"Cache-Control": "public, max-age=86400"
|
|
13673
14589
|
});
|
|
13674
14590
|
});
|
|
13675
|
-
|
|
13676
|
-
|
|
13677
|
-
|
|
13678
|
-
|
|
14591
|
+
app45.route("/sites", sites_default);
|
|
14592
|
+
app45.route("/listings", listings_default);
|
|
14593
|
+
app45.route("/v", visitor_event_default);
|
|
14594
|
+
app45.route("/v", visitor_consent_default);
|
|
13679
14595
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
13680
14596
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
13681
14597
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
13682
14598
|
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
13683
14599
|
try {
|
|
13684
|
-
const fullBrand = JSON.parse(
|
|
14600
|
+
const fullBrand = JSON.parse(readFileSync21(BRAND_JSON_PATH, "utf-8"));
|
|
13685
14601
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
13686
14602
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
13687
14603
|
} catch {
|
|
@@ -13697,10 +14613,10 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
13697
14613
|
})}</script>`;
|
|
13698
14614
|
function readInstalledVersion() {
|
|
13699
14615
|
try {
|
|
13700
|
-
if (!
|
|
13701
|
-
const versionFile = join15(
|
|
14616
|
+
if (!PLATFORM_ROOT9) return "unknown";
|
|
14617
|
+
const versionFile = join15(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
|
|
13702
14618
|
if (!existsSync24(versionFile)) return "unknown";
|
|
13703
|
-
const content =
|
|
14619
|
+
const content = readFileSync21(versionFile, "utf-8").trim();
|
|
13704
14620
|
return content || "unknown";
|
|
13705
14621
|
} catch {
|
|
13706
14622
|
return "unknown";
|
|
@@ -13741,7 +14657,7 @@ var clientErrorReporterScript = `<script>
|
|
|
13741
14657
|
function cachedHtml(file) {
|
|
13742
14658
|
let html = htmlCache.get(file);
|
|
13743
14659
|
if (!html) {
|
|
13744
|
-
html =
|
|
14660
|
+
html = readFileSync21(resolve22(process.cwd(), "public", file), "utf-8");
|
|
13745
14661
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
13746
14662
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
13747
14663
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -13761,12 +14677,12 @@ function loadBrandingCache(agentSlug) {
|
|
|
13761
14677
|
try {
|
|
13762
14678
|
const accountJsonPath = join15(configDir2, "account.json");
|
|
13763
14679
|
if (!existsSync24(accountJsonPath)) return null;
|
|
13764
|
-
const account = JSON.parse(
|
|
14680
|
+
const account = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
|
|
13765
14681
|
const accountId = account.accountId;
|
|
13766
14682
|
if (!accountId) return null;
|
|
13767
14683
|
const cachePath = join15(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
13768
14684
|
if (!existsSync24(cachePath)) return null;
|
|
13769
|
-
return JSON.parse(
|
|
14685
|
+
return JSON.parse(readFileSync21(cachePath, "utf-8"));
|
|
13770
14686
|
} catch {
|
|
13771
14687
|
return null;
|
|
13772
14688
|
}
|
|
@@ -13776,7 +14692,7 @@ function resolveDefaultSlug() {
|
|
|
13776
14692
|
const configDir2 = join15(homedir3(), BRAND.configDir);
|
|
13777
14693
|
const accountJsonPath = join15(configDir2, "account.json");
|
|
13778
14694
|
if (!existsSync24(accountJsonPath)) return null;
|
|
13779
|
-
const account = JSON.parse(
|
|
14695
|
+
const account = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
|
|
13780
14696
|
return account.defaultAgent || null;
|
|
13781
14697
|
} catch {
|
|
13782
14698
|
return null;
|
|
@@ -13812,7 +14728,7 @@ function brandedPublicHtml(agentSlug) {
|
|
|
13812
14728
|
function escapeHtml(s) {
|
|
13813
14729
|
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
13814
14730
|
}
|
|
13815
|
-
|
|
14731
|
+
app45.get("/", (c) => {
|
|
13816
14732
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13817
14733
|
if (isPublicHost(host)) {
|
|
13818
14734
|
const defaultSlug = resolveDefaultSlug();
|
|
@@ -13820,12 +14736,12 @@ app39.get("/", (c) => {
|
|
|
13820
14736
|
}
|
|
13821
14737
|
return c.html(cachedHtml("index.html"));
|
|
13822
14738
|
});
|
|
13823
|
-
|
|
14739
|
+
app45.get("/public", (c) => {
|
|
13824
14740
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13825
14741
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13826
14742
|
return c.html(cachedHtml("public.html"));
|
|
13827
14743
|
});
|
|
13828
|
-
|
|
14744
|
+
app45.get("/chat", (c) => {
|
|
13829
14745
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13830
14746
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13831
14747
|
return c.html(cachedHtml("public.html"));
|
|
@@ -13844,12 +14760,12 @@ async function logViewerFetch(c, next) {
|
|
|
13844
14760
|
duration_ms: Date.now() - start
|
|
13845
14761
|
});
|
|
13846
14762
|
}
|
|
13847
|
-
|
|
13848
|
-
|
|
13849
|
-
|
|
14763
|
+
app45.use("/vnc-viewer.html", logViewerFetch);
|
|
14764
|
+
app45.use("/vnc-popout.html", logViewerFetch);
|
|
14765
|
+
app45.get("/vnc-popout.html", (c) => {
|
|
13850
14766
|
let html = htmlCache.get("vnc-popout.html");
|
|
13851
14767
|
if (!html) {
|
|
13852
|
-
html =
|
|
14768
|
+
html = readFileSync21(resolve22(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
13853
14769
|
const name = escapeHtml(BRAND.productName);
|
|
13854
14770
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
13855
14771
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -13859,7 +14775,7 @@ app39.get("/vnc-popout.html", (c) => {
|
|
|
13859
14775
|
}
|
|
13860
14776
|
return c.html(html);
|
|
13861
14777
|
});
|
|
13862
|
-
|
|
14778
|
+
app45.post("/api/vnc/client-event", async (c) => {
|
|
13863
14779
|
let body;
|
|
13864
14780
|
try {
|
|
13865
14781
|
body = await c.req.json();
|
|
@@ -13880,25 +14796,25 @@ app39.post("/api/vnc/client-event", async (c) => {
|
|
|
13880
14796
|
});
|
|
13881
14797
|
return c.json({ ok: true });
|
|
13882
14798
|
});
|
|
13883
|
-
|
|
14799
|
+
app45.get("/g/:slug", (c) => {
|
|
13884
14800
|
return c.html(brandedPublicHtml());
|
|
13885
14801
|
});
|
|
13886
|
-
|
|
14802
|
+
app45.get("/graph", (c) => {
|
|
13887
14803
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13888
14804
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13889
14805
|
return c.html(cachedHtml("graph.html"));
|
|
13890
14806
|
});
|
|
13891
|
-
|
|
14807
|
+
app45.get("/sessions", (c) => {
|
|
13892
14808
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13893
14809
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13894
14810
|
return c.html(cachedHtml("sessions.html"));
|
|
13895
14811
|
});
|
|
13896
|
-
|
|
14812
|
+
app45.get("/data", (c) => {
|
|
13897
14813
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13898
14814
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13899
14815
|
return c.html(cachedHtml("data.html"));
|
|
13900
14816
|
});
|
|
13901
|
-
|
|
14817
|
+
app45.get("/:slug", async (c, next) => {
|
|
13902
14818
|
const slug = c.req.param("slug");
|
|
13903
14819
|
if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
|
|
13904
14820
|
const branding = loadBrandingCache(slug);
|
|
@@ -13908,15 +14824,15 @@ app39.get("/:slug", async (c, next) => {
|
|
|
13908
14824
|
await next();
|
|
13909
14825
|
});
|
|
13910
14826
|
if (brandFaviconPath !== "/favicon.ico") {
|
|
13911
|
-
|
|
14827
|
+
app45.get("/favicon.ico", (c) => {
|
|
13912
14828
|
c.header("Cache-Control", "public, max-age=300");
|
|
13913
14829
|
return c.redirect(brandFaviconPath, 302);
|
|
13914
14830
|
});
|
|
13915
14831
|
}
|
|
13916
|
-
|
|
14832
|
+
app45.use("/*", serveStatic({ root: "./public" }));
|
|
13917
14833
|
var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
|
|
13918
14834
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
13919
|
-
var httpServer = serve({ fetch:
|
|
14835
|
+
var httpServer = serve({ fetch: app45.fetch, port, hostname });
|
|
13920
14836
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
13921
14837
|
{
|
|
13922
14838
|
const loopHist = monitorEventLoopDelay({ resolution: 50 });
|
|
@@ -13940,6 +14856,7 @@ var SUBAPP_MANIFEST = [
|
|
|
13940
14856
|
{ prefix: "/api/whatsapp", file: "server/routes/whatsapp.ts", subapp: whatsapp_default },
|
|
13941
14857
|
{ prefix: "/api/onboarding", file: "server/routes/onboarding.ts", subapp: onboarding_default },
|
|
13942
14858
|
{ prefix: "/api/admin", file: "server/routes/admin/index.ts", subapp: admin_default },
|
|
14859
|
+
{ prefix: "/api/access", file: "server/routes/access/index.ts", subapp: access_default },
|
|
13943
14860
|
{ prefix: "/api/_client-error", file: "server/routes/client-error.ts", subapp: client_error_default },
|
|
13944
14861
|
{ prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default },
|
|
13945
14862
|
{ prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default },
|
|
@@ -13951,7 +14868,7 @@ for (const m of SUBAPP_MANIFEST) {
|
|
|
13951
14868
|
}
|
|
13952
14869
|
try {
|
|
13953
14870
|
const registered = [];
|
|
13954
|
-
for (const r of
|
|
14871
|
+
for (const r of app45.routes ?? []) {
|
|
13955
14872
|
if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
|
|
13956
14873
|
if (AGENT_SLUG_PATTERN.test(r.path)) {
|
|
13957
14874
|
registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
|
|
@@ -13974,7 +14891,7 @@ try {
|
|
|
13974
14891
|
(async () => {
|
|
13975
14892
|
try {
|
|
13976
14893
|
if (!existsSync24(USERS_FILE)) return;
|
|
13977
|
-
const usersRaw =
|
|
14894
|
+
const usersRaw = readFileSync21(USERS_FILE, "utf-8").trim();
|
|
13978
14895
|
if (!usersRaw) return;
|
|
13979
14896
|
const users = JSON.parse(usersRaw);
|
|
13980
14897
|
const userId = users[0]?.userId;
|
|
@@ -14066,7 +14983,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
14066
14983
|
}
|
|
14067
14984
|
init({
|
|
14068
14985
|
configDir: configDirForWhatsApp,
|
|
14069
|
-
platformRoot:
|
|
14986
|
+
platformRoot: resolve22(process.env.MAXY_PLATFORM_ROOT ?? join15(__dirname, "..")),
|
|
14070
14987
|
accountConfig: bootAccountConfig,
|
|
14071
14988
|
onMessage: async (msg) => {
|
|
14072
14989
|
if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {
|