@rubytech/create-maxy-code 0.1.182 → 0.1.183
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 +5 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +76 -2
- 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 +12 -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 +32 -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 +154 -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 +1158 -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,146 @@ 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
|
+
function managerBase2() {
|
|
4153
|
+
const port2 = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "public-session-end-review:manager" });
|
|
4154
|
+
return `http://127.0.0.1:${port2}`;
|
|
4155
|
+
}
|
|
4156
|
+
function uiBase() {
|
|
4157
|
+
const port2 = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "public-session-end-review:ui" });
|
|
4158
|
+
return `http://127.0.0.1:${port2}`;
|
|
4159
|
+
}
|
|
4160
|
+
async function fetchJsonl(sessionId) {
|
|
4161
|
+
try {
|
|
4162
|
+
const res = await fetch(`${managerBase2()}/${sessionId}/log?download=1`);
|
|
4163
|
+
if (!res.ok) return null;
|
|
4164
|
+
return await res.text();
|
|
4165
|
+
} catch (err) {
|
|
4166
|
+
console.error(
|
|
4167
|
+
`${TAG14} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
|
|
4168
|
+
);
|
|
4169
|
+
return null;
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
function countOperatorTurns(jsonl) {
|
|
4173
|
+
let count = 0;
|
|
4174
|
+
for (const raw of jsonl.split("\n")) {
|
|
4175
|
+
const line = raw.trim();
|
|
4176
|
+
if (!line) continue;
|
|
4177
|
+
try {
|
|
4178
|
+
const obj = JSON.parse(line);
|
|
4179
|
+
if (obj.type === "user") count += 1;
|
|
4180
|
+
} catch {
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
4183
|
+
return count;
|
|
4184
|
+
}
|
|
4185
|
+
async function fetchPriorWrites(sliceToken, accountId) {
|
|
4186
|
+
const url = `${uiBase()}/api/admin/public-session-context?sliceToken=${encodeURIComponent(sliceToken)}&accountId=${encodeURIComponent(accountId)}`;
|
|
4187
|
+
try {
|
|
4188
|
+
const res = await fetch(url);
|
|
4189
|
+
if (!res.ok) {
|
|
4190
|
+
console.error(
|
|
4191
|
+
`${TAG14} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
|
|
4192
|
+
);
|
|
4193
|
+
return [];
|
|
4194
|
+
}
|
|
4195
|
+
const body = await res.json();
|
|
4196
|
+
return Array.isArray(body.writes) ? body.writes : [];
|
|
4197
|
+
} catch (err) {
|
|
4198
|
+
console.error(
|
|
4199
|
+
`${TAG14} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
4200
|
+
);
|
|
4201
|
+
return [];
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
function composeInitialMessage(input) {
|
|
4205
|
+
const priorJson = JSON.stringify(input.priorWrites, null, 2);
|
|
4206
|
+
return [
|
|
4207
|
+
`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.`,
|
|
4208
|
+
"",
|
|
4209
|
+
"<slice-token>",
|
|
4210
|
+
input.sliceToken,
|
|
4211
|
+
"</slice-token>",
|
|
4212
|
+
"",
|
|
4213
|
+
"<person-id>",
|
|
4214
|
+
input.personId ?? "(unknown)",
|
|
4215
|
+
"</person-id>",
|
|
4216
|
+
"",
|
|
4217
|
+
"<prior-writes>",
|
|
4218
|
+
priorJson,
|
|
4219
|
+
"</prior-writes>",
|
|
4220
|
+
"",
|
|
4221
|
+
"<transcript>",
|
|
4222
|
+
input.jsonl,
|
|
4223
|
+
"</transcript>"
|
|
4224
|
+
].join("\n");
|
|
4225
|
+
}
|
|
4226
|
+
async function dispatchReviewer(input, initialMessage) {
|
|
4227
|
+
try {
|
|
4228
|
+
const res = await fetch(`${managerBase2()}/spawn`, {
|
|
4229
|
+
method: "POST",
|
|
4230
|
+
headers: { "content-type": "application/json" },
|
|
4231
|
+
body: JSON.stringify({
|
|
4232
|
+
senderId: `public-session-reviewer-${input.sessionId}`,
|
|
4233
|
+
role: "admin",
|
|
4234
|
+
channel: "webchat",
|
|
4235
|
+
accountId: input.accountId,
|
|
4236
|
+
specialist: "public-session-reviewer",
|
|
4237
|
+
hidden: true,
|
|
4238
|
+
initialMessage,
|
|
4239
|
+
sliceToken: input.sliceToken,
|
|
4240
|
+
personId: input.personId ?? void 0
|
|
4241
|
+
})
|
|
4242
|
+
});
|
|
4243
|
+
if (!res.ok) {
|
|
4244
|
+
return { ok: false, reason: `spawn-status-${res.status}` };
|
|
4245
|
+
}
|
|
4246
|
+
const body = await res.json();
|
|
4247
|
+
return { ok: true, managerSessionId: body.sessionId ?? "unknown" };
|
|
4248
|
+
} catch (err) {
|
|
4249
|
+
return { ok: false, reason: `spawn-threw-${err instanceof Error ? err.message : String(err)}` };
|
|
4250
|
+
}
|
|
4251
|
+
}
|
|
4252
|
+
async function firePublicSessionEndReview(input) {
|
|
4253
|
+
const sliceShort = input.sliceToken.slice(0, 8);
|
|
4254
|
+
const personField = input.personId ?? "none";
|
|
4255
|
+
const dispatchedAt = Date.now();
|
|
4256
|
+
const jsonl = await fetchJsonl(input.sessionId);
|
|
4257
|
+
if (!jsonl) {
|
|
4258
|
+
console.log(
|
|
4259
|
+
`${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
|
|
4260
|
+
);
|
|
4261
|
+
return;
|
|
4262
|
+
}
|
|
4263
|
+
const operatorTurns = countOperatorTurns(jsonl);
|
|
4264
|
+
if (operatorTurns === 0) {
|
|
4265
|
+
console.log(
|
|
4266
|
+
`${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
|
|
4267
|
+
);
|
|
4268
|
+
return;
|
|
4269
|
+
}
|
|
4270
|
+
const priorWrites = await fetchPriorWrites(input.sliceToken, input.accountId);
|
|
4271
|
+
const initialMessage = composeInitialMessage({
|
|
4272
|
+
agentSlug: input.agentSlug,
|
|
4273
|
+
sliceToken: input.sliceToken,
|
|
4274
|
+
personId: input.personId,
|
|
4275
|
+
jsonl,
|
|
4276
|
+
priorWrites
|
|
4277
|
+
});
|
|
4278
|
+
const dispatched = await dispatchReviewer(input, initialMessage);
|
|
4279
|
+
if (!dispatched.ok) {
|
|
4280
|
+
console.error(
|
|
4281
|
+
`${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
|
|
4282
|
+
);
|
|
4283
|
+
return;
|
|
4284
|
+
}
|
|
4285
|
+
console.log(
|
|
4286
|
+
`${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}`
|
|
4287
|
+
);
|
|
4288
|
+
}
|
|
4289
|
+
|
|
4148
4290
|
// app/lib/channel-pty-bridge/follower.ts
|
|
4149
4291
|
function startFollower(opts) {
|
|
4150
4292
|
const abort = new AbortController();
|
|
@@ -4235,12 +4377,15 @@ function publicIdleMs() {
|
|
|
4235
4377
|
return Number(process.env.CHANNEL_PTY_IDLE_MS ?? String(30 * 6e4));
|
|
4236
4378
|
}
|
|
4237
4379
|
var index = /* @__PURE__ */ new Map();
|
|
4238
|
-
function indexKey(accountId, agentSlug, senderId, liveMemory) {
|
|
4239
|
-
|
|
4380
|
+
function indexKey(accountId, agentSlug, senderId, liveMemory, sliceToken = "") {
|
|
4381
|
+
const slice = sliceToken && sliceToken.length > 0 ? sliceToken : "open";
|
|
4382
|
+
return `${accountId}:${agentSlug}:${liveMemory ? "lm1" : "lm0"}:${slice}:${senderId}`;
|
|
4240
4383
|
}
|
|
4241
4384
|
async function ensureEntry(input) {
|
|
4242
4385
|
startReaper();
|
|
4243
|
-
const
|
|
4386
|
+
const sliceTokenValue = input.sliceToken ?? "";
|
|
4387
|
+
const personIdValue = input.personId ?? null;
|
|
4388
|
+
const key = indexKey(input.accountId, input.agentSlug, input.senderId, input.liveMemory, sliceTokenValue);
|
|
4244
4389
|
const existing = index.get(key);
|
|
4245
4390
|
const tag = tagFor(input.channel);
|
|
4246
4391
|
if (existing) {
|
|
@@ -4264,7 +4409,9 @@ async function ensureEntry(input) {
|
|
|
4264
4409
|
accountId: input.accountId,
|
|
4265
4410
|
agentSlug: input.agentSlug,
|
|
4266
4411
|
liveMemory: input.liveMemory,
|
|
4267
|
-
attachmentDir
|
|
4412
|
+
attachmentDir,
|
|
4413
|
+
sliceToken: sliceTokenValue.length > 0 ? sliceTokenValue : void 0,
|
|
4414
|
+
personId: personIdValue ?? void 0
|
|
4268
4415
|
});
|
|
4269
4416
|
if ("error" in spawned) {
|
|
4270
4417
|
if (spawned.error !== "claude-auth-dead") {
|
|
@@ -4287,7 +4434,9 @@ async function ensureEntry(input) {
|
|
|
4287
4434
|
pendingTurnText: "",
|
|
4288
4435
|
followerAbort: null,
|
|
4289
4436
|
followerRunning: false,
|
|
4290
|
-
liveMemory: input.liveMemory
|
|
4437
|
+
liveMemory: input.liveMemory,
|
|
4438
|
+
sliceToken: sliceTokenValue,
|
|
4439
|
+
personId: personIdValue
|
|
4291
4440
|
};
|
|
4292
4441
|
entry.followerAbort = startFollower({
|
|
4293
4442
|
entry,
|
|
@@ -4314,14 +4463,16 @@ async function writeInput(entry, text) {
|
|
|
4314
4463
|
`${tag} session-resolved senderId=${entry.senderId} action=respawn reason=session-gone-${result.status} sessionId=${entry.sessionId.slice(0, 8)}`
|
|
4315
4464
|
);
|
|
4316
4465
|
if (entry.followerAbort) entry.followerAbort.abort();
|
|
4317
|
-
index.delete(indexKey(entry.accountId, entry.agentSlug, entry.senderId, entry.liveMemory));
|
|
4466
|
+
index.delete(indexKey(entry.accountId, entry.agentSlug, entry.senderId, entry.liveMemory, entry.sliceToken));
|
|
4318
4467
|
const respawned = await ensureEntry({
|
|
4319
4468
|
accountId: entry.accountId,
|
|
4320
4469
|
senderId: entry.senderId,
|
|
4321
4470
|
role: entry.role,
|
|
4322
4471
|
channel: entry.channel,
|
|
4323
4472
|
agentSlug: entry.agentSlug,
|
|
4324
|
-
liveMemory: entry.liveMemory
|
|
4473
|
+
liveMemory: entry.liveMemory,
|
|
4474
|
+
sliceToken: entry.sliceToken.length > 0 ? entry.sliceToken : void 0,
|
|
4475
|
+
personId: entry.personId
|
|
4325
4476
|
});
|
|
4326
4477
|
if (!respawned) return false;
|
|
4327
4478
|
for (const cb of entry.subscribers) respawned.subscribers.add(cb);
|
|
@@ -4359,16 +4510,18 @@ async function dispatchOnce(input) {
|
|
|
4359
4510
|
role: input.role,
|
|
4360
4511
|
channel: input.channel,
|
|
4361
4512
|
agentSlug: input.agentSlug,
|
|
4362
|
-
liveMemory: input.liveMemory
|
|
4513
|
+
liveMemory: input.liveMemory,
|
|
4514
|
+
sliceToken: input.sliceToken,
|
|
4515
|
+
personId: input.personId
|
|
4363
4516
|
});
|
|
4364
4517
|
if (!entry) return { error: "spawn-failed" };
|
|
4365
4518
|
entry.lastInboundAt = Date.now();
|
|
4366
|
-
let
|
|
4519
|
+
let resolve23;
|
|
4367
4520
|
const turnPromise = new Promise((r) => {
|
|
4368
|
-
|
|
4521
|
+
resolve23 = r;
|
|
4369
4522
|
});
|
|
4370
4523
|
const listener = (text) => {
|
|
4371
|
-
|
|
4524
|
+
resolve23(text);
|
|
4372
4525
|
};
|
|
4373
4526
|
entry.subscribers.add(listener);
|
|
4374
4527
|
const writeOk = await writeInput(entry, input.text);
|
|
@@ -4418,12 +4571,49 @@ function startReaper() {
|
|
|
4418
4571
|
);
|
|
4419
4572
|
if (entry.followerAbort) entry.followerAbort.abort();
|
|
4420
4573
|
index.delete(key);
|
|
4574
|
+
if (entry.role === "public" && entry.sliceToken.length > 0) {
|
|
4575
|
+
void firePublicSessionEndReview({
|
|
4576
|
+
sessionId: entry.sessionId,
|
|
4577
|
+
sliceToken: entry.sliceToken,
|
|
4578
|
+
personId: entry.personId,
|
|
4579
|
+
accountId: entry.accountId,
|
|
4580
|
+
agentSlug: entry.agentSlug,
|
|
4581
|
+
dispatchFor: "eviction"
|
|
4582
|
+
});
|
|
4583
|
+
}
|
|
4421
4584
|
void managerDelete(entry.sessionId);
|
|
4422
4585
|
}
|
|
4423
4586
|
}, 6e4);
|
|
4424
4587
|
if (reaperHandle.unref) reaperHandle.unref();
|
|
4425
4588
|
}
|
|
4426
4589
|
|
|
4590
|
+
// app/lib/access-session-store.ts
|
|
4591
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4592
|
+
var sessions = /* @__PURE__ */ new Map();
|
|
4593
|
+
function createAccessSession(input) {
|
|
4594
|
+
const sessionId = randomUUID4();
|
|
4595
|
+
const session = {
|
|
4596
|
+
...input,
|
|
4597
|
+
sessionId,
|
|
4598
|
+
createdAt: Date.now()
|
|
4599
|
+
};
|
|
4600
|
+
sessions.set(sessionId, session);
|
|
4601
|
+
return session;
|
|
4602
|
+
}
|
|
4603
|
+
function getAccessSession(sessionId) {
|
|
4604
|
+
return sessions.get(sessionId) ?? null;
|
|
4605
|
+
}
|
|
4606
|
+
function evictAccessSessionsByGrant(grantId) {
|
|
4607
|
+
let dropped = 0;
|
|
4608
|
+
for (const [sessionId, session] of sessions) {
|
|
4609
|
+
if (session.grantId === grantId) {
|
|
4610
|
+
sessions.delete(sessionId);
|
|
4611
|
+
dropped += 1;
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
return dropped;
|
|
4615
|
+
}
|
|
4616
|
+
|
|
4427
4617
|
// server/routes/chat.ts
|
|
4428
4618
|
var WEBCHAT_TAG = "[webchat-adaptor]";
|
|
4429
4619
|
var SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
|
|
@@ -4545,13 +4735,37 @@ ${lines}]`;
|
|
|
4545
4735
|
return c.json({ error: "No default agent configured" }, 500);
|
|
4546
4736
|
}
|
|
4547
4737
|
let liveMemory = false;
|
|
4738
|
+
let accessMode = "open";
|
|
4548
4739
|
try {
|
|
4549
|
-
|
|
4740
|
+
const config = resolveAgentConfig(account.accountDir, agentSlug);
|
|
4741
|
+
liveMemory = config.liveMemory;
|
|
4742
|
+
accessMode = config.accessMode;
|
|
4550
4743
|
} catch (err) {
|
|
4551
4744
|
const m = err instanceof Error ? err.message : String(err);
|
|
4552
4745
|
console.error(`${WEBCHAT_TAG} reject reason=agent-config-failed agentSlug=${agentSlug} message=${m}`);
|
|
4553
4746
|
return c.json({ error: "Agent config invalid" }, 500);
|
|
4554
4747
|
}
|
|
4748
|
+
let sliceToken;
|
|
4749
|
+
let personId = null;
|
|
4750
|
+
if (accessMode === "gated" || accessMode === "paid") {
|
|
4751
|
+
const cookieHeader = c.req.header("cookie") ?? "";
|
|
4752
|
+
const accessSessionId = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="))?.slice("__access_session=".length);
|
|
4753
|
+
const session = accessSessionId ? getAccessSession(accessSessionId) : null;
|
|
4754
|
+
if (!session) {
|
|
4755
|
+
console.error(
|
|
4756
|
+
`${WEBCHAT_TAG} reject reason=gate-required agentSlug=${agentSlug} senderId=${session_key.slice(0, 8)} cookie=${accessSessionId ? "unknown-id" : "absent"}`
|
|
4757
|
+
);
|
|
4758
|
+
return c.json({ error: "gate-required", agentSlug }, 401);
|
|
4759
|
+
}
|
|
4760
|
+
if (session.agentSlug !== agentSlug) {
|
|
4761
|
+
console.error(
|
|
4762
|
+
`${WEBCHAT_TAG} reject reason=gate-agent-mismatch agentSlug=${agentSlug} cookieAgent=${session.agentSlug}`
|
|
4763
|
+
);
|
|
4764
|
+
return c.json({ error: "gate-required", agentSlug }, 401);
|
|
4765
|
+
}
|
|
4766
|
+
sliceToken = session.sliceToken;
|
|
4767
|
+
personId = session.personId;
|
|
4768
|
+
}
|
|
4555
4769
|
const sk = session_key.slice(0, 8);
|
|
4556
4770
|
console.error(
|
|
4557
4771
|
`${WEBCHAT_TAG} inbound from=${sk} bytes=${Buffer.byteLength(fullMessage, "utf8")} attachments=${attachmentCount} accountId=${account.accountId}`
|
|
@@ -4566,6 +4780,8 @@ ${lines}]`;
|
|
|
4566
4780
|
channel: "webchat",
|
|
4567
4781
|
agentSlug,
|
|
4568
4782
|
liveMemory,
|
|
4783
|
+
sliceToken,
|
|
4784
|
+
personId,
|
|
4569
4785
|
text: fullMessage,
|
|
4570
4786
|
timeoutMs: webchatTurnTimeoutMs()
|
|
4571
4787
|
});
|
|
@@ -4617,15 +4833,15 @@ import { readFile as readFile2, stat as stat3 } from "fs/promises";
|
|
|
4617
4833
|
import { realpathSync as realpathSync2, readdirSync as readdirSync2, readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
|
|
4618
4834
|
|
|
4619
4835
|
// app/lib/whatsapp/login.ts
|
|
4620
|
-
import { randomUUID as
|
|
4621
|
-
var
|
|
4836
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
4837
|
+
var TAG15 = "[whatsapp:login]";
|
|
4622
4838
|
var ACTIVE_LOGIN_TTL_MS = 3 * 6e4;
|
|
4623
4839
|
var activeLogins = /* @__PURE__ */ new Map();
|
|
4624
4840
|
function closeSocket(sock) {
|
|
4625
4841
|
try {
|
|
4626
4842
|
sock.ws?.close?.();
|
|
4627
4843
|
} catch (err) {
|
|
4628
|
-
console.warn(`${
|
|
4844
|
+
console.warn(`${TAG15} socket close error during cleanup: ${String(err)}`);
|
|
4629
4845
|
}
|
|
4630
4846
|
}
|
|
4631
4847
|
function resetActiveLogin(accountId) {
|
|
@@ -4648,7 +4864,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
4648
4864
|
const current = activeLogins.get(accountId);
|
|
4649
4865
|
if (current?.id === login.id) {
|
|
4650
4866
|
current.connected = true;
|
|
4651
|
-
console.error(`${
|
|
4867
|
+
console.error(`${TAG15} loginConnectionLoop: connected account=${accountId} attempt=${attempt}`);
|
|
4652
4868
|
}
|
|
4653
4869
|
return;
|
|
4654
4870
|
} catch (err) {
|
|
@@ -4658,7 +4874,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
4658
4874
|
if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
|
|
4659
4875
|
if (attempt >= LOGIN_MAX_RECONNECTS) {
|
|
4660
4876
|
console.error(
|
|
4661
|
-
`${
|
|
4877
|
+
`${TAG15} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
|
|
4662
4878
|
);
|
|
4663
4879
|
current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
|
|
4664
4880
|
} else {
|
|
@@ -4670,7 +4886,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
4670
4886
|
attempt++;
|
|
4671
4887
|
const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
|
|
4672
4888
|
console.error(
|
|
4673
|
-
`${
|
|
4889
|
+
`${TAG15} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
|
|
4674
4890
|
);
|
|
4675
4891
|
closeSocket(current.sock);
|
|
4676
4892
|
await new Promise((r) => setTimeout(r, delay));
|
|
@@ -4681,7 +4897,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
4681
4897
|
current.sock = newSock;
|
|
4682
4898
|
} catch (sockErr) {
|
|
4683
4899
|
console.error(
|
|
4684
|
-
`${
|
|
4900
|
+
`${TAG15} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
|
|
4685
4901
|
);
|
|
4686
4902
|
current.error = `Reconnection failed: ${String(sockErr)}`;
|
|
4687
4903
|
return;
|
|
@@ -4695,7 +4911,7 @@ async function startLogin(opts) {
|
|
|
4695
4911
|
const hasAuth = await authExists(authDir);
|
|
4696
4912
|
const selfId = readSelfId(authDir);
|
|
4697
4913
|
console.error(
|
|
4698
|
-
`${
|
|
4914
|
+
`${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
4915
|
);
|
|
4700
4916
|
if (hasAuth && !force) {
|
|
4701
4917
|
const who = selfId.e164 ?? selfId.jid ?? "unknown";
|
|
@@ -4707,7 +4923,7 @@ async function startLogin(opts) {
|
|
|
4707
4923
|
await clearAuth(authDir);
|
|
4708
4924
|
const existing = activeLogins.get(accountId);
|
|
4709
4925
|
if (existing && isLoginFresh(existing) && existing.qrDataUrl && !force) {
|
|
4710
|
-
console.error(`${
|
|
4926
|
+
console.error(`${TAG15} startLogin account=${accountId} guard: returning existing QR (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
|
|
4711
4927
|
return {
|
|
4712
4928
|
qrDataUrl: existing.qrDataUrl,
|
|
4713
4929
|
qrRaw: existing.qr,
|
|
@@ -4715,13 +4931,13 @@ async function startLogin(opts) {
|
|
|
4715
4931
|
};
|
|
4716
4932
|
}
|
|
4717
4933
|
if (existing) {
|
|
4718
|
-
console.error(`${
|
|
4934
|
+
console.error(`${TAG15} startLogin account=${accountId} ${force ? "force override" : "stale/no-QR"}, resetting active login`);
|
|
4719
4935
|
}
|
|
4720
4936
|
resetActiveLogin(accountId);
|
|
4721
4937
|
let resolveQr = null;
|
|
4722
4938
|
let rejectQr = null;
|
|
4723
|
-
const qrPromise = new Promise((
|
|
4724
|
-
resolveQr =
|
|
4939
|
+
const qrPromise = new Promise((resolve23, reject) => {
|
|
4940
|
+
resolveQr = resolve23;
|
|
4725
4941
|
rejectQr = reject;
|
|
4726
4942
|
});
|
|
4727
4943
|
const qrTimer = setTimeout(
|
|
@@ -4737,14 +4953,14 @@ async function startLogin(opts) {
|
|
|
4737
4953
|
onQr: (qr2) => {
|
|
4738
4954
|
loginQrCount++;
|
|
4739
4955
|
if (pendingQr) {
|
|
4740
|
-
console.error(`${
|
|
4956
|
+
console.error(`${TAG15} QR rotation #${loginQrCount} received for account=${accountId} \u2014 not forwarded (initial QR already captured)`);
|
|
4741
4957
|
return;
|
|
4742
4958
|
}
|
|
4743
4959
|
pendingQr = qr2;
|
|
4744
4960
|
const current = activeLogins.get(accountId);
|
|
4745
4961
|
if (current && !current.qr) current.qr = qr2;
|
|
4746
4962
|
clearTimeout(qrTimer);
|
|
4747
|
-
console.error(`${
|
|
4963
|
+
console.error(`${TAG15} QR #${loginQrCount} received for account=${accountId} \u2014 forwarding to caller`);
|
|
4748
4964
|
resolveQr?.(qr2);
|
|
4749
4965
|
}
|
|
4750
4966
|
});
|
|
@@ -4756,7 +4972,7 @@ async function startLogin(opts) {
|
|
|
4756
4972
|
const login = {
|
|
4757
4973
|
accountId,
|
|
4758
4974
|
authDir,
|
|
4759
|
-
id:
|
|
4975
|
+
id: randomUUID5(),
|
|
4760
4976
|
sock,
|
|
4761
4977
|
startedAt: Date.now(),
|
|
4762
4978
|
connected: false
|
|
@@ -4764,7 +4980,7 @@ async function startLogin(opts) {
|
|
|
4764
4980
|
activeLogins.set(accountId, login);
|
|
4765
4981
|
if (pendingQr && !login.qr) login.qr = pendingQr;
|
|
4766
4982
|
loginConnectionLoop(accountId, login).catch((err) => {
|
|
4767
|
-
console.error(`${
|
|
4983
|
+
console.error(`${TAG15} loginConnectionLoop unexpected error: ${String(err)}`);
|
|
4768
4984
|
const current = activeLogins.get(accountId);
|
|
4769
4985
|
if (current?.id === login.id) {
|
|
4770
4986
|
current.error = `Unexpected login error: ${String(err)}`;
|
|
@@ -4789,7 +5005,7 @@ async function waitForLogin(opts) {
|
|
|
4789
5005
|
const { accountId, timeoutMs = 6e4 } = opts;
|
|
4790
5006
|
const login = activeLogins.get(accountId);
|
|
4791
5007
|
console.error(
|
|
4792
|
-
`${
|
|
5008
|
+
`${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
5009
|
);
|
|
4794
5010
|
if (!login) {
|
|
4795
5011
|
return { connected: false, message: "No active WhatsApp login in progress." };
|
|
@@ -4802,7 +5018,7 @@ async function waitForLogin(opts) {
|
|
|
4802
5018
|
while (Date.now() < deadline) {
|
|
4803
5019
|
if (login.connected) {
|
|
4804
5020
|
const selfId = readSelfId(login.authDir);
|
|
4805
|
-
console.error(`${
|
|
5021
|
+
console.error(`${TAG15} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
4806
5022
|
const sock = login.sock;
|
|
4807
5023
|
const authDir = login.authDir;
|
|
4808
5024
|
activeLogins.delete(accountId);
|
|
@@ -4822,7 +5038,7 @@ async function waitForLogin(opts) {
|
|
|
4822
5038
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
4823
5039
|
}
|
|
4824
5040
|
const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
|
|
4825
|
-
console.error(`${
|
|
5041
|
+
console.error(`${TAG15} waitForLogin timeout account=${accountId} elapsed=${elapsed}s \u2014 cleaning up active login`);
|
|
4826
5042
|
resetActiveLogin(accountId);
|
|
4827
5043
|
return { connected: false, message: "Login timed out. Try generating a new QR." };
|
|
4828
5044
|
}
|
|
@@ -4830,7 +5046,7 @@ async function waitForLogin(opts) {
|
|
|
4830
5046
|
// app/lib/whatsapp/config-persist.ts
|
|
4831
5047
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
|
|
4832
5048
|
import { resolve as resolve5, join as join4 } from "path";
|
|
4833
|
-
var
|
|
5049
|
+
var TAG16 = "[whatsapp:config]";
|
|
4834
5050
|
function configPath(accountDir) {
|
|
4835
5051
|
return resolve5(accountDir, "account.json");
|
|
4836
5052
|
}
|
|
@@ -4847,9 +5063,9 @@ function reloadManagerConfig(accountDir) {
|
|
|
4847
5063
|
try {
|
|
4848
5064
|
const config = readConfig(accountDir);
|
|
4849
5065
|
reloadConfig(config);
|
|
4850
|
-
console.error(`${
|
|
5066
|
+
console.error(`${TAG16} reloaded manager config`);
|
|
4851
5067
|
} catch (err) {
|
|
4852
|
-
console.error(`${
|
|
5068
|
+
console.error(`${TAG16} manager config reload failed: ${String(err)}`);
|
|
4853
5069
|
}
|
|
4854
5070
|
}
|
|
4855
5071
|
var E164_PATTERN = /^\+\d{7,15}$/;
|
|
@@ -4875,25 +5091,25 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
|
|
|
4875
5091
|
const adminPhones = wa.adminPhones;
|
|
4876
5092
|
if (!adminPhones.includes(normalized)) {
|
|
4877
5093
|
adminPhones.push(normalized);
|
|
4878
|
-
console.error(`${
|
|
5094
|
+
console.error(`${TAG16} added selfPhone=${normalized} to adminPhones`);
|
|
4879
5095
|
}
|
|
4880
5096
|
} else {
|
|
4881
|
-
console.error(`${
|
|
5097
|
+
console.error(`${TAG16} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
|
|
4882
5098
|
}
|
|
4883
5099
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
4884
5100
|
if (!parsed.success) {
|
|
4885
5101
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
4886
|
-
console.error(`${
|
|
5102
|
+
console.error(`${TAG16} validation failed after pairing: ${msg}`);
|
|
4887
5103
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
4888
5104
|
}
|
|
4889
5105
|
config.whatsapp = parsed.data;
|
|
4890
5106
|
writeConfig(accountDir, config);
|
|
4891
|
-
console.error(`${
|
|
5107
|
+
console.error(`${TAG16} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
|
|
4892
5108
|
reloadManagerConfig(accountDir);
|
|
4893
5109
|
return { ok: true };
|
|
4894
5110
|
} catch (err) {
|
|
4895
5111
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4896
|
-
console.error(`${
|
|
5112
|
+
console.error(`${TAG16} persist failed account=${accountId}: ${msg}`);
|
|
4897
5113
|
return { ok: false, error: msg };
|
|
4898
5114
|
}
|
|
4899
5115
|
}
|
|
@@ -4923,12 +5139,12 @@ function addAdminPhone(accountDir, phone) {
|
|
|
4923
5139
|
}
|
|
4924
5140
|
config.whatsapp = parsed.data;
|
|
4925
5141
|
writeConfig(accountDir, config);
|
|
4926
|
-
console.error(`${
|
|
5142
|
+
console.error(`${TAG16} added admin phone=${normalized}`);
|
|
4927
5143
|
reloadManagerConfig(accountDir);
|
|
4928
5144
|
return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
|
|
4929
5145
|
} catch (err) {
|
|
4930
5146
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4931
|
-
console.error(`${
|
|
5147
|
+
console.error(`${TAG16} addAdminPhone failed: ${msg}`);
|
|
4932
5148
|
return { ok: false, error: msg };
|
|
4933
5149
|
}
|
|
4934
5150
|
}
|
|
@@ -4956,12 +5172,12 @@ function removeAdminPhone(accountDir, phone) {
|
|
|
4956
5172
|
}
|
|
4957
5173
|
config.whatsapp = parsed.data;
|
|
4958
5174
|
writeConfig(accountDir, config);
|
|
4959
|
-
console.error(`${
|
|
5175
|
+
console.error(`${TAG16} removed admin phone=${normalized}`);
|
|
4960
5176
|
reloadManagerConfig(accountDir);
|
|
4961
5177
|
return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
|
|
4962
5178
|
} catch (err) {
|
|
4963
5179
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4964
|
-
console.error(`${
|
|
5180
|
+
console.error(`${TAG16} removeAdminPhone failed: ${msg}`);
|
|
4965
5181
|
return { ok: false, error: msg };
|
|
4966
5182
|
}
|
|
4967
5183
|
}
|
|
@@ -4999,12 +5215,12 @@ function setPublicAgent(accountDir, slug) {
|
|
|
4999
5215
|
}
|
|
5000
5216
|
config.whatsapp = parsed.data;
|
|
5001
5217
|
writeConfig(accountDir, config);
|
|
5002
|
-
console.error(`${
|
|
5218
|
+
console.error(`${TAG16} publicAgent set to ${trimmed}`);
|
|
5003
5219
|
reloadManagerConfig(accountDir);
|
|
5004
5220
|
return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
|
|
5005
5221
|
} catch (err) {
|
|
5006
5222
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5007
|
-
console.error(`${
|
|
5223
|
+
console.error(`${TAG16} setPublicAgent failed: ${msg}`);
|
|
5008
5224
|
return { ok: false, error: msg };
|
|
5009
5225
|
}
|
|
5010
5226
|
}
|
|
@@ -5068,12 +5284,12 @@ function setGroupPublicAgent(accountDir, accountId, groupJid, slug) {
|
|
|
5068
5284
|
}
|
|
5069
5285
|
config.whatsapp = parsed.data;
|
|
5070
5286
|
writeConfig(accountDir, config);
|
|
5071
|
-
console.error(`${
|
|
5287
|
+
console.error(`${TAG16} setGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup} slug=${trimmedSlug}`);
|
|
5072
5288
|
reloadManagerConfig(accountDir);
|
|
5073
5289
|
return { ok: true, message: `Per-group public agent set to "${trimmedSlug}" for group ${trimmedGroup}.` };
|
|
5074
5290
|
} catch (err) {
|
|
5075
5291
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5076
|
-
console.error(`${
|
|
5292
|
+
console.error(`${TAG16} setGroupPublicAgent failed: ${msg}`);
|
|
5077
5293
|
return { ok: false, error: msg };
|
|
5078
5294
|
}
|
|
5079
5295
|
}
|
|
@@ -5100,12 +5316,12 @@ function unsetGroupPublicAgent(accountDir, accountId, groupJid) {
|
|
|
5100
5316
|
}
|
|
5101
5317
|
config.whatsapp = parsed.data;
|
|
5102
5318
|
writeConfig(accountDir, config);
|
|
5103
|
-
console.error(`${
|
|
5319
|
+
console.error(`${TAG16} unsetGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup}`);
|
|
5104
5320
|
reloadManagerConfig(accountDir);
|
|
5105
5321
|
return { ok: true, message: `Per-group public agent override removed for group ${trimmedGroup}.` };
|
|
5106
5322
|
} catch (err) {
|
|
5107
5323
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5108
|
-
console.error(`${
|
|
5324
|
+
console.error(`${TAG16} unsetGroupPublicAgent failed: ${msg}`);
|
|
5109
5325
|
return { ok: false, error: msg };
|
|
5110
5326
|
}
|
|
5111
5327
|
}
|
|
@@ -5126,17 +5342,17 @@ function updateConfig(accountDir, fields) {
|
|
|
5126
5342
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
5127
5343
|
if (!parsed.success) {
|
|
5128
5344
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
5129
|
-
console.error(`${
|
|
5345
|
+
console.error(`${TAG16} update validation failed: ${msg}`);
|
|
5130
5346
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
5131
5347
|
}
|
|
5132
5348
|
config.whatsapp = parsed.data;
|
|
5133
5349
|
writeConfig(accountDir, config);
|
|
5134
|
-
console.error(`${
|
|
5350
|
+
console.error(`${TAG16} updated fields=[${fieldNames.join(",")}]`);
|
|
5135
5351
|
reloadManagerConfig(accountDir);
|
|
5136
5352
|
return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
|
|
5137
5353
|
} catch (err) {
|
|
5138
5354
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5139
|
-
console.error(`${
|
|
5355
|
+
console.error(`${TAG16} updateConfig failed: ${msg}`);
|
|
5140
5356
|
return { ok: false, error: msg };
|
|
5141
5357
|
}
|
|
5142
5358
|
}
|
|
@@ -5262,17 +5478,17 @@ function serializeWhatsAppSchema() {
|
|
|
5262
5478
|
}
|
|
5263
5479
|
|
|
5264
5480
|
// server/routes/whatsapp.ts
|
|
5265
|
-
var
|
|
5481
|
+
var TAG17 = "[whatsapp:api]";
|
|
5266
5482
|
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
5267
5483
|
var app3 = new Hono();
|
|
5268
5484
|
app3.get("/status", (c) => {
|
|
5269
5485
|
try {
|
|
5270
5486
|
const status = getStatus();
|
|
5271
5487
|
const summary = status.map((a) => `${a.accountId}:${a.connected ? "up" : "down"}`).join(", ");
|
|
5272
|
-
console.error(`${
|
|
5488
|
+
console.error(`${TAG17} status accounts=${status.length} [${summary}]`);
|
|
5273
5489
|
return c.json({ accounts: status });
|
|
5274
5490
|
} catch (err) {
|
|
5275
|
-
console.error(`${
|
|
5491
|
+
console.error(`${TAG17} status error: ${String(err)}`);
|
|
5276
5492
|
return c.json({ error: String(err) }, 500);
|
|
5277
5493
|
}
|
|
5278
5494
|
});
|
|
@@ -5283,10 +5499,10 @@ app3.post("/login/start", async (c) => {
|
|
|
5283
5499
|
const force = body.force ?? false;
|
|
5284
5500
|
const authDir = join5(MAXY_DIR, "credentials", "whatsapp", accountId);
|
|
5285
5501
|
const result = await startLogin({ accountId, authDir, force });
|
|
5286
|
-
console.error(`${
|
|
5502
|
+
console.error(`${TAG17} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
|
|
5287
5503
|
return c.json(result);
|
|
5288
5504
|
} catch (err) {
|
|
5289
|
-
console.error(`${
|
|
5505
|
+
console.error(`${TAG17} login/start error: ${String(err)}`);
|
|
5290
5506
|
return c.json({ error: String(err) }, 500);
|
|
5291
5507
|
}
|
|
5292
5508
|
});
|
|
@@ -5301,7 +5517,7 @@ app3.post("/login/wait", async (c) => {
|
|
|
5301
5517
|
try {
|
|
5302
5518
|
await registerLoginSocket(accountId, result.sock, result.authDir);
|
|
5303
5519
|
} catch (regErr) {
|
|
5304
|
-
console.error(`${
|
|
5520
|
+
console.error(`${TAG17} registerLoginSocket failed account=${accountId}: ${String(regErr)}`);
|
|
5305
5521
|
}
|
|
5306
5522
|
try {
|
|
5307
5523
|
const account = resolveAccount();
|
|
@@ -5309,16 +5525,16 @@ app3.post("/login/wait", async (c) => {
|
|
|
5309
5525
|
const persistResult = persistAfterPairing(account.accountDir, accountId, result.selfPhone ?? null);
|
|
5310
5526
|
configPersisted = persistResult.ok;
|
|
5311
5527
|
if (!persistResult.ok) {
|
|
5312
|
-
console.error(`${
|
|
5528
|
+
console.error(`${TAG17} config persist failed account=${accountId}: ${persistResult.error}`);
|
|
5313
5529
|
}
|
|
5314
5530
|
} else {
|
|
5315
|
-
console.error(`${
|
|
5531
|
+
console.error(`${TAG17} config persist skipped \u2014 no account resolved`);
|
|
5316
5532
|
}
|
|
5317
5533
|
} catch (persistErr) {
|
|
5318
|
-
console.error(`${
|
|
5534
|
+
console.error(`${TAG17} config persist error account=${accountId}: ${String(persistErr)}`);
|
|
5319
5535
|
}
|
|
5320
5536
|
}
|
|
5321
|
-
console.error(`${
|
|
5537
|
+
console.error(`${TAG17} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${configPersisted}`);
|
|
5322
5538
|
return c.json({
|
|
5323
5539
|
connected: result.connected,
|
|
5324
5540
|
message: result.message,
|
|
@@ -5326,7 +5542,7 @@ app3.post("/login/wait", async (c) => {
|
|
|
5326
5542
|
configPersisted
|
|
5327
5543
|
});
|
|
5328
5544
|
} catch (err) {
|
|
5329
|
-
console.error(`${
|
|
5545
|
+
console.error(`${TAG17} login/wait error: ${String(err)}`);
|
|
5330
5546
|
return c.json({ error: String(err) }, 500);
|
|
5331
5547
|
}
|
|
5332
5548
|
});
|
|
@@ -5337,7 +5553,7 @@ app3.post("/disconnect", async (c) => {
|
|
|
5337
5553
|
await stopConnection(accountId);
|
|
5338
5554
|
return c.json({ disconnected: true, accountId });
|
|
5339
5555
|
} catch (err) {
|
|
5340
|
-
console.error(`${
|
|
5556
|
+
console.error(`${TAG17} disconnect error: ${String(err)}`);
|
|
5341
5557
|
return c.json({ error: String(err) }, 500);
|
|
5342
5558
|
}
|
|
5343
5559
|
});
|
|
@@ -5348,7 +5564,7 @@ app3.post("/reconnect", async (c) => {
|
|
|
5348
5564
|
await startConnection(accountId);
|
|
5349
5565
|
return c.json({ reconnecting: true, accountId });
|
|
5350
5566
|
} catch (err) {
|
|
5351
|
-
console.error(`${
|
|
5567
|
+
console.error(`${TAG17} reconnect error: ${String(err)}`);
|
|
5352
5568
|
return c.json({ error: String(err) }, 500);
|
|
5353
5569
|
}
|
|
5354
5570
|
});
|
|
@@ -5367,7 +5583,7 @@ app3.post("/send", async (c) => {
|
|
|
5367
5583
|
const result = await sendTextMessage(sock, to, text, { accountId });
|
|
5368
5584
|
return c.json(result);
|
|
5369
5585
|
} catch (err) {
|
|
5370
|
-
console.error(`${
|
|
5586
|
+
console.error(`${TAG17} send error: ${String(err)}`);
|
|
5371
5587
|
return c.json({ error: String(err) }, 500);
|
|
5372
5588
|
}
|
|
5373
5589
|
});
|
|
@@ -5388,7 +5604,7 @@ app3.post("/config", async (c) => {
|
|
|
5388
5604
|
return c.json({ ok: false, error: 'Missing required field "phone" (E.164 format, e.g. +441234567890).' }, 400);
|
|
5389
5605
|
}
|
|
5390
5606
|
const result = addAdminPhone(account.accountDir, phone);
|
|
5391
|
-
console.error(`${
|
|
5607
|
+
console.error(`${TAG17} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
|
|
5392
5608
|
return c.json(result, result.ok ? 200 : 400);
|
|
5393
5609
|
}
|
|
5394
5610
|
case "remove-admin-phone": {
|
|
@@ -5396,12 +5612,12 @@ app3.post("/config", async (c) => {
|
|
|
5396
5612
|
return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
|
|
5397
5613
|
}
|
|
5398
5614
|
const result = removeAdminPhone(account.accountDir, phone);
|
|
5399
|
-
console.error(`${
|
|
5615
|
+
console.error(`${TAG17} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
|
|
5400
5616
|
return c.json(result, result.ok ? 200 : 400);
|
|
5401
5617
|
}
|
|
5402
5618
|
case "list-admin-phones": {
|
|
5403
5619
|
const phones = readAdminPhones(account.accountDir);
|
|
5404
|
-
console.error(`${
|
|
5620
|
+
console.error(`${TAG17} config action=list-admin-phones count=${phones.length}`);
|
|
5405
5621
|
return c.json({ ok: true, phones });
|
|
5406
5622
|
}
|
|
5407
5623
|
case "set-public-agent": {
|
|
@@ -5409,14 +5625,14 @@ app3.post("/config", async (c) => {
|
|
|
5409
5625
|
return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
|
|
5410
5626
|
}
|
|
5411
5627
|
const result = setPublicAgent(account.accountDir, slug);
|
|
5412
|
-
console.error(`${
|
|
5628
|
+
console.error(`${TAG17} config action=set-public-agent slug=${slug} ok=${result.ok}`);
|
|
5413
5629
|
return c.json(result, result.ok ? 200 : 400);
|
|
5414
5630
|
}
|
|
5415
5631
|
case "get-public-agent": {
|
|
5416
5632
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
5417
5633
|
const targetGroup = typeof groupJid === "string" && groupJid.trim() ? groupJid.trim() : void 0;
|
|
5418
5634
|
const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount, groupJid: targetGroup });
|
|
5419
|
-
console.error(`${
|
|
5635
|
+
console.error(`${TAG17} config action=get-public-agent accountId=${targetAccount} groupJid=${targetGroup ?? "none"} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
|
|
5420
5636
|
return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
|
|
5421
5637
|
}
|
|
5422
5638
|
case "set-group-public-agent": {
|
|
@@ -5428,7 +5644,7 @@ app3.post("/config", async (c) => {
|
|
|
5428
5644
|
}
|
|
5429
5645
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
5430
5646
|
const result = setGroupPublicAgent(account.accountDir, targetAccount, groupJid, slug);
|
|
5431
|
-
console.error(`${
|
|
5647
|
+
console.error(`${TAG17} config action=set-group-public-agent accountId=${targetAccount} groupJid=${groupJid} slug=${slug} ok=${result.ok}`);
|
|
5432
5648
|
return c.json(result, result.ok ? 200 : 400);
|
|
5433
5649
|
}
|
|
5434
5650
|
case "unset-group-public-agent": {
|
|
@@ -5437,7 +5653,7 @@ app3.post("/config", async (c) => {
|
|
|
5437
5653
|
}
|
|
5438
5654
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
5439
5655
|
const result = unsetGroupPublicAgent(account.accountDir, targetAccount, groupJid);
|
|
5440
|
-
console.error(`${
|
|
5656
|
+
console.error(`${TAG17} config action=unset-group-public-agent accountId=${targetAccount} groupJid=${groupJid} ok=${result.ok}`);
|
|
5441
5657
|
return c.json(result, result.ok ? 200 : 400);
|
|
5442
5658
|
}
|
|
5443
5659
|
case "list-public-agents": {
|
|
@@ -5454,26 +5670,26 @@ app3.post("/config", async (c) => {
|
|
|
5454
5670
|
const config = JSON.parse(readFileSync6(configPath2, "utf-8"));
|
|
5455
5671
|
agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
|
|
5456
5672
|
} catch {
|
|
5457
|
-
console.error(`${
|
|
5673
|
+
console.error(`${TAG17} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
|
|
5458
5674
|
}
|
|
5459
5675
|
}
|
|
5460
5676
|
} catch (err) {
|
|
5461
|
-
console.error(`${
|
|
5677
|
+
console.error(`${TAG17} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
|
|
5462
5678
|
}
|
|
5463
5679
|
}
|
|
5464
|
-
console.error(`${
|
|
5680
|
+
console.error(`${TAG17} config action=list-public-agents count=${agents.length}`);
|
|
5465
5681
|
return c.json({ ok: true, agents });
|
|
5466
5682
|
}
|
|
5467
5683
|
case "schema": {
|
|
5468
5684
|
const text = serializeWhatsAppSchema();
|
|
5469
|
-
console.error(`${
|
|
5685
|
+
console.error(`${TAG17} config action=schema`);
|
|
5470
5686
|
return c.json({ ok: true, text });
|
|
5471
5687
|
}
|
|
5472
5688
|
case "list-groups": {
|
|
5473
5689
|
const groupAccountId = accountId ?? "default";
|
|
5474
5690
|
const sock = getSocket(groupAccountId);
|
|
5475
5691
|
if (!sock) {
|
|
5476
|
-
console.error(`${
|
|
5692
|
+
console.error(`${TAG17} config action=list-groups error="not connected" accountId=${groupAccountId}`);
|
|
5477
5693
|
return c.json({ ok: false, error: `WhatsApp account "${groupAccountId}" is not connected. Connect first, then retry.` });
|
|
5478
5694
|
}
|
|
5479
5695
|
try {
|
|
@@ -5483,10 +5699,10 @@ app3.post("/config", async (c) => {
|
|
|
5483
5699
|
name: g.subject ?? g.id,
|
|
5484
5700
|
participantCount: Array.isArray(g.participants) ? g.participants.length : 0
|
|
5485
5701
|
}));
|
|
5486
|
-
console.error(`${
|
|
5702
|
+
console.error(`${TAG17} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
|
|
5487
5703
|
return c.json({ ok: true, groups });
|
|
5488
5704
|
} catch (err) {
|
|
5489
|
-
console.error(`${
|
|
5705
|
+
console.error(`${TAG17} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
|
|
5490
5706
|
return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
|
|
5491
5707
|
}
|
|
5492
5708
|
}
|
|
@@ -5496,12 +5712,12 @@ app3.post("/config", async (c) => {
|
|
|
5496
5712
|
}
|
|
5497
5713
|
const result = updateConfig(account.accountDir, fields);
|
|
5498
5714
|
const fieldNames = Object.keys(fields);
|
|
5499
|
-
console.error(`${
|
|
5715
|
+
console.error(`${TAG17} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
|
|
5500
5716
|
return c.json(result, result.ok ? 200 : 400);
|
|
5501
5717
|
}
|
|
5502
5718
|
case "get-config": {
|
|
5503
5719
|
const waConfig = getConfig(account.accountDir);
|
|
5504
|
-
console.error(`${
|
|
5720
|
+
console.error(`${TAG17} config action=get-config`);
|
|
5505
5721
|
return c.json({ ok: true, config: waConfig });
|
|
5506
5722
|
}
|
|
5507
5723
|
default:
|
|
@@ -5511,7 +5727,7 @@ app3.post("/config", async (c) => {
|
|
|
5511
5727
|
);
|
|
5512
5728
|
}
|
|
5513
5729
|
} catch (err) {
|
|
5514
|
-
console.error(`${
|
|
5730
|
+
console.error(`${TAG17} config error: ${String(err)}`);
|
|
5515
5731
|
return c.json({ ok: false, error: String(err) }, 500);
|
|
5516
5732
|
}
|
|
5517
5733
|
});
|
|
@@ -5533,16 +5749,16 @@ app3.post("/send-document", async (c) => {
|
|
|
5533
5749
|
const accountResolved = realpathSync2(accountDir);
|
|
5534
5750
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
5535
5751
|
const sanitised = filePath.replace(accountDir, "<account>/");
|
|
5536
|
-
console.error(`${
|
|
5752
|
+
console.error(`${TAG17} send-document REJECTED path=${sanitised} reason=outside_account_directory`);
|
|
5537
5753
|
return c.json({ error: "Access denied: file is outside the account directory" }, 403);
|
|
5538
5754
|
}
|
|
5539
5755
|
} catch (err) {
|
|
5540
5756
|
const code = err.code;
|
|
5541
5757
|
if (code === "ENOENT") {
|
|
5542
|
-
console.error(`${
|
|
5758
|
+
console.error(`${TAG17} send-document ENOENT path=${filePath}`);
|
|
5543
5759
|
return c.json({ error: `File not found: ${filePath}` }, 404);
|
|
5544
5760
|
}
|
|
5545
|
-
console.error(`${
|
|
5761
|
+
console.error(`${TAG17} send-document path error: ${String(err)}`);
|
|
5546
5762
|
return c.json({ error: String(err) }, 500);
|
|
5547
5763
|
}
|
|
5548
5764
|
const fileStat = await stat3(resolvedPath);
|
|
@@ -5564,11 +5780,11 @@ app3.post("/send-document", async (c) => {
|
|
|
5564
5780
|
caption
|
|
5565
5781
|
}, { accountId });
|
|
5566
5782
|
console.error(
|
|
5567
|
-
`${
|
|
5783
|
+
`${TAG17} send-document to=${to} size=${fileStat.size} mime=${mimetype} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
|
|
5568
5784
|
);
|
|
5569
5785
|
return c.json(result);
|
|
5570
5786
|
} catch (err) {
|
|
5571
|
-
console.error(`${
|
|
5787
|
+
console.error(`${TAG17} send-document error: ${String(err)}`);
|
|
5572
5788
|
return c.json({ error: String(err) }, 500);
|
|
5573
5789
|
}
|
|
5574
5790
|
});
|
|
@@ -5578,11 +5794,11 @@ app3.get("/activity", (c) => {
|
|
|
5578
5794
|
const result = getChannelActivity(accountId);
|
|
5579
5795
|
const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
|
|
5580
5796
|
console.error(
|
|
5581
|
-
`${
|
|
5797
|
+
`${TAG17} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
|
|
5582
5798
|
);
|
|
5583
5799
|
return c.json(result);
|
|
5584
5800
|
} catch (err) {
|
|
5585
|
-
console.error(`${
|
|
5801
|
+
console.error(`${TAG17} activity error: ${String(err)}`);
|
|
5586
5802
|
return c.json({ error: String(err) }, 500);
|
|
5587
5803
|
}
|
|
5588
5804
|
});
|
|
@@ -5601,10 +5817,10 @@ app3.get("/conversations", (c) => {
|
|
|
5601
5817
|
};
|
|
5602
5818
|
});
|
|
5603
5819
|
conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
|
|
5604
|
-
console.error(`${
|
|
5820
|
+
console.error(`${TAG17} conversations account=${accountId} count=${conversations.length}`);
|
|
5605
5821
|
return c.json({ conversations });
|
|
5606
5822
|
} catch (err) {
|
|
5607
|
-
console.error(`${
|
|
5823
|
+
console.error(`${TAG17} conversations error: ${String(err)}`);
|
|
5608
5824
|
return c.json({ error: String(err) }, 500);
|
|
5609
5825
|
}
|
|
5610
5826
|
});
|
|
@@ -5619,10 +5835,10 @@ app3.get("/messages", (c) => {
|
|
|
5619
5835
|
const limit = limitParam ? parseInt(limitParam, 10) : void 0;
|
|
5620
5836
|
const effectiveLimit = limit && !Number.isNaN(limit) && limit > 0 ? limit : void 0;
|
|
5621
5837
|
const messages = getMessages(accountId, jid, effectiveLimit);
|
|
5622
|
-
console.error(`${
|
|
5838
|
+
console.error(`${TAG17} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
|
|
5623
5839
|
return c.json({ messages });
|
|
5624
5840
|
} catch (err) {
|
|
5625
|
-
console.error(`${
|
|
5841
|
+
console.error(`${TAG17} messages error: ${String(err)}`);
|
|
5626
5842
|
return c.json({ error: String(err) }, 500);
|
|
5627
5843
|
}
|
|
5628
5844
|
});
|
|
@@ -5703,7 +5919,7 @@ app3.get("/conversation-graph-state", async (c) => {
|
|
|
5703
5919
|
}
|
|
5704
5920
|
} catch (err) {
|
|
5705
5921
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5706
|
-
console.error(`${
|
|
5922
|
+
console.error(`${TAG17} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
|
|
5707
5923
|
return c.json({ error: `Graph query failed: ${msg}`, cacheKey, cypher: cypher.trim() }, 500);
|
|
5708
5924
|
}
|
|
5709
5925
|
const ms = Date.now() - t0;
|
|
@@ -5716,7 +5932,7 @@ app3.get("/conversation-graph-state", async (c) => {
|
|
|
5716
5932
|
ms
|
|
5717
5933
|
});
|
|
5718
5934
|
} catch (err) {
|
|
5719
|
-
console.error(`${
|
|
5935
|
+
console.error(`${TAG17} conversation-graph-state error: ${String(err)}`);
|
|
5720
5936
|
return c.json({ error: String(err) }, 500);
|
|
5721
5937
|
}
|
|
5722
5938
|
});
|
|
@@ -5728,12 +5944,12 @@ app3.get("/group-info", async (c) => {
|
|
|
5728
5944
|
return c.json({ error: "Missing required parameter: jid" }, 400);
|
|
5729
5945
|
}
|
|
5730
5946
|
if (!isGroupJid(jid)) {
|
|
5731
|
-
console.error(`${
|
|
5947
|
+
console.error(`${TAG17} group-info error="not a group JID" jid=${jid} account=${accountId}`);
|
|
5732
5948
|
return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
|
|
5733
5949
|
}
|
|
5734
5950
|
const sock = getSocket(accountId);
|
|
5735
5951
|
if (!sock) {
|
|
5736
|
-
console.error(`${
|
|
5952
|
+
console.error(`${TAG17} group-info error="not connected" account=${accountId}`);
|
|
5737
5953
|
return c.json({ error: `WhatsApp account "${accountId}" is not connected. Connect first, then retry.` }, 400);
|
|
5738
5954
|
}
|
|
5739
5955
|
const meta = await sock.groupMetadata(jid);
|
|
@@ -5746,10 +5962,10 @@ app3.get("/group-info", async (c) => {
|
|
|
5746
5962
|
participantCount: meta.participants.length,
|
|
5747
5963
|
participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
|
|
5748
5964
|
};
|
|
5749
|
-
console.error(`${
|
|
5965
|
+
console.error(`${TAG17} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
|
|
5750
5966
|
return c.json(result);
|
|
5751
5967
|
} catch (err) {
|
|
5752
|
-
console.error(`${
|
|
5968
|
+
console.error(`${TAG17} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
|
|
5753
5969
|
return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
|
|
5754
5970
|
}
|
|
5755
5971
|
});
|
|
@@ -5758,7 +5974,7 @@ var whatsapp_default = app3;
|
|
|
5758
5974
|
// server/routes/onboarding.ts
|
|
5759
5975
|
import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
|
|
5760
5976
|
import { openSync, closeSync, writeFileSync as writeFileSync5, writeSync, existsSync as existsSync6, readFileSync as readFileSync8, unlinkSync } from "fs";
|
|
5761
|
-
import { createHash, randomUUID as
|
|
5977
|
+
import { createHash, randomUUID as randomUUID6 } from "crypto";
|
|
5762
5978
|
|
|
5763
5979
|
// ../lib/admins-write/src/index.ts
|
|
5764
5980
|
import { existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4, renameSync, mkdirSync as mkdirSync2, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
|
|
@@ -6034,7 +6250,7 @@ app4.post("/set-pin", async (c) => {
|
|
|
6034
6250
|
const hash = hashPin(body.pin);
|
|
6035
6251
|
const account = resolveAccount();
|
|
6036
6252
|
const existingOwnerUserId = account?.config.admins?.find((a) => a.role === "owner")?.userId;
|
|
6037
|
-
const userId = existingOwnerUserId ??
|
|
6253
|
+
const userId = existingOwnerUserId ?? randomUUID6();
|
|
6038
6254
|
if (existingOwnerUserId) {
|
|
6039
6255
|
console.log(`[set-pin] reusing existing owner userId=${userId.slice(0, 8)}\u2026 (change-PIN preserves identity)`);
|
|
6040
6256
|
} else {
|
|
@@ -6295,7 +6511,7 @@ var client_error_default = app5;
|
|
|
6295
6511
|
import { readFileSync as readFileSync9, readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync8 } from "fs";
|
|
6296
6512
|
import { join as join8 } from "path";
|
|
6297
6513
|
import { createHash as createHash2 } from "crypto";
|
|
6298
|
-
import { randomUUID as
|
|
6514
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
6299
6515
|
function hashPin2(pin) {
|
|
6300
6516
|
return createHash2("sha256").update(pin).digest("hex");
|
|
6301
6517
|
}
|
|
@@ -6352,7 +6568,7 @@ async function resolveUserIdentity(accountId, userId) {
|
|
|
6352
6568
|
async function createAdminSession(accountId, thinkingView, userId, userName, role, avatar) {
|
|
6353
6569
|
const account = resolveAccount();
|
|
6354
6570
|
const effectiveThinkingView = thinkingView ?? account?.config.thinkingView ?? "default";
|
|
6355
|
-
const signedSessionToken =
|
|
6571
|
+
const signedSessionToken = randomUUID7();
|
|
6356
6572
|
const cacheKey = fingerprintSessionKey(signedSessionToken);
|
|
6357
6573
|
registerSession(cacheKey, "admin", accountId, void 0, userId, userName, role);
|
|
6358
6574
|
if (userId) setWantsPriorConversation(cacheKey);
|
|
@@ -7223,7 +7439,7 @@ app14.get("/", requireAdminSession, async (c) => {
|
|
|
7223
7439
|
if (!userId) return c.json({ error: "User identity required \u2014 authenticate with users.json PIN" }, 401);
|
|
7224
7440
|
try {
|
|
7225
7441
|
const flushed = await listAdminSessions(accountId, userId, 20);
|
|
7226
|
-
const
|
|
7442
|
+
const sessions2 = flushed.map((r) => ({
|
|
7227
7443
|
sessionId: r.sessionId,
|
|
7228
7444
|
cacheKey: null,
|
|
7229
7445
|
name: r.name,
|
|
@@ -7231,15 +7447,15 @@ app14.get("/", requireAdminSession, async (c) => {
|
|
|
7231
7447
|
phase: "flushed",
|
|
7232
7448
|
channel: r.channel
|
|
7233
7449
|
})).sort((a, b) => a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0).slice(0, 20);
|
|
7234
|
-
const channelCounts =
|
|
7450
|
+
const channelCounts = sessions2.reduce((acc, s) => {
|
|
7235
7451
|
const k = s.channel ?? "unknown";
|
|
7236
7452
|
acc[k] = (acc[k] ?? 0) + 1;
|
|
7237
7453
|
return acc;
|
|
7238
7454
|
}, {});
|
|
7239
7455
|
console.error(
|
|
7240
|
-
`[conversations-list] render rows=${
|
|
7456
|
+
`[conversations-list] render rows=${sessions2.length} channels=${JSON.stringify(channelCounts)}`
|
|
7241
7457
|
);
|
|
7242
|
-
return c.json({ sessions });
|
|
7458
|
+
return c.json({ sessions: sessions2 });
|
|
7243
7459
|
} catch (err) {
|
|
7244
7460
|
console.error(`[sessions-list] Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7245
7461
|
return c.json({ error: "Failed to fetch sessions" }, 500);
|
|
@@ -7298,11 +7514,11 @@ app14.delete("/:id", requireAdminSession, async (c) => {
|
|
|
7298
7514
|
const owned = await verifyConversationOwnership(sessionId, accountId);
|
|
7299
7515
|
if (!owned) return c.json({ error: "Conversation not found" }, 404);
|
|
7300
7516
|
const managerPort = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "admin-conversation-delete" });
|
|
7301
|
-
const
|
|
7517
|
+
const managerBase4 = `http://127.0.0.1:${managerPort}`;
|
|
7302
7518
|
const outcome = await cascadeAdminConversationDelete({
|
|
7303
7519
|
sessionId,
|
|
7304
7520
|
accountId,
|
|
7305
|
-
managerBase:
|
|
7521
|
+
managerBase: managerBase4
|
|
7306
7522
|
});
|
|
7307
7523
|
if (outcome.ok) return c.json({ ok: true, claudeSessionIds: outcome.claudeSessionIds });
|
|
7308
7524
|
if (outcome.reason === "pty-still-alive") {
|
|
@@ -7948,7 +8164,7 @@ function resolveTunnelUrl() {
|
|
|
7948
8164
|
if (!state) return null;
|
|
7949
8165
|
return `https://${hostname2}.${state.domain}`;
|
|
7950
8166
|
}
|
|
7951
|
-
var
|
|
8167
|
+
var TAG18 = "[claude-session-manager:wrapper]";
|
|
7952
8168
|
async function refuseIfClaudeAuthDead(c, route, sessionId) {
|
|
7953
8169
|
const auth = await ensureAuth();
|
|
7954
8170
|
if (auth.status !== "dead" && auth.status !== "missing") return null;
|
|
@@ -7960,7 +8176,7 @@ async function refuseIfClaudeAuthDead(c, route, sessionId) {
|
|
|
7960
8176
|
503
|
|
7961
8177
|
);
|
|
7962
8178
|
}
|
|
7963
|
-
function
|
|
8179
|
+
function managerBase3() {
|
|
7964
8180
|
const port2 = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "claude-session-manager:wrapper" });
|
|
7965
8181
|
return `http://127.0.0.1:${port2}`;
|
|
7966
8182
|
}
|
|
@@ -7971,12 +8187,12 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
7971
8187
|
const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
|
|
7972
8188
|
const ownerMs = Date.now() - ownerStart;
|
|
7973
8189
|
const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
|
|
7974
|
-
console.log(`${
|
|
8190
|
+
console.log(`${TAG18} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
|
|
7975
8191
|
const dormantPlugins = computeDormantPlugins(args.senderId);
|
|
7976
8192
|
const activePlugins = computeActivePlugins(args.senderId);
|
|
7977
8193
|
const specialistDomains = computeSpecialistDomains(args.senderId);
|
|
7978
8194
|
const tunnelUrl = resolveTunnelUrl();
|
|
7979
|
-
console.log(`${
|
|
8195
|
+
console.log(`${TAG18} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
|
|
7980
8196
|
const upstreamPayload = JSON.stringify({
|
|
7981
8197
|
senderId: args.senderId,
|
|
7982
8198
|
// Task 205 — pass userId through to the manager so it lands as
|
|
@@ -8003,24 +8219,24 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
8003
8219
|
// unshapely values.
|
|
8004
8220
|
conversationNodeId: args.conversationNodeId
|
|
8005
8221
|
});
|
|
8006
|
-
console.log(`${
|
|
8222
|
+
console.log(`${TAG18} forward-spawn-start managerBase=${managerBase3()} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
|
|
8007
8223
|
const forwardStart = Date.now();
|
|
8008
|
-
const upstream = await fetch(`${
|
|
8224
|
+
const upstream = await fetch(`${managerBase3()}/spawn`, {
|
|
8009
8225
|
method: "POST",
|
|
8010
8226
|
headers: { "content-type": "application/json" },
|
|
8011
8227
|
body: upstreamPayload
|
|
8012
8228
|
}).catch((err) => {
|
|
8013
|
-
console.error(`${
|
|
8229
|
+
console.error(`${TAG18} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
|
|
8014
8230
|
return null;
|
|
8015
8231
|
});
|
|
8016
8232
|
if (!upstream) return {
|
|
8017
8233
|
response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
|
|
8018
8234
|
claudeSessionId: null
|
|
8019
8235
|
};
|
|
8020
|
-
console.log(`${
|
|
8236
|
+
console.log(`${TAG18} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
|
|
8021
8237
|
if (args.initialMessage) {
|
|
8022
8238
|
const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
|
|
8023
|
-
console.log(`${
|
|
8239
|
+
console.log(`${TAG18} initial-message-inlined bytes=${inputBytes}`);
|
|
8024
8240
|
}
|
|
8025
8241
|
const bodyText = await upstream.text().catch(() => "");
|
|
8026
8242
|
let claudeSessionId = null;
|
|
@@ -8069,7 +8285,7 @@ app15.post("/", async (c) => {
|
|
|
8069
8285
|
authSurface = "cookie";
|
|
8070
8286
|
senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
8071
8287
|
if (!senderId) {
|
|
8072
|
-
console.error(`${
|
|
8288
|
+
console.error(`${TAG18} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
|
|
8073
8289
|
return c.json({ error: "admin-account-not-resolved" }, 500);
|
|
8074
8290
|
}
|
|
8075
8291
|
userId = getUserIdForSession(cacheKey) ?? void 0;
|
|
@@ -8080,9 +8296,9 @@ app15.post("/", async (c) => {
|
|
|
8080
8296
|
}
|
|
8081
8297
|
const lookupSessionId = body.adminSessionId;
|
|
8082
8298
|
const operatorMeta = await fetch(
|
|
8083
|
-
`${
|
|
8299
|
+
`${managerBase3()}/${encodeURIComponent(lookupSessionId)}/meta`
|
|
8084
8300
|
).then((r) => r.ok ? r.json() : null).catch((err) => {
|
|
8085
|
-
console.error(`${
|
|
8301
|
+
console.error(`${TAG18} fetch-failed op=operator-meta-lookup message=${err instanceof Error ? err.message : String(err)}`);
|
|
8086
8302
|
return null;
|
|
8087
8303
|
});
|
|
8088
8304
|
const operatorSenderId = operatorMeta && typeof operatorMeta.senderId === "string" && operatorMeta.senderId.length > 0 ? operatorMeta.senderId : null;
|
|
@@ -8097,7 +8313,7 @@ app15.post("/", async (c) => {
|
|
|
8097
8313
|
const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
|
|
8098
8314
|
const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
|
|
8099
8315
|
const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
|
|
8100
|
-
console.log(`${
|
|
8316
|
+
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
8317
|
const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
|
|
8102
8318
|
const { response, claudeSessionId } = await performSpawnWithInitialMessage({
|
|
8103
8319
|
senderId,
|
|
@@ -8116,14 +8332,14 @@ app15.post("/", async (c) => {
|
|
|
8116
8332
|
claudeSessionId,
|
|
8117
8333
|
senderId
|
|
8118
8334
|
);
|
|
8119
|
-
console.log(`${
|
|
8335
|
+
console.log(`${TAG18} route-done surface=${authSurface} status=${response.status} route-ms=${Date.now() - routeStart}`);
|
|
8120
8336
|
return response;
|
|
8121
8337
|
});
|
|
8122
8338
|
app15.get("/events", async (c) => {
|
|
8123
|
-
const upstream = await fetch(`${
|
|
8339
|
+
const upstream = await fetch(`${managerBase3()}/events`, {
|
|
8124
8340
|
headers: { accept: "text/event-stream" }
|
|
8125
8341
|
}).catch((err) => {
|
|
8126
|
-
console.error(`${
|
|
8342
|
+
console.error(`${TAG18} fetch-failed op=events message=${err instanceof Error ? err.message : String(err)}`);
|
|
8127
8343
|
return null;
|
|
8128
8344
|
});
|
|
8129
8345
|
if (!upstream || !upstream.body) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8142,9 +8358,9 @@ app15.get("/", async (c) => {
|
|
|
8142
8358
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
8143
8359
|
if (!senderId) return c.json({ error: "admin-account-not-resolved" }, 500);
|
|
8144
8360
|
const upstream = await fetch(
|
|
8145
|
-
`${
|
|
8361
|
+
`${managerBase3()}/list?senderId=${encodeURIComponent(senderId)}`
|
|
8146
8362
|
).catch((err) => {
|
|
8147
|
-
console.error(`${
|
|
8363
|
+
console.error(`${TAG18} fetch-failed op=list message=${err instanceof Error ? err.message : String(err)}`);
|
|
8148
8364
|
return null;
|
|
8149
8365
|
});
|
|
8150
8366
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8173,7 +8389,7 @@ app15.post("/resume", async (c) => {
|
|
|
8173
8389
|
const activePlugins = computeActivePlugins(senderId);
|
|
8174
8390
|
const specialistDomains = computeSpecialistDomains(senderId);
|
|
8175
8391
|
const tunnelUrl = resolveTunnelUrl();
|
|
8176
|
-
const upstream = await fetch(`${
|
|
8392
|
+
const upstream = await fetch(`${managerBase3()}/resume`, {
|
|
8177
8393
|
method: "POST",
|
|
8178
8394
|
headers: { "content-type": "application/json" },
|
|
8179
8395
|
body: JSON.stringify({
|
|
@@ -8198,7 +8414,7 @@ app15.post("/resume", async (c) => {
|
|
|
8198
8414
|
conversationNodeId: getSessionIdForSession(cacheKey)
|
|
8199
8415
|
})
|
|
8200
8416
|
}).catch((err) => {
|
|
8201
|
-
console.error(`${
|
|
8417
|
+
console.error(`${TAG18} fetch-failed op=resume message=${err instanceof Error ? err.message : String(err)}`);
|
|
8202
8418
|
return null;
|
|
8203
8419
|
});
|
|
8204
8420
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8223,10 +8439,10 @@ app15.post("/resume", async (c) => {
|
|
|
8223
8439
|
app15.post("/:sessionId/stop", async (c) => {
|
|
8224
8440
|
const sessionId = c.req.param("sessionId");
|
|
8225
8441
|
const upstream = await fetch(
|
|
8226
|
-
`${
|
|
8442
|
+
`${managerBase3()}/${encodeURIComponent(sessionId)}/stop`,
|
|
8227
8443
|
{ method: "POST" }
|
|
8228
8444
|
).catch((err) => {
|
|
8229
|
-
console.error(`${
|
|
8445
|
+
console.error(`${TAG18} fetch-failed op=stop message=${err instanceof Error ? err.message : String(err)}`);
|
|
8230
8446
|
return null;
|
|
8231
8447
|
});
|
|
8232
8448
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8236,14 +8452,14 @@ app15.post("/:sessionId/rename", async (c) => {
|
|
|
8236
8452
|
const sessionId = c.req.param("sessionId");
|
|
8237
8453
|
const body = await c.req.text();
|
|
8238
8454
|
const upstream = await fetch(
|
|
8239
|
-
`${
|
|
8455
|
+
`${managerBase3()}/${encodeURIComponent(sessionId)}/rename`,
|
|
8240
8456
|
{
|
|
8241
8457
|
method: "POST",
|
|
8242
8458
|
headers: { "content-type": "application/json" },
|
|
8243
8459
|
body
|
|
8244
8460
|
}
|
|
8245
8461
|
).catch((err) => {
|
|
8246
|
-
console.error(`${
|
|
8462
|
+
console.error(`${TAG18} fetch-failed op=rename message=${err instanceof Error ? err.message : String(err)}`);
|
|
8247
8463
|
return null;
|
|
8248
8464
|
});
|
|
8249
8465
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8258,10 +8474,10 @@ app15.delete("/:sessionId", async (c) => {
|
|
|
8258
8474
|
if (!sessionId) {
|
|
8259
8475
|
console.log(`[admin-conversation-delete] reason=no-conversation-mapping claudeSessionId=${claudeSessionId.slice(0, 8)}`);
|
|
8260
8476
|
const upstream = await fetch(
|
|
8261
|
-
`${
|
|
8477
|
+
`${managerBase3()}/${encodeURIComponent(claudeSessionId)}`,
|
|
8262
8478
|
{ method: "DELETE" }
|
|
8263
8479
|
).catch((err) => {
|
|
8264
|
-
console.error(`${
|
|
8480
|
+
console.error(`${TAG18} fetch-failed op=delete message=${err instanceof Error ? err.message : String(err)}`);
|
|
8265
8481
|
return null;
|
|
8266
8482
|
});
|
|
8267
8483
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8277,7 +8493,7 @@ app15.delete("/:sessionId", async (c) => {
|
|
|
8277
8493
|
const outcome = await cascadeAdminConversationDelete({
|
|
8278
8494
|
sessionId,
|
|
8279
8495
|
accountId,
|
|
8280
|
-
managerBase:
|
|
8496
|
+
managerBase: managerBase3(),
|
|
8281
8497
|
claudeSessionIds: all
|
|
8282
8498
|
});
|
|
8283
8499
|
if (outcome.ok) return c.json({ ok: true, claudeSessionIds: outcome.claudeSessionIds });
|
|
@@ -8316,14 +8532,14 @@ app15.post("/:sessionId/archive", async (c) => {
|
|
|
8316
8532
|
const sessionId = c.req.param("sessionId");
|
|
8317
8533
|
const body = await c.req.text();
|
|
8318
8534
|
const upstream = await fetch(
|
|
8319
|
-
`${
|
|
8535
|
+
`${managerBase3()}/${encodeURIComponent(sessionId)}/archive`,
|
|
8320
8536
|
{
|
|
8321
8537
|
method: "POST",
|
|
8322
8538
|
headers: { "content-type": "application/json" },
|
|
8323
8539
|
body
|
|
8324
8540
|
}
|
|
8325
8541
|
).catch((err) => {
|
|
8326
|
-
console.error(`${
|
|
8542
|
+
console.error(`${TAG18} fetch-failed op=archive message=${err instanceof Error ? err.message : String(err)}`);
|
|
8327
8543
|
return null;
|
|
8328
8544
|
});
|
|
8329
8545
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8332,9 +8548,9 @@ app15.post("/:sessionId/archive", async (c) => {
|
|
|
8332
8548
|
app15.get("/:sessionId/meta", async (c) => {
|
|
8333
8549
|
const sessionId = c.req.param("sessionId");
|
|
8334
8550
|
const upstream = await fetch(
|
|
8335
|
-
`${
|
|
8551
|
+
`${managerBase3()}/${encodeURIComponent(sessionId)}/meta`
|
|
8336
8552
|
).catch((err) => {
|
|
8337
|
-
console.error(`${
|
|
8553
|
+
console.error(`${TAG18} fetch-failed op=meta message=${err instanceof Error ? err.message : String(err)}`);
|
|
8338
8554
|
return null;
|
|
8339
8555
|
});
|
|
8340
8556
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8344,14 +8560,14 @@ app15.post("/:sessionId/input", async (c) => {
|
|
|
8344
8560
|
const sessionId = c.req.param("sessionId");
|
|
8345
8561
|
const rawBody = await c.req.text();
|
|
8346
8562
|
const upstream = await fetch(
|
|
8347
|
-
`${
|
|
8563
|
+
`${managerBase3()}/${encodeURIComponent(sessionId)}/input`,
|
|
8348
8564
|
{
|
|
8349
8565
|
method: "POST",
|
|
8350
8566
|
headers: { "content-type": "application/json" },
|
|
8351
8567
|
body: rawBody
|
|
8352
8568
|
}
|
|
8353
8569
|
).catch((err) => {
|
|
8354
|
-
console.error(`${
|
|
8570
|
+
console.error(`${TAG18} fetch-failed op=input message=${err instanceof Error ? err.message : String(err)}`);
|
|
8355
8571
|
return null;
|
|
8356
8572
|
});
|
|
8357
8573
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8364,9 +8580,9 @@ app15.get("/:sessionId/log", async (c) => {
|
|
|
8364
8580
|
if (c.req.query("download") === "1") search.set("download", "1");
|
|
8365
8581
|
const qs = search.toString() ? `?${search.toString()}` : "";
|
|
8366
8582
|
const upstream = await fetch(
|
|
8367
|
-
`${
|
|
8583
|
+
`${managerBase3()}/${encodeURIComponent(sessionId)}/log${qs}`
|
|
8368
8584
|
).catch((err) => {
|
|
8369
|
-
console.error(`${
|
|
8585
|
+
console.error(`${TAG18} fetch-failed op=log message=${err instanceof Error ? err.message : String(err)}`);
|
|
8370
8586
|
return null;
|
|
8371
8587
|
});
|
|
8372
8588
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8375,7 +8591,7 @@ app15.get("/:sessionId/log", async (c) => {
|
|
|
8375
8591
|
var claude_sessions_default = app15;
|
|
8376
8592
|
|
|
8377
8593
|
// server/routes/admin/log-ingest.ts
|
|
8378
|
-
var
|
|
8594
|
+
var TAG19 = "[log-ingest]";
|
|
8379
8595
|
var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
|
|
8380
8596
|
var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
|
|
8381
8597
|
var MAX_LINE_BYTES = 4096;
|
|
@@ -8386,7 +8602,7 @@ function isLoopbackAddr(addr) {
|
|
|
8386
8602
|
app16.post("/", async (c) => {
|
|
8387
8603
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
8388
8604
|
if (!isLoopbackAddr(remoteAddr)) {
|
|
8389
|
-
console.error(`${
|
|
8605
|
+
console.error(`${TAG19} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
8390
8606
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
8391
8607
|
}
|
|
8392
8608
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -8395,7 +8611,7 @@ app16.post("/", async (c) => {
|
|
|
8395
8611
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
8396
8612
|
const offender = tokens.find((t) => !isLoopbackAddr(t));
|
|
8397
8613
|
if (offender !== void 0) {
|
|
8398
|
-
console.error(`${
|
|
8614
|
+
console.error(`${TAG19} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
8399
8615
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
8400
8616
|
}
|
|
8401
8617
|
}
|
|
@@ -8437,18 +8653,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
8437
8653
|
]);
|
|
8438
8654
|
var app17 = new Hono();
|
|
8439
8655
|
app17.post("/", async (c) => {
|
|
8440
|
-
const
|
|
8656
|
+
const TAG27 = "[admin:events]";
|
|
8441
8657
|
let body;
|
|
8442
8658
|
try {
|
|
8443
8659
|
body = await c.req.json();
|
|
8444
8660
|
} catch (err) {
|
|
8445
8661
|
const detail = err instanceof Error ? err.message : String(err);
|
|
8446
|
-
console.error(`${
|
|
8662
|
+
console.error(`${TAG27} reject reason=body-not-json detail=${detail}`);
|
|
8447
8663
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
8448
8664
|
}
|
|
8449
8665
|
const event = typeof body.event === "string" ? body.event : "";
|
|
8450
8666
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
8451
|
-
console.error(`${
|
|
8667
|
+
console.error(`${TAG27} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
8452
8668
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
8453
8669
|
}
|
|
8454
8670
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -9326,6 +9542,27 @@ function pickFulltextIndex(allowedScopes) {
|
|
|
9326
9542
|
function isPublicOnlyScope(allowedScopes) {
|
|
9327
9543
|
return Array.isArray(allowedScopes) && allowedScopes.length === 1 && allowedScopes[0] === "public";
|
|
9328
9544
|
}
|
|
9545
|
+
function buildScopeAndSliceClause(allowedScopes, sliceToken, alias = "node") {
|
|
9546
|
+
const hasSlice = typeof sliceToken === "string" && sliceToken.length > 0;
|
|
9547
|
+
const hasScopes = Array.isArray(allowedScopes) && allowedScopes.length > 0;
|
|
9548
|
+
if (!hasSlice && !hasScopes) return { clause: "", params: {} };
|
|
9549
|
+
if (hasSlice && hasScopes) {
|
|
9550
|
+
return {
|
|
9551
|
+
clause: `AND (${alias}.sliceToken = $sliceToken OR ${alias}.scope IS NULL OR ${alias}.scope IN $allowedScopes)`,
|
|
9552
|
+
params: { sliceToken, allowedScopes }
|
|
9553
|
+
};
|
|
9554
|
+
}
|
|
9555
|
+
if (hasSlice) {
|
|
9556
|
+
return {
|
|
9557
|
+
clause: `AND (${alias}.sliceToken = $sliceToken OR ${alias}.scope IS NULL)`,
|
|
9558
|
+
params: { sliceToken }
|
|
9559
|
+
};
|
|
9560
|
+
}
|
|
9561
|
+
return {
|
|
9562
|
+
clause: `AND (${alias}.scope IS NULL OR ${alias}.scope IN $allowedScopes)`,
|
|
9563
|
+
params: { allowedScopes }
|
|
9564
|
+
};
|
|
9565
|
+
}
|
|
9329
9566
|
function escapeLucene(query) {
|
|
9330
9567
|
return query.replace(/[+\-&|!(){}[\]^"~*?:\\/]/g, "\\$&");
|
|
9331
9568
|
}
|
|
@@ -9361,8 +9598,9 @@ function buildKeywordFilter(keywords, keywordMatch = "any") {
|
|
|
9361
9598
|
};
|
|
9362
9599
|
}
|
|
9363
9600
|
async function bm25Only(session, params) {
|
|
9364
|
-
const { query, accountId, limit, allowedScopes, agentSlug, keywords, keywordMatch, labels } = params;
|
|
9365
|
-
const
|
|
9601
|
+
const { query, accountId, limit, allowedScopes, agentSlug, keywords, keywordMatch, labels, sliceToken } = params;
|
|
9602
|
+
const scope = buildScopeAndSliceClause(allowedScopes, sliceToken);
|
|
9603
|
+
const scopeClause = scope.clause;
|
|
9366
9604
|
const agentClause = agentSlug ? "AND node.agents IS NOT NULL AND $agentSlug IN node.agents" : "";
|
|
9367
9605
|
const labelClause = labels && labels.length > 0 ? "AND any(l IN labels(node) WHERE l IN $labels)" : "";
|
|
9368
9606
|
const keywordFilter = buildKeywordFilter(keywords, keywordMatch);
|
|
@@ -9386,7 +9624,7 @@ async function bm25Only(session, params) {
|
|
|
9386
9624
|
query: escaped,
|
|
9387
9625
|
accountId,
|
|
9388
9626
|
limit: int(limit),
|
|
9389
|
-
...
|
|
9627
|
+
...scope.params,
|
|
9390
9628
|
...agentSlug ? { agentSlug } : {},
|
|
9391
9629
|
...labels && labels.length > 0 ? { labels } : {},
|
|
9392
9630
|
...keywordFilter?.params ?? {}
|
|
@@ -9418,6 +9656,7 @@ async function hybrid(session, embed2, params) {
|
|
|
9418
9656
|
accountId,
|
|
9419
9657
|
limit,
|
|
9420
9658
|
allowedScopes,
|
|
9659
|
+
sliceToken,
|
|
9421
9660
|
keywords,
|
|
9422
9661
|
keywordMatch = "any",
|
|
9423
9662
|
agentSlug,
|
|
@@ -9496,22 +9735,18 @@ async function hybrid(session, embed2, params) {
|
|
|
9496
9735
|
if (effectiveLabels && effectiveLabels.length > 0) {
|
|
9497
9736
|
indexesToQuery = effectiveLabels.map((l) => labelToIndex.get(l)).filter((idx) => idx !== void 0);
|
|
9498
9737
|
if (indexesToQuery.length === 0) {
|
|
9499
|
-
|
|
9500
|
-
|
|
9501
|
-
|
|
9502
|
-
|
|
9503
|
-
|
|
9504
|
-
suppressed: 0,
|
|
9505
|
-
bm25Bypass: 0,
|
|
9506
|
-
threshold: params.vectorThreshold ?? null,
|
|
9507
|
-
stageCounts: emptyStageCounts
|
|
9508
|
-
};
|
|
9738
|
+
const missing = effectiveLabels.filter((l) => !labelToIndex.has(l));
|
|
9739
|
+
process.stderr.write(
|
|
9740
|
+
`[graph-search] labels-without-vector-index=${missing.join(",")} accountId=${(accountId ?? "").slice(0, 8)} \u2014 falling through to BM25-only for these labels
|
|
9741
|
+
`
|
|
9742
|
+
);
|
|
9509
9743
|
}
|
|
9510
9744
|
} else {
|
|
9511
9745
|
indexesToQuery = [...new Set(labelToIndex.values())];
|
|
9512
9746
|
}
|
|
9513
|
-
const
|
|
9514
|
-
const
|
|
9747
|
+
const hybridScope = buildScopeAndSliceClause(allowedScopes, sliceToken);
|
|
9748
|
+
const scopeClause = hybridScope.clause;
|
|
9749
|
+
const scopeParams = hybridScope.params;
|
|
9515
9750
|
const agentClause = agentSlug ? "AND node.agents IS NOT NULL AND $agentSlug IN node.agents" : "";
|
|
9516
9751
|
const agentParams = agentSlug ? { agentSlug } : {};
|
|
9517
9752
|
const keywordFilter = buildKeywordFilter(keywords, keywordMatch);
|
|
@@ -9626,7 +9861,8 @@ async function hybrid(session, embed2, params) {
|
|
|
9626
9861
|
query: kw,
|
|
9627
9862
|
accountId,
|
|
9628
9863
|
limit,
|
|
9629
|
-
allowedScopes
|
|
9864
|
+
allowedScopes,
|
|
9865
|
+
sliceToken
|
|
9630
9866
|
});
|
|
9631
9867
|
if (kwHits.length === 0) continue;
|
|
9632
9868
|
const rawScores = kwHits.map((h) => h.score);
|
|
@@ -9635,21 +9871,21 @@ async function hybrid(session, embed2, params) {
|
|
|
9635
9871
|
mergeBm25Hit(scoreMap, kwHits[i], normalised[i]);
|
|
9636
9872
|
}
|
|
9637
9873
|
}
|
|
9638
|
-
const
|
|
9874
|
+
const propScope = buildScopeAndSliceClause(allowedScopes, sliceToken);
|
|
9639
9875
|
const propResult = await session.run(
|
|
9640
9876
|
`MATCH (node)
|
|
9641
9877
|
WHERE node.accountId = $accountId
|
|
9642
9878
|
AND ${(0, import_dist.notTrashed)("node")}
|
|
9643
9879
|
AND node.keywords IS NOT NULL
|
|
9644
9880
|
AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
|
|
9645
|
-
${
|
|
9881
|
+
${propScope.clause}
|
|
9646
9882
|
RETURN node, labels(node) AS nodeLabels, elementId(node) AS nodeId
|
|
9647
9883
|
LIMIT $limit`,
|
|
9648
9884
|
{
|
|
9649
9885
|
accountId,
|
|
9650
9886
|
kwSubs: keywordSubscriptions,
|
|
9651
9887
|
limit: int(limit),
|
|
9652
|
-
...
|
|
9888
|
+
...propScope.params
|
|
9653
9889
|
}
|
|
9654
9890
|
);
|
|
9655
9891
|
for (const record of propResult.records) {
|
|
@@ -9677,7 +9913,8 @@ async function hybrid(session, embed2, params) {
|
|
|
9677
9913
|
query: kw,
|
|
9678
9914
|
accountId,
|
|
9679
9915
|
limit,
|
|
9680
|
-
allowedScopes
|
|
9916
|
+
allowedScopes,
|
|
9917
|
+
sliceToken
|
|
9681
9918
|
});
|
|
9682
9919
|
if (kwHits.length === 0) continue;
|
|
9683
9920
|
const rawScores = kwHits.map((h) => h.score);
|
|
@@ -9686,21 +9923,21 @@ async function hybrid(session, embed2, params) {
|
|
|
9686
9923
|
mergeBm25Hit(scoreMap, kwHits[i], normalised[i]);
|
|
9687
9924
|
}
|
|
9688
9925
|
}
|
|
9689
|
-
const
|
|
9926
|
+
const propScope = buildScopeAndSliceClause(allowedScopes, sliceToken);
|
|
9690
9927
|
const propResult = await session.run(
|
|
9691
9928
|
`MATCH (node)
|
|
9692
9929
|
WHERE node.accountId = $accountId
|
|
9693
9930
|
AND ${(0, import_dist.notTrashed)("node")}
|
|
9694
9931
|
AND node.keywords IS NOT NULL
|
|
9695
9932
|
AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
|
|
9696
|
-
${
|
|
9933
|
+
${propScope.clause}
|
|
9697
9934
|
RETURN node, labels(node) AS nodeLabels, elementId(node) AS nodeId
|
|
9698
9935
|
LIMIT $limit`,
|
|
9699
9936
|
{
|
|
9700
9937
|
accountId,
|
|
9701
9938
|
kwSubs: keywordSubscriptions,
|
|
9702
9939
|
limit: int(limit),
|
|
9703
|
-
...
|
|
9940
|
+
...propScope.params
|
|
9704
9941
|
}
|
|
9705
9942
|
);
|
|
9706
9943
|
for (const record of propResult.records) {
|
|
@@ -10731,7 +10968,7 @@ app21.post("/", requireAdminSession, async (c) => {
|
|
|
10731
10968
|
return c.json({ error: "AdminConversation node is missing sessionId" }, 500);
|
|
10732
10969
|
}
|
|
10733
10970
|
const managerPort = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "graph-page-cascade" });
|
|
10734
|
-
const
|
|
10971
|
+
const managerBase4 = `http://127.0.0.1:${managerPort}`;
|
|
10735
10972
|
try {
|
|
10736
10973
|
await session.close();
|
|
10737
10974
|
} catch {
|
|
@@ -10739,7 +10976,7 @@ app21.post("/", requireAdminSession, async (c) => {
|
|
|
10739
10976
|
const outcome = await cascadeAdminConversationDelete({
|
|
10740
10977
|
sessionId,
|
|
10741
10978
|
accountId,
|
|
10742
|
-
managerBase:
|
|
10979
|
+
managerBase: managerBase4
|
|
10743
10980
|
});
|
|
10744
10981
|
const elapsed2 = Date.now() - started;
|
|
10745
10982
|
const labelSummary2 = preflightLabels.join(",");
|
|
@@ -11848,7 +12085,7 @@ app31.get("/", async (c) => {
|
|
|
11848
12085
|
var health_default2 = app31;
|
|
11849
12086
|
|
|
11850
12087
|
// server/routes/admin/linkedin-ingest.ts
|
|
11851
|
-
var
|
|
12088
|
+
var TAG20 = "[linkedin-ingest-route]";
|
|
11852
12089
|
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
12090
|
var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
11854
12091
|
var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
|
|
@@ -11947,25 +12184,25 @@ app32.post("/", requireAdminSession, async (c) => {
|
|
|
11947
12184
|
try {
|
|
11948
12185
|
body = await c.req.json();
|
|
11949
12186
|
} catch {
|
|
11950
|
-
console.error(
|
|
12187
|
+
console.error(TAG20 + " rejected status=400 reason=schema:body-not-json");
|
|
11951
12188
|
return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
|
|
11952
12189
|
}
|
|
11953
12190
|
const v = validate(body);
|
|
11954
12191
|
if (!v.ok) {
|
|
11955
|
-
console.error(
|
|
12192
|
+
console.error(TAG20 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
|
|
11956
12193
|
return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
|
|
11957
12194
|
}
|
|
11958
12195
|
const envelope = v.envelope;
|
|
11959
12196
|
const cacheKey = c.var.cacheKey ?? "";
|
|
11960
12197
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
11961
12198
|
if (!senderId) {
|
|
11962
|
-
console.error(
|
|
12199
|
+
console.error(TAG20 + " rejected status=500 reason=admin-account-not-resolved");
|
|
11963
12200
|
return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
|
|
11964
12201
|
}
|
|
11965
12202
|
const userId = getUserIdForSession(cacheKey) ?? void 0;
|
|
11966
12203
|
const payloadBytes = JSON.stringify(envelope).length;
|
|
11967
12204
|
console.log(
|
|
11968
|
-
|
|
12205
|
+
TAG20 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
|
|
11969
12206
|
);
|
|
11970
12207
|
const initialMessage = buildInitialMessage(envelope);
|
|
11971
12208
|
const spawnStart = Date.now();
|
|
@@ -11987,12 +12224,12 @@ app32.post("/", requireAdminSession, async (c) => {
|
|
|
11987
12224
|
if (!response.ok) {
|
|
11988
12225
|
const detail = await response.text().catch(() => "");
|
|
11989
12226
|
console.error(
|
|
11990
|
-
|
|
12227
|
+
TAG20 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + response.status + " ms=" + (Date.now() - spawnStart) + " detail=" + detail.slice(0, 200)
|
|
11991
12228
|
);
|
|
11992
12229
|
return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: response.status, detail: detail.slice(0, 500) }, 502);
|
|
11993
12230
|
}
|
|
11994
12231
|
console.log(
|
|
11995
|
-
|
|
12232
|
+
TAG20 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + (claudeSessionId ?? "unknown") + " ms=" + (Date.now() - spawnStart)
|
|
11996
12233
|
);
|
|
11997
12234
|
return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: claudeSessionId }, 202);
|
|
11998
12235
|
});
|
|
@@ -12000,7 +12237,7 @@ var linkedin_ingest_default = app32;
|
|
|
12000
12237
|
|
|
12001
12238
|
// server/routes/admin/post-turn-context.ts
|
|
12002
12239
|
import neo4j3 from "neo4j-driver";
|
|
12003
|
-
var
|
|
12240
|
+
var TAG21 = "[post-turn-context]";
|
|
12004
12241
|
var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
|
|
12005
12242
|
"embedding",
|
|
12006
12243
|
"passwordHash",
|
|
@@ -12042,7 +12279,7 @@ var app33 = new Hono();
|
|
|
12042
12279
|
app33.get("/", async (c) => {
|
|
12043
12280
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
12044
12281
|
if (!isLoopbackAddr2(remoteAddr)) {
|
|
12045
|
-
console.error(`${
|
|
12282
|
+
console.error(`${TAG21} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
12046
12283
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
12047
12284
|
}
|
|
12048
12285
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -12051,7 +12288,7 @@ app33.get("/", async (c) => {
|
|
|
12051
12288
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
12052
12289
|
const offender = tokens.find((t) => !isLoopbackAddr2(t));
|
|
12053
12290
|
if (offender !== void 0) {
|
|
12054
|
-
console.error(`${
|
|
12291
|
+
console.error(`${TAG21} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
12055
12292
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
12056
12293
|
}
|
|
12057
12294
|
}
|
|
@@ -12083,7 +12320,7 @@ app33.get("/", async (c) => {
|
|
|
12083
12320
|
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
12084
12321
|
const total = Date.now() - started;
|
|
12085
12322
|
console.log(
|
|
12086
|
-
`${
|
|
12323
|
+
`${TAG21} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
|
|
12087
12324
|
);
|
|
12088
12325
|
return c.json({
|
|
12089
12326
|
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
@@ -12092,7 +12329,7 @@ app33.get("/", async (c) => {
|
|
|
12092
12329
|
const elapsed = Date.now() - started;
|
|
12093
12330
|
const message = err instanceof Error ? err.message : String(err);
|
|
12094
12331
|
console.error(
|
|
12095
|
-
`${
|
|
12332
|
+
`${TAG21} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
|
|
12096
12333
|
);
|
|
12097
12334
|
return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
|
|
12098
12335
|
} finally {
|
|
@@ -12101,41 +12338,623 @@ app33.get("/", async (c) => {
|
|
|
12101
12338
|
});
|
|
12102
12339
|
var post_turn_context_default = app33;
|
|
12103
12340
|
|
|
12104
|
-
// server/routes/admin/
|
|
12341
|
+
// server/routes/admin/public-session-context.ts
|
|
12342
|
+
import neo4j4 from "neo4j-driver";
|
|
12343
|
+
var TAG22 = "[public-session-context]";
|
|
12344
|
+
var STRIPPED_PROPERTIES3 = /* @__PURE__ */ new Set([
|
|
12345
|
+
"embedding",
|
|
12346
|
+
"passwordHash",
|
|
12347
|
+
"magicToken",
|
|
12348
|
+
"otpCode",
|
|
12349
|
+
"cacheKey"
|
|
12350
|
+
]);
|
|
12351
|
+
function isLoopbackAddr3(addr) {
|
|
12352
|
+
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
12353
|
+
}
|
|
12354
|
+
function convertValue2(value) {
|
|
12355
|
+
if (neo4j4.isDateTime(value) || neo4j4.isDate(value) || neo4j4.isLocalDateTime(value) || neo4j4.isLocalTime(value) || neo4j4.isTime(value) || neo4j4.isDuration(value)) {
|
|
12356
|
+
return value.toString();
|
|
12357
|
+
}
|
|
12358
|
+
if (neo4j4.isInt(value)) {
|
|
12359
|
+
return value.inSafeRange() ? value.toNumber() : value.toString();
|
|
12360
|
+
}
|
|
12361
|
+
if (Array.isArray(value)) {
|
|
12362
|
+
return value.map((v) => convertValue2(v));
|
|
12363
|
+
}
|
|
12364
|
+
if (value !== null && typeof value === "object" && value.constructor === Object) {
|
|
12365
|
+
const out = {};
|
|
12366
|
+
for (const [k, v] of Object.entries(value)) {
|
|
12367
|
+
out[k] = convertValue2(v);
|
|
12368
|
+
}
|
|
12369
|
+
return out;
|
|
12370
|
+
}
|
|
12371
|
+
return value;
|
|
12372
|
+
}
|
|
12373
|
+
function pruneProperties2(raw) {
|
|
12374
|
+
const out = {};
|
|
12375
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
12376
|
+
if (STRIPPED_PROPERTIES3.has(k)) continue;
|
|
12377
|
+
out[k] = convertValue2(v);
|
|
12378
|
+
}
|
|
12379
|
+
return out;
|
|
12380
|
+
}
|
|
12105
12381
|
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
|
-
|
|
12382
|
+
app34.get("/", async (c) => {
|
|
12383
|
+
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
12384
|
+
if (!isLoopbackAddr3(remoteAddr)) {
|
|
12385
|
+
console.error(`${TAG22} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
12386
|
+
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
12387
|
+
}
|
|
12388
|
+
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
12389
|
+
const xffRaw = Array.isArray(xffHeader) ? xffHeader.join(",") : typeof xffHeader === "string" ? xffHeader : "";
|
|
12390
|
+
if (xffRaw.length > 0) {
|
|
12391
|
+
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
12392
|
+
const offender = tokens.find((t) => !isLoopbackAddr3(t));
|
|
12393
|
+
if (offender !== void 0) {
|
|
12394
|
+
console.error(`${TAG22} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
12395
|
+
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
12396
|
+
}
|
|
12397
|
+
}
|
|
12398
|
+
const sliceToken = (c.req.query("sliceToken") ?? "").trim();
|
|
12399
|
+
const accountId = (c.req.query("accountId") ?? "").trim();
|
|
12400
|
+
if (!sliceToken) return c.json({ error: "sliceToken required" }, 400);
|
|
12401
|
+
if (!accountId) return c.json({ error: "accountId required" }, 400);
|
|
12402
|
+
const session = getSession();
|
|
12403
|
+
const started = Date.now();
|
|
12404
|
+
try {
|
|
12405
|
+
const cypher = `
|
|
12406
|
+
MATCH (n)
|
|
12407
|
+
WHERE n.sliceToken = $sliceToken AND n.accountId = $accountId
|
|
12408
|
+
RETURN elementId(n) AS elementId,
|
|
12409
|
+
labels(n) AS labels,
|
|
12410
|
+
properties(n) AS properties,
|
|
12411
|
+
coalesce(n.createdAt, n.observedAt, '') AS sortKey
|
|
12412
|
+
`;
|
|
12413
|
+
const res = await session.executeRead(
|
|
12414
|
+
(tx) => tx.run(cypher, { sliceToken, accountId })
|
|
12415
|
+
);
|
|
12416
|
+
const writes = res.records.map((r) => ({
|
|
12417
|
+
elementId: r.get("elementId"),
|
|
12418
|
+
labels: r.get("labels"),
|
|
12419
|
+
properties: pruneProperties2(r.get("properties")),
|
|
12420
|
+
sortKey: String(r.get("sortKey") ?? "")
|
|
12421
|
+
}));
|
|
12422
|
+
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
12423
|
+
const total = Date.now() - started;
|
|
12424
|
+
console.log(
|
|
12425
|
+
`${TAG22} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
|
|
12426
|
+
);
|
|
12427
|
+
return c.json({
|
|
12428
|
+
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
12429
|
+
});
|
|
12430
|
+
} catch (err) {
|
|
12431
|
+
const elapsed = Date.now() - started;
|
|
12432
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12433
|
+
console.error(
|
|
12434
|
+
`${TAG22} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
|
|
12435
|
+
);
|
|
12436
|
+
return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
|
|
12437
|
+
} finally {
|
|
12438
|
+
await session.close();
|
|
12439
|
+
}
|
|
12440
|
+
});
|
|
12441
|
+
var public_session_context_default = app34;
|
|
12135
12442
|
|
|
12136
|
-
// server/routes/
|
|
12137
|
-
|
|
12443
|
+
// server/routes/admin/access-session-evict.ts
|
|
12444
|
+
var TAG23 = "[access-session-evict]";
|
|
12445
|
+
function isLoopbackAddr4(addr) {
|
|
12446
|
+
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
12447
|
+
}
|
|
12448
|
+
var app35 = new Hono();
|
|
12449
|
+
app35.post("/", async (c) => {
|
|
12450
|
+
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
12451
|
+
if (!isLoopbackAddr4(remoteAddr)) {
|
|
12452
|
+
console.error(`${TAG23} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
12453
|
+
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
12454
|
+
}
|
|
12455
|
+
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
12456
|
+
const xffRaw = Array.isArray(xffHeader) ? xffHeader.join(",") : typeof xffHeader === "string" ? xffHeader : "";
|
|
12457
|
+
if (xffRaw.length > 0) {
|
|
12458
|
+
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
12459
|
+
const offender = tokens.find((t) => !isLoopbackAddr4(t));
|
|
12460
|
+
if (offender !== void 0) {
|
|
12461
|
+
console.error(`${TAG23} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
12462
|
+
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
12463
|
+
}
|
|
12464
|
+
}
|
|
12465
|
+
let body;
|
|
12466
|
+
try {
|
|
12467
|
+
body = await c.req.json();
|
|
12468
|
+
} catch {
|
|
12469
|
+
return c.json({ error: "invalid-json" }, 400);
|
|
12470
|
+
}
|
|
12471
|
+
const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
|
|
12472
|
+
if (!grantId) return c.json({ error: "grantId required" }, 400);
|
|
12473
|
+
const dropped = evictAccessSessionsByGrant(grantId);
|
|
12474
|
+
console.log(`${TAG23} grantId=${grantId} dropped=${dropped}`);
|
|
12475
|
+
return c.json({ ok: true, dropped });
|
|
12476
|
+
});
|
|
12477
|
+
var access_session_evict_default = app35;
|
|
12478
|
+
|
|
12479
|
+
// server/routes/admin/index.ts
|
|
12480
|
+
var app36 = new Hono();
|
|
12481
|
+
app36.route("/session", session_default);
|
|
12482
|
+
app36.route("/new-session-failure", new_session_failure_default);
|
|
12483
|
+
app36.route("/new-session-submit", new_session_submit_default);
|
|
12484
|
+
app36.route("/logs", logs_default);
|
|
12485
|
+
app36.route("/claude-info", claude_info_default);
|
|
12486
|
+
app36.route("/claude-capabilities", claude_capabilities_default);
|
|
12487
|
+
app36.route("/attachment", attachment_default);
|
|
12488
|
+
app36.route("/agents", agents_default);
|
|
12489
|
+
app36.route("/sessions", sessions_default);
|
|
12490
|
+
app36.route("/claude-sessions", claude_sessions_default);
|
|
12491
|
+
app36.route("/log-ingest", log_ingest_default);
|
|
12492
|
+
app36.route("/events", events_default);
|
|
12493
|
+
app36.route("/files", files_default);
|
|
12494
|
+
app36.route("/graph-search", graph_search_default);
|
|
12495
|
+
app36.route("/graph-subgraph", graph_subgraph_default);
|
|
12496
|
+
app36.route("/graph-delete", graph_delete_default);
|
|
12497
|
+
app36.route("/graph-restore", graph_restore_default);
|
|
12498
|
+
app36.route("/graph-labels-in-graph", graph_labels_in_graph_default);
|
|
12499
|
+
app36.route("/graph-default-view", graph_default_view_default);
|
|
12500
|
+
app36.route("/session-defaults", session_defaults_default);
|
|
12501
|
+
app36.route("/file-attach", file_attach_default);
|
|
12502
|
+
app36.route("/sidebar-artefacts", sidebar_artefacts_default);
|
|
12503
|
+
app36.route("/sidebar-artefact-save", sidebar_artefact_save_default);
|
|
12504
|
+
app36.route("/sidebar-artefact-content", sidebar_artefact_content_default);
|
|
12505
|
+
app36.route("/system-stats", system_stats_default);
|
|
12506
|
+
app36.route("/health-brand", health_default2);
|
|
12507
|
+
app36.route("/linkedin-ingest", linkedin_ingest_default);
|
|
12508
|
+
app36.route("/post-turn-context", post_turn_context_default);
|
|
12509
|
+
app36.route("/public-session-context", public_session_context_default);
|
|
12510
|
+
app36.route("/access-session-evict", access_session_evict_default);
|
|
12511
|
+
var admin_default = app36;
|
|
12512
|
+
|
|
12513
|
+
// app/lib/access-gate.ts
|
|
12514
|
+
import neo4j5 from "neo4j-driver";
|
|
12515
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
12138
12516
|
import { resolve as resolve18 } from "path";
|
|
12517
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
12518
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
|
|
12519
|
+
var driver = null;
|
|
12520
|
+
function readPassword() {
|
|
12521
|
+
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
12522
|
+
const passwordFile = resolve18(PLATFORM_ROOT7, "config/.neo4j-password");
|
|
12523
|
+
try {
|
|
12524
|
+
return readFileSync16(passwordFile, "utf-8").trim();
|
|
12525
|
+
} catch {
|
|
12526
|
+
throw new Error(
|
|
12527
|
+
`Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
|
|
12528
|
+
);
|
|
12529
|
+
}
|
|
12530
|
+
}
|
|
12531
|
+
function getDriver() {
|
|
12532
|
+
if (!driver) {
|
|
12533
|
+
const uri = process.env.NEO4J_URI;
|
|
12534
|
+
if (!uri) {
|
|
12535
|
+
throw new Error(
|
|
12536
|
+
"[ui/access-gate] NEO4J_URI unset \u2014 refusing to default to bolt://localhost:7687"
|
|
12537
|
+
);
|
|
12538
|
+
}
|
|
12539
|
+
const user = process.env.NEO4J_USER ?? "neo4j";
|
|
12540
|
+
const password = readPassword();
|
|
12541
|
+
console.error(`[ui/access-gate] resolved neo4j_uri=${uri}`);
|
|
12542
|
+
driver = neo4j5.driver(uri, neo4j5.auth.basic(user, password), {
|
|
12543
|
+
maxConnectionPoolSize: 5
|
|
12544
|
+
});
|
|
12545
|
+
}
|
|
12546
|
+
return driver;
|
|
12547
|
+
}
|
|
12548
|
+
function getSession3() {
|
|
12549
|
+
return getDriver().session();
|
|
12550
|
+
}
|
|
12551
|
+
process.on("SIGINT", async () => {
|
|
12552
|
+
if (driver) {
|
|
12553
|
+
await driver.close();
|
|
12554
|
+
driver = null;
|
|
12555
|
+
}
|
|
12556
|
+
});
|
|
12557
|
+
var rateLimitMap = /* @__PURE__ */ new Map();
|
|
12558
|
+
var ACCESS_MAX_ATTEMPTS = 5;
|
|
12559
|
+
var ACCESS_LOCKOUT_MS = 15 * 60 * 1e3;
|
|
12560
|
+
var ACCESS_WINDOW_MS = 15 * 60 * 1e3;
|
|
12561
|
+
function checkAccessRateLimit(ip, agentSlug) {
|
|
12562
|
+
const key = `${ip}:${agentSlug}`;
|
|
12563
|
+
const entry = rateLimitMap.get(key);
|
|
12564
|
+
if (!entry) return null;
|
|
12565
|
+
const now = Date.now();
|
|
12566
|
+
if (entry.lockedUntil && now < entry.lockedUntil) {
|
|
12567
|
+
const remainingMs = entry.lockedUntil - now;
|
|
12568
|
+
const remainingS = Math.ceil(remainingMs / 1e3);
|
|
12569
|
+
return `Too many attempts. Try again in ${remainingS}s`;
|
|
12570
|
+
}
|
|
12571
|
+
if (now - entry.firstAttempt > ACCESS_WINDOW_MS) {
|
|
12572
|
+
rateLimitMap.delete(key);
|
|
12573
|
+
return null;
|
|
12574
|
+
}
|
|
12575
|
+
return null;
|
|
12576
|
+
}
|
|
12577
|
+
function recordAccessFailedAttempt(ip, agentSlug) {
|
|
12578
|
+
const key = `${ip}:${agentSlug}`;
|
|
12579
|
+
const now = Date.now();
|
|
12580
|
+
const entry = rateLimitMap.get(key);
|
|
12581
|
+
if (!entry || now - entry.firstAttempt > ACCESS_WINDOW_MS) {
|
|
12582
|
+
rateLimitMap.set(key, { attempts: 1, firstAttempt: now });
|
|
12583
|
+
return;
|
|
12584
|
+
}
|
|
12585
|
+
entry.attempts++;
|
|
12586
|
+
if (entry.attempts >= ACCESS_MAX_ATTEMPTS) {
|
|
12587
|
+
entry.lockedUntil = now + ACCESS_LOCKOUT_MS;
|
|
12588
|
+
}
|
|
12589
|
+
}
|
|
12590
|
+
function clearAccessRateLimit(ip, agentSlug) {
|
|
12591
|
+
rateLimitMap.delete(`${ip}:${agentSlug}`);
|
|
12592
|
+
}
|
|
12593
|
+
var contactRateMap = /* @__PURE__ */ new Map();
|
|
12594
|
+
var REQUEST_LINK_MAX = 3;
|
|
12595
|
+
var REQUEST_LINK_WINDOW_MS = 60 * 60 * 1e3;
|
|
12596
|
+
function checkRequestLinkRateLimit(contactValue) {
|
|
12597
|
+
const entry = contactRateMap.get(contactValue);
|
|
12598
|
+
if (!entry) return null;
|
|
12599
|
+
const now = Date.now();
|
|
12600
|
+
if (now - entry.windowStart > REQUEST_LINK_WINDOW_MS) {
|
|
12601
|
+
contactRateMap.delete(contactValue);
|
|
12602
|
+
return null;
|
|
12603
|
+
}
|
|
12604
|
+
if (entry.count >= REQUEST_LINK_MAX) {
|
|
12605
|
+
const remainingMs = REQUEST_LINK_WINDOW_MS - (now - entry.windowStart);
|
|
12606
|
+
const remainingM = Math.ceil(remainingMs / 6e4);
|
|
12607
|
+
return `Too many requests. Try again in ${remainingM} minute(s)`;
|
|
12608
|
+
}
|
|
12609
|
+
return null;
|
|
12610
|
+
}
|
|
12611
|
+
function recordRequestLinkAttempt(contactValue) {
|
|
12612
|
+
const now = Date.now();
|
|
12613
|
+
const entry = contactRateMap.get(contactValue);
|
|
12614
|
+
if (!entry || now - entry.windowStart > REQUEST_LINK_WINDOW_MS) {
|
|
12615
|
+
contactRateMap.set(contactValue, { count: 1, windowStart: now });
|
|
12616
|
+
return;
|
|
12617
|
+
}
|
|
12618
|
+
entry.count++;
|
|
12619
|
+
}
|
|
12620
|
+
function maskContact(value) {
|
|
12621
|
+
if (value.includes("@")) {
|
|
12622
|
+
const [local, domain] = value.split("@");
|
|
12623
|
+
return `${local[0]}***@${domain}`;
|
|
12624
|
+
}
|
|
12625
|
+
return "****";
|
|
12626
|
+
}
|
|
12627
|
+
function readGrant(record) {
|
|
12628
|
+
const g = record.get("g").properties;
|
|
12629
|
+
const personId = record.get("personId");
|
|
12630
|
+
return {
|
|
12631
|
+
grantId: record.get("grantId"),
|
|
12632
|
+
agentSlug: String(g.agentSlug),
|
|
12633
|
+
accountId: String(g.accountId),
|
|
12634
|
+
contactValue: String(g.contactValue),
|
|
12635
|
+
displayName: record.get("givenName") ?? null,
|
|
12636
|
+
personId,
|
|
12637
|
+
expiresAt: g.expiresAt ? new Date(String(g.expiresAt)).getTime() : null,
|
|
12638
|
+
status: String(g.status),
|
|
12639
|
+
sliceToken: String(g.sliceToken ?? "")
|
|
12640
|
+
};
|
|
12641
|
+
}
|
|
12642
|
+
async function findGrantByMagicToken(token) {
|
|
12643
|
+
const session = getSession3();
|
|
12644
|
+
try {
|
|
12645
|
+
const result = await session.run(
|
|
12646
|
+
`MATCH (p:Person)-[:HAS_ACCESS]->(g:AccessGrant {magicToken: $token})
|
|
12647
|
+
RETURN g, p.givenName AS givenName,
|
|
12648
|
+
elementId(g) AS grantId,
|
|
12649
|
+
elementId(p) AS personId`,
|
|
12650
|
+
{ token }
|
|
12651
|
+
);
|
|
12652
|
+
if (result.records.length === 0) return null;
|
|
12653
|
+
return readGrant(result.records[0]);
|
|
12654
|
+
} finally {
|
|
12655
|
+
await session.close();
|
|
12656
|
+
}
|
|
12657
|
+
}
|
|
12658
|
+
async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
|
|
12659
|
+
const session = getSession3();
|
|
12660
|
+
try {
|
|
12661
|
+
const result = await session.run(
|
|
12662
|
+
`MATCH (p:Person)-[:HAS_ACCESS]->(g:AccessGrant {
|
|
12663
|
+
contactValue: $contactValue,
|
|
12664
|
+
agentSlug: $agentSlug,
|
|
12665
|
+
accountId: $accountId
|
|
12666
|
+
})
|
|
12667
|
+
WHERE g.status IN ['invited', 'active']
|
|
12668
|
+
RETURN g, p.givenName AS givenName,
|
|
12669
|
+
elementId(g) AS grantId,
|
|
12670
|
+
elementId(p) AS personId`,
|
|
12671
|
+
{ contactValue, agentSlug, accountId }
|
|
12672
|
+
);
|
|
12673
|
+
if (result.records.length === 0) return null;
|
|
12674
|
+
return readGrant(result.records[0]);
|
|
12675
|
+
} finally {
|
|
12676
|
+
await session.close();
|
|
12677
|
+
}
|
|
12678
|
+
}
|
|
12679
|
+
async function consumeMagicTokenAndActivate(grantId) {
|
|
12680
|
+
const session = getSession3();
|
|
12681
|
+
try {
|
|
12682
|
+
const result = await session.run(
|
|
12683
|
+
`MATCH (g:AccessGrant) WHERE elementId(g) = $grantId
|
|
12684
|
+
SET g.magicToken = null,
|
|
12685
|
+
g.magicTokenExpiresAt = null,
|
|
12686
|
+
g.status = "active"
|
|
12687
|
+
RETURN count(g) AS affected`,
|
|
12688
|
+
{ grantId }
|
|
12689
|
+
);
|
|
12690
|
+
const affected = result.records[0]?.get("affected");
|
|
12691
|
+
const count = typeof affected === "number" ? affected : affected?.toNumber?.() ?? 0;
|
|
12692
|
+
if (count === 0) {
|
|
12693
|
+
throw new Error(`[access-gate] consumeMagicTokenAndActivate: no grant matched grantId=${grantId}`);
|
|
12694
|
+
}
|
|
12695
|
+
} finally {
|
|
12696
|
+
await session.close();
|
|
12697
|
+
}
|
|
12698
|
+
}
|
|
12699
|
+
async function generateNewMagicToken(grantId) {
|
|
12700
|
+
const token = randomUUID8();
|
|
12701
|
+
const session = getSession3();
|
|
12702
|
+
try {
|
|
12703
|
+
const result = await session.run(
|
|
12704
|
+
`MATCH (g:AccessGrant) WHERE elementId(g) = $grantId
|
|
12705
|
+
SET g.magicToken = $token,
|
|
12706
|
+
g.magicTokenExpiresAt = datetime() + duration('PT15M')
|
|
12707
|
+
RETURN count(g) AS affected`,
|
|
12708
|
+
{ grantId, token }
|
|
12709
|
+
);
|
|
12710
|
+
const affected = result.records[0]?.get("affected");
|
|
12711
|
+
const count = typeof affected === "number" ? affected : affected?.toNumber?.() ?? 0;
|
|
12712
|
+
if (count === 0) {
|
|
12713
|
+
throw new Error(`[access-gate] generateNewMagicToken: no grant matched grantId=${grantId}`);
|
|
12714
|
+
}
|
|
12715
|
+
} finally {
|
|
12716
|
+
await session.close();
|
|
12717
|
+
}
|
|
12718
|
+
return token;
|
|
12719
|
+
}
|
|
12720
|
+
|
|
12721
|
+
// server/routes/access/verify-token.ts
|
|
12722
|
+
var TAG24 = "[access-verify]";
|
|
12723
|
+
var MINT_TAG = "[access-session-mint]";
|
|
12724
|
+
var COOKIE_NAME = "__access_session";
|
|
12725
|
+
var app37 = new Hono();
|
|
12726
|
+
app37.post("/", async (c) => {
|
|
12727
|
+
const ip = c.var.clientIp || "unknown";
|
|
12728
|
+
let body;
|
|
12729
|
+
try {
|
|
12730
|
+
body = await c.req.json();
|
|
12731
|
+
} catch {
|
|
12732
|
+
return c.json({ error: "invalid-json" }, 400);
|
|
12733
|
+
}
|
|
12734
|
+
const token = typeof body.token === "string" ? body.token.trim() : "";
|
|
12735
|
+
const agentSlug = typeof body.agentSlug === "string" ? body.agentSlug.trim() : "";
|
|
12736
|
+
if (!token || !agentSlug) {
|
|
12737
|
+
return c.json({ error: "token and agentSlug required" }, 400);
|
|
12738
|
+
}
|
|
12739
|
+
const rateMsg = checkAccessRateLimit(ip, agentSlug);
|
|
12740
|
+
if (rateMsg) {
|
|
12741
|
+
console.error(`${TAG24} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
|
|
12742
|
+
return c.json({ error: rateMsg }, 429);
|
|
12743
|
+
}
|
|
12744
|
+
const grant = await findGrantByMagicToken(token);
|
|
12745
|
+
if (!grant) {
|
|
12746
|
+
recordAccessFailedAttempt(ip, agentSlug);
|
|
12747
|
+
console.error(`${TAG24} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
|
|
12748
|
+
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
12749
|
+
}
|
|
12750
|
+
if (grant.agentSlug !== agentSlug) {
|
|
12751
|
+
recordAccessFailedAttempt(ip, agentSlug);
|
|
12752
|
+
console.error(
|
|
12753
|
+
`${TAG24} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
|
|
12754
|
+
);
|
|
12755
|
+
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
12756
|
+
}
|
|
12757
|
+
if (grant.status === "expired" || grant.status === "revoked") {
|
|
12758
|
+
recordAccessFailedAttempt(ip, agentSlug);
|
|
12759
|
+
console.error(
|
|
12760
|
+
`${TAG24} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
|
|
12761
|
+
);
|
|
12762
|
+
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
12763
|
+
}
|
|
12764
|
+
if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
|
|
12765
|
+
recordAccessFailedAttempt(ip, agentSlug);
|
|
12766
|
+
console.error(
|
|
12767
|
+
`${TAG24} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
|
|
12768
|
+
);
|
|
12769
|
+
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
12770
|
+
}
|
|
12771
|
+
if (!grant.sliceToken) {
|
|
12772
|
+
console.error(
|
|
12773
|
+
`${TAG24} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
|
|
12774
|
+
);
|
|
12775
|
+
return c.json({ error: "grant-misconfigured" }, 500);
|
|
12776
|
+
}
|
|
12777
|
+
const session = createAccessSession({
|
|
12778
|
+
grantId: grant.grantId,
|
|
12779
|
+
sliceToken: grant.sliceToken,
|
|
12780
|
+
agentSlug: grant.agentSlug,
|
|
12781
|
+
accountId: grant.accountId,
|
|
12782
|
+
personId: grant.personId,
|
|
12783
|
+
contactValue: grant.contactValue
|
|
12784
|
+
});
|
|
12785
|
+
try {
|
|
12786
|
+
await consumeMagicTokenAndActivate(grant.grantId);
|
|
12787
|
+
} catch (err) {
|
|
12788
|
+
console.error(
|
|
12789
|
+
`${TAG24} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
12790
|
+
);
|
|
12791
|
+
return c.json({ error: "verification-failed" }, 500);
|
|
12792
|
+
}
|
|
12793
|
+
clearAccessRateLimit(ip, agentSlug);
|
|
12794
|
+
console.log(`${TAG24} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
|
|
12795
|
+
console.log(
|
|
12796
|
+
`${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
|
|
12797
|
+
);
|
|
12798
|
+
c.header(
|
|
12799
|
+
"Set-Cookie",
|
|
12800
|
+
`${COOKIE_NAME}=${session.sessionId}; HttpOnly; Secure; SameSite=Lax; Path=/`
|
|
12801
|
+
);
|
|
12802
|
+
return c.json({
|
|
12803
|
+
ok: true,
|
|
12804
|
+
agentSlug,
|
|
12805
|
+
displayName: grant.displayName
|
|
12806
|
+
});
|
|
12807
|
+
});
|
|
12808
|
+
var verify_token_default = app37;
|
|
12809
|
+
|
|
12810
|
+
// app/lib/access-email.ts
|
|
12811
|
+
import { spawn as spawn2 } from "child_process";
|
|
12812
|
+
import { resolve as resolve19 } from "path";
|
|
12813
|
+
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
|
|
12814
|
+
var SEND_SCRIPT = resolve19(
|
|
12815
|
+
PLATFORM_ROOT8,
|
|
12816
|
+
"plugins",
|
|
12817
|
+
"email",
|
|
12818
|
+
"mcp",
|
|
12819
|
+
"dist",
|
|
12820
|
+
"scripts",
|
|
12821
|
+
"send-transactional.js"
|
|
12822
|
+
);
|
|
12823
|
+
async function sendMagicLinkEmail(payload) {
|
|
12824
|
+
return new Promise((resolveResult) => {
|
|
12825
|
+
const child = spawn2("node", [SEND_SCRIPT], {
|
|
12826
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
12827
|
+
env: process.env
|
|
12828
|
+
});
|
|
12829
|
+
let stdout = "";
|
|
12830
|
+
let stderr = "";
|
|
12831
|
+
let settled = false;
|
|
12832
|
+
const timeout = setTimeout(() => {
|
|
12833
|
+
if (settled) return;
|
|
12834
|
+
settled = true;
|
|
12835
|
+
try {
|
|
12836
|
+
child.kill("SIGTERM");
|
|
12837
|
+
} catch {
|
|
12838
|
+
}
|
|
12839
|
+
resolveResult({ ok: false, error: "send-transactional timeout (30s)" });
|
|
12840
|
+
}, 3e4);
|
|
12841
|
+
child.stdout.on("data", (chunk) => {
|
|
12842
|
+
stdout += chunk.toString();
|
|
12843
|
+
});
|
|
12844
|
+
child.stderr.on("data", (chunk) => {
|
|
12845
|
+
stderr += chunk.toString();
|
|
12846
|
+
});
|
|
12847
|
+
child.on("error", (err) => {
|
|
12848
|
+
if (settled) return;
|
|
12849
|
+
settled = true;
|
|
12850
|
+
clearTimeout(timeout);
|
|
12851
|
+
resolveResult({ ok: false, error: `spawn-failed: ${err.message}` });
|
|
12852
|
+
});
|
|
12853
|
+
child.on("exit", (code) => {
|
|
12854
|
+
if (settled) return;
|
|
12855
|
+
settled = true;
|
|
12856
|
+
clearTimeout(timeout);
|
|
12857
|
+
if (code === 0) {
|
|
12858
|
+
const match = stdout.match(/^ok messageId=(.+)$/m);
|
|
12859
|
+
resolveResult({ ok: true, messageId: match?.[1]?.trim() ?? "unknown" });
|
|
12860
|
+
} else {
|
|
12861
|
+
resolveResult({ ok: false, error: stderr.trim() || `exit-${code}` });
|
|
12862
|
+
}
|
|
12863
|
+
});
|
|
12864
|
+
child.stdin.write(JSON.stringify(payload));
|
|
12865
|
+
child.stdin.end();
|
|
12866
|
+
});
|
|
12867
|
+
}
|
|
12868
|
+
|
|
12869
|
+
// server/routes/access/request-magic-link.ts
|
|
12870
|
+
var TAG25 = "[access-request-link]";
|
|
12871
|
+
var app38 = new Hono();
|
|
12872
|
+
var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
|
|
12873
|
+
app38.post("/", async (c) => {
|
|
12874
|
+
let body;
|
|
12875
|
+
try {
|
|
12876
|
+
body = await c.req.json();
|
|
12877
|
+
} catch {
|
|
12878
|
+
return c.json({ error: "invalid-json" }, 400);
|
|
12879
|
+
}
|
|
12880
|
+
const contactValue = typeof body.contactValue === "string" ? body.contactValue.trim().toLowerCase() : "";
|
|
12881
|
+
const agentSlug = typeof body.agentSlug === "string" ? body.agentSlug.trim() : "";
|
|
12882
|
+
if (!contactValue || !agentSlug) {
|
|
12883
|
+
return c.json({ error: "contactValue and agentSlug required" }, 400);
|
|
12884
|
+
}
|
|
12885
|
+
const rateMsg = checkRequestLinkRateLimit(contactValue);
|
|
12886
|
+
if (rateMsg) {
|
|
12887
|
+
console.error(`${TAG25} contactValue=${maskContact(contactValue)} result=rate-limited`);
|
|
12888
|
+
return c.json({ error: rateMsg }, 429);
|
|
12889
|
+
}
|
|
12890
|
+
recordRequestLinkAttempt(contactValue);
|
|
12891
|
+
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
12892
|
+
if (!accountId) {
|
|
12893
|
+
console.error(`${TAG25} contactValue=${maskContact(contactValue)} result=no-account-id`);
|
|
12894
|
+
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
12895
|
+
}
|
|
12896
|
+
const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
|
|
12897
|
+
if (!grant) {
|
|
12898
|
+
console.log(`${TAG25} contactValue=${maskContact(contactValue)} result=notfound`);
|
|
12899
|
+
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
12900
|
+
}
|
|
12901
|
+
let token;
|
|
12902
|
+
try {
|
|
12903
|
+
token = await generateNewMagicToken(grant.grantId);
|
|
12904
|
+
} catch (err) {
|
|
12905
|
+
console.error(
|
|
12906
|
+
`${TAG25} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
12907
|
+
);
|
|
12908
|
+
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
12909
|
+
}
|
|
12910
|
+
const host = c.req.header("host") ?? "";
|
|
12911
|
+
const proto = (c.req.header("x-forwarded-proto") ?? "https").toLowerCase();
|
|
12912
|
+
const magicUrl = `${proto}://${host}/${agentSlug}?token=${token}`;
|
|
12913
|
+
const subject = `Your link to ${grant.agentSlug}`;
|
|
12914
|
+
const bodyText = [
|
|
12915
|
+
grant.displayName ? `Hi ${grant.displayName},` : "Hello,",
|
|
12916
|
+
"",
|
|
12917
|
+
`Here's a fresh link to access ${grant.agentSlug}:`,
|
|
12918
|
+
"",
|
|
12919
|
+
magicUrl,
|
|
12920
|
+
"",
|
|
12921
|
+
"This link expires in 15 minutes."
|
|
12922
|
+
].join("\n");
|
|
12923
|
+
const htmlBody = `
|
|
12924
|
+
<p>${grant.displayName ? `Hi ${grant.displayName},` : "Hello,"}</p>
|
|
12925
|
+
<p>Here's a fresh link to access <strong>${grant.agentSlug}</strong>:</p>
|
|
12926
|
+
<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>
|
|
12927
|
+
<p style="color:#888;font-size:14px;">This link expires in 15 minutes.</p>
|
|
12928
|
+
`;
|
|
12929
|
+
const sendResult = await sendMagicLinkEmail({
|
|
12930
|
+
accountId,
|
|
12931
|
+
to: grant.contactValue,
|
|
12932
|
+
subject,
|
|
12933
|
+
body: bodyText,
|
|
12934
|
+
html: htmlBody
|
|
12935
|
+
});
|
|
12936
|
+
if (!sendResult.ok) {
|
|
12937
|
+
console.error(
|
|
12938
|
+
`${TAG25} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
|
|
12939
|
+
);
|
|
12940
|
+
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
12941
|
+
}
|
|
12942
|
+
console.log(
|
|
12943
|
+
`${TAG25} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
|
|
12944
|
+
);
|
|
12945
|
+
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
12946
|
+
});
|
|
12947
|
+
var request_magic_link_default = app38;
|
|
12948
|
+
|
|
12949
|
+
// server/routes/access/index.ts
|
|
12950
|
+
var app39 = new Hono();
|
|
12951
|
+
app39.route("/verify-token", verify_token_default);
|
|
12952
|
+
app39.route("/request-magic-link", request_magic_link_default);
|
|
12953
|
+
var access_default = app39;
|
|
12954
|
+
|
|
12955
|
+
// server/routes/sites.ts
|
|
12956
|
+
import { existsSync as existsSync21, readFileSync as readFileSync17, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
|
|
12957
|
+
import { resolve as resolve20 } from "path";
|
|
12139
12958
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
12140
12959
|
var MIME = {
|
|
12141
12960
|
".html": "text/html; charset=utf-8",
|
|
@@ -12166,8 +12985,8 @@ function getExt(p) {
|
|
|
12166
12985
|
if (idx < p.lastIndexOf("/")) return "";
|
|
12167
12986
|
return p.slice(idx).toLowerCase();
|
|
12168
12987
|
}
|
|
12169
|
-
var
|
|
12170
|
-
|
|
12988
|
+
var app40 = new Hono();
|
|
12989
|
+
app40.get("/:rel{.*}", (c) => {
|
|
12171
12990
|
const reqPath = c.req.path;
|
|
12172
12991
|
const rawRel = c.req.param("rel") ?? "";
|
|
12173
12992
|
const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
@@ -12192,8 +13011,8 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
12192
13011
|
}
|
|
12193
13012
|
segments.push(seg);
|
|
12194
13013
|
}
|
|
12195
|
-
const rootDir =
|
|
12196
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
13014
|
+
const rootDir = resolve20(account.accountDir, "sites");
|
|
13015
|
+
let filePath = segments.length === 0 ? rootDir : resolve20(rootDir, ...segments);
|
|
12197
13016
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
12198
13017
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
12199
13018
|
return c.text("Forbidden", 403);
|
|
@@ -12213,7 +13032,7 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
12213
13032
|
return c.redirect(target, 301);
|
|
12214
13033
|
}
|
|
12215
13034
|
if (stat7?.isDirectory()) {
|
|
12216
|
-
filePath =
|
|
13035
|
+
filePath = resolve20(filePath, "index.html");
|
|
12217
13036
|
}
|
|
12218
13037
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
12219
13038
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
@@ -12238,7 +13057,7 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
12238
13057
|
}
|
|
12239
13058
|
let body;
|
|
12240
13059
|
try {
|
|
12241
|
-
body =
|
|
13060
|
+
body = readFileSync17(realPath);
|
|
12242
13061
|
} catch (err) {
|
|
12243
13062
|
const code = err?.code;
|
|
12244
13063
|
if (code === "EISDIR") {
|
|
@@ -12270,11 +13089,11 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
12270
13089
|
"X-Content-Type-Options": "nosniff"
|
|
12271
13090
|
});
|
|
12272
13091
|
});
|
|
12273
|
-
var sites_default =
|
|
13092
|
+
var sites_default = app40;
|
|
12274
13093
|
|
|
12275
13094
|
// app/lib/visitor-token.ts
|
|
12276
13095
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
12277
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
13096
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync18, writeFileSync as writeFileSync6 } from "fs";
|
|
12278
13097
|
import { dirname as dirname5 } from "path";
|
|
12279
13098
|
var TOKEN_PREFIX = "v1.";
|
|
12280
13099
|
var SECRET_BYTES = 32;
|
|
@@ -12283,7 +13102,7 @@ var cachedSecret = null;
|
|
|
12283
13102
|
function getSecret() {
|
|
12284
13103
|
if (cachedSecret) return cachedSecret;
|
|
12285
13104
|
try {
|
|
12286
|
-
const hex2 =
|
|
13105
|
+
const hex2 = readFileSync18(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
12287
13106
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
12288
13107
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
12289
13108
|
return cachedSecret;
|
|
@@ -12297,7 +13116,7 @@ function getSecret() {
|
|
|
12297
13116
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
12298
13117
|
} catch {
|
|
12299
13118
|
}
|
|
12300
|
-
const hex =
|
|
13119
|
+
const hex = readFileSync18(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
12301
13120
|
cachedSecret = Buffer.from(hex, "hex");
|
|
12302
13121
|
return cachedSecret;
|
|
12303
13122
|
}
|
|
@@ -12350,7 +13169,7 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
12350
13169
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
12351
13170
|
|
|
12352
13171
|
// app/lib/brand-config.ts
|
|
12353
|
-
import { existsSync as existsSync22, readFileSync as
|
|
13172
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
|
|
12354
13173
|
import { join as join14 } from "path";
|
|
12355
13174
|
var cached2 = null;
|
|
12356
13175
|
var cachedAttempted = false;
|
|
@@ -12362,7 +13181,7 @@ function readBrandConfig() {
|
|
|
12362
13181
|
const brandPath = join14(platformRoot, "config", "brand.json");
|
|
12363
13182
|
if (!existsSync22(brandPath)) return null;
|
|
12364
13183
|
try {
|
|
12365
|
-
cached2 = JSON.parse(
|
|
13184
|
+
cached2 = JSON.parse(readFileSync19(brandPath, "utf-8"));
|
|
12366
13185
|
return cached2;
|
|
12367
13186
|
} catch {
|
|
12368
13187
|
return null;
|
|
@@ -12370,7 +13189,7 @@ function readBrandConfig() {
|
|
|
12370
13189
|
}
|
|
12371
13190
|
|
|
12372
13191
|
// server/routes/visitor-consent.ts
|
|
12373
|
-
var
|
|
13192
|
+
var app41 = new Hono();
|
|
12374
13193
|
var CONSENT_COOKIE_NAME = "mxy_consent";
|
|
12375
13194
|
var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
|
|
12376
13195
|
var DEFAULT_CONSENT_COPY = {
|
|
@@ -12415,17 +13234,17 @@ function siteSlugFromReferer(referer) {
|
|
|
12415
13234
|
return "";
|
|
12416
13235
|
}
|
|
12417
13236
|
}
|
|
12418
|
-
|
|
13237
|
+
app41.options("/consent", (c) => {
|
|
12419
13238
|
const origin = getOrigin(c);
|
|
12420
13239
|
setCorsHeaders(c, origin);
|
|
12421
13240
|
return c.body(null, 204);
|
|
12422
13241
|
});
|
|
12423
|
-
|
|
13242
|
+
app41.options("/brand-config", (c) => {
|
|
12424
13243
|
const origin = getOrigin(c);
|
|
12425
13244
|
setCorsHeaders(c, origin);
|
|
12426
13245
|
return c.body(null, 204);
|
|
12427
13246
|
});
|
|
12428
|
-
|
|
13247
|
+
app41.post("/consent", async (c) => {
|
|
12429
13248
|
const origin = getOrigin(c);
|
|
12430
13249
|
setCorsHeaders(c, origin);
|
|
12431
13250
|
let raw;
|
|
@@ -12465,7 +13284,7 @@ app36.post("/consent", async (c) => {
|
|
|
12465
13284
|
console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
|
|
12466
13285
|
return c.body(null, 204);
|
|
12467
13286
|
});
|
|
12468
|
-
|
|
13287
|
+
app41.get("/brand-config", (c) => {
|
|
12469
13288
|
const origin = getOrigin(c);
|
|
12470
13289
|
setCorsHeaders(c, origin);
|
|
12471
13290
|
const brand = readBrandConfig();
|
|
@@ -12475,7 +13294,7 @@ app36.get("/brand-config", (c) => {
|
|
|
12475
13294
|
c.header("Cache-Control", "public, max-age=300");
|
|
12476
13295
|
return c.json({ consent: { copy, palette } });
|
|
12477
13296
|
});
|
|
12478
|
-
var visitor_consent_default =
|
|
13297
|
+
var visitor_consent_default = app41;
|
|
12479
13298
|
|
|
12480
13299
|
// server/routes/listings.ts
|
|
12481
13300
|
function getCookie(headerValue, name) {
|
|
@@ -12502,8 +13321,8 @@ function appendConsentParams(pageUrl, token) {
|
|
|
12502
13321
|
}
|
|
12503
13322
|
var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
|
|
12504
13323
|
var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
|
|
12505
|
-
var
|
|
12506
|
-
|
|
13324
|
+
var app42 = new Hono();
|
|
13325
|
+
app42.get("/:slug/click", async (c) => {
|
|
12507
13326
|
const slug = c.req.param("slug") ?? "";
|
|
12508
13327
|
const rawSession = c.req.query("session") ?? "";
|
|
12509
13328
|
const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
|
|
@@ -12567,10 +13386,10 @@ app37.get("/:slug/click", async (c) => {
|
|
|
12567
13386
|
console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
12568
13387
|
return c.redirect(redirectUrl, 302);
|
|
12569
13388
|
});
|
|
12570
|
-
var listings_default =
|
|
13389
|
+
var listings_default = app42;
|
|
12571
13390
|
|
|
12572
13391
|
// server/routes/visitor-event.ts
|
|
12573
|
-
var
|
|
13392
|
+
var app43 = new Hono();
|
|
12574
13393
|
var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
|
|
12575
13394
|
var buckets = /* @__PURE__ */ new Map();
|
|
12576
13395
|
var RATE_LIMIT = 60;
|
|
@@ -12637,12 +13456,12 @@ function originAllowed(origin, allowlist) {
|
|
|
12637
13456
|
return false;
|
|
12638
13457
|
}
|
|
12639
13458
|
}
|
|
12640
|
-
|
|
13459
|
+
app43.options("/event", (c) => {
|
|
12641
13460
|
const origin = getOrigin2(c);
|
|
12642
13461
|
setCorsHeaders2(c, origin);
|
|
12643
13462
|
return c.body(null, 204);
|
|
12644
13463
|
});
|
|
12645
|
-
|
|
13464
|
+
app43.post("/event", async (c) => {
|
|
12646
13465
|
const origin = getOrigin2(c);
|
|
12647
13466
|
setCorsHeaders2(c, origin);
|
|
12648
13467
|
const ua = c.req.header("user-agent") ?? "";
|
|
@@ -12847,7 +13666,7 @@ async function writeEvent(opts) {
|
|
|
12847
13666
|
);
|
|
12848
13667
|
}
|
|
12849
13668
|
}
|
|
12850
|
-
var visitor_event_default =
|
|
13669
|
+
var visitor_event_default = app43;
|
|
12851
13670
|
|
|
12852
13671
|
// app/lib/graph-health.ts
|
|
12853
13672
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
@@ -12931,7 +13750,7 @@ function startGraphHealthTimer() {
|
|
|
12931
13750
|
}
|
|
12932
13751
|
|
|
12933
13752
|
// app/lib/whatsapp/inbound/claude-bridge.ts
|
|
12934
|
-
var
|
|
13753
|
+
var TAG26 = "[whatsapp-adaptor]";
|
|
12935
13754
|
function whatsappTurnTimeoutMs() {
|
|
12936
13755
|
return Number(process.env.WHATSAPP_PTY_TURN_TIMEOUT_MS ?? String(5 * 6e4));
|
|
12937
13756
|
}
|
|
@@ -12954,7 +13773,7 @@ async function dispatchToClaude(input) {
|
|
|
12954
13773
|
await input.reply(result.turnText);
|
|
12955
13774
|
} catch (err) {
|
|
12956
13775
|
const m = err instanceof Error ? err.message : String(err);
|
|
12957
|
-
console.error(`${
|
|
13776
|
+
console.error(`${TAG26} reject reason=reply-failed senderId=${input.senderId} message=${m}`);
|
|
12958
13777
|
}
|
|
12959
13778
|
}
|
|
12960
13779
|
function startReaper2() {
|
|
@@ -12991,8 +13810,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
12991
13810
|
|
|
12992
13811
|
// ../lib/entitlement/src/index.ts
|
|
12993
13812
|
import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
|
|
12994
|
-
import { existsSync as existsSync23, readFileSync as
|
|
12995
|
-
import { resolve as
|
|
13813
|
+
import { existsSync as existsSync23, readFileSync as readFileSync20, statSync as statSync8 } from "fs";
|
|
13814
|
+
import { resolve as resolve21 } from "path";
|
|
12996
13815
|
|
|
12997
13816
|
// ../lib/entitlement/src/canonicalize.ts
|
|
12998
13817
|
function canonicalize(value) {
|
|
@@ -13027,7 +13846,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
13027
13846
|
var GRACE_DAYS = 7;
|
|
13028
13847
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
13029
13848
|
function pubkeyPath(brand) {
|
|
13030
|
-
return
|
|
13849
|
+
return resolve21(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
13031
13850
|
}
|
|
13032
13851
|
var memo = null;
|
|
13033
13852
|
function memoKey(mtimeMs, account) {
|
|
@@ -13039,7 +13858,7 @@ function resolveEntitlement(brand, account) {
|
|
|
13039
13858
|
if (brand.commercialMode !== true) {
|
|
13040
13859
|
return logResolved(implicitTrust(account), null);
|
|
13041
13860
|
}
|
|
13042
|
-
const entitlementPath =
|
|
13861
|
+
const entitlementPath = resolve21(brand.configDir, "entitlement.json");
|
|
13043
13862
|
if (!existsSync23(entitlementPath)) {
|
|
13044
13863
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
13045
13864
|
}
|
|
@@ -13055,7 +13874,7 @@ function resolveEntitlement(brand, account) {
|
|
|
13055
13874
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
13056
13875
|
let pubkeyPem;
|
|
13057
13876
|
try {
|
|
13058
|
-
pubkeyPem =
|
|
13877
|
+
pubkeyPem = readFileSync20(pubkeyPath(brand), "utf-8");
|
|
13059
13878
|
} catch (err) {
|
|
13060
13879
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
13061
13880
|
reason: "pubkey-missing"
|
|
@@ -13069,7 +13888,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
13069
13888
|
}
|
|
13070
13889
|
let envelope;
|
|
13071
13890
|
try {
|
|
13072
|
-
envelope = JSON.parse(
|
|
13891
|
+
envelope = JSON.parse(readFileSync20(entitlementPath, "utf-8"));
|
|
13073
13892
|
} catch {
|
|
13074
13893
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
13075
13894
|
}
|
|
@@ -13229,15 +14048,15 @@ function clientFrom(c) {
|
|
|
13229
14048
|
c.req.header("x-forwarded-for")
|
|
13230
14049
|
);
|
|
13231
14050
|
}
|
|
13232
|
-
var
|
|
13233
|
-
var BRAND_JSON_PATH =
|
|
14051
|
+
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
14052
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join15(PLATFORM_ROOT9, "config", "brand.json") : "";
|
|
13234
14053
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
13235
14054
|
if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
|
|
13236
14055
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
13237
14056
|
}
|
|
13238
14057
|
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
13239
14058
|
try {
|
|
13240
|
-
const parsed = JSON.parse(
|
|
14059
|
+
const parsed = JSON.parse(readFileSync21(BRAND_JSON_PATH, "utf-8"));
|
|
13241
14060
|
BRAND = { ...BRAND, ...parsed };
|
|
13242
14061
|
} catch (err) {
|
|
13243
14062
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -13260,7 +14079,7 @@ var ALIAS_DOMAINS_PATH = join15(homedir3(), BRAND.configDir, "alias-domains.json
|
|
|
13260
14079
|
function loadAliasDomains() {
|
|
13261
14080
|
try {
|
|
13262
14081
|
if (!existsSync24(ALIAS_DOMAINS_PATH)) return null;
|
|
13263
|
-
const parsed = JSON.parse(
|
|
14082
|
+
const parsed = JSON.parse(readFileSync21(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
13264
14083
|
if (!Array.isArray(parsed)) {
|
|
13265
14084
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
13266
14085
|
return null;
|
|
@@ -13284,9 +14103,9 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
|
|
|
13284
14103
|
function isPublicHost(host) {
|
|
13285
14104
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
13286
14105
|
}
|
|
13287
|
-
var
|
|
13288
|
-
|
|
13289
|
-
|
|
14106
|
+
var app44 = new Hono();
|
|
14107
|
+
app44.use("*", clientIpMiddleware);
|
|
14108
|
+
app44.use("*", async (c, next) => {
|
|
13290
14109
|
await next();
|
|
13291
14110
|
c.header("X-Content-Type-Options", "nosniff");
|
|
13292
14111
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
@@ -13296,7 +14115,7 @@ app39.use("*", async (c, next) => {
|
|
|
13296
14115
|
);
|
|
13297
14116
|
});
|
|
13298
14117
|
var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
|
|
13299
|
-
|
|
14118
|
+
app44.use("*", async (c, next) => {
|
|
13300
14119
|
if (!HTTP_LOG_PATHS.has(c.req.path)) {
|
|
13301
14120
|
await next();
|
|
13302
14121
|
return;
|
|
@@ -13329,7 +14148,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
|
|
|
13329
14148
|
"/sites/"
|
|
13330
14149
|
];
|
|
13331
14150
|
var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
|
|
13332
|
-
|
|
14151
|
+
app44.use("*", async (c, next) => {
|
|
13333
14152
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13334
14153
|
if (!isPublicHost(host)) {
|
|
13335
14154
|
await next();
|
|
@@ -13369,7 +14188,7 @@ function resolveRemoteAuthOpts() {
|
|
|
13369
14188
|
return brandLoginOpts;
|
|
13370
14189
|
}
|
|
13371
14190
|
var MAX_LOGIN_BODY = 8 * 1024;
|
|
13372
|
-
|
|
14191
|
+
app44.post("/__remote-auth/login", async (c) => {
|
|
13373
14192
|
const client = clientFrom(c);
|
|
13374
14193
|
const clientIp = client.ip || "unknown";
|
|
13375
14194
|
if (!requestIsTlsTerminated(c)) {
|
|
@@ -13414,7 +14233,7 @@ app39.post("/__remote-auth/login", async (c) => {
|
|
|
13414
14233
|
}
|
|
13415
14234
|
});
|
|
13416
14235
|
});
|
|
13417
|
-
|
|
14236
|
+
app44.get("/__remote-auth/logout", (c) => {
|
|
13418
14237
|
const client = clientFrom(c);
|
|
13419
14238
|
const clientIp = client.ip || "unknown";
|
|
13420
14239
|
console.error(`[remote-auth] logout ip=${clientIp}`);
|
|
@@ -13427,7 +14246,7 @@ app39.get("/__remote-auth/logout", (c) => {
|
|
|
13427
14246
|
}
|
|
13428
14247
|
});
|
|
13429
14248
|
});
|
|
13430
|
-
|
|
14249
|
+
app44.post("/__remote-auth/change-password", async (c) => {
|
|
13431
14250
|
const client = clientFrom(c);
|
|
13432
14251
|
const clientIp = client.ip || "unknown";
|
|
13433
14252
|
const rateLimited = checkRateLimit(client);
|
|
@@ -13478,13 +14297,13 @@ app39.post("/__remote-auth/change-password", async (c) => {
|
|
|
13478
14297
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
|
|
13479
14298
|
}
|
|
13480
14299
|
});
|
|
13481
|
-
|
|
14300
|
+
app44.get("/__remote-auth/setup", (c) => {
|
|
13482
14301
|
if (isRemoteAuthConfigured()) {
|
|
13483
14302
|
return c.redirect("/");
|
|
13484
14303
|
}
|
|
13485
14304
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
|
|
13486
14305
|
});
|
|
13487
|
-
|
|
14306
|
+
app44.post("/__remote-auth/set-initial-password", async (c) => {
|
|
13488
14307
|
if (isRemoteAuthConfigured()) {
|
|
13489
14308
|
return c.redirect("/");
|
|
13490
14309
|
}
|
|
@@ -13522,10 +14341,10 @@ app39.post("/__remote-auth/set-initial-password", async (c) => {
|
|
|
13522
14341
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
|
|
13523
14342
|
}
|
|
13524
14343
|
});
|
|
13525
|
-
|
|
14344
|
+
app44.get("/api/remote-auth/status", (c) => {
|
|
13526
14345
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
13527
14346
|
});
|
|
13528
|
-
|
|
14347
|
+
app44.post("/api/remote-auth/set-password", async (c) => {
|
|
13529
14348
|
let body;
|
|
13530
14349
|
try {
|
|
13531
14350
|
body = await c.req.json();
|
|
@@ -13556,9 +14375,9 @@ app39.post("/api/remote-auth/set-password", async (c) => {
|
|
|
13556
14375
|
return c.json({ error: "Failed to save password" }, 500);
|
|
13557
14376
|
}
|
|
13558
14377
|
});
|
|
13559
|
-
|
|
14378
|
+
app44.route("/api/_client-error", client_error_default);
|
|
13560
14379
|
console.log("[client-error-route] mounted");
|
|
13561
|
-
|
|
14380
|
+
app44.use("*", async (c, next) => {
|
|
13562
14381
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13563
14382
|
const path2 = c.req.path;
|
|
13564
14383
|
if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
|
|
@@ -13591,11 +14410,12 @@ app39.use("*", async (c, next) => {
|
|
|
13591
14410
|
console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
|
|
13592
14411
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
|
|
13593
14412
|
});
|
|
13594
|
-
|
|
13595
|
-
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
14413
|
+
app44.route("/api/health", health_default);
|
|
14414
|
+
app44.route("/api/chat", chat_default);
|
|
14415
|
+
app44.route("/api/whatsapp", whatsapp_default);
|
|
14416
|
+
app44.route("/api/onboarding", onboarding_default);
|
|
14417
|
+
app44.route("/api/admin", admin_default);
|
|
14418
|
+
app44.route("/api/access", access_default);
|
|
13599
14419
|
var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
|
|
13600
14420
|
var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
13601
14421
|
var IMAGE_MIME = {
|
|
@@ -13607,7 +14427,7 @@ var IMAGE_MIME = {
|
|
|
13607
14427
|
".svg": "image/svg+xml",
|
|
13608
14428
|
".ico": "image/x-icon"
|
|
13609
14429
|
};
|
|
13610
|
-
|
|
14430
|
+
app44.get("/agent-assets/:slug/:filename", (c) => {
|
|
13611
14431
|
const slug = c.req.param("slug");
|
|
13612
14432
|
const filename = c.req.param("filename");
|
|
13613
14433
|
if (!SAFE_SLUG_RE2.test(slug)) {
|
|
@@ -13623,8 +14443,8 @@ app39.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
13623
14443
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
13624
14444
|
return c.text("Not found", 404);
|
|
13625
14445
|
}
|
|
13626
|
-
const filePath =
|
|
13627
|
-
const expectedDir =
|
|
14446
|
+
const filePath = resolve22(account.accountDir, "agents", slug, "assets", filename);
|
|
14447
|
+
const expectedDir = resolve22(account.accountDir, "agents", slug, "assets");
|
|
13628
14448
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
13629
14449
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
13630
14450
|
return c.text("Forbidden", 403);
|
|
@@ -13636,13 +14456,13 @@ app39.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
13636
14456
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
13637
14457
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
13638
14458
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
13639
|
-
const body =
|
|
14459
|
+
const body = readFileSync21(filePath);
|
|
13640
14460
|
return c.body(body, 200, {
|
|
13641
14461
|
"Content-Type": contentType,
|
|
13642
14462
|
"Cache-Control": "public, max-age=3600"
|
|
13643
14463
|
});
|
|
13644
14464
|
});
|
|
13645
|
-
|
|
14465
|
+
app44.get("/generated/:filename", (c) => {
|
|
13646
14466
|
const filename = c.req.param("filename");
|
|
13647
14467
|
if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
|
|
13648
14468
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
@@ -13653,8 +14473,8 @@ app39.get("/generated/:filename", (c) => {
|
|
|
13653
14473
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
13654
14474
|
return c.text("Not found", 404);
|
|
13655
14475
|
}
|
|
13656
|
-
const filePath =
|
|
13657
|
-
const expectedDir =
|
|
14476
|
+
const filePath = resolve22(account.accountDir, "generated", filename);
|
|
14477
|
+
const expectedDir = resolve22(account.accountDir, "generated");
|
|
13658
14478
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
13659
14479
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
13660
14480
|
return c.text("Forbidden", 403);
|
|
@@ -13666,22 +14486,22 @@ app39.get("/generated/:filename", (c) => {
|
|
|
13666
14486
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
13667
14487
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
13668
14488
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
13669
|
-
const body =
|
|
14489
|
+
const body = readFileSync21(filePath);
|
|
13670
14490
|
return c.body(body, 200, {
|
|
13671
14491
|
"Content-Type": contentType,
|
|
13672
14492
|
"Cache-Control": "public, max-age=86400"
|
|
13673
14493
|
});
|
|
13674
14494
|
});
|
|
13675
|
-
|
|
13676
|
-
|
|
13677
|
-
|
|
13678
|
-
|
|
14495
|
+
app44.route("/sites", sites_default);
|
|
14496
|
+
app44.route("/listings", listings_default);
|
|
14497
|
+
app44.route("/v", visitor_event_default);
|
|
14498
|
+
app44.route("/v", visitor_consent_default);
|
|
13679
14499
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
13680
14500
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
13681
14501
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
13682
14502
|
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
13683
14503
|
try {
|
|
13684
|
-
const fullBrand = JSON.parse(
|
|
14504
|
+
const fullBrand = JSON.parse(readFileSync21(BRAND_JSON_PATH, "utf-8"));
|
|
13685
14505
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
13686
14506
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
13687
14507
|
} catch {
|
|
@@ -13697,10 +14517,10 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
13697
14517
|
})}</script>`;
|
|
13698
14518
|
function readInstalledVersion() {
|
|
13699
14519
|
try {
|
|
13700
|
-
if (!
|
|
13701
|
-
const versionFile = join15(
|
|
14520
|
+
if (!PLATFORM_ROOT9) return "unknown";
|
|
14521
|
+
const versionFile = join15(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
|
|
13702
14522
|
if (!existsSync24(versionFile)) return "unknown";
|
|
13703
|
-
const content =
|
|
14523
|
+
const content = readFileSync21(versionFile, "utf-8").trim();
|
|
13704
14524
|
return content || "unknown";
|
|
13705
14525
|
} catch {
|
|
13706
14526
|
return "unknown";
|
|
@@ -13741,7 +14561,7 @@ var clientErrorReporterScript = `<script>
|
|
|
13741
14561
|
function cachedHtml(file) {
|
|
13742
14562
|
let html = htmlCache.get(file);
|
|
13743
14563
|
if (!html) {
|
|
13744
|
-
html =
|
|
14564
|
+
html = readFileSync21(resolve22(process.cwd(), "public", file), "utf-8");
|
|
13745
14565
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
13746
14566
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
13747
14567
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -13761,12 +14581,12 @@ function loadBrandingCache(agentSlug) {
|
|
|
13761
14581
|
try {
|
|
13762
14582
|
const accountJsonPath = join15(configDir2, "account.json");
|
|
13763
14583
|
if (!existsSync24(accountJsonPath)) return null;
|
|
13764
|
-
const account = JSON.parse(
|
|
14584
|
+
const account = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
|
|
13765
14585
|
const accountId = account.accountId;
|
|
13766
14586
|
if (!accountId) return null;
|
|
13767
14587
|
const cachePath = join15(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
13768
14588
|
if (!existsSync24(cachePath)) return null;
|
|
13769
|
-
return JSON.parse(
|
|
14589
|
+
return JSON.parse(readFileSync21(cachePath, "utf-8"));
|
|
13770
14590
|
} catch {
|
|
13771
14591
|
return null;
|
|
13772
14592
|
}
|
|
@@ -13776,7 +14596,7 @@ function resolveDefaultSlug() {
|
|
|
13776
14596
|
const configDir2 = join15(homedir3(), BRAND.configDir);
|
|
13777
14597
|
const accountJsonPath = join15(configDir2, "account.json");
|
|
13778
14598
|
if (!existsSync24(accountJsonPath)) return null;
|
|
13779
|
-
const account = JSON.parse(
|
|
14599
|
+
const account = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
|
|
13780
14600
|
return account.defaultAgent || null;
|
|
13781
14601
|
} catch {
|
|
13782
14602
|
return null;
|
|
@@ -13812,7 +14632,7 @@ function brandedPublicHtml(agentSlug) {
|
|
|
13812
14632
|
function escapeHtml(s) {
|
|
13813
14633
|
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
13814
14634
|
}
|
|
13815
|
-
|
|
14635
|
+
app44.get("/", (c) => {
|
|
13816
14636
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13817
14637
|
if (isPublicHost(host)) {
|
|
13818
14638
|
const defaultSlug = resolveDefaultSlug();
|
|
@@ -13820,12 +14640,12 @@ app39.get("/", (c) => {
|
|
|
13820
14640
|
}
|
|
13821
14641
|
return c.html(cachedHtml("index.html"));
|
|
13822
14642
|
});
|
|
13823
|
-
|
|
14643
|
+
app44.get("/public", (c) => {
|
|
13824
14644
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13825
14645
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13826
14646
|
return c.html(cachedHtml("public.html"));
|
|
13827
14647
|
});
|
|
13828
|
-
|
|
14648
|
+
app44.get("/chat", (c) => {
|
|
13829
14649
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13830
14650
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13831
14651
|
return c.html(cachedHtml("public.html"));
|
|
@@ -13844,12 +14664,12 @@ async function logViewerFetch(c, next) {
|
|
|
13844
14664
|
duration_ms: Date.now() - start
|
|
13845
14665
|
});
|
|
13846
14666
|
}
|
|
13847
|
-
|
|
13848
|
-
|
|
13849
|
-
|
|
14667
|
+
app44.use("/vnc-viewer.html", logViewerFetch);
|
|
14668
|
+
app44.use("/vnc-popout.html", logViewerFetch);
|
|
14669
|
+
app44.get("/vnc-popout.html", (c) => {
|
|
13850
14670
|
let html = htmlCache.get("vnc-popout.html");
|
|
13851
14671
|
if (!html) {
|
|
13852
|
-
html =
|
|
14672
|
+
html = readFileSync21(resolve22(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
13853
14673
|
const name = escapeHtml(BRAND.productName);
|
|
13854
14674
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
13855
14675
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -13859,7 +14679,7 @@ app39.get("/vnc-popout.html", (c) => {
|
|
|
13859
14679
|
}
|
|
13860
14680
|
return c.html(html);
|
|
13861
14681
|
});
|
|
13862
|
-
|
|
14682
|
+
app44.post("/api/vnc/client-event", async (c) => {
|
|
13863
14683
|
let body;
|
|
13864
14684
|
try {
|
|
13865
14685
|
body = await c.req.json();
|
|
@@ -13880,25 +14700,25 @@ app39.post("/api/vnc/client-event", async (c) => {
|
|
|
13880
14700
|
});
|
|
13881
14701
|
return c.json({ ok: true });
|
|
13882
14702
|
});
|
|
13883
|
-
|
|
14703
|
+
app44.get("/g/:slug", (c) => {
|
|
13884
14704
|
return c.html(brandedPublicHtml());
|
|
13885
14705
|
});
|
|
13886
|
-
|
|
14706
|
+
app44.get("/graph", (c) => {
|
|
13887
14707
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13888
14708
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13889
14709
|
return c.html(cachedHtml("graph.html"));
|
|
13890
14710
|
});
|
|
13891
|
-
|
|
14711
|
+
app44.get("/sessions", (c) => {
|
|
13892
14712
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13893
14713
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13894
14714
|
return c.html(cachedHtml("sessions.html"));
|
|
13895
14715
|
});
|
|
13896
|
-
|
|
14716
|
+
app44.get("/data", (c) => {
|
|
13897
14717
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13898
14718
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
13899
14719
|
return c.html(cachedHtml("data.html"));
|
|
13900
14720
|
});
|
|
13901
|
-
|
|
14721
|
+
app44.get("/:slug", async (c, next) => {
|
|
13902
14722
|
const slug = c.req.param("slug");
|
|
13903
14723
|
if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
|
|
13904
14724
|
const branding = loadBrandingCache(slug);
|
|
@@ -13908,15 +14728,15 @@ app39.get("/:slug", async (c, next) => {
|
|
|
13908
14728
|
await next();
|
|
13909
14729
|
});
|
|
13910
14730
|
if (brandFaviconPath !== "/favicon.ico") {
|
|
13911
|
-
|
|
14731
|
+
app44.get("/favicon.ico", (c) => {
|
|
13912
14732
|
c.header("Cache-Control", "public, max-age=300");
|
|
13913
14733
|
return c.redirect(brandFaviconPath, 302);
|
|
13914
14734
|
});
|
|
13915
14735
|
}
|
|
13916
|
-
|
|
14736
|
+
app44.use("/*", serveStatic({ root: "./public" }));
|
|
13917
14737
|
var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
|
|
13918
14738
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
13919
|
-
var httpServer = serve({ fetch:
|
|
14739
|
+
var httpServer = serve({ fetch: app44.fetch, port, hostname });
|
|
13920
14740
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
13921
14741
|
{
|
|
13922
14742
|
const loopHist = monitorEventLoopDelay({ resolution: 50 });
|
|
@@ -13940,6 +14760,7 @@ var SUBAPP_MANIFEST = [
|
|
|
13940
14760
|
{ prefix: "/api/whatsapp", file: "server/routes/whatsapp.ts", subapp: whatsapp_default },
|
|
13941
14761
|
{ prefix: "/api/onboarding", file: "server/routes/onboarding.ts", subapp: onboarding_default },
|
|
13942
14762
|
{ prefix: "/api/admin", file: "server/routes/admin/index.ts", subapp: admin_default },
|
|
14763
|
+
{ prefix: "/api/access", file: "server/routes/access/index.ts", subapp: access_default },
|
|
13943
14764
|
{ prefix: "/api/_client-error", file: "server/routes/client-error.ts", subapp: client_error_default },
|
|
13944
14765
|
{ prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default },
|
|
13945
14766
|
{ prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default },
|
|
@@ -13951,7 +14772,7 @@ for (const m of SUBAPP_MANIFEST) {
|
|
|
13951
14772
|
}
|
|
13952
14773
|
try {
|
|
13953
14774
|
const registered = [];
|
|
13954
|
-
for (const r of
|
|
14775
|
+
for (const r of app44.routes ?? []) {
|
|
13955
14776
|
if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
|
|
13956
14777
|
if (AGENT_SLUG_PATTERN.test(r.path)) {
|
|
13957
14778
|
registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
|
|
@@ -13974,7 +14795,7 @@ try {
|
|
|
13974
14795
|
(async () => {
|
|
13975
14796
|
try {
|
|
13976
14797
|
if (!existsSync24(USERS_FILE)) return;
|
|
13977
|
-
const usersRaw =
|
|
14798
|
+
const usersRaw = readFileSync21(USERS_FILE, "utf-8").trim();
|
|
13978
14799
|
if (!usersRaw) return;
|
|
13979
14800
|
const users = JSON.parse(usersRaw);
|
|
13980
14801
|
const userId = users[0]?.userId;
|
|
@@ -14066,7 +14887,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
14066
14887
|
}
|
|
14067
14888
|
init({
|
|
14068
14889
|
configDir: configDirForWhatsApp,
|
|
14069
|
-
platformRoot:
|
|
14890
|
+
platformRoot: resolve22(process.env.MAXY_PLATFORM_ROOT ?? join15(__dirname, "..")),
|
|
14070
14891
|
accountConfig: bootAccountConfig,
|
|
14071
14892
|
onMessage: async (msg) => {
|
|
14072
14893
|
if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {
|