@rubytech/create-maxy-code 0.1.478 → 0.1.479
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/package.json +1 -1
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1789-reseat-channel-row-fork.md +1244 -0
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1818-loop-gate-app-routes.md +462 -0
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1819-top-level-label-allowlist.md +321 -0
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1831-data-portal-class-derived-allowlist.md +2177 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1789-reseat-channel-row-fork-design.md +201 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1818-loop-gate-app-routes-design.md +217 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1819-top-level-label-allowlist-design.md +80 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1831-data-portal-class-derived-allowlist-design.md +163 -0
- package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-style/dist/index.js +22 -0
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/__tests__/parity.test.ts +6 -0
- package/payload/platform/lib/graph-style/src/index.ts +20 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +13 -1
- package/payload/platform/plugins/cloudflare/PLUGIN.md +26 -1
- package/payload/platform/plugins/cloudflare/bin/d1-http.mjs +80 -0
- package/payload/platform/plugins/cloudflare/bin/portal-enrol.mjs +17 -2
- package/payload/platform/plugins/cloudflare/bin/portal-index-push.mjs +357 -0
- package/payload/platform/plugins/cloudflare/bin/schema-exposed-dirs.mjs +103 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/authorize.test.ts +2 -2
- package/payload/platform/plugins/cloudflare/mcp/__tests__/d1-http.test.ts +96 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-directory-listing.test.ts +120 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-enrol.test.ts +59 -4
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-index-push.test.ts +176 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-index-state-contract.test.ts +75 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-indexed-download.test.ts +131 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-sign.test.ts +68 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/schema-exposed-dirs.test.ts +88 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/template-config.test.ts +39 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +68 -1
- package/payload/platform/plugins/cloudflare/skills/data-portal/schema.sql +30 -1
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/portal-sign.mjs +87 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/session.ts +8 -4
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/types.ts +10 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/download.ts +107 -1
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/files.ts +82 -2
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/index.html +9 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/portal.css +56 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/portal.js +98 -2
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/wrangler.toml +12 -0
- package/payload/platform/plugins/docs/references/cloudflare.md +12 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/portal-index-audit.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/portal-index-audit.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/portal-index-audit.test.js +66 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/portal-index-audit.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/portal-index-audit.d.ts +41 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/portal-index-audit.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/portal-index-audit.js +75 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/portal-index-audit.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +122 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/server/public/activity.html +8 -6
- package/payload/server/public/assets/{AdminLoginScreens-DR5eLF-a.js → AdminLoginScreens-DkxQZnvB.js} +1 -1
- package/payload/server/public/assets/AdminShell-Cv_nIFGe.js +2 -0
- package/payload/server/public/assets/Checkbox-CsTWK8mm.js +1 -0
- package/payload/server/public/assets/activity-BbnTiVBT.js +1 -0
- package/payload/server/public/assets/{admin-D2KLZnQW.js → admin-BZY3i_Ff.js} +1 -1
- package/payload/server/public/assets/{browser-CDWqEiCD.js → browser-BbIiSFKr.js} +1 -1
- package/payload/server/public/assets/{calendar-BG7fOpbB.js → calendar-BvxeTHvQ.js} +1 -1
- package/payload/server/public/assets/chat-DUKxufRk.js +1 -0
- package/payload/server/public/assets/chevron-left-BtfJD6LL.js +1 -0
- package/payload/server/public/assets/data-hl1wa-Ax.js +1 -0
- package/payload/server/public/assets/{graph-DRloNic4.js → graph-CDSGRoeb.js} +1 -1
- package/payload/server/public/assets/graph-labels-C5qLwnWA.js +1 -0
- package/payload/server/public/assets/{maximize-2-CzDaBugs.js → maximize-2-CAzH8dED.js} +1 -1
- package/payload/server/public/assets/{operator-BZeeyait.js → operator-BK7r8DAB.js} +1 -1
- package/payload/server/public/assets/{page-ef-6rRPL.js → page-B5YngOrK.js} +1 -1
- package/payload/server/public/assets/{page-B0obcg3b.js → page-BsCS0Dmc.js} +1 -1
- package/payload/server/public/assets/{public-9D3MMOkC.js → public-C1Pz-IBs.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-DXEKUhXP.js → rotate-ccw-UDln4Tou.js} +1 -1
- package/payload/server/public/assets/{tasks-BB0L7e5a.js → tasks-B-cLsn3p.js} +1 -1
- package/payload/server/public/assets/{time-entry-format-CsYAjplS.js → time-entry-format-DPOBIE18.js} +1 -1
- package/payload/server/public/assets/{triangle-alert-0lSBIUg-.js → triangle-alert-Dz-CZKo5.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-jTBQ2h0V.js → useCopyFeedback-wtUHGohk.js} +1 -1
- package/payload/server/public/assets/{useSelectionMode-BooArySZ.js → useSelectionMode-sph7AQT5.js} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-BRIluIKf.css +1 -0
- package/payload/server/public/assets/{useVoiceRecorder-PUT8q_sm.js → useVoiceRecorder-DN4jYQ0A.js} +1 -1
- package/payload/server/public/brand-defaults.css +1 -0
- package/payload/server/public/browser.html +5 -5
- package/payload/server/public/calendar.html +6 -6
- package/payload/server/public/chat.html +12 -12
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +11 -11
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/privacy.html +1 -0
- package/payload/server/public/public.html +12 -12
- package/payload/server/public/tasks.html +6 -5
- package/payload/server/public/vnc-popout.html +1 -0
- package/payload/server/server.js +741 -366
- package/payload/server/public/assets/AdminShell-D9zaUxPd.js +0 -2
- package/payload/server/public/assets/Checkbox-DcbbAIe5.js +0 -1
- package/payload/server/public/assets/activity-C9P5NjEZ.js +0 -1
- package/payload/server/public/assets/chat-RyH-WYQC.js +0 -1
- package/payload/server/public/assets/chevron-left-DmXCr6nB.js +0 -1
- package/payload/server/public/assets/data-BaY_LkLR.js +0 -1
- package/payload/server/public/assets/graph-labels-DD-GqS4v.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-D9knHOKq.css +0 -1
- /package/payload/server/public/assets/{useSubAccountSwitcher-BZnzc5C3.js → useSubAccountSwitcher-v8LyBzZN.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -652,7 +652,17 @@ var require_dist3 = __commonJS({
|
|
|
652
652
|
Risk: "#C15B57",
|
|
653
653
|
Source: "#2F8FA6",
|
|
654
654
|
Finding: "#4FA389",
|
|
655
|
-
Hypothesis: "#9A8C3E"
|
|
655
|
+
Hypothesis: "#9A8C3E",
|
|
656
|
+
// Ledger cash movement (schema-base.md, added by 7b225f814). One movement
|
|
657
|
+
// of money attached to no invoice — operator-entry with no parent to drill
|
|
658
|
+
// down from, so it is a top-level chip. Burnished olive-gold sits in the
|
|
659
|
+
// money register beside Invoice's brown without colliding with it.
|
|
660
|
+
//
|
|
661
|
+
// Deliberately appended at the END rather than beside the ledger family:
|
|
662
|
+
// SHAPE_BY_LABEL assigns shapes by index position in ALL_GRAPH_LABELS
|
|
663
|
+
// (this object's key order), so a mid-table insert would silently change
|
|
664
|
+
// the rendered shape of every label after it.
|
|
665
|
+
CashEntry: "#7D5A2B"
|
|
656
666
|
};
|
|
657
667
|
exports.FALLBACK_LABEL_COLOUR = "#94A3B8";
|
|
658
668
|
exports.ALL_GRAPH_LABELS = Object.freeze(Object.keys(exports.GRAPH_LABEL_COLOURS));
|
|
@@ -749,7 +759,8 @@ var require_dist3 = __commonJS({
|
|
|
749
759
|
Risk: '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"></path><path d="M12 9v4"></path><path d="M12 17h.01"></path>',
|
|
750
760
|
Source: '<path d="M12 7v14"></path><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"></path>',
|
|
751
761
|
Finding: '<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"></path><path d="M9 18h6"></path><path d="M10 22h4"></path>',
|
|
752
|
-
Hypothesis: '<path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"></path><path d="M6.453 15h11.094"></path><path d="M8.5 2h7"></path>'
|
|
762
|
+
Hypothesis: '<path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"></path><path d="M6.453 15h11.094"></path><path d="M8.5 2h7"></path>',
|
|
763
|
+
CashEntry: '<rect width="20" height="12" x="2" y="6" rx="2"></rect><circle cx="12" cy="12" r="2"></circle><path d="M6 12h.01"></path><path d="M18 12h.01"></path>'
|
|
753
764
|
};
|
|
754
765
|
function iconForLabel2(label) {
|
|
755
766
|
return Object.prototype.hasOwnProperty.call(LABEL_ICONS, label) ? LABEL_ICONS[label] : null;
|
|
@@ -845,6 +856,11 @@ var require_dist3 = __commonJS({
|
|
|
845
856
|
if (clientName.length > 0) {
|
|
846
857
|
return clientName.length > 24 ? clientName.slice(0, 24) + "\u2026" : clientName;
|
|
847
858
|
}
|
|
859
|
+
} else if (primaryLabel2 === "CashEntry") {
|
|
860
|
+
const desc = typeof props.description === "string" ? props.description : "";
|
|
861
|
+
if (desc.length > 0) {
|
|
862
|
+
return desc.length > 24 ? desc.slice(0, 24) + "\u2026" : desc;
|
|
863
|
+
}
|
|
848
864
|
} else if (primaryLabel2 === "Message") {
|
|
849
865
|
const role = typeof props.role === "string" ? props.role : "";
|
|
850
866
|
const roleShort = role === "user" ? "user" : role === "assistant" ? "asst" : role === "system" ? "sys" : role === "tool" ? "tool" : null;
|
|
@@ -918,6 +934,10 @@ var require_dist3 = __commonJS({
|
|
|
918
934
|
const clientName = typeof props.clientName === "string" ? props.clientName : "";
|
|
919
935
|
if (clientName.length > 0)
|
|
920
936
|
return clientName;
|
|
937
|
+
} else if (primaryLabel2 === "CashEntry") {
|
|
938
|
+
const desc = typeof props.description === "string" ? props.description : "";
|
|
939
|
+
if (desc.length > 0)
|
|
940
|
+
return desc;
|
|
921
941
|
}
|
|
922
942
|
for (const k of ["name", "title", "summary", "givenName", "subject", "text"]) {
|
|
923
943
|
const v = props[k];
|
|
@@ -1291,8 +1311,8 @@ var require_dist4 = __commonJS({
|
|
|
1291
1311
|
let edgesCreated = 0;
|
|
1292
1312
|
for (const rel of relationships) {
|
|
1293
1313
|
const type = rel.type.replace(/`/g, "");
|
|
1294
|
-
const
|
|
1295
|
-
const r = await tx.run(
|
|
1314
|
+
const q2 = rel.direction === "outgoing" ? `MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to CREATE (a)-[:\`${type}\`]->(b)` : `MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to CREATE (b)-[:\`${type}\`]->(a)`;
|
|
1315
|
+
const r = await tx.run(q2, { from: nodeId, to: rel.targetNodeId });
|
|
1296
1316
|
const created = r.summary.counters.updates().relationshipsCreated;
|
|
1297
1317
|
if (created === 0) {
|
|
1298
1318
|
process.stderr.write(`[graph-write] reject reason=unresolved-target-on-create labels=${labelCsv} agent=${agentLabel} relType=${rel.type} targetId=${rel.targetNodeId}
|
|
@@ -1540,10 +1560,10 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1540
1560
|
};
|
|
1541
1561
|
|
|
1542
1562
|
// server/index.ts
|
|
1543
|
-
import { readFileSync as
|
|
1563
|
+
import { readFileSync as readFileSync42, existsSync as existsSync38, watchFile, readdirSync as readdirSync26, statSync as statSync17 } from "fs";
|
|
1544
1564
|
import { spawn as spawn3 } from "child_process";
|
|
1545
1565
|
import { createHash as createHash7 } from "crypto";
|
|
1546
|
-
import { resolve as
|
|
1566
|
+
import { resolve as resolve37, join as join44, basename as basename12 } from "path";
|
|
1547
1567
|
import { homedir as homedir4 } from "os";
|
|
1548
1568
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
1549
1569
|
|
|
@@ -1958,14 +1978,14 @@ function readAllConversationsCached(accountId) {
|
|
|
1958
1978
|
for (const file of dir) {
|
|
1959
1979
|
if (!file.endsWith(".jsonl")) continue;
|
|
1960
1980
|
const path3 = join4(STORE_ROOT(), accountId, file);
|
|
1961
|
-
let
|
|
1981
|
+
let stat9;
|
|
1962
1982
|
try {
|
|
1963
|
-
|
|
1983
|
+
stat9 = statSync3(path3);
|
|
1964
1984
|
} catch {
|
|
1965
1985
|
continue;
|
|
1966
1986
|
}
|
|
1967
1987
|
const hit = idCache.get(path3);
|
|
1968
|
-
if (hit && hit.size ===
|
|
1988
|
+
if (hit && hit.size === stat9.size && hit.mtimeMs === stat9.mtimeMs) {
|
|
1969
1989
|
out.set(file, hit.ids);
|
|
1970
1990
|
continue;
|
|
1971
1991
|
}
|
|
@@ -1982,7 +2002,7 @@ function readAllConversationsCached(accountId) {
|
|
|
1982
2002
|
} catch {
|
|
1983
2003
|
continue;
|
|
1984
2004
|
}
|
|
1985
|
-
idCache.set(path3, { size:
|
|
2005
|
+
idCache.set(path3, { size: stat9.size, mtimeMs: stat9.mtimeMs, ids: parsed });
|
|
1986
2006
|
out.set(file, parsed);
|
|
1987
2007
|
}
|
|
1988
2008
|
return out;
|
|
@@ -2816,7 +2836,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
2816
2836
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
2817
2837
|
console.error(`${TAG6} draining credential save queue\u2026`);
|
|
2818
2838
|
const timer = new Promise(
|
|
2819
|
-
(
|
|
2839
|
+
(resolve38) => setTimeout(() => resolve38("timeout"), timeoutMs)
|
|
2820
2840
|
);
|
|
2821
2841
|
const result = await Promise.race([
|
|
2822
2842
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -2993,11 +3013,11 @@ async function createWaSocket(opts) {
|
|
|
2993
3013
|
return sock;
|
|
2994
3014
|
}
|
|
2995
3015
|
async function waitForConnection(sock) {
|
|
2996
|
-
return new Promise((
|
|
3016
|
+
return new Promise((resolve38, reject) => {
|
|
2997
3017
|
const handler = (update) => {
|
|
2998
3018
|
if (update.connection === "open") {
|
|
2999
3019
|
sock.ev.off("connection.update", handler);
|
|
3000
|
-
|
|
3020
|
+
resolve38();
|
|
3001
3021
|
}
|
|
3002
3022
|
if (update.connection === "close") {
|
|
3003
3023
|
sock.ev.off("connection.update", handler);
|
|
@@ -3105,7 +3125,7 @@ function isTimeoutError(err) {
|
|
|
3105
3125
|
return Boolean(err?.__maxyTimeout);
|
|
3106
3126
|
}
|
|
3107
3127
|
function withTimeout(label, promise, timeoutMs) {
|
|
3108
|
-
return new Promise((
|
|
3128
|
+
return new Promise((resolve38, reject) => {
|
|
3109
3129
|
const timer = setTimeout(() => {
|
|
3110
3130
|
const err = new Error(`${label} timed out after ${timeoutMs}ms`);
|
|
3111
3131
|
err.__maxyTimeout = true;
|
|
@@ -3115,7 +3135,7 @@ function withTimeout(label, promise, timeoutMs) {
|
|
|
3115
3135
|
promise.then(
|
|
3116
3136
|
(value) => {
|
|
3117
3137
|
clearTimeout(timer);
|
|
3118
|
-
|
|
3138
|
+
resolve38(value);
|
|
3119
3139
|
},
|
|
3120
3140
|
(err) => {
|
|
3121
3141
|
clearTimeout(timer);
|
|
@@ -4029,15 +4049,15 @@ function getChannelActivity(accountId) {
|
|
|
4029
4049
|
|
|
4030
4050
|
// app/lib/whatsapp/outbound/send.ts
|
|
4031
4051
|
var TAG10 = "[whatsapp:outbound]";
|
|
4032
|
-
function buildQuoted(
|
|
4052
|
+
function buildQuoted(q2) {
|
|
4033
4053
|
return {
|
|
4034
4054
|
key: {
|
|
4035
|
-
remoteJid:
|
|
4036
|
-
id:
|
|
4037
|
-
fromMe:
|
|
4038
|
-
...
|
|
4055
|
+
remoteJid: q2.remoteJid,
|
|
4056
|
+
id: q2.id,
|
|
4057
|
+
fromMe: q2.fromMe,
|
|
4058
|
+
...q2.participant ? { participant: q2.participant } : {}
|
|
4039
4059
|
},
|
|
4040
|
-
message: { conversation:
|
|
4060
|
+
message: { conversation: q2.body ?? "" }
|
|
4041
4061
|
};
|
|
4042
4062
|
}
|
|
4043
4063
|
async function sendTextMessage(sock, to, text, opts) {
|
|
@@ -4155,8 +4175,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
4155
4175
|
const { givenName, familyName } = splitName(input.pushName);
|
|
4156
4176
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
4157
4177
|
let release;
|
|
4158
|
-
const mine = new Promise((
|
|
4159
|
-
release =
|
|
4178
|
+
const mine = new Promise((resolve38) => {
|
|
4179
|
+
release = resolve38;
|
|
4160
4180
|
});
|
|
4161
4181
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
4162
4182
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -5860,11 +5880,11 @@ async function connectWithReconnect(conn) {
|
|
|
5860
5880
|
console.error(
|
|
5861
5881
|
`${TAG17} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
5862
5882
|
);
|
|
5863
|
-
await new Promise((
|
|
5864
|
-
const timer = setTimeout(
|
|
5883
|
+
await new Promise((resolve38) => {
|
|
5884
|
+
const timer = setTimeout(resolve38, delay);
|
|
5865
5885
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
5866
5886
|
clearTimeout(timer);
|
|
5867
|
-
|
|
5887
|
+
resolve38();
|
|
5868
5888
|
}, { once: true });
|
|
5869
5889
|
});
|
|
5870
5890
|
}
|
|
@@ -5872,16 +5892,16 @@ async function connectWithReconnect(conn) {
|
|
|
5872
5892
|
}
|
|
5873
5893
|
}
|
|
5874
5894
|
function waitForDisconnectEvent(conn) {
|
|
5875
|
-
return new Promise((
|
|
5895
|
+
return new Promise((resolve38) => {
|
|
5876
5896
|
if (!conn.sock) {
|
|
5877
|
-
|
|
5897
|
+
resolve38();
|
|
5878
5898
|
return;
|
|
5879
5899
|
}
|
|
5880
5900
|
const sock = conn.sock;
|
|
5881
5901
|
const handler = (update) => {
|
|
5882
5902
|
if (update.connection === "close") {
|
|
5883
5903
|
sock.ev.off("connection.update", handler);
|
|
5884
|
-
|
|
5904
|
+
resolve38();
|
|
5885
5905
|
}
|
|
5886
5906
|
};
|
|
5887
5907
|
sock.ev.on("connection.update", handler);
|
|
@@ -6291,8 +6311,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6291
6311
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
6292
6312
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
6293
6313
|
let resolvePending;
|
|
6294
|
-
const sttPending = new Promise((
|
|
6295
|
-
resolvePending =
|
|
6314
|
+
const sttPending = new Promise((resolve38) => {
|
|
6315
|
+
resolvePending = resolve38;
|
|
6296
6316
|
});
|
|
6297
6317
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
6298
6318
|
try {
|
|
@@ -6720,20 +6740,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
6720
6740
|
|
|
6721
6741
|
// server/routes/health.ts
|
|
6722
6742
|
function checkPort(port2, timeoutMs = 500) {
|
|
6723
|
-
return new Promise((
|
|
6743
|
+
return new Promise((resolve38) => {
|
|
6724
6744
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
6725
6745
|
socket.setTimeout(timeoutMs);
|
|
6726
6746
|
socket.once("connect", () => {
|
|
6727
6747
|
socket.destroy();
|
|
6728
|
-
|
|
6748
|
+
resolve38(true);
|
|
6729
6749
|
});
|
|
6730
6750
|
socket.once("error", () => {
|
|
6731
6751
|
socket.destroy();
|
|
6732
|
-
|
|
6752
|
+
resolve38(false);
|
|
6733
6753
|
});
|
|
6734
6754
|
socket.once("timeout", () => {
|
|
6735
6755
|
socket.destroy();
|
|
6736
|
-
|
|
6756
|
+
resolve38(false);
|
|
6737
6757
|
});
|
|
6738
6758
|
});
|
|
6739
6759
|
}
|
|
@@ -6988,8 +7008,8 @@ function webchatTurnTimeoutMs() {
|
|
|
6988
7008
|
return Number(process.env.WEBCHAT_TURN_TIMEOUT_MS ?? String(2 * 6e4));
|
|
6989
7009
|
}
|
|
6990
7010
|
function createChatRoutes(deps) {
|
|
6991
|
-
const
|
|
6992
|
-
|
|
7011
|
+
const app65 = new Hono();
|
|
7012
|
+
app65.post("/", async (c) => {
|
|
6993
7013
|
console.log(`[chat-route] entered route=public method=POST`);
|
|
6994
7014
|
const contentType = c.req.header("content-type") ?? "";
|
|
6995
7015
|
const account = resolveAccount();
|
|
@@ -7253,7 +7273,7 @@ ${result.result.text}` : result.result.text;
|
|
|
7253
7273
|
}
|
|
7254
7274
|
});
|
|
7255
7275
|
});
|
|
7256
|
-
return
|
|
7276
|
+
return app65;
|
|
7257
7277
|
}
|
|
7258
7278
|
|
|
7259
7279
|
// app/lib/channel-pty-bridge/admin-session-id.ts
|
|
@@ -7287,8 +7307,8 @@ function isLoopbackAddr(addr) {
|
|
|
7287
7307
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
7288
7308
|
}
|
|
7289
7309
|
function createScheduleInjectRoutes(deps) {
|
|
7290
|
-
const
|
|
7291
|
-
|
|
7310
|
+
const app65 = new Hono();
|
|
7311
|
+
app65.post("/", async (c) => {
|
|
7292
7312
|
const env = c.env;
|
|
7293
7313
|
const remoteAddr = env?.incoming?.socket?.remoteAddress ?? "";
|
|
7294
7314
|
if (!isLoopbackAddr(remoteAddr)) {
|
|
@@ -7389,7 +7409,7 @@ function createScheduleInjectRoutes(deps) {
|
|
|
7389
7409
|
console.error(`${TAG20} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=ok status=-`);
|
|
7390
7410
|
return c.json({ ok: true }, 200);
|
|
7391
7411
|
});
|
|
7392
|
-
return
|
|
7412
|
+
return app65;
|
|
7393
7413
|
}
|
|
7394
7414
|
|
|
7395
7415
|
// server/index.ts
|
|
@@ -8022,8 +8042,8 @@ async function startLogin(opts) {
|
|
|
8022
8042
|
await clearAuth(authDir);
|
|
8023
8043
|
let resolveCode = null;
|
|
8024
8044
|
let rejectCode = null;
|
|
8025
|
-
const codePromise = new Promise((
|
|
8026
|
-
resolveCode =
|
|
8045
|
+
const codePromise = new Promise((resolve38, reject) => {
|
|
8046
|
+
resolveCode = resolve38;
|
|
8027
8047
|
rejectCode = reject;
|
|
8028
8048
|
});
|
|
8029
8049
|
const codeTimer = setTimeout(
|
|
@@ -11337,12 +11357,12 @@ app5.get("/store-stream", requireAdminSession, (c) => {
|
|
|
11337
11357
|
...reactions && reactions.length > 0 ? { reactions } : {}
|
|
11338
11358
|
};
|
|
11339
11359
|
if (r.quotedId) {
|
|
11340
|
-
const
|
|
11360
|
+
const q2 = byMsgKeyId.get(r.quotedId);
|
|
11341
11361
|
turn = {
|
|
11342
11362
|
...turn,
|
|
11343
|
-
quoted:
|
|
11363
|
+
quoted: q2 ? { body: q2.body, sender: q2.senderName ?? (q2.fromMe ? null : q2.senderTelephone || null), fromMe: q2.fromMe } : { body: "", sender: r.quotedSender, fromMe: false }
|
|
11344
11364
|
};
|
|
11345
|
-
console.log(`[wa-reader] op=store-quote msgId=${r.msgKeyId} quoted=${!!
|
|
11365
|
+
console.log(`[wa-reader] op=store-quote msgId=${r.msgKeyId} quoted=${!!q2}`);
|
|
11346
11366
|
}
|
|
11347
11367
|
if (r.attachmentId) {
|
|
11348
11368
|
const meta = readAttachmentMeta(join19(uploadsBase, r.attachmentId));
|
|
@@ -12252,10 +12272,7 @@ function locateSidecar(sessionId) {
|
|
|
12252
12272
|
}
|
|
12253
12273
|
for (const slug of slugs) {
|
|
12254
12274
|
const metaPath = join23(projectsRoot, slug, `${sessionId}.meta.json`);
|
|
12255
|
-
if (existsSync15(metaPath))
|
|
12256
|
-
const meta = readSidecarMeta(metaPath);
|
|
12257
|
-
return { channel: meta.channel, adminUserId: meta.adminUserId, accountId: meta.accountId };
|
|
12258
|
-
}
|
|
12275
|
+
if (existsSync15(metaPath)) return readSidecarMeta(metaPath);
|
|
12259
12276
|
}
|
|
12260
12277
|
return null;
|
|
12261
12278
|
}
|
|
@@ -12268,6 +12285,13 @@ function readTargetOwner(sessionId) {
|
|
|
12268
12285
|
}
|
|
12269
12286
|
return { found: false, owner: null };
|
|
12270
12287
|
}
|
|
12288
|
+
function readSourceRowMeta(sessionId) {
|
|
12289
|
+
const sidecar = locateSidecar(sessionId);
|
|
12290
|
+
if (sidecar !== null) return sidecar;
|
|
12291
|
+
const { projectDir } = locateSession(sessionId);
|
|
12292
|
+
if (projectDir !== null) return readSidecarMeta(join23(projectDir, `${sessionId}.meta.json`));
|
|
12293
|
+
return null;
|
|
12294
|
+
}
|
|
12271
12295
|
function ownedByRequester(owner, requesterUserId, primaryUserId) {
|
|
12272
12296
|
if (requesterUserId === null) return false;
|
|
12273
12297
|
return owner === requesterUserId || owner === null && requesterUserId === primaryUserId;
|
|
@@ -12476,8 +12500,8 @@ async function reapplyLeversViaManager() {
|
|
|
12476
12500
|
var WEBCHAT_SEND_TURN_WINDOW_MS = Number(process.env.WEBCHAT_SEND_TURN_WINDOW_MS ?? String(15e3));
|
|
12477
12501
|
var sendSeq = 0;
|
|
12478
12502
|
function createWebchatRoutes(deps) {
|
|
12479
|
-
const
|
|
12480
|
-
|
|
12503
|
+
const app65 = new Hono();
|
|
12504
|
+
app65.post("/send", requireAdminSession, async (c) => {
|
|
12481
12505
|
let accountId;
|
|
12482
12506
|
try {
|
|
12483
12507
|
accountId = resolvePlatformAccountId();
|
|
@@ -12689,7 +12713,7 @@ ${note}` : note;
|
|
|
12689
12713
|
if (turnTimer.unref) turnTimer.unref();
|
|
12690
12714
|
return c.json({ ok: true });
|
|
12691
12715
|
});
|
|
12692
|
-
|
|
12716
|
+
app65.post("/interrupt", requireAdminSession, async (c) => {
|
|
12693
12717
|
const cacheKey = c.get("cacheKey");
|
|
12694
12718
|
const requesterUserId = (cacheKey ? getUserIdForSession(cacheKey) : void 0) ?? null;
|
|
12695
12719
|
const primaryUserId = resolvePrimaryAdminUserId();
|
|
@@ -12717,7 +12741,7 @@ ${note}` : note;
|
|
|
12717
12741
|
}
|
|
12718
12742
|
return c.json({ ok: true, stopped: outcome.stopped, intId: outcome.intId, deadChild: outcome.deadChild }, 200);
|
|
12719
12743
|
});
|
|
12720
|
-
|
|
12744
|
+
app65.post("/settings", requireAdminSession, async (c) => {
|
|
12721
12745
|
const body = await c.req.json().catch(() => null);
|
|
12722
12746
|
const op = body?.op;
|
|
12723
12747
|
const value = body?.value;
|
|
@@ -12757,7 +12781,7 @@ ${note}` : note;
|
|
|
12757
12781
|
console.log(`[webchat:settings] op=${op} outcome=accepted value=${value} settingsApplied=${settingsApplied}`);
|
|
12758
12782
|
return c.json({ ok: true, settingsApplied });
|
|
12759
12783
|
});
|
|
12760
|
-
|
|
12784
|
+
app65.get("/session", requireAdminSession, async (c) => {
|
|
12761
12785
|
let accountId;
|
|
12762
12786
|
try {
|
|
12763
12787
|
accountId = resolvePlatformAccountId();
|
|
@@ -12835,7 +12859,7 @@ ${note}` : note;
|
|
|
12835
12859
|
const indicators = await fetchComposerIndicators(sessionId);
|
|
12836
12860
|
return c.json({ sessionId, projectDir, source, sizeBytes: jsonlSizeBytes(projectDir, sessionId), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${sessionId}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${sessionId}`) ?? null, ...indicators, ...readLevers(account) });
|
|
12837
12861
|
});
|
|
12838
|
-
|
|
12862
|
+
app65.post("/permission-verdict", requireAdminSession, async (c) => {
|
|
12839
12863
|
const body = await c.req.json().catch(() => null);
|
|
12840
12864
|
const sessionId = body?.sessionId;
|
|
12841
12865
|
const requestId = body?.request_id;
|
|
@@ -12852,7 +12876,7 @@ ${note}` : note;
|
|
|
12852
12876
|
const ok = deps.resolvePermissionVerdict?.(`session:${sessionId}`, requestId, behavior) ?? false;
|
|
12853
12877
|
return c.json({ ok });
|
|
12854
12878
|
});
|
|
12855
|
-
return
|
|
12879
|
+
return app65;
|
|
12856
12880
|
}
|
|
12857
12881
|
|
|
12858
12882
|
// server/routes/webchat-greeting.ts
|
|
@@ -14208,15 +14232,15 @@ function operatorRoleFor(config, userId) {
|
|
|
14208
14232
|
async function buildAccountOptionList(accounts, userId) {
|
|
14209
14233
|
const houseAccount = accounts.find((a) => a.config.role === "house");
|
|
14210
14234
|
console.log(`[admin-accounts] op=list count=${accounts.length} house=${houseAccount?.accountId ?? "none"}`);
|
|
14211
|
-
const
|
|
14235
|
+
const resolve38 = async (a) => {
|
|
14212
14236
|
const businessName = await fetchAccountName(a.accountId) || void 0;
|
|
14213
14237
|
return { accountId: a.accountId, businessName, role: operatorRoleFor(a.config, userId), isHouse: a.config.role === "house" };
|
|
14214
14238
|
};
|
|
14215
14239
|
const houseOptions = await Promise.all(
|
|
14216
|
-
accounts.filter((a) => a.config.role === "house").map(
|
|
14240
|
+
accounts.filter((a) => a.config.role === "house").map(resolve38)
|
|
14217
14241
|
);
|
|
14218
14242
|
const nonHouseOptions = await Promise.all(
|
|
14219
|
-
accounts.filter((a) => a.config.role !== "house").map(
|
|
14243
|
+
accounts.filter((a) => a.config.role !== "house").map(resolve38)
|
|
14220
14244
|
);
|
|
14221
14245
|
const sortKey = (o) => (o.businessName || o.accountId).toLowerCase();
|
|
14222
14246
|
nonHouseOptions.sort((x, y) => {
|
|
@@ -15223,9 +15247,9 @@ app18.post("/:id/resume", async (c) => {
|
|
|
15223
15247
|
if (healQueue.length > 0) {
|
|
15224
15248
|
void (async () => {
|
|
15225
15249
|
for (let i = 0; i < healQueue.length; i++) {
|
|
15226
|
-
const
|
|
15250
|
+
const q2 = healQueue[i];
|
|
15227
15251
|
try {
|
|
15228
|
-
const healComponents = await Promise.all(
|
|
15252
|
+
const healComponents = await Promise.all(q2.components.map(async (c2) => {
|
|
15229
15253
|
const base = {
|
|
15230
15254
|
componentId: c2.componentId,
|
|
15231
15255
|
name: c2.name,
|
|
@@ -15263,25 +15287,25 @@ app18.post("/:id/resume", async (c) => {
|
|
|
15263
15287
|
}));
|
|
15264
15288
|
const messageId = await persistMessage(
|
|
15265
15289
|
sessionId,
|
|
15266
|
-
|
|
15267
|
-
|
|
15290
|
+
q2.role,
|
|
15291
|
+
q2.content,
|
|
15268
15292
|
accountId,
|
|
15269
15293
|
void 0,
|
|
15270
|
-
|
|
15294
|
+
q2.createdAt,
|
|
15271
15295
|
void 0,
|
|
15272
15296
|
healComponents,
|
|
15273
15297
|
void 0
|
|
15274
15298
|
);
|
|
15275
15299
|
jsonlHealOk += 1;
|
|
15276
15300
|
try {
|
|
15277
|
-
streamLogPath.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${sessionId.slice(0, 8)} turnIndex=${i} role=${
|
|
15301
|
+
streamLogPath.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${sessionId.slice(0, 8)} turnIndex=${i} role=${q2.role} outcome=ok messageId=${(messageId ?? "").slice(0, 8)}
|
|
15278
15302
|
`);
|
|
15279
15303
|
} catch {
|
|
15280
15304
|
}
|
|
15281
15305
|
} catch (err) {
|
|
15282
15306
|
jsonlHealFail += 1;
|
|
15283
15307
|
try {
|
|
15284
|
-
streamLogPath.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${sessionId.slice(0, 8)} turnIndex=${i} role=${
|
|
15308
|
+
streamLogPath.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${sessionId.slice(0, 8)} turnIndex=${i} role=${q2.role} outcome=fail reason=${JSON.stringify((err instanceof Error ? err.message : String(err)).slice(0, 200))}
|
|
15285
15309
|
`);
|
|
15286
15310
|
} catch {
|
|
15287
15311
|
}
|
|
@@ -15606,10 +15630,10 @@ function computeSpecialistDomains(accountId) {
|
|
|
15606
15630
|
const resolveDesc = (qualified) => {
|
|
15607
15631
|
if (!qualified.startsWith("mcp__")) return void 0;
|
|
15608
15632
|
const rest = qualified.slice(5);
|
|
15609
|
-
const
|
|
15610
|
-
if (
|
|
15611
|
-
const plugin = rest.slice(0,
|
|
15612
|
-
const tool = rest.slice(
|
|
15633
|
+
const sep10 = rest.indexOf("__");
|
|
15634
|
+
if (sep10 < 0) return void 0;
|
|
15635
|
+
const plugin = rest.slice(0, sep10);
|
|
15636
|
+
const tool = rest.slice(sep10 + 2);
|
|
15613
15637
|
let map = descByPlugin.get(plugin);
|
|
15614
15638
|
if (!map) {
|
|
15615
15639
|
const dir = pluginDirs.get(plugin);
|
|
@@ -15912,18 +15936,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
15912
15936
|
]);
|
|
15913
15937
|
var app21 = new Hono();
|
|
15914
15938
|
app21.post("/", async (c) => {
|
|
15915
|
-
const
|
|
15939
|
+
const TAG54 = "[admin:events]";
|
|
15916
15940
|
let body;
|
|
15917
15941
|
try {
|
|
15918
15942
|
body = await c.req.json();
|
|
15919
15943
|
} catch (err) {
|
|
15920
15944
|
const detail = err instanceof Error ? err.message : String(err);
|
|
15921
|
-
console.error(`${
|
|
15945
|
+
console.error(`${TAG54} reject reason=body-not-json detail=${detail}`);
|
|
15922
15946
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
15923
15947
|
}
|
|
15924
15948
|
const event = typeof body.event === "string" ? body.event : "";
|
|
15925
15949
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
15926
|
-
console.error(`${
|
|
15950
|
+
console.error(`${TAG54} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
15927
15951
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
15928
15952
|
}
|
|
15929
15953
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -18564,7 +18588,7 @@ var CONVERSATION_PARENT_LABELS = /* @__PURE__ */ new Set(["AdminConversation", "
|
|
|
18564
18588
|
var app23 = new Hono();
|
|
18565
18589
|
app23.get("/", requireAdminSession, async (c) => {
|
|
18566
18590
|
const cacheKey = c.var.cacheKey;
|
|
18567
|
-
const
|
|
18591
|
+
const q2 = (c.req.query("q") ?? "").trim();
|
|
18568
18592
|
const rawLimit = c.req.query("limit");
|
|
18569
18593
|
const rawLabels = c.req.query("labels");
|
|
18570
18594
|
const rawThreshold = c.req.query("threshold");
|
|
@@ -18573,13 +18597,13 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18573
18597
|
console.error(`[graph-search] auth-rejected endpoint="/api/admin/graph-search" reason="no account for session"`);
|
|
18574
18598
|
return c.json({ error: "Account not found for session" }, 401);
|
|
18575
18599
|
}
|
|
18576
|
-
if (!
|
|
18600
|
+
if (!q2) return c.json({ error: "q (search query) required" }, 400);
|
|
18577
18601
|
const labels = rawLabels ? rawLabels.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : [];
|
|
18578
18602
|
const wildcard = labels.includes("*");
|
|
18579
18603
|
const noChips = labels.length === 0;
|
|
18580
18604
|
const allLabels = wildcard || noChips;
|
|
18581
18605
|
if (noChips && !wildcard) {
|
|
18582
|
-
console.error(`[graph-search] op=labels-default applied=all query="${
|
|
18606
|
+
console.error(`[graph-search] op=labels-default applied=all query="${q2}"`);
|
|
18583
18607
|
}
|
|
18584
18608
|
const parsedLimit = rawLimit ? parseInt(rawLimit, 10) : DEFAULT_LIMIT;
|
|
18585
18609
|
const limit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, MAX_LIMIT) : DEFAULT_LIMIT;
|
|
@@ -18599,7 +18623,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18599
18623
|
const session = getSession();
|
|
18600
18624
|
try {
|
|
18601
18625
|
const res = await hybrid(session, embed, {
|
|
18602
|
-
query:
|
|
18626
|
+
query: q2,
|
|
18603
18627
|
accountId,
|
|
18604
18628
|
labels: forwardedLabels,
|
|
18605
18629
|
limit,
|
|
@@ -18609,7 +18633,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18609
18633
|
const total = Date.now() - started;
|
|
18610
18634
|
if (res.embedError) {
|
|
18611
18635
|
console.error(`[graph-search] embed-unavailable err="${res.embedError}" \u2014 bm25-only`);
|
|
18612
|
-
console.error(`[graph-search] embed-degraded query="${
|
|
18636
|
+
console.error(`[graph-search] embed-degraded query="${q2}" results=${res.results.length}`);
|
|
18613
18637
|
}
|
|
18614
18638
|
let labelMatchCount = 0;
|
|
18615
18639
|
let bodyFulltextCount = 0;
|
|
@@ -18660,16 +18684,16 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18660
18684
|
}
|
|
18661
18685
|
resolvedResults = Array.from(seen.values()).sort((a, b) => b.score - a.score);
|
|
18662
18686
|
console.error(
|
|
18663
|
-
`[graph-search] label-match query="${
|
|
18687
|
+
`[graph-search] label-match query="${q2}" hits=${labelMatchCount}`
|
|
18664
18688
|
);
|
|
18665
18689
|
console.error(
|
|
18666
|
-
`[graph-search] body-fulltext query="${
|
|
18690
|
+
`[graph-search] body-fulltext query="${q2}" hits=${bodyFulltextCount} ms=${total}`
|
|
18667
18691
|
);
|
|
18668
18692
|
}
|
|
18669
18693
|
const labelsToken = wildcard ? "*" : noChips ? "all" : labels.join(",");
|
|
18670
18694
|
const expandedFlag = !wildcard && wantsBodyFulltext ? 1 : 0;
|
|
18671
18695
|
console.error(
|
|
18672
|
-
`[graph-search] query="${
|
|
18696
|
+
`[graph-search] query="${q2}" labels=${labelsToken} expanded=${expandedFlag} limit=${limit} mode=${res.mode} raw-results=${res.results.length} resolved-results=${resolvedResults.length} expand-ms=${res.expandMs} total-ms=${total}`
|
|
18673
18697
|
);
|
|
18674
18698
|
console.error(
|
|
18675
18699
|
`[graph-search] threshold=${res.threshold ?? "off"} raw-merged=${res.rawMerged} suppressed=${res.suppressed} rendered=${res.results.length} bm25-bypass=${res.bm25Bypass}`
|
|
@@ -18692,7 +18716,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18692
18716
|
} catch (err) {
|
|
18693
18717
|
const elapsed = Date.now() - started;
|
|
18694
18718
|
const message = err instanceof Error ? err.message : String(err);
|
|
18695
|
-
console.error(`[graph-search] neo4j-unreachable query="${
|
|
18719
|
+
console.error(`[graph-search] neo4j-unreachable query="${q2}" ms=${elapsed} err="${message}"`);
|
|
18696
18720
|
return c.json({ error: `Graph search unavailable: ${message}` }, 503);
|
|
18697
18721
|
} finally {
|
|
18698
18722
|
await session.close();
|
|
@@ -18759,7 +18783,11 @@ var FILTER_TOP_LEVEL_LABELS = Object.freeze(
|
|
|
18759
18783
|
"Risk",
|
|
18760
18784
|
"Source",
|
|
18761
18785
|
"Finding",
|
|
18762
|
-
"Hypothesis"
|
|
18786
|
+
"Hypothesis",
|
|
18787
|
+
// Base-schema ledger (schema-base.md, Task 1819). A cash movement
|
|
18788
|
+
// attached to no invoice — its only edges are outbound, so nothing
|
|
18789
|
+
// drills down to it and a chip is the sole way to reach it.
|
|
18790
|
+
"CashEntry"
|
|
18763
18791
|
])
|
|
18764
18792
|
);
|
|
18765
18793
|
var AGENT_ACTION_LABELS = Object.freeze(
|
|
@@ -18810,6 +18838,17 @@ var STATIC_TOP_LEVEL_LABELS = Object.freeze(
|
|
|
18810
18838
|
"Review",
|
|
18811
18839
|
"ImageObject",
|
|
18812
18840
|
"Invoice",
|
|
18841
|
+
// Supplier invoice. Declared in schema-base.md's `## Node Types` table
|
|
18842
|
+
// (promoted there from schema-construction.md's top-level table by
|
|
18843
|
+
// 7b225f814), which this module never parses — so it is static, not
|
|
18844
|
+
// derived. Top-level because it is operator-entry with a natural key
|
|
18845
|
+
// (accountId, supplier, confirmationNumber), unlike its InvoiceLine /
|
|
18846
|
+
// InvoicePayment / Credit children.
|
|
18847
|
+
"InboundInvoice",
|
|
18848
|
+
// Ledger cash movement, declared in schema-base.md. Operator-entry with
|
|
18849
|
+
// a natural key (accountId, entryId) and no parent label, so it is
|
|
18850
|
+
// reachable only as a chip.
|
|
18851
|
+
"CashEntry",
|
|
18813
18852
|
"Task",
|
|
18814
18853
|
"Project",
|
|
18815
18854
|
"Event",
|
|
@@ -18860,8 +18899,8 @@ function parseTopLevelLabels(content) {
|
|
|
18860
18899
|
const line = lines[i];
|
|
18861
18900
|
if (line.startsWith("## ")) return [];
|
|
18862
18901
|
if (!line.includes("|")) continue;
|
|
18863
|
-
const
|
|
18864
|
-
if (!/^\s*\|?[\s:|-]+\|?\s*$/.test(
|
|
18902
|
+
const sep10 = lines[i + 1];
|
|
18903
|
+
if (!/^\s*\|?[\s:|-]+\|?\s*$/.test(sep10) || !sep10.includes("-")) continue;
|
|
18865
18904
|
const headers = splitPipeRow(line).map((h) => stripBackticks(h).toLowerCase());
|
|
18866
18905
|
const labelCol = headers.findIndex((h) => h === "neo4j label");
|
|
18867
18906
|
if (labelCol === -1) return [];
|
|
@@ -19112,7 +19151,7 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
19112
19151
|
}
|
|
19113
19152
|
const includeAgentActions = c.req.query("includeAgentActions") === "1";
|
|
19114
19153
|
const agentActionLabels = includeAgentActions ? [] : [...AGENT_ACTION_LABELS];
|
|
19115
|
-
const
|
|
19154
|
+
const q2 = (c.req.query("q") ?? "").trim();
|
|
19116
19155
|
const rawLimit = c.req.query("limit");
|
|
19117
19156
|
const parsedLimit = rawLimit ? parseInt(rawLimit, 10) : NEIGHBOURHOOD_SEARCH_DEFAULT_LIMIT;
|
|
19118
19157
|
const searchLimit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, NEIGHBOURHOOD_SEARCH_MAX_LIMIT) : NEIGHBOURHOOD_SEARCH_DEFAULT_LIMIT;
|
|
@@ -19120,9 +19159,9 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
19120
19159
|
const session = getSession();
|
|
19121
19160
|
try {
|
|
19122
19161
|
let allowedIds = null;
|
|
19123
|
-
if (
|
|
19162
|
+
if (q2) {
|
|
19124
19163
|
const searchRes = await hybrid(session, embed, {
|
|
19125
|
-
query:
|
|
19164
|
+
query: q2,
|
|
19126
19165
|
accountId,
|
|
19127
19166
|
labels: [],
|
|
19128
19167
|
limit: searchLimit,
|
|
@@ -19205,7 +19244,7 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
19205
19244
|
}
|
|
19206
19245
|
if (allowedIds !== null) {
|
|
19207
19246
|
console.error(
|
|
19208
|
-
`[graph-page] neighbourhood-search-intersect q="${
|
|
19247
|
+
`[graph-page] neighbourhood-search-intersect q="${q2}" allowed=${allowedIds.length} root=${elementId} rendered=${nodes.length}`
|
|
19209
19248
|
);
|
|
19210
19249
|
}
|
|
19211
19250
|
return c.json({ nodes, edges });
|
|
@@ -20546,18 +20585,68 @@ var session_rc_spawn_default = app35;
|
|
|
20546
20585
|
|
|
20547
20586
|
// server/routes/admin/session-reseat.ts
|
|
20548
20587
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
20549
|
-
|
|
20588
|
+
|
|
20589
|
+
// server/channel-session-override.ts
|
|
20590
|
+
import { readFileSync as readFileSync34, writeFileSync as writeFileSync12, renameSync as renameSync9 } from "fs";
|
|
20591
|
+
import { join as join35 } from "path";
|
|
20592
|
+
var FILE2 = "channel-session-override.json";
|
|
20593
|
+
var UUID2 = /^[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}$/;
|
|
20594
|
+
function channelOverridePath(accountDir) {
|
|
20595
|
+
return join35(accountDir, FILE2);
|
|
20596
|
+
}
|
|
20597
|
+
function senderKey(channel, senderId) {
|
|
20598
|
+
return `${channel}:${senderId}`;
|
|
20599
|
+
}
|
|
20600
|
+
function readRaw2(accountDir) {
|
|
20601
|
+
try {
|
|
20602
|
+
return JSON.parse(readFileSync34(channelOverridePath(accountDir), "utf8"));
|
|
20603
|
+
} catch {
|
|
20604
|
+
return null;
|
|
20605
|
+
}
|
|
20606
|
+
}
|
|
20607
|
+
function readChannelOverrideId(accountDir, channel, senderId) {
|
|
20608
|
+
const raw = readRaw2(accountDir);
|
|
20609
|
+
if (!raw || !raw.bySender || typeof raw.bySender !== "object") return null;
|
|
20610
|
+
const v = raw.bySender[senderKey(channel, senderId)];
|
|
20611
|
+
return typeof v === "string" && UUID2.test(v) ? v : null;
|
|
20612
|
+
}
|
|
20613
|
+
function writeChannelOverrideId(accountDir, channel, senderId, sessionId) {
|
|
20614
|
+
const raw = readRaw2(accountDir);
|
|
20615
|
+
const bySender = {};
|
|
20616
|
+
if (raw?.bySender && typeof raw.bySender === "object") {
|
|
20617
|
+
for (const [k, v] of Object.entries(raw.bySender)) {
|
|
20618
|
+
if (typeof v === "string") bySender[k] = v;
|
|
20619
|
+
}
|
|
20620
|
+
}
|
|
20621
|
+
bySender[senderKey(channel, senderId)] = sessionId;
|
|
20622
|
+
const path3 = channelOverridePath(accountDir);
|
|
20623
|
+
const tmp = `${path3}.${process.pid}.tmp`;
|
|
20624
|
+
writeFileSync12(tmp, JSON.stringify({ bySender }, null, 2), "utf8");
|
|
20625
|
+
renameSync9(tmp, path3);
|
|
20626
|
+
}
|
|
20627
|
+
|
|
20628
|
+
// server/routes/admin/session-reseat.ts
|
|
20629
|
+
function isAdminChannelSource(source) {
|
|
20630
|
+
return !!source && source.role === "admin" && (source.channel === "whatsapp" || source.channel === "telegram") && typeof source.senderId === "string" && source.senderId.length > 0;
|
|
20631
|
+
}
|
|
20632
|
+
function resolveReseatPlan(body, mintId = randomUUID12, adminUserId, authenticatedName, source) {
|
|
20550
20633
|
const sessionId = mintId();
|
|
20551
20634
|
const key = `session:${sessionId}`;
|
|
20552
20635
|
const payload = {
|
|
20553
20636
|
sessionId,
|
|
20554
20637
|
forkFromSessionId: body.fromSessionId,
|
|
20555
20638
|
model: body.model,
|
|
20556
|
-
role: "admin"
|
|
20557
|
-
channel: "webchat",
|
|
20558
|
-
senderId: key,
|
|
20559
|
-
webchatChannel: webchatChannelDescriptor(key)
|
|
20639
|
+
role: "admin"
|
|
20560
20640
|
};
|
|
20641
|
+
if (isAdminChannelSource(source)) {
|
|
20642
|
+
payload.channel = source.channel;
|
|
20643
|
+
payload.senderId = source.senderId;
|
|
20644
|
+
if (source.accountId) payload.targetAccountId = source.accountId;
|
|
20645
|
+
} else {
|
|
20646
|
+
payload.channel = "webchat";
|
|
20647
|
+
payload.senderId = key;
|
|
20648
|
+
payload.webchatChannel = webchatChannelDescriptor(key);
|
|
20649
|
+
}
|
|
20561
20650
|
if (body.name) payload.name = body.name;
|
|
20562
20651
|
if (body.permissionMode) payload.permissionMode = body.permissionMode;
|
|
20563
20652
|
if (body.effort) payload.effort = body.effort;
|
|
@@ -20598,14 +20687,18 @@ app36.post("/", requireAdminSession, async (c) => {
|
|
|
20598
20687
|
}
|
|
20599
20688
|
const accountId = cacheKey ? getAccountIdForSession(cacheKey) : void 0;
|
|
20600
20689
|
const authenticatedName = accountId ? await resolveAuthenticatedName(accountId, adminUserId) : void 0;
|
|
20690
|
+
const sourceMeta = readSourceRowMeta(fromSessionId);
|
|
20691
|
+
const source = sourceMeta ? { channel: sourceMeta.channel, senderId: sourceMeta.senderId, role: sourceMeta.role, accountId: sourceMeta.accountId } : null;
|
|
20601
20692
|
const plan = resolveReseatPlan(
|
|
20602
20693
|
{ fromSessionId, model, ...permissionMode ? { permissionMode } : {}, ...effort ? { effort } : {} },
|
|
20603
20694
|
randomUUID12,
|
|
20604
20695
|
adminUserId,
|
|
20605
|
-
authenticatedName
|
|
20696
|
+
authenticatedName,
|
|
20697
|
+
source
|
|
20606
20698
|
);
|
|
20699
|
+
const isChannelFork = isAdminChannelSource(source);
|
|
20607
20700
|
const reseatReqId = randomUUID12();
|
|
20608
|
-
console.log(`[chat-reseat] op=request reseatReqId=${reseatReqId} from=${fromSessionId} to-model=${model} to-mode=${permissionMode ?? "none"} to-effort=${effort ?? "none"} new=${plan.sessionId}`);
|
|
20701
|
+
console.log(`[chat-reseat] op=request reseatReqId=${reseatReqId} from=${fromSessionId} from-channel=${source?.channel ?? "none"} from-sender=${source?.senderId ?? "none"} to-model=${model} to-mode=${permissionMode ?? "none"} to-effort=${effort ?? "none"} new=${plan.sessionId}`);
|
|
20609
20702
|
let res;
|
|
20610
20703
|
try {
|
|
20611
20704
|
res = await fetch(`${managerBase("session-reseat:wrapper")}/rc-spawn`, {
|
|
@@ -20645,6 +20738,23 @@ app36.post("/", requireAdminSession, async (c) => {
|
|
|
20645
20738
|
} catch (err) {
|
|
20646
20739
|
console.error(`[session-reseat] canonical-forward-failed err=${err instanceof Error ? err.message : String(err)}`);
|
|
20647
20740
|
}
|
|
20741
|
+
if (isChannelFork) {
|
|
20742
|
+
const channel = plan.payload.channel;
|
|
20743
|
+
const senderId = plan.payload.senderId;
|
|
20744
|
+
try {
|
|
20745
|
+
const provenance = source?.accountId ? "source-account" : "boot-account-fallback";
|
|
20746
|
+
const dir = source?.accountId ? listValidAccounts().find((a) => a.accountId === source.accountId)?.accountDir ?? null : resolveAccount()?.accountDir ?? null;
|
|
20747
|
+
if (dir) {
|
|
20748
|
+
writeChannelOverrideId(dir, channel, senderId, plan.sessionId);
|
|
20749
|
+
console.log(`[chat-reseat] op=channel-forward reseatReqId=${reseatReqId} senderId=${senderId} from=${fromSessionId} to=${plan.sessionId} keyedOn=${provenance}`);
|
|
20750
|
+
} else {
|
|
20751
|
+
console.error(`[session-reseat] channel-forward-failed reason=no-account-dir accountId=${source?.accountId ?? "none"} senderId=${senderId}`);
|
|
20752
|
+
}
|
|
20753
|
+
} catch (err) {
|
|
20754
|
+
console.error(`[session-reseat] channel-forward-failed err=${err instanceof Error ? err.message : String(err)} senderId=${senderId}`);
|
|
20755
|
+
}
|
|
20756
|
+
console.log(`[chat-reseat] op=channel-fork-unbound reseatReqId=${reseatReqId} senderId=${senderId} session=${plan.sessionId} note=binds-on-next-inbound`);
|
|
20757
|
+
}
|
|
20648
20758
|
const target = `/chat?session=${plan.sessionId}`;
|
|
20649
20759
|
console.log(`[chat-reseat] op=acquired reseatReqId=${reseatReqId} pid=${parsed?.spawnedPid ?? "none"} new=${plan.sessionId}`);
|
|
20650
20760
|
console.log(`[chat-reseat] op=navigate reseatReqId=${reseatReqId} target=${target}`);
|
|
@@ -20779,14 +20889,14 @@ app37.get("/", async (c) => {
|
|
|
20779
20889
|
var system_stats_default = app37;
|
|
20780
20890
|
|
|
20781
20891
|
// server/routes/admin/health.ts
|
|
20782
|
-
import { existsSync as existsSync31, readFileSync as
|
|
20783
|
-
import { resolve as resolve26, join as
|
|
20892
|
+
import { existsSync as existsSync31, readFileSync as readFileSync35 } from "fs";
|
|
20893
|
+
import { resolve as resolve26, join as join36 } from "path";
|
|
20784
20894
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "..");
|
|
20785
20895
|
var brandHostname = "maxy";
|
|
20786
|
-
var brandJsonPath =
|
|
20896
|
+
var brandJsonPath = join36(PLATFORM_ROOT6, "config", "brand.json");
|
|
20787
20897
|
if (existsSync31(brandJsonPath)) {
|
|
20788
20898
|
try {
|
|
20789
|
-
const brand = JSON.parse(
|
|
20899
|
+
const brand = JSON.parse(readFileSync35(brandJsonPath, "utf-8"));
|
|
20790
20900
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
20791
20901
|
} catch {
|
|
20792
20902
|
}
|
|
@@ -20796,7 +20906,7 @@ var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
|
20796
20906
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
20797
20907
|
function readVersion() {
|
|
20798
20908
|
if (!existsSync31(VERSION_FILE)) return "unknown";
|
|
20799
|
-
return
|
|
20909
|
+
return readFileSync35(VERSION_FILE, "utf-8").trim() || "unknown";
|
|
20800
20910
|
}
|
|
20801
20911
|
async function probeConversationDb() {
|
|
20802
20912
|
let session;
|
|
@@ -20976,7 +21086,7 @@ function managerLogFollowUrl(sessionId, opts) {
|
|
|
20976
21086
|
|
|
20977
21087
|
// server/routes/admin/linkedin-ingest.ts
|
|
20978
21088
|
var TAG31 = "[linkedin-ingest-route]";
|
|
20979
|
-
var
|
|
21089
|
+
var UUID3 = /^[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}$/;
|
|
20980
21090
|
var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
20981
21091
|
var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
|
|
20982
21092
|
function rejectSchema(reason, missing = []) {
|
|
@@ -20990,7 +21100,7 @@ function validate(body) {
|
|
|
20990
21100
|
if (b.kind !== "profile" && b.kind !== "thread") missing.push("kind");
|
|
20991
21101
|
if (typeof b.capturedAt !== "string" || !ISO.test(b.capturedAt)) missing.push("capturedAt");
|
|
20992
21102
|
if (typeof b.pageUrl !== "string" || !LINKEDIN_URL.test(b.pageUrl)) missing.push("pageUrl");
|
|
20993
|
-
if (typeof b.dispatchId !== "string" || !
|
|
21103
|
+
if (typeof b.dispatchId !== "string" || !UUID3.test(b.dispatchId)) missing.push("dispatchId");
|
|
20994
21104
|
if (missing.length) return rejectSchema("envelope", missing);
|
|
20995
21105
|
if (b.kind === "profile") {
|
|
20996
21106
|
const p = b.profile;
|
|
@@ -21573,7 +21683,7 @@ var browser_default = app45;
|
|
|
21573
21683
|
|
|
21574
21684
|
// server/routes/admin/calendar.ts
|
|
21575
21685
|
import { existsSync as existsSync32 } from "fs";
|
|
21576
|
-
import { join as
|
|
21686
|
+
import { join as join38 } from "path";
|
|
21577
21687
|
|
|
21578
21688
|
// server/lib/calendar-ics.ts
|
|
21579
21689
|
var CRLF = "\r\n";
|
|
@@ -21672,8 +21782,8 @@ function countVEvents(ics) {
|
|
|
21672
21782
|
}
|
|
21673
21783
|
|
|
21674
21784
|
// server/lib/calendar-availability.ts
|
|
21675
|
-
import { readFileSync as
|
|
21676
|
-
import { join as
|
|
21785
|
+
import { readFileSync as readFileSync36 } from "fs";
|
|
21786
|
+
import { join as join37 } from "path";
|
|
21677
21787
|
var AVAILABILITY_FILENAME = "calendar-availability.json";
|
|
21678
21788
|
var REQUIRED_KEYS = [
|
|
21679
21789
|
"timezone",
|
|
@@ -21682,10 +21792,10 @@ var REQUIRED_KEYS = [
|
|
|
21682
21792
|
"weekly"
|
|
21683
21793
|
];
|
|
21684
21794
|
function readAvailabilityConfig(accountDir) {
|
|
21685
|
-
const path3 =
|
|
21795
|
+
const path3 = join37(accountDir, AVAILABILITY_FILENAME);
|
|
21686
21796
|
let raw;
|
|
21687
21797
|
try {
|
|
21688
|
-
raw =
|
|
21798
|
+
raw = readFileSync36(path3, "utf-8");
|
|
21689
21799
|
} catch {
|
|
21690
21800
|
throw new Error(`availability not configured: ${path3} not found`);
|
|
21691
21801
|
}
|
|
@@ -22044,7 +22154,7 @@ app46.get("/booking-link", requireAdminSession, (c) => {
|
|
|
22044
22154
|
console.error('[calendar] op=booking-link-fail err="no account configured"');
|
|
22045
22155
|
return c.json({ bookingDomain: null });
|
|
22046
22156
|
}
|
|
22047
|
-
if (!existsSync32(
|
|
22157
|
+
if (!existsSync32(join38(account.accountDir, AVAILABILITY_FILENAME))) {
|
|
22048
22158
|
console.log("[calendar] op=booking-link bookingDomain=none source=availability-config");
|
|
22049
22159
|
return c.json({ bookingDomain: null });
|
|
22050
22160
|
}
|
|
@@ -22847,7 +22957,7 @@ app56.route("/request-magic-link", request_magic_link_default);
|
|
|
22847
22957
|
var access_default = app56;
|
|
22848
22958
|
|
|
22849
22959
|
// server/routes/sites.ts
|
|
22850
|
-
import { existsSync as existsSync33, readFileSync as
|
|
22960
|
+
import { existsSync as existsSync33, readFileSync as readFileSync37, realpathSync as realpathSync7, statSync as statSync14 } from "fs";
|
|
22851
22961
|
import { resolve as resolve28 } from "path";
|
|
22852
22962
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
22853
22963
|
var MIME = {
|
|
@@ -22911,13 +23021,13 @@ app57.get("/:rel{.*}", (c) => {
|
|
|
22911
23021
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
22912
23022
|
return c.text("Forbidden", 403);
|
|
22913
23023
|
}
|
|
22914
|
-
let
|
|
23024
|
+
let stat9;
|
|
22915
23025
|
try {
|
|
22916
|
-
|
|
23026
|
+
stat9 = existsSync33(filePath) ? statSync14(filePath) : null;
|
|
22917
23027
|
} catch {
|
|
22918
|
-
|
|
23028
|
+
stat9 = null;
|
|
22919
23029
|
}
|
|
22920
|
-
if (
|
|
23030
|
+
if (stat9?.isDirectory() && !reqPath.endsWith("/")) {
|
|
22921
23031
|
const search = new URL(c.req.url).search;
|
|
22922
23032
|
const target = `${reqPath}/${search}`;
|
|
22923
23033
|
console.log(
|
|
@@ -22925,7 +23035,7 @@ app57.get("/:rel{.*}", (c) => {
|
|
|
22925
23035
|
);
|
|
22926
23036
|
return c.redirect(target, 301);
|
|
22927
23037
|
}
|
|
22928
|
-
if (
|
|
23038
|
+
if (stat9?.isDirectory()) {
|
|
22929
23039
|
filePath = resolve28(filePath, "index.html");
|
|
22930
23040
|
}
|
|
22931
23041
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
@@ -22951,7 +23061,7 @@ app57.get("/:rel{.*}", (c) => {
|
|
|
22951
23061
|
}
|
|
22952
23062
|
let body;
|
|
22953
23063
|
try {
|
|
22954
|
-
body =
|
|
23064
|
+
body = readFileSync37(realPath);
|
|
22955
23065
|
} catch (err) {
|
|
22956
23066
|
const code = err?.code;
|
|
22957
23067
|
if (code === "EISDIR") {
|
|
@@ -22987,7 +23097,7 @@ var sites_default = app57;
|
|
|
22987
23097
|
|
|
22988
23098
|
// app/lib/visitor-token.ts
|
|
22989
23099
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
22990
|
-
import { mkdirSync as mkdirSync8, readFileSync as
|
|
23100
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync38, writeFileSync as writeFileSync13 } from "fs";
|
|
22991
23101
|
import { dirname as dirname12 } from "path";
|
|
22992
23102
|
var TOKEN_PREFIX = "v1.";
|
|
22993
23103
|
var SECRET_BYTES = 32;
|
|
@@ -22996,7 +23106,7 @@ var cachedSecret = null;
|
|
|
22996
23106
|
function getSecret() {
|
|
22997
23107
|
if (cachedSecret) return cachedSecret;
|
|
22998
23108
|
try {
|
|
22999
|
-
const hex2 =
|
|
23109
|
+
const hex2 = readFileSync38(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
23000
23110
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
23001
23111
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
23002
23112
|
return cachedSecret;
|
|
@@ -23006,11 +23116,11 @@ function getSecret() {
|
|
|
23006
23116
|
const fresh = randomBytes(SECRET_BYTES).toString("hex");
|
|
23007
23117
|
try {
|
|
23008
23118
|
mkdirSync8(dirname12(VISITOR_TOKEN_SECRET_FILE), { recursive: true, mode: 448 });
|
|
23009
|
-
|
|
23119
|
+
writeFileSync13(VISITOR_TOKEN_SECRET_FILE, fresh, { mode: 384, flag: "wx" });
|
|
23010
23120
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
23011
23121
|
} catch {
|
|
23012
23122
|
}
|
|
23013
|
-
const hex =
|
|
23123
|
+
const hex = readFileSync38(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
23014
23124
|
cachedSecret = Buffer.from(hex, "hex");
|
|
23015
23125
|
return cachedSecret;
|
|
23016
23126
|
}
|
|
@@ -23063,8 +23173,8 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
23063
23173
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
23064
23174
|
|
|
23065
23175
|
// app/lib/brand-config.ts
|
|
23066
|
-
import { existsSync as existsSync34, readFileSync as
|
|
23067
|
-
import { join as
|
|
23176
|
+
import { existsSync as existsSync34, readFileSync as readFileSync39 } from "fs";
|
|
23177
|
+
import { join as join39 } from "path";
|
|
23068
23178
|
var cached2 = null;
|
|
23069
23179
|
var cachedAttempted = false;
|
|
23070
23180
|
function readBrandConfig() {
|
|
@@ -23072,10 +23182,10 @@ function readBrandConfig() {
|
|
|
23072
23182
|
cachedAttempted = true;
|
|
23073
23183
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
|
|
23074
23184
|
if (!platformRoot5) return null;
|
|
23075
|
-
const brandPath =
|
|
23185
|
+
const brandPath = join39(platformRoot5, "config", "brand.json");
|
|
23076
23186
|
if (!existsSync34(brandPath)) return null;
|
|
23077
23187
|
try {
|
|
23078
|
-
cached2 = JSON.parse(
|
|
23188
|
+
cached2 = JSON.parse(readFileSync39(brandPath, "utf-8"));
|
|
23079
23189
|
return cached2;
|
|
23080
23190
|
} catch {
|
|
23081
23191
|
return null;
|
|
@@ -23209,8 +23319,8 @@ function appendConsentParams(pageUrl, token) {
|
|
|
23209
23319
|
const hashIdx = pageUrl.indexOf("#");
|
|
23210
23320
|
const hash = hashIdx >= 0 ? pageUrl.slice(hashIdx) : "";
|
|
23211
23321
|
const base = hashIdx >= 0 ? pageUrl.slice(0, hashIdx) : pageUrl;
|
|
23212
|
-
const
|
|
23213
|
-
return base +
|
|
23322
|
+
const sep10 = base.indexOf("?") >= 0 ? "&" : "?";
|
|
23323
|
+
return base + sep10 + `consent=needed&v=${encodeURIComponent(token)}` + hash;
|
|
23214
23324
|
}
|
|
23215
23325
|
}
|
|
23216
23326
|
var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
|
|
@@ -23573,13 +23683,13 @@ var visitor_event_default = app60;
|
|
|
23573
23683
|
|
|
23574
23684
|
// server/routes/session.ts
|
|
23575
23685
|
import { resolve as resolve29 } from "path";
|
|
23576
|
-
import { existsSync as existsSync35, writeFileSync as
|
|
23686
|
+
import { existsSync as existsSync35, writeFileSync as writeFileSync14, mkdirSync as mkdirSync9 } from "fs";
|
|
23577
23687
|
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
23578
23688
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
23579
23689
|
try {
|
|
23580
23690
|
const cacheDir = resolve29(MAXY_DIR, "branding-cache", accountId);
|
|
23581
23691
|
mkdirSync9(cacheDir, { recursive: true });
|
|
23582
|
-
|
|
23692
|
+
writeFileSync14(resolve29(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
23583
23693
|
} catch (err) {
|
|
23584
23694
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
23585
23695
|
}
|
|
@@ -23904,8 +24014,205 @@ app62.get("/free-busy", async (c) => {
|
|
|
23904
24014
|
});
|
|
23905
24015
|
var calendar_public_default = app62;
|
|
23906
24016
|
|
|
24017
|
+
// server/routes/portal-fetch.ts
|
|
24018
|
+
import { createReadStream as createReadStream3 } from "fs";
|
|
24019
|
+
import { stat as stat6, readFile as readFile6, realpath } from "fs/promises";
|
|
24020
|
+
import { join as join40, resolve as resolve30, sep as sep8 } from "path";
|
|
24021
|
+
import { Readable as Readable3 } from "stream";
|
|
24022
|
+
|
|
24023
|
+
// ../plugins/cloudflare/bin/schema-exposed-dirs.mjs
|
|
24024
|
+
var ONT_START = "<!-- ontology-buckets:start -->";
|
|
24025
|
+
var ONT_END = "<!-- ontology-buckets:end -->";
|
|
24026
|
+
var OWNED_START = "<!-- plugin-owned-dirs:start -->";
|
|
24027
|
+
var OWNED_END = "<!-- plugin-owned-dirs:end -->";
|
|
24028
|
+
var ALWAYS_EXPOSED = "output";
|
|
24029
|
+
var DOMAIN_LINE = /^- `([^`/]+)\/` - one folder per /;
|
|
24030
|
+
var OWNED_LINE = /^- `([^`/]+)\/` — /;
|
|
24031
|
+
function region(text, start, end) {
|
|
24032
|
+
const i = text.indexOf(start);
|
|
24033
|
+
if (i === -1) return null;
|
|
24034
|
+
const j = text.indexOf(end, i);
|
|
24035
|
+
return text.slice(i, j === -1 ? text.length : j);
|
|
24036
|
+
}
|
|
24037
|
+
function allowedBlockOrNull(text) {
|
|
24038
|
+
const lines = text.split("\n");
|
|
24039
|
+
const start = lines.findIndex((l) => l.trim() === "```allowed-top-level");
|
|
24040
|
+
if (start === -1) return null;
|
|
24041
|
+
for (let j = start + 1; j < lines.length; j++) {
|
|
24042
|
+
if (lines[j].trim() === "```") {
|
|
24043
|
+
return lines.slice(start + 1, j).map((l) => l.trim()).filter(Boolean);
|
|
24044
|
+
}
|
|
24045
|
+
}
|
|
24046
|
+
return null;
|
|
24047
|
+
}
|
|
24048
|
+
function resolveExposedDirs(schemaText) {
|
|
24049
|
+
const empty = {
|
|
24050
|
+
schemaPresent: false,
|
|
24051
|
+
allowed: (
|
|
24052
|
+
/** @type {string[]} */
|
|
24053
|
+
[]
|
|
24054
|
+
),
|
|
24055
|
+
domainBuckets: (
|
|
24056
|
+
/** @type {string[]} */
|
|
24057
|
+
[]
|
|
24058
|
+
),
|
|
24059
|
+
pluginOwned: (
|
|
24060
|
+
/** @type {string[]} */
|
|
24061
|
+
[]
|
|
24062
|
+
),
|
|
24063
|
+
collisions: (
|
|
24064
|
+
/** @type {string[]} */
|
|
24065
|
+
[]
|
|
24066
|
+
),
|
|
24067
|
+
exposed: (
|
|
24068
|
+
/** @type {string[]} */
|
|
24069
|
+
[]
|
|
24070
|
+
)
|
|
24071
|
+
};
|
|
24072
|
+
if (typeof schemaText !== "string" || schemaText.length === 0) return empty;
|
|
24073
|
+
const allowed = allowedBlockOrNull(schemaText);
|
|
24074
|
+
if (allowed === null) return { ...empty, schemaPresent: true };
|
|
24075
|
+
const ont = region(schemaText, ONT_START, ONT_END);
|
|
24076
|
+
const owned = region(schemaText, OWNED_START, OWNED_END);
|
|
24077
|
+
const dirsIn = (text, re) => {
|
|
24078
|
+
if (!text) return [];
|
|
24079
|
+
const out = [];
|
|
24080
|
+
for (const line of text.split("\n")) {
|
|
24081
|
+
const m = re.exec(line.trim());
|
|
24082
|
+
if (m && !out.includes(m[1])) out.push(m[1]);
|
|
24083
|
+
}
|
|
24084
|
+
return out;
|
|
24085
|
+
};
|
|
24086
|
+
const domainBuckets = dirsIn(ont, DOMAIN_LINE);
|
|
24087
|
+
const pluginOwned = dirsIn(owned, OWNED_LINE);
|
|
24088
|
+
const collisions = domainBuckets.filter((d) => pluginOwned.includes(d));
|
|
24089
|
+
const candidates = [ALWAYS_EXPOSED, ...domainBuckets.filter((d) => !collisions.includes(d))];
|
|
24090
|
+
const exposed = [...new Set(candidates)].filter((d) => allowed.includes(d)).sort();
|
|
24091
|
+
return { schemaPresent: true, allowed, domainBuckets, pluginOwned, collisions, exposed };
|
|
24092
|
+
}
|
|
24093
|
+
|
|
24094
|
+
// server/routes/portal-fetch.ts
|
|
24095
|
+
var app63 = new Hono();
|
|
24096
|
+
var TAG45 = "[portal-fetch]";
|
|
24097
|
+
async function verifySignature(secret, accountId, relPath, expiresAtMs, signature, nowMs) {
|
|
24098
|
+
if (!secret || !signature) return false;
|
|
24099
|
+
if (!Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs) return false;
|
|
24100
|
+
const key = await crypto.subtle.importKey(
|
|
24101
|
+
"raw",
|
|
24102
|
+
new TextEncoder().encode(secret),
|
|
24103
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
24104
|
+
false,
|
|
24105
|
+
["sign"]
|
|
24106
|
+
);
|
|
24107
|
+
const mac = await crypto.subtle.sign(
|
|
24108
|
+
"HMAC",
|
|
24109
|
+
key,
|
|
24110
|
+
new TextEncoder().encode(`${accountId}
|
|
24111
|
+
${relPath}
|
|
24112
|
+
${expiresAtMs}`)
|
|
24113
|
+
);
|
|
24114
|
+
const expected = [...new Uint8Array(mac)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
24115
|
+
if (expected.length !== signature.length) return false;
|
|
24116
|
+
let diff = 0;
|
|
24117
|
+
for (let i = 0; i < expected.length; i++) diff |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
|
|
24118
|
+
return diff === 0;
|
|
24119
|
+
}
|
|
24120
|
+
async function readAccountSecret(accountId) {
|
|
24121
|
+
const path3 = resolve30(DATA_ROOT, "accounts", accountId, "secrets", "data-portal.env");
|
|
24122
|
+
let text;
|
|
24123
|
+
try {
|
|
24124
|
+
text = await readFile6(path3, "utf8");
|
|
24125
|
+
} catch {
|
|
24126
|
+
console.error(`${TAG45} op=no-secret account="${accountId}" path="${path3}"`);
|
|
24127
|
+
return "";
|
|
24128
|
+
}
|
|
24129
|
+
for (const line of text.split("\n")) {
|
|
24130
|
+
const m = line.match(/^PORTAL_FETCH_SECRET=(.*)$/);
|
|
24131
|
+
if (m) return m[1].trim();
|
|
24132
|
+
}
|
|
24133
|
+
console.error(`${TAG45} op=no-secret account="${accountId}" reason=key-absent path="${path3}"`);
|
|
24134
|
+
return "";
|
|
24135
|
+
}
|
|
24136
|
+
function isValidAccountId2(value) {
|
|
24137
|
+
return /^[A-Za-z0-9-]+$/.test(value);
|
|
24138
|
+
}
|
|
24139
|
+
async function resolveFetchTarget(accountDir, relPath) {
|
|
24140
|
+
let schemaText = null;
|
|
24141
|
+
try {
|
|
24142
|
+
schemaText = await readFile6(join40(accountDir, "SCHEMA.md"), "utf8");
|
|
24143
|
+
} catch {
|
|
24144
|
+
schemaText = null;
|
|
24145
|
+
}
|
|
24146
|
+
const { exposed } = resolveExposedDirs(schemaText);
|
|
24147
|
+
if (exposed.length === 0) return { ok: false, reason: "not-exposed" };
|
|
24148
|
+
const lexical = resolve30(accountDir, relPath);
|
|
24149
|
+
let root;
|
|
24150
|
+
let absolute;
|
|
24151
|
+
try {
|
|
24152
|
+
root = await realpath(accountDir);
|
|
24153
|
+
absolute = await realpath(lexical);
|
|
24154
|
+
} catch {
|
|
24155
|
+
return { ok: false, reason: "enoent" };
|
|
24156
|
+
}
|
|
24157
|
+
if (absolute === root || !absolute.startsWith(root + sep8)) return { ok: false, reason: "not-exposed" };
|
|
24158
|
+
const top = absolute.slice(root.length + 1).split(sep8)[0];
|
|
24159
|
+
if (!exposed.includes(top)) return { ok: false, reason: "not-exposed" };
|
|
24160
|
+
try {
|
|
24161
|
+
const st = await stat6(absolute);
|
|
24162
|
+
if (!st.isFile()) return { ok: false, reason: "enoent" };
|
|
24163
|
+
return { ok: true, absolute, sizeBytes: st.size };
|
|
24164
|
+
} catch {
|
|
24165
|
+
return { ok: false, reason: "enoent" };
|
|
24166
|
+
}
|
|
24167
|
+
}
|
|
24168
|
+
function q(value) {
|
|
24169
|
+
return `"${value.replace(/[\\"]/g, "\\$&").replace(/[\r\n\t]/g, " ")}"`;
|
|
24170
|
+
}
|
|
24171
|
+
async function handle(c, headOnly) {
|
|
24172
|
+
const accountId = c.req.query("accountId") ?? "";
|
|
24173
|
+
const relPath = c.req.query("path") ?? "";
|
|
24174
|
+
const exp = Number(c.req.query("exp") ?? "0");
|
|
24175
|
+
const sig = c.req.query("sig") ?? "";
|
|
24176
|
+
console.log(`${TAG45} op=request account=${q(accountId)} path=${q(relPath)} head=${headOnly}`);
|
|
24177
|
+
if (!isValidAccountId2(accountId)) {
|
|
24178
|
+
console.error(`${TAG45} op=denied account=${q(accountId)} reason=account-id`);
|
|
24179
|
+
return c.json({ ok: false, error: "denied" }, 401);
|
|
24180
|
+
}
|
|
24181
|
+
const secret = await readAccountSecret(accountId);
|
|
24182
|
+
if (!await verifySignature(secret, accountId, relPath, exp, sig, Date.now())) {
|
|
24183
|
+
console.error(`${TAG45} op=denied account=${q(accountId)} reason=secret`);
|
|
24184
|
+
return c.json({ ok: false, error: "denied" }, 401);
|
|
24185
|
+
}
|
|
24186
|
+
const target = await resolveFetchTarget(resolve30(DATA_ROOT, "accounts", accountId), relPath);
|
|
24187
|
+
if (!target.ok) {
|
|
24188
|
+
const op = target.reason === "enoent" ? "miss" : "denied";
|
|
24189
|
+
console.error(`${TAG45} op=${op} account=${accountId} reason=${target.reason}`);
|
|
24190
|
+
return c.json({ ok: false, error: "not found" }, 404);
|
|
24191
|
+
}
|
|
24192
|
+
if (headOnly) {
|
|
24193
|
+
console.log(`${TAG45} op=served account=${accountId} bytes=${target.sizeBytes} head=true`);
|
|
24194
|
+
return c.body(null, 200);
|
|
24195
|
+
}
|
|
24196
|
+
console.log(`${TAG45} op=served account=${accountId} bytes=${target.sizeBytes}`);
|
|
24197
|
+
return new Response(Readable3.toWeb(createReadStream3(target.absolute)), {
|
|
24198
|
+
status: 200,
|
|
24199
|
+
headers: {
|
|
24200
|
+
"content-type": detectMimeType(target.absolute),
|
|
24201
|
+
// Percent-encoded per RFC 6266, so a name carrying a quote cannot break
|
|
24202
|
+
// out of the form and CR/LF cannot reach the Headers constructor.
|
|
24203
|
+
"content-disposition": `attachment; filename*=UTF-8''${encodeURIComponent(relPath.split("/").pop() ?? "file")}`,
|
|
24204
|
+
"x-content-type-options": "nosniff",
|
|
24205
|
+
// Private client documents: never let a browser or proxy cache one.
|
|
24206
|
+
"cache-control": "private, no-store"
|
|
24207
|
+
}
|
|
24208
|
+
});
|
|
24209
|
+
}
|
|
24210
|
+
app63.get("/fetch", (c) => handle(c, false));
|
|
24211
|
+
app63.on("HEAD", "/fetch", (c) => handle(c, true));
|
|
24212
|
+
var portal_fetch_default = app63;
|
|
24213
|
+
|
|
23907
24214
|
// app/lib/timeentry-census.ts
|
|
23908
|
-
var
|
|
24215
|
+
var TAG46 = "[timeentry-census]";
|
|
23909
24216
|
function reconcileTimeEntries(rows, now, horizonMs) {
|
|
23910
24217
|
const openEntries = rows.openEntries.length;
|
|
23911
24218
|
let oldestOpenAgeMs = 0;
|
|
@@ -23945,12 +24252,12 @@ async function runTimeEntryCensusOnce(session, now, horizonMs) {
|
|
|
23945
24252
|
sumAdjustments: toNum(r.get("sumAdjustments"))
|
|
23946
24253
|
}));
|
|
23947
24254
|
const finding = reconcileTimeEntries({ openEntries, taskSums }, now, horizonMs);
|
|
23948
|
-
const line = `${
|
|
24255
|
+
const line = `${TAG46} openEntries=${finding.openEntries} oldestOpenAgeMin=${finding.oldestOpenAgeMin} maxOpenPerAccount=${finding.maxOpenPerAccount} driftTasks=${finding.driftTasks}`;
|
|
23949
24256
|
if (finding.alarm) console.error(`${line} alarm=true`);
|
|
23950
24257
|
else console.log(line);
|
|
23951
24258
|
return finding;
|
|
23952
24259
|
} catch (err) {
|
|
23953
|
-
console.error(`${
|
|
24260
|
+
console.error(`${TAG46} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23954
24261
|
return null;
|
|
23955
24262
|
}
|
|
23956
24263
|
}
|
|
@@ -23964,13 +24271,13 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
23964
24271
|
try {
|
|
23965
24272
|
session = openSession();
|
|
23966
24273
|
} catch (err) {
|
|
23967
|
-
console.error(`${
|
|
24274
|
+
console.error(`${TAG46} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23968
24275
|
return;
|
|
23969
24276
|
}
|
|
23970
24277
|
try {
|
|
23971
24278
|
await runTimeEntryCensusOnce(session, Date.now(), horizonMs);
|
|
23972
24279
|
} catch (err) {
|
|
23973
|
-
console.error(`${
|
|
24280
|
+
console.error(`${TAG46} tick-failed error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23974
24281
|
} finally {
|
|
23975
24282
|
try {
|
|
23976
24283
|
await session.close();
|
|
@@ -23987,8 +24294,8 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
23987
24294
|
}
|
|
23988
24295
|
|
|
23989
24296
|
// app/lib/ledger-census.ts
|
|
23990
|
-
import { readdirSync as readdirSync25, readFileSync as
|
|
23991
|
-
import { join as
|
|
24297
|
+
import { readdirSync as readdirSync25, readFileSync as readFileSync40, statSync as statSync15 } from "fs";
|
|
24298
|
+
import { join as join41 } from "path";
|
|
23992
24299
|
|
|
23993
24300
|
// ../lib/ledger-core/dist/reconcile.js
|
|
23994
24301
|
var LEDGER_INDEX_NAMES = [
|
|
@@ -24031,7 +24338,7 @@ function formatCensusLine(f) {
|
|
|
24031
24338
|
}
|
|
24032
24339
|
|
|
24033
24340
|
// app/lib/ledger-census.ts
|
|
24034
|
-
var
|
|
24341
|
+
var TAG47 = "[ledger-census]";
|
|
24035
24342
|
var DAY_MS = 864e5;
|
|
24036
24343
|
function countWriteRejects24h(logDir, now) {
|
|
24037
24344
|
let entries;
|
|
@@ -24042,10 +24349,10 @@ function countWriteRejects24h(logDir, now) {
|
|
|
24042
24349
|
}
|
|
24043
24350
|
let total = 0;
|
|
24044
24351
|
for (const name of entries) {
|
|
24045
|
-
const path3 =
|
|
24352
|
+
const path3 = join41(logDir, name);
|
|
24046
24353
|
try {
|
|
24047
24354
|
if (now - statSync15(path3).mtimeMs > DAY_MS) continue;
|
|
24048
|
-
for (const line of
|
|
24355
|
+
for (const line of readFileSync40(path3, "utf8").split("\n")) {
|
|
24049
24356
|
if (line.includes("[graph-write] reject")) total += 1;
|
|
24050
24357
|
}
|
|
24051
24358
|
} catch {
|
|
@@ -24107,7 +24414,7 @@ async function runLedgerCensusOnce(session, now) {
|
|
|
24107
24414
|
else console.log(line);
|
|
24108
24415
|
return finding;
|
|
24109
24416
|
} catch (err) {
|
|
24110
|
-
console.error(`${
|
|
24417
|
+
console.error(`${TAG47} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
24111
24418
|
return null;
|
|
24112
24419
|
}
|
|
24113
24420
|
}
|
|
@@ -24120,7 +24427,7 @@ function startLedgerCensus(openSession, opts = {}) {
|
|
|
24120
24427
|
session = openSession();
|
|
24121
24428
|
} catch (err) {
|
|
24122
24429
|
console.error(
|
|
24123
|
-
`${
|
|
24430
|
+
`${TAG47} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`
|
|
24124
24431
|
);
|
|
24125
24432
|
return;
|
|
24126
24433
|
}
|
|
@@ -24128,7 +24435,7 @@ function startLedgerCensus(openSession, opts = {}) {
|
|
|
24128
24435
|
await runLedgerCensusOnce(session, Date.now());
|
|
24129
24436
|
} catch (err) {
|
|
24130
24437
|
console.error(
|
|
24131
|
-
`${
|
|
24438
|
+
`${TAG47} tick-failed error="${err instanceof Error ? err.message : String(err)}"`
|
|
24132
24439
|
);
|
|
24133
24440
|
} finally {
|
|
24134
24441
|
try {
|
|
@@ -24146,7 +24453,7 @@ function startLedgerCensus(openSession, opts = {}) {
|
|
|
24146
24453
|
}
|
|
24147
24454
|
|
|
24148
24455
|
// app/lib/conversation-audit.ts
|
|
24149
|
-
var
|
|
24456
|
+
var TAG48 = "[conversation-audit]";
|
|
24150
24457
|
async function runConversationAudit() {
|
|
24151
24458
|
const session = getSession();
|
|
24152
24459
|
try {
|
|
@@ -24172,12 +24479,12 @@ async function runConversationAudit() {
|
|
|
24172
24479
|
unpairable: Number(rec?.get("unpairable") ?? 0)
|
|
24173
24480
|
};
|
|
24174
24481
|
console.error(
|
|
24175
|
-
`${
|
|
24482
|
+
`${TAG48} channel=whatsapp total=${result.total} unreconciled=${result.unreconciled} unpairable=${result.unpairable}`
|
|
24176
24483
|
);
|
|
24177
24484
|
return result;
|
|
24178
24485
|
} catch (err) {
|
|
24179
24486
|
const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
|
|
24180
|
-
console.error(`${
|
|
24487
|
+
console.error(`${TAG48} op=failed reason=${reason}`);
|
|
24181
24488
|
return null;
|
|
24182
24489
|
} finally {
|
|
24183
24490
|
await session.close();
|
|
@@ -24303,13 +24610,13 @@ function startGraphHealthTimer() {
|
|
|
24303
24610
|
|
|
24304
24611
|
// app/lib/file-watcher.ts
|
|
24305
24612
|
import * as fsp2 from "fs/promises";
|
|
24306
|
-
import { resolve as
|
|
24613
|
+
import { resolve as resolve31, sep as sep9 } from "path";
|
|
24307
24614
|
var ACCOUNT_UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
24308
24615
|
var DEFAULT_COALESCE_MS = 500;
|
|
24309
24616
|
var ROOTS = ["accounts"];
|
|
24310
24617
|
function _routeEvent(rootName, filename) {
|
|
24311
24618
|
if (!filename) return null;
|
|
24312
|
-
const segs = filename.split(
|
|
24619
|
+
const segs = filename.split(sep9).filter(Boolean);
|
|
24313
24620
|
if (segs.length < 2) return null;
|
|
24314
24621
|
const accountId = segs[0];
|
|
24315
24622
|
if (!ACCOUNT_UUID_RE4.test(accountId)) return null;
|
|
@@ -24322,7 +24629,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24322
24629
|
const dropFn = opts.drop ?? dropFileIndex;
|
|
24323
24630
|
for (const r of ROOTS) {
|
|
24324
24631
|
try {
|
|
24325
|
-
await fsp2.mkdir(
|
|
24632
|
+
await fsp2.mkdir(resolve31(dataRoot, r), { recursive: true });
|
|
24326
24633
|
} catch (err) {
|
|
24327
24634
|
console.error(
|
|
24328
24635
|
`[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
|
|
@@ -24343,7 +24650,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24343
24650
|
timers.set(relativePath, t);
|
|
24344
24651
|
}
|
|
24345
24652
|
async function runHook(relativePath, accountId) {
|
|
24346
|
-
const absolute =
|
|
24653
|
+
const absolute = resolve31(dataRoot, relativePath);
|
|
24347
24654
|
let exists = false;
|
|
24348
24655
|
try {
|
|
24349
24656
|
const st = await fsp2.stat(absolute);
|
|
@@ -24367,7 +24674,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24367
24674
|
}
|
|
24368
24675
|
}
|
|
24369
24676
|
async function watchRoot(rootName) {
|
|
24370
|
-
const absRoot =
|
|
24677
|
+
const absRoot = resolve31(dataRoot, rootName);
|
|
24371
24678
|
try {
|
|
24372
24679
|
const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
|
|
24373
24680
|
for await (const event of iter) {
|
|
@@ -24406,7 +24713,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24406
24713
|
|
|
24407
24714
|
// app/lib/migrate-uploads.ts
|
|
24408
24715
|
import { mkdir as mkdir5, readdir as readdir5, rename as rename2, rm as rm3 } from "fs/promises";
|
|
24409
|
-
import { dirname as dirname13, relative as relative6, resolve as
|
|
24716
|
+
import { dirname as dirname13, relative as relative6, resolve as resolve32 } from "path";
|
|
24410
24717
|
var ACCOUNT_UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
24411
24718
|
async function walkFiles(dir) {
|
|
24412
24719
|
const out = [];
|
|
@@ -24417,7 +24724,7 @@ async function walkFiles(dir) {
|
|
|
24417
24724
|
return out;
|
|
24418
24725
|
}
|
|
24419
24726
|
for (const e of entries) {
|
|
24420
|
-
const abs =
|
|
24727
|
+
const abs = resolve32(dir, e.name);
|
|
24421
24728
|
if (e.isDirectory()) {
|
|
24422
24729
|
out.push(...await walkFiles(abs));
|
|
24423
24730
|
} else if (e.isFile()) {
|
|
@@ -24437,7 +24744,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
24437
24744
|
let moved = 0;
|
|
24438
24745
|
for (const oldAbs of files) {
|
|
24439
24746
|
const suffix = relative6(srcDir, oldAbs);
|
|
24440
|
-
const newAbs =
|
|
24747
|
+
const newAbs = resolve32(destDir, suffix);
|
|
24441
24748
|
await mkdir5(dirname13(newAbs), { recursive: true });
|
|
24442
24749
|
await rename2(oldAbs, newAbs);
|
|
24443
24750
|
moved++;
|
|
@@ -24464,7 +24771,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
24464
24771
|
}
|
|
24465
24772
|
async function migrateUploads(opts = {}) {
|
|
24466
24773
|
const dataRoot = opts.dataRoot ?? DATA_ROOT;
|
|
24467
|
-
const oldRoot =
|
|
24774
|
+
const oldRoot = resolve32(dataRoot, "uploads");
|
|
24468
24775
|
let topEntries;
|
|
24469
24776
|
try {
|
|
24470
24777
|
topEntries = await readdir5(oldRoot, { withFileTypes: true });
|
|
@@ -24485,8 +24792,8 @@ async function migrateUploads(opts = {}) {
|
|
|
24485
24792
|
const name = entry.name;
|
|
24486
24793
|
if (ACCOUNT_UUID_RE5.test(name)) {
|
|
24487
24794
|
moved += await relocateTree(
|
|
24488
|
-
|
|
24489
|
-
|
|
24795
|
+
resolve32(oldRoot, name),
|
|
24796
|
+
resolve32(dataRoot, "accounts", name, "uploads"),
|
|
24490
24797
|
dataRoot,
|
|
24491
24798
|
name,
|
|
24492
24799
|
session
|
|
@@ -24498,8 +24805,8 @@ async function migrateUploads(opts = {}) {
|
|
|
24498
24805
|
continue;
|
|
24499
24806
|
}
|
|
24500
24807
|
moved += await relocateTree(
|
|
24501
|
-
|
|
24502
|
-
|
|
24808
|
+
resolve32(oldRoot, "public"),
|
|
24809
|
+
resolve32(dataRoot, "accounts", installAccountId, "uploads", "public"),
|
|
24503
24810
|
dataRoot,
|
|
24504
24811
|
null,
|
|
24505
24812
|
// public uploads carry no graph nodes
|
|
@@ -24528,9 +24835,9 @@ async function migrateUploads(opts = {}) {
|
|
|
24528
24835
|
}
|
|
24529
24836
|
|
|
24530
24837
|
// app/lib/migrate-admin-webchat-sidecars.ts
|
|
24531
|
-
import { readdir as readdir6, readFile as
|
|
24838
|
+
import { readdir as readdir6, readFile as readFile7, rename as rename3, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
|
|
24532
24839
|
import { existsSync as existsSync36 } from "fs";
|
|
24533
|
-
import { join as
|
|
24840
|
+
import { join as join42 } from "path";
|
|
24534
24841
|
var ADMIN_ROLE2 = "admin";
|
|
24535
24842
|
var WEBCHAT_CHANNEL2 = "webchat";
|
|
24536
24843
|
var SESSION_META_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
|
|
@@ -24538,10 +24845,10 @@ var defaultResolveName = async (_accountId, userId) => {
|
|
|
24538
24845
|
const res = await loadAdminUserName(userId);
|
|
24539
24846
|
return res.source === "neo4j" && res.joined.length > 0 ? res.joined : null;
|
|
24540
24847
|
};
|
|
24541
|
-
async function
|
|
24848
|
+
async function readRaw3(path3) {
|
|
24542
24849
|
let raw;
|
|
24543
24850
|
try {
|
|
24544
|
-
raw = await
|
|
24851
|
+
raw = await readFile7(path3, "utf8");
|
|
24545
24852
|
} catch {
|
|
24546
24853
|
return null;
|
|
24547
24854
|
}
|
|
@@ -24575,7 +24882,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
24575
24882
|
}
|
|
24576
24883
|
for (const slug of slugs) {
|
|
24577
24884
|
if (!slug.isDirectory()) continue;
|
|
24578
|
-
const slugDir =
|
|
24885
|
+
const slugDir = join42(projectsRoot, slug.name);
|
|
24579
24886
|
let entries;
|
|
24580
24887
|
try {
|
|
24581
24888
|
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
@@ -24584,9 +24891,9 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
24584
24891
|
}
|
|
24585
24892
|
for (const entry of entries) {
|
|
24586
24893
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
24587
|
-
out.push(
|
|
24894
|
+
out.push(join42(slugDir, entry.name));
|
|
24588
24895
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
24589
|
-
const subDir =
|
|
24896
|
+
const subDir = join42(slugDir, entry.name);
|
|
24590
24897
|
let subs;
|
|
24591
24898
|
try {
|
|
24592
24899
|
subs = await readdir6(subDir, { withFileTypes: true });
|
|
@@ -24594,7 +24901,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
24594
24901
|
continue;
|
|
24595
24902
|
}
|
|
24596
24903
|
for (const s of subs) {
|
|
24597
|
-
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(
|
|
24904
|
+
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join42(subDir, s.name));
|
|
24598
24905
|
}
|
|
24599
24906
|
}
|
|
24600
24907
|
}
|
|
@@ -24606,7 +24913,7 @@ function shortId(path3) {
|
|
|
24606
24913
|
return base.slice(0, 8);
|
|
24607
24914
|
}
|
|
24608
24915
|
async function migrateAdminWebchatSidecars(opts = {}) {
|
|
24609
|
-
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ?
|
|
24916
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join42(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
24610
24917
|
const usersFile = opts.usersFile ?? USERS_FILE;
|
|
24611
24918
|
const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
|
|
24612
24919
|
const resolveName = opts.resolveName ?? defaultResolveName;
|
|
@@ -24646,7 +24953,7 @@ async function migrateAdminWebchatSidecars(opts = {}) {
|
|
|
24646
24953
|
};
|
|
24647
24954
|
const sidecars = await collectLiveSidecars(projectsRoot);
|
|
24648
24955
|
for (const path3 of sidecars) {
|
|
24649
|
-
const raw = await
|
|
24956
|
+
const raw = await readRaw3(path3);
|
|
24650
24957
|
if (!raw) continue;
|
|
24651
24958
|
if (raw.role !== ADMIN_ROLE2 || raw.channel !== WEBCHAT_CHANNEL2) continue;
|
|
24652
24959
|
result.scanned++;
|
|
@@ -24664,7 +24971,7 @@ async function migrateAdminWebchatSidecars(opts = {}) {
|
|
|
24664
24971
|
await writeRaw(path3, raw);
|
|
24665
24972
|
result.backfilled++;
|
|
24666
24973
|
await resolveAdminName();
|
|
24667
|
-
const back = await
|
|
24974
|
+
const back = await readRaw3(path3);
|
|
24668
24975
|
const wrote = back && back.adminUserId === soleAdminUserId;
|
|
24669
24976
|
if (wrote && resolvedNameForAdmin) {
|
|
24670
24977
|
result.resolvedName++;
|
|
@@ -24951,8 +25258,8 @@ var streamSSE = (c, cb, onError) => {
|
|
|
24951
25258
|
|
|
24952
25259
|
// app/lib/whatsapp/gateway/routes.ts
|
|
24953
25260
|
function createWaChannelRoutes(deps) {
|
|
24954
|
-
const
|
|
24955
|
-
|
|
25261
|
+
const app65 = new Hono();
|
|
25262
|
+
app65.get("/wa-channel/inbound", (c) => {
|
|
24956
25263
|
const senderId = c.req.query("senderId");
|
|
24957
25264
|
if (!senderId) return c.json({ error: "senderId required" }, 400);
|
|
24958
25265
|
return streamSSE(c, async (stream2) => {
|
|
@@ -24960,9 +25267,11 @@ function createWaChannelRoutes(deps) {
|
|
|
24960
25267
|
void stream2.writeSSE({ data: JSON.stringify(payload) });
|
|
24961
25268
|
});
|
|
24962
25269
|
console.error(`[whatsapp-native] op=channel-attached senderId=${senderId}`);
|
|
25270
|
+
deps.onAttached?.(senderId);
|
|
24963
25271
|
stream2.onAbort(() => {
|
|
24964
25272
|
deps.hub.detach(senderId);
|
|
24965
25273
|
console.error(`[whatsapp-native] op=channel-detached senderId=${senderId}`);
|
|
25274
|
+
deps.onDetached?.(senderId);
|
|
24966
25275
|
});
|
|
24967
25276
|
while (!stream2.aborted) {
|
|
24968
25277
|
await stream2.sleep(15e3);
|
|
@@ -24970,7 +25279,7 @@ function createWaChannelRoutes(deps) {
|
|
|
24970
25279
|
}
|
|
24971
25280
|
});
|
|
24972
25281
|
});
|
|
24973
|
-
|
|
25282
|
+
app65.post("/wa-channel/reply", async (c) => {
|
|
24974
25283
|
const body = await c.req.json().catch(() => null);
|
|
24975
25284
|
const senderId = body?.senderId;
|
|
24976
25285
|
const text = body?.text;
|
|
@@ -24991,7 +25300,7 @@ function createWaChannelRoutes(deps) {
|
|
|
24991
25300
|
console.error(`[whatsapp-native] op=reply-dispatch senderId=${senderId} bytes=${bytes}`);
|
|
24992
25301
|
return c.json({ ok: true });
|
|
24993
25302
|
});
|
|
24994
|
-
|
|
25303
|
+
app65.post("/wa-channel/reply-document", async (c) => {
|
|
24995
25304
|
const body = await c.req.json().catch(() => null);
|
|
24996
25305
|
const senderId = body?.senderId;
|
|
24997
25306
|
const files = body?.files;
|
|
@@ -25030,7 +25339,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25030
25339
|
}
|
|
25031
25340
|
return c.json({ ok: true, results });
|
|
25032
25341
|
});
|
|
25033
|
-
|
|
25342
|
+
app65.post("/wa-channel/ready", async (c) => {
|
|
25034
25343
|
const body = await c.req.json().catch(() => null);
|
|
25035
25344
|
const senderId = body?.senderId;
|
|
25036
25345
|
if (typeof senderId !== "string") {
|
|
@@ -25039,7 +25348,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25039
25348
|
deps.onReady?.(senderId);
|
|
25040
25349
|
return c.json({ ok: true });
|
|
25041
25350
|
});
|
|
25042
|
-
|
|
25351
|
+
app65.post("/wa-channel/received", async (c) => {
|
|
25043
25352
|
const body = await c.req.json().catch(() => null);
|
|
25044
25353
|
const senderId = body?.senderId;
|
|
25045
25354
|
const waMessageId = body?.waMessageId;
|
|
@@ -25049,7 +25358,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25049
25358
|
deps.onReceived?.(senderId, waMessageId);
|
|
25050
25359
|
return c.json({ ok: true });
|
|
25051
25360
|
});
|
|
25052
|
-
|
|
25361
|
+
app65.post("/wa-channel/turn-end", async (c) => {
|
|
25053
25362
|
const body = await c.req.json().catch(() => null);
|
|
25054
25363
|
const senderId = body?.senderId;
|
|
25055
25364
|
const sessionId = body?.sessionId;
|
|
@@ -25082,7 +25391,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25082
25391
|
console.error(`[whatsapp-native] op=turn-end sessionId=${sid} replied=no delivered=none`);
|
|
25083
25392
|
return c.json({ ok: true, delivered: "none" });
|
|
25084
25393
|
});
|
|
25085
|
-
return
|
|
25394
|
+
return app65;
|
|
25086
25395
|
}
|
|
25087
25396
|
|
|
25088
25397
|
// app/lib/whatsapp/gateway/wa-gateway.ts
|
|
@@ -25104,9 +25413,30 @@ var WaGateway = class {
|
|
|
25104
25413
|
spawning = /* @__PURE__ */ new Set();
|
|
25105
25414
|
seq = 0;
|
|
25106
25415
|
/** Hono sub-app exposing the loopback channel routes; mount on the UI app. */
|
|
25416
|
+
/** Task 1789 — senderId -> the ms at which its channel server last detached,
|
|
25417
|
+
* cleared on attach. The standing audit reads this; nothing else does. */
|
|
25418
|
+
detachedAt = /* @__PURE__ */ new Map();
|
|
25419
|
+
/** Seat lost. `atMs` is injected so the audit's accounting is testable. */
|
|
25420
|
+
recordDetached(senderId, atMs) {
|
|
25421
|
+
this.detachedAt.set(senderId, atMs);
|
|
25422
|
+
}
|
|
25423
|
+
/** Seat regained — the sender is no longer unseated. */
|
|
25424
|
+
recordAttached(senderId) {
|
|
25425
|
+
this.detachedAt.delete(senderId);
|
|
25426
|
+
}
|
|
25427
|
+
/** Senders with no channel server for longer than `olderThanMs`. */
|
|
25428
|
+
unseatedSenders(nowMs, olderThanMs) {
|
|
25429
|
+
const out = [];
|
|
25430
|
+
for (const [senderId, sinceMs] of this.detachedAt) {
|
|
25431
|
+
if (nowMs - sinceMs > olderThanMs) out.push({ senderId, sinceMs });
|
|
25432
|
+
}
|
|
25433
|
+
return out.sort((a, b) => a.senderId.localeCompare(b.senderId));
|
|
25434
|
+
}
|
|
25107
25435
|
routes() {
|
|
25108
25436
|
return createWaChannelRoutes({
|
|
25109
25437
|
hub: this.hub,
|
|
25438
|
+
onAttached: (senderId) => this.recordAttached(senderId),
|
|
25439
|
+
onDetached: (senderId) => this.recordDetached(senderId, Date.now()),
|
|
25110
25440
|
sendOutbound: (senderId, text) => this.sendOutbound(senderId, text),
|
|
25111
25441
|
sendDocument: (senderId, filePath, caption) => this.sendDocument(senderId, filePath, caption),
|
|
25112
25442
|
onReady: (senderId) => {
|
|
@@ -25217,7 +25547,7 @@ var WaGateway = class {
|
|
|
25217
25547
|
},
|
|
25218
25548
|
Date.now()
|
|
25219
25549
|
);
|
|
25220
|
-
const resolvedSessionId = adminSessionIdFor(
|
|
25550
|
+
const resolvedSessionId = (input.role === "admin" ? this.deps.resolveSessionOverride?.(input.effectiveAccountId, senderId) : null) || adminSessionIdFor(
|
|
25221
25551
|
input.effectiveAccountId,
|
|
25222
25552
|
senderId,
|
|
25223
25553
|
input.role === "public" && input.personId ? input.personId : void 0
|
|
@@ -25285,17 +25615,28 @@ function logDuplicateSenderGroups(groups) {
|
|
|
25285
25615
|
);
|
|
25286
25616
|
}
|
|
25287
25617
|
}
|
|
25618
|
+
function logUnseatedSenders(entries, nowMs) {
|
|
25619
|
+
for (const e of entries) {
|
|
25620
|
+
const secs = Math.round((nowMs - e.sinceMs) / 1e3);
|
|
25621
|
+
console.error(`[whatsapp-native] op=sender-unseated senderId=${e.senderId} since=${new Date(e.sinceMs).toISOString()} forSeconds=${secs}`);
|
|
25622
|
+
}
|
|
25623
|
+
}
|
|
25288
25624
|
|
|
25289
25625
|
// app/lib/whatsapp/gateway/spawn-request.ts
|
|
25290
25626
|
function buildWaSpawnRequest(input) {
|
|
25291
25627
|
const personId = input.role === "public" && input.personId ? input.personId : void 0;
|
|
25292
25628
|
return {
|
|
25293
|
-
// Task 1746 — the deterministic per-sender id is the
|
|
25294
|
-
// session
|
|
25295
|
-
//
|
|
25296
|
-
//
|
|
25297
|
-
//
|
|
25298
|
-
|
|
25629
|
+
// Task 1746 — the deterministic per-sender id is the DEFAULT id a sender's
|
|
25630
|
+
// session has. Task 1526's per-firing seat override used to replace it on a
|
|
25631
|
+
// scheduled cold start, which spawned a second session per firing; that
|
|
25632
|
+
// session then attached under the sender's own hub key and swallowed their
|
|
25633
|
+
// real messages until it detached, splitting the conversation in two.
|
|
25634
|
+
//
|
|
25635
|
+
// Task 1789's override is not that. It is a stable per-sender forward
|
|
25636
|
+
// pointer written once by an operator re-seat and resolving identically for
|
|
25637
|
+
// every subsequent inbound, so it can never yield a second id for a sender.
|
|
25638
|
+
// Do not remove it without reading server/channel-session-override.ts.
|
|
25639
|
+
sessionId: input.overrideSessionId || adminSessionIdFor(input.accountId, input.senderId, personId),
|
|
25299
25640
|
role: input.role,
|
|
25300
25641
|
channel: "whatsapp",
|
|
25301
25642
|
personId,
|
|
@@ -25479,8 +25820,8 @@ var import_dist7 = __toESM(require_dist(), 1);
|
|
|
25479
25820
|
|
|
25480
25821
|
// app/lib/webchat/gateway/routes.ts
|
|
25481
25822
|
function createWebchatChannelRoutes(deps) {
|
|
25482
|
-
const
|
|
25483
|
-
|
|
25823
|
+
const app65 = new Hono();
|
|
25824
|
+
app65.get("/webchat-channel/inbound", (c) => {
|
|
25484
25825
|
const key = c.req.query("key");
|
|
25485
25826
|
if (!key) return c.json({ error: "key required" }, 400);
|
|
25486
25827
|
return streamSSE(c, async (stream2) => {
|
|
@@ -25498,7 +25839,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25498
25839
|
}
|
|
25499
25840
|
});
|
|
25500
25841
|
});
|
|
25501
|
-
|
|
25842
|
+
app65.post("/webchat-channel/reply", async (c) => {
|
|
25502
25843
|
const body = await c.req.json().catch(() => null);
|
|
25503
25844
|
const key = body?.key;
|
|
25504
25845
|
const text = body?.text;
|
|
@@ -25508,7 +25849,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25508
25849
|
deps.onReply?.(key, text);
|
|
25509
25850
|
return c.json({ ok: true });
|
|
25510
25851
|
});
|
|
25511
|
-
|
|
25852
|
+
app65.post("/webchat-channel/connector-auth", async (c) => {
|
|
25512
25853
|
const body = await c.req.json().catch(() => null);
|
|
25513
25854
|
const key = body?.key;
|
|
25514
25855
|
const sessionId = body?.sessionId;
|
|
@@ -25521,7 +25862,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25521
25862
|
const result = await deps.onConnectorAuth({ key, sessionId, name, completed });
|
|
25522
25863
|
return c.json(result);
|
|
25523
25864
|
});
|
|
25524
|
-
|
|
25865
|
+
app65.post("/webchat-channel/ready", async (c) => {
|
|
25525
25866
|
const body = await c.req.json().catch(() => null);
|
|
25526
25867
|
const key = body?.key;
|
|
25527
25868
|
if (typeof key !== "string") {
|
|
@@ -25530,7 +25871,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25530
25871
|
deps.onReady?.(key);
|
|
25531
25872
|
return c.json({ ok: true });
|
|
25532
25873
|
});
|
|
25533
|
-
|
|
25874
|
+
app65.post("/webchat-channel/permission-request", async (c) => {
|
|
25534
25875
|
const body = await c.req.json().catch(() => null);
|
|
25535
25876
|
const key = body?.key;
|
|
25536
25877
|
const requestId = body?.request_id;
|
|
@@ -25544,7 +25885,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25544
25885
|
const verdict = await deps.awaitPermissionVerdict({ key, requestId, toolName, description, inputPreview });
|
|
25545
25886
|
return c.json(verdict);
|
|
25546
25887
|
});
|
|
25547
|
-
|
|
25888
|
+
app65.post("/webchat-channel/received", async (c) => {
|
|
25548
25889
|
const body = await c.req.json().catch(() => null);
|
|
25549
25890
|
const key = body?.key;
|
|
25550
25891
|
const messageId = body?.messageId;
|
|
@@ -25554,7 +25895,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25554
25895
|
deps.onReceived?.(key, messageId);
|
|
25555
25896
|
return c.json({ ok: true });
|
|
25556
25897
|
});
|
|
25557
|
-
|
|
25898
|
+
app65.post("/webchat-channel/turn-end", async (c) => {
|
|
25558
25899
|
const body = await c.req.json().catch(() => null);
|
|
25559
25900
|
const key = body?.key;
|
|
25560
25901
|
const sessionId = body?.sessionId;
|
|
@@ -25577,7 +25918,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25577
25918
|
console.error(`[webchat-native] op=turn-end channel=webchat key=${k} sessionId=${sid} replied=no delivered=none`);
|
|
25578
25919
|
return c.json({ ok: true, delivered: "none" });
|
|
25579
25920
|
});
|
|
25580
|
-
return
|
|
25921
|
+
return app65;
|
|
25581
25922
|
}
|
|
25582
25923
|
|
|
25583
25924
|
// app/lib/webchat/gateway/webchat-gateway.ts
|
|
@@ -25635,16 +25976,16 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
25635
25976
|
* The public /api/chat route awaits this to return the reply on the same SSE
|
|
25636
25977
|
* response, preserving the pre-756 POST→single-blob browser contract. */
|
|
25637
25978
|
awaitReply(key, timeoutMs) {
|
|
25638
|
-
return new Promise((
|
|
25979
|
+
return new Promise((resolve38) => {
|
|
25639
25980
|
const timer = setTimeout(() => {
|
|
25640
25981
|
this.replyAwaiters.delete(key);
|
|
25641
|
-
|
|
25982
|
+
resolve38({ timeout: true });
|
|
25642
25983
|
}, timeoutMs);
|
|
25643
25984
|
if (timer.unref) timer.unref();
|
|
25644
25985
|
this.replyAwaiters.set(key, (r) => {
|
|
25645
25986
|
clearTimeout(timer);
|
|
25646
25987
|
this.replyAwaiters.delete(key);
|
|
25647
|
-
|
|
25988
|
+
resolve38(r);
|
|
25648
25989
|
});
|
|
25649
25990
|
});
|
|
25650
25991
|
}
|
|
@@ -25655,11 +25996,11 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
25655
25996
|
* terminal dialog stays answerable). */
|
|
25656
25997
|
awaitPermissionVerdict(p, timeoutMs) {
|
|
25657
25998
|
const k = _WebchatGateway.promptKey(p.key, p.requestId);
|
|
25658
|
-
return new Promise((
|
|
25999
|
+
return new Promise((resolve38) => {
|
|
25659
26000
|
this.pendingPrompts.get(k)?.resolve({ timeout: true });
|
|
25660
26001
|
const timer = setTimeout(() => {
|
|
25661
26002
|
this.pendingPrompts.delete(k);
|
|
25662
|
-
|
|
26003
|
+
resolve38({ timeout: true });
|
|
25663
26004
|
}, timeoutMs);
|
|
25664
26005
|
if (timer.unref) timer.unref();
|
|
25665
26006
|
this.pendingPrompts.set(k, {
|
|
@@ -25668,7 +26009,7 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
25668
26009
|
resolve: (r) => {
|
|
25669
26010
|
clearTimeout(timer);
|
|
25670
26011
|
this.pendingPrompts.delete(k);
|
|
25671
|
-
|
|
26012
|
+
resolve38(r);
|
|
25672
26013
|
}
|
|
25673
26014
|
});
|
|
25674
26015
|
console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
|
|
@@ -26233,7 +26574,7 @@ async function fanOut(subscribers, text, onError, tag) {
|
|
|
26233
26574
|
|
|
26234
26575
|
// app/lib/webchat/file-delivery.ts
|
|
26235
26576
|
import { realpathSync as realpathSync8 } from "fs";
|
|
26236
|
-
import { resolve as
|
|
26577
|
+
import { resolve as resolve33 } from "path";
|
|
26237
26578
|
|
|
26238
26579
|
// app/lib/channel-pty-bridge/file-delivery.ts
|
|
26239
26580
|
var SEND_USER_FILE = "SendUserFile";
|
|
@@ -26324,29 +26665,29 @@ function makeFileDelivery(opts) {
|
|
|
26324
26665
|
}
|
|
26325
26666
|
|
|
26326
26667
|
// app/lib/webchat/file-delivery.ts
|
|
26327
|
-
var
|
|
26668
|
+
var TAG49 = "[webchat-adaptor]";
|
|
26328
26669
|
function platformRoot2() {
|
|
26329
26670
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
26330
26671
|
}
|
|
26331
26672
|
function makeWebchatSendFile(entry) {
|
|
26332
26673
|
return async (filePath) => {
|
|
26333
26674
|
if (!entry.accountId) {
|
|
26334
|
-
console.error(`${
|
|
26675
|
+
console.error(`${TAG49} file-delivery reject reason=no-account sender=${entry.senderId}`);
|
|
26335
26676
|
return { ok: false, error: "no-account" };
|
|
26336
26677
|
}
|
|
26337
|
-
const accountDir =
|
|
26678
|
+
const accountDir = resolve33(platformRoot2(), "..", "data/accounts", entry.accountId);
|
|
26338
26679
|
try {
|
|
26339
26680
|
const resolved = realpathSync8(filePath);
|
|
26340
26681
|
const accountResolved = realpathSync8(accountDir);
|
|
26341
26682
|
if (!resolved.startsWith(accountResolved + "/")) {
|
|
26342
|
-
console.error(`${
|
|
26683
|
+
console.error(`${TAG49} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
|
|
26343
26684
|
return { ok: false, error: "outside-account" };
|
|
26344
26685
|
}
|
|
26345
26686
|
return { ok: true };
|
|
26346
26687
|
} catch (err) {
|
|
26347
26688
|
const code = err.code;
|
|
26348
26689
|
console.error(
|
|
26349
|
-
`${
|
|
26690
|
+
`${TAG49} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
|
|
26350
26691
|
);
|
|
26351
26692
|
return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
|
|
26352
26693
|
}
|
|
@@ -26355,7 +26696,7 @@ function makeWebchatSendFile(entry) {
|
|
|
26355
26696
|
function makeWebchatFileDelivery(entry) {
|
|
26356
26697
|
return makeFileDelivery({
|
|
26357
26698
|
entry,
|
|
26358
|
-
tag:
|
|
26699
|
+
tag: TAG49,
|
|
26359
26700
|
channel: "webchat",
|
|
26360
26701
|
sendFile: makeWebchatSendFile(entry),
|
|
26361
26702
|
deferUntilVerdict: true
|
|
@@ -26417,7 +26758,7 @@ function buildPublicWebchatSpawnRequest(input) {
|
|
|
26417
26758
|
}
|
|
26418
26759
|
|
|
26419
26760
|
// app/lib/whatsapp/inbound/file-delivery-bridge.ts
|
|
26420
|
-
var
|
|
26761
|
+
var TAG50 = "[whatsapp-adaptor]";
|
|
26421
26762
|
var SEND_USER_FILE2 = "SendUserFile";
|
|
26422
26763
|
var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
|
|
26423
26764
|
function platformRoot3() {
|
|
@@ -26435,7 +26776,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
|
|
|
26435
26776
|
});
|
|
26436
26777
|
if (result.ok) return { ok: true };
|
|
26437
26778
|
console.error(
|
|
26438
|
-
`${
|
|
26779
|
+
`${TAG50} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
|
|
26439
26780
|
);
|
|
26440
26781
|
return { ok: false, error: result.error };
|
|
26441
26782
|
};
|
|
@@ -26443,7 +26784,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
|
|
|
26443
26784
|
function makeWhatsAppFileDelivery(entry, maxyAccountId) {
|
|
26444
26785
|
const shared = makeFileDelivery({
|
|
26445
26786
|
entry,
|
|
26446
|
-
tag:
|
|
26787
|
+
tag: TAG50,
|
|
26447
26788
|
channel: "whatsapp",
|
|
26448
26789
|
sendFile: makeWhatsAppSendFile(entry, maxyAccountId)
|
|
26449
26790
|
});
|
|
@@ -26478,7 +26819,7 @@ function makeWhatsAppFileDelivery(entry, maxyAccountId) {
|
|
|
26478
26819
|
if (!delivered) {
|
|
26479
26820
|
const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
|
|
26480
26821
|
console.error(
|
|
26481
|
-
`${
|
|
26822
|
+
`${TAG50} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
|
|
26482
26823
|
);
|
|
26483
26824
|
}
|
|
26484
26825
|
}
|
|
@@ -26664,8 +27005,8 @@ var import_dist8 = __toESM(require_dist(), 1);
|
|
|
26664
27005
|
|
|
26665
27006
|
// app/lib/telegram/gateway/routes.ts
|
|
26666
27007
|
function createTelegramChannelRoutes(deps) {
|
|
26667
|
-
const
|
|
26668
|
-
|
|
27008
|
+
const app65 = new Hono();
|
|
27009
|
+
app65.get("/tg-channel/inbound", (c) => {
|
|
26669
27010
|
const key = c.req.query("key");
|
|
26670
27011
|
if (!key) return c.json({ error: "key required" }, 400);
|
|
26671
27012
|
return streamSSE(c, async (stream2) => {
|
|
@@ -26683,7 +27024,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26683
27024
|
}
|
|
26684
27025
|
});
|
|
26685
27026
|
});
|
|
26686
|
-
|
|
27027
|
+
app65.post("/tg-channel/reply", async (c) => {
|
|
26687
27028
|
const body = await c.req.json().catch(() => null);
|
|
26688
27029
|
const key = body?.key;
|
|
26689
27030
|
const text = body?.text;
|
|
@@ -26699,7 +27040,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26699
27040
|
console.error(`[telegram-native] op=reply key=${key} bytes=${Buffer.byteLength(text, "utf8")}`);
|
|
26700
27041
|
return c.json({ ok: true });
|
|
26701
27042
|
});
|
|
26702
|
-
|
|
27043
|
+
app65.post("/tg-channel/ready", async (c) => {
|
|
26703
27044
|
const body = await c.req.json().catch(() => null);
|
|
26704
27045
|
const key = body?.key;
|
|
26705
27046
|
if (typeof key !== "string") {
|
|
@@ -26708,7 +27049,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26708
27049
|
deps.onReady?.(key);
|
|
26709
27050
|
return c.json({ ok: true });
|
|
26710
27051
|
});
|
|
26711
|
-
|
|
27052
|
+
app65.post("/tg-channel/received", async (c) => {
|
|
26712
27053
|
const body = await c.req.json().catch(() => null);
|
|
26713
27054
|
const key = body?.key;
|
|
26714
27055
|
const messageId = body?.messageId;
|
|
@@ -26718,7 +27059,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26718
27059
|
deps.onReceived?.(key, messageId);
|
|
26719
27060
|
return c.json({ ok: true });
|
|
26720
27061
|
});
|
|
26721
|
-
|
|
27062
|
+
app65.post("/tg-channel/turn-end", async (c) => {
|
|
26722
27063
|
const body = await c.req.json().catch(() => null);
|
|
26723
27064
|
const key = body?.key;
|
|
26724
27065
|
const sessionId = body?.sessionId;
|
|
@@ -26751,7 +27092,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26751
27092
|
console.error(`[telegram-native] op=turn-end key=${key} sessionId=${sid} replied=no delivered=none`);
|
|
26752
27093
|
return c.json({ ok: true, delivered: "none" });
|
|
26753
27094
|
});
|
|
26754
|
-
return
|
|
27095
|
+
return app65;
|
|
26755
27096
|
}
|
|
26756
27097
|
|
|
26757
27098
|
// app/lib/telegram/gateway/telegram-gateway.ts
|
|
@@ -26842,7 +27183,7 @@ var TelegramGateway = class {
|
|
|
26842
27183
|
{ key: input.senderId, text: input.text, messageId: `tg-${++this.seq}`, source, standingRules, scheduleProvenance: input.scheduleProvenance },
|
|
26843
27184
|
Date.now()
|
|
26844
27185
|
);
|
|
26845
|
-
const resolvedSessionId = adminSessionIdFor(
|
|
27186
|
+
const resolvedSessionId = (input.role === "admin" ? this.deps.resolveSessionOverride?.(input.accountId, input.senderId) : null) || adminSessionIdFor(
|
|
26846
27187
|
input.accountId,
|
|
26847
27188
|
input.senderId,
|
|
26848
27189
|
input.role === "public" && input.personId ? input.personId : void 0
|
|
@@ -26879,10 +27220,11 @@ var TelegramGateway = class {
|
|
|
26879
27220
|
function buildTelegramSpawnRequest(input) {
|
|
26880
27221
|
const personId = input.role === "public" && input.personId ? input.personId : void 0;
|
|
26881
27222
|
return {
|
|
26882
|
-
// Task 1746 — the deterministic per-sender id is the
|
|
26883
|
-
//
|
|
26884
|
-
//
|
|
26885
|
-
|
|
27223
|
+
// Task 1746 / 1789 — the deterministic per-sender id is the DEFAULT; an
|
|
27224
|
+
// operator re-seat sets a stable per-sender forward pointer that wins here.
|
|
27225
|
+
// Mirror of the WhatsApp builder; see its comment for the full distinction
|
|
27226
|
+
// between this pointer and Task 1526's removed per-firing seat.
|
|
27227
|
+
sessionId: input.overrideSessionId || adminSessionIdFor(input.accountId, input.senderId, personId),
|
|
26886
27228
|
role: input.role,
|
|
26887
27229
|
channel: "telegram",
|
|
26888
27230
|
personId,
|
|
@@ -26899,9 +27241,9 @@ function buildTelegramSpawnRequest(input) {
|
|
|
26899
27241
|
|
|
26900
27242
|
// app/lib/telegram/outbound/send-document.ts
|
|
26901
27243
|
import { realpathSync as realpathSync9 } from "fs";
|
|
26902
|
-
import { readFile as
|
|
26903
|
-
import { resolve as
|
|
26904
|
-
var
|
|
27244
|
+
import { readFile as readFile8, stat as stat8 } from "fs/promises";
|
|
27245
|
+
import { resolve as resolve34, basename as basename11 } from "path";
|
|
27246
|
+
var TAG51 = "[telegram:outbound]";
|
|
26905
27247
|
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
26906
27248
|
async function sendTelegramDocument(input) {
|
|
26907
27249
|
const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
|
|
@@ -26911,25 +27253,25 @@ async function sendTelegramDocument(input) {
|
|
|
26911
27253
|
if (!maxyAccountId || !platformRoot5) {
|
|
26912
27254
|
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
26913
27255
|
}
|
|
26914
|
-
const accountDir =
|
|
27256
|
+
const accountDir = resolve34(platformRoot5, "..", "data/accounts", maxyAccountId);
|
|
26915
27257
|
let resolvedPath;
|
|
26916
27258
|
try {
|
|
26917
27259
|
resolvedPath = realpathSync9(filePath);
|
|
26918
27260
|
const accountResolved = realpathSync9(accountDir);
|
|
26919
27261
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
26920
|
-
console.error(`${
|
|
27262
|
+
console.error(`${TAG51} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
|
|
26921
27263
|
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
26922
27264
|
}
|
|
26923
27265
|
} catch (err) {
|
|
26924
27266
|
const code = err.code;
|
|
26925
27267
|
if (code === "ENOENT") {
|
|
26926
|
-
console.error(`${
|
|
27268
|
+
console.error(`${TAG51} document ENOENT path=${filePath}`);
|
|
26927
27269
|
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
26928
27270
|
}
|
|
26929
|
-
console.error(`${
|
|
27271
|
+
console.error(`${TAG51} document path error: ${String(err)}`);
|
|
26930
27272
|
return { ok: false, status: 500, error: String(err) };
|
|
26931
27273
|
}
|
|
26932
|
-
const fileStat = await
|
|
27274
|
+
const fileStat = await stat8(resolvedPath);
|
|
26933
27275
|
if (fileStat.size > TELEGRAM_DOCUMENT_MAX_BYTES) {
|
|
26934
27276
|
return {
|
|
26935
27277
|
ok: false,
|
|
@@ -26938,7 +27280,7 @@ async function sendTelegramDocument(input) {
|
|
|
26938
27280
|
};
|
|
26939
27281
|
}
|
|
26940
27282
|
const filename = basename11(resolvedPath);
|
|
26941
|
-
const buffer = Buffer.from(await
|
|
27283
|
+
const buffer = Buffer.from(await readFile8(resolvedPath));
|
|
26942
27284
|
const form = new FormData();
|
|
26943
27285
|
form.append("chat_id", String(chatId));
|
|
26944
27286
|
if (caption) form.append("caption", caption);
|
|
@@ -26959,13 +27301,13 @@ async function sendTelegramDocument(input) {
|
|
|
26959
27301
|
error = e instanceof Error ? e.message : String(e);
|
|
26960
27302
|
}
|
|
26961
27303
|
console.error(
|
|
26962
|
-
`${
|
|
27304
|
+
`${TAG51} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
|
|
26963
27305
|
);
|
|
26964
27306
|
return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
|
|
26965
27307
|
}
|
|
26966
27308
|
|
|
26967
27309
|
// app/lib/telegram/outbound/file-delivery.ts
|
|
26968
|
-
var
|
|
27310
|
+
var TAG52 = "[telegram:outbound]";
|
|
26969
27311
|
function platformRoot4() {
|
|
26970
27312
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
26971
27313
|
}
|
|
@@ -26977,11 +27319,11 @@ function makeTelegramSendFile(entry) {
|
|
|
26977
27319
|
botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
|
|
26978
27320
|
}
|
|
26979
27321
|
if (!botToken) {
|
|
26980
|
-
console.error(`${
|
|
27322
|
+
console.error(`${TAG52} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
|
|
26981
27323
|
return { ok: false, error: "no-bot-token" };
|
|
26982
27324
|
}
|
|
26983
27325
|
if (entry.replyTarget == null) {
|
|
26984
|
-
console.error(`${
|
|
27326
|
+
console.error(`${TAG52} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
|
|
26985
27327
|
return { ok: false, error: "no-reply-target" };
|
|
26986
27328
|
}
|
|
26987
27329
|
const result = await sendTelegramDocument({
|
|
@@ -26998,7 +27340,7 @@ function makeTelegramSendFile(entry) {
|
|
|
26998
27340
|
function makeTelegramFileDelivery(entry) {
|
|
26999
27341
|
return makeFileDelivery({
|
|
27000
27342
|
entry,
|
|
27001
|
-
tag:
|
|
27343
|
+
tag: TAG52,
|
|
27002
27344
|
channel: "telegram",
|
|
27003
27345
|
sendFile: makeTelegramSendFile(entry)
|
|
27004
27346
|
});
|
|
@@ -27038,17 +27380,17 @@ function startTelegramNativeFileFollower(input) {
|
|
|
27038
27380
|
}
|
|
27039
27381
|
|
|
27040
27382
|
// server/telegram-descriptor.ts
|
|
27041
|
-
import { resolve as
|
|
27383
|
+
import { resolve as resolve35, join as join43 } from "path";
|
|
27042
27384
|
function telegramGatewayUrl() {
|
|
27043
27385
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
27044
27386
|
}
|
|
27045
27387
|
function telegramServerPath() {
|
|
27046
|
-
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ??
|
|
27388
|
+
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve35(process.env.MAXY_PLATFORM_ROOT ?? join43(__dirname, ".."), "services/telegram-channel/dist/server.js");
|
|
27047
27389
|
}
|
|
27048
27390
|
|
|
27049
27391
|
// app/lib/channel-pty-bridge/public-session-end-review.ts
|
|
27050
27392
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
27051
|
-
var
|
|
27393
|
+
var TAG53 = "[public-session-review]";
|
|
27052
27394
|
var CONSUMED_CAP = 500;
|
|
27053
27395
|
var consumed = /* @__PURE__ */ new Set();
|
|
27054
27396
|
function consumeOnce(sessionId) {
|
|
@@ -27075,7 +27417,7 @@ async function fetchJsonl(sessionId) {
|
|
|
27075
27417
|
return await res.text();
|
|
27076
27418
|
} catch (err) {
|
|
27077
27419
|
console.error(
|
|
27078
|
-
`${
|
|
27420
|
+
`${TAG53} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
|
|
27079
27421
|
);
|
|
27080
27422
|
return null;
|
|
27081
27423
|
}
|
|
@@ -27099,7 +27441,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
27099
27441
|
const res = await fetch(url);
|
|
27100
27442
|
if (!res.ok) {
|
|
27101
27443
|
console.error(
|
|
27102
|
-
`${
|
|
27444
|
+
`${TAG53} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
|
|
27103
27445
|
);
|
|
27104
27446
|
return [];
|
|
27105
27447
|
}
|
|
@@ -27107,7 +27449,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
27107
27449
|
return Array.isArray(body.writes) ? body.writes : [];
|
|
27108
27450
|
} catch (err) {
|
|
27109
27451
|
console.error(
|
|
27110
|
-
`${
|
|
27452
|
+
`${TAG53} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
27111
27453
|
);
|
|
27112
27454
|
return [];
|
|
27113
27455
|
}
|
|
@@ -27136,7 +27478,7 @@ function composeInitialMessage(input) {
|
|
|
27136
27478
|
}
|
|
27137
27479
|
async function dispatchReviewer(input, initialMessage) {
|
|
27138
27480
|
const sessionId = randomUUID16();
|
|
27139
|
-
console.log(`${
|
|
27481
|
+
console.log(`${TAG53} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
|
|
27140
27482
|
const spawned = await managerRcSpawn({
|
|
27141
27483
|
sessionId,
|
|
27142
27484
|
initialMessage,
|
|
@@ -27156,21 +27498,21 @@ async function firePublicSessionEndReview(input) {
|
|
|
27156
27498
|
const dispatchedAt = Date.now();
|
|
27157
27499
|
if (consumed.has(input.sessionId)) {
|
|
27158
27500
|
console.log(
|
|
27159
|
-
`${
|
|
27501
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
27160
27502
|
);
|
|
27161
27503
|
return;
|
|
27162
27504
|
}
|
|
27163
27505
|
const jsonl = await fetchJsonl(input.sessionId);
|
|
27164
27506
|
if (!jsonl) {
|
|
27165
27507
|
console.log(
|
|
27166
|
-
`${
|
|
27508
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
|
|
27167
27509
|
);
|
|
27168
27510
|
return;
|
|
27169
27511
|
}
|
|
27170
27512
|
const operatorTurns = countOperatorTurns(jsonl);
|
|
27171
27513
|
if (operatorTurns === 0) {
|
|
27172
27514
|
console.log(
|
|
27173
|
-
`${
|
|
27515
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
|
|
27174
27516
|
);
|
|
27175
27517
|
return;
|
|
27176
27518
|
}
|
|
@@ -27184,19 +27526,19 @@ async function firePublicSessionEndReview(input) {
|
|
|
27184
27526
|
});
|
|
27185
27527
|
if (!consumeOnce(input.sessionId)) {
|
|
27186
27528
|
console.log(
|
|
27187
|
-
`${
|
|
27529
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
27188
27530
|
);
|
|
27189
27531
|
return;
|
|
27190
27532
|
}
|
|
27191
27533
|
const dispatched = await dispatchReviewer(input, initialMessage);
|
|
27192
27534
|
if (!dispatched.ok) {
|
|
27193
27535
|
console.error(
|
|
27194
|
-
`${
|
|
27536
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
|
|
27195
27537
|
);
|
|
27196
27538
|
return;
|
|
27197
27539
|
}
|
|
27198
27540
|
console.log(
|
|
27199
|
-
`${
|
|
27541
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
|
|
27200
27542
|
);
|
|
27201
27543
|
}
|
|
27202
27544
|
|
|
@@ -27330,8 +27672,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
27330
27672
|
|
|
27331
27673
|
// ../lib/entitlement/src/index.ts
|
|
27332
27674
|
import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
|
|
27333
|
-
import { existsSync as existsSync37, readFileSync as
|
|
27334
|
-
import { resolve as
|
|
27675
|
+
import { existsSync as existsSync37, readFileSync as readFileSync41, statSync as statSync16 } from "fs";
|
|
27676
|
+
import { resolve as resolve36 } from "path";
|
|
27335
27677
|
|
|
27336
27678
|
// ../lib/entitlement/src/canonicalize.ts
|
|
27337
27679
|
function canonicalize(value) {
|
|
@@ -27366,7 +27708,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
27366
27708
|
var GRACE_DAYS = 7;
|
|
27367
27709
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
27368
27710
|
function pubkeyPath(brand) {
|
|
27369
|
-
return
|
|
27711
|
+
return resolve36(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
27370
27712
|
}
|
|
27371
27713
|
var memo = null;
|
|
27372
27714
|
function memoKey(mtimeMs, account) {
|
|
@@ -27378,12 +27720,12 @@ function resolveEntitlement(brand, account) {
|
|
|
27378
27720
|
if (brand.commercialMode !== true) {
|
|
27379
27721
|
return logResolved(implicitTrust(account), null);
|
|
27380
27722
|
}
|
|
27381
|
-
const entitlementPath =
|
|
27723
|
+
const entitlementPath = resolve36(brand.configDir, "entitlement.json");
|
|
27382
27724
|
if (!existsSync37(entitlementPath)) {
|
|
27383
27725
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
27384
27726
|
}
|
|
27385
|
-
const
|
|
27386
|
-
const key = memoKey(
|
|
27727
|
+
const stat9 = statSync16(entitlementPath);
|
|
27728
|
+
const key = memoKey(stat9.mtimeMs, account);
|
|
27387
27729
|
if (memo && memo.key === key) {
|
|
27388
27730
|
return memo.result;
|
|
27389
27731
|
}
|
|
@@ -27394,7 +27736,7 @@ function resolveEntitlement(brand, account) {
|
|
|
27394
27736
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
27395
27737
|
let pubkeyPem;
|
|
27396
27738
|
try {
|
|
27397
|
-
pubkeyPem =
|
|
27739
|
+
pubkeyPem = readFileSync41(pubkeyPath(brand), "utf-8");
|
|
27398
27740
|
} catch (err) {
|
|
27399
27741
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
27400
27742
|
reason: "pubkey-missing"
|
|
@@ -27408,7 +27750,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
27408
27750
|
}
|
|
27409
27751
|
let envelope;
|
|
27410
27752
|
try {
|
|
27411
|
-
envelope = JSON.parse(
|
|
27753
|
+
envelope = JSON.parse(readFileSync41(entitlementPath, "utf-8"));
|
|
27412
27754
|
} catch {
|
|
27413
27755
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
27414
27756
|
}
|
|
@@ -27570,14 +27912,14 @@ function clientFrom(c) {
|
|
|
27570
27912
|
}
|
|
27571
27913
|
installMediaDownloadGuard();
|
|
27572
27914
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
27573
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT8 ?
|
|
27915
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join44(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
27574
27916
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
27575
27917
|
if (BRAND_JSON_PATH && !existsSync38(BRAND_JSON_PATH)) {
|
|
27576
27918
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
27577
27919
|
}
|
|
27578
27920
|
if (BRAND_JSON_PATH && existsSync38(BRAND_JSON_PATH)) {
|
|
27579
27921
|
try {
|
|
27580
|
-
const parsed = JSON.parse(
|
|
27922
|
+
const parsed = JSON.parse(readFileSync42(BRAND_JSON_PATH, "utf-8"));
|
|
27581
27923
|
BRAND = { ...BRAND, ...parsed };
|
|
27582
27924
|
} catch (err) {
|
|
27583
27925
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -27609,11 +27951,11 @@ var brandLoginOpts = {
|
|
|
27609
27951
|
appleTouchIconPath: brandAppIcon512Path,
|
|
27610
27952
|
themeColor: BRAND.defaultColors?.primary
|
|
27611
27953
|
};
|
|
27612
|
-
var ALIAS_DOMAINS_PATH =
|
|
27954
|
+
var ALIAS_DOMAINS_PATH = join44(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
27613
27955
|
function loadAliasDomains() {
|
|
27614
27956
|
try {
|
|
27615
27957
|
if (!existsSync38(ALIAS_DOMAINS_PATH)) return null;
|
|
27616
|
-
const parsed = JSON.parse(
|
|
27958
|
+
const parsed = JSON.parse(readFileSync42(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
27617
27959
|
if (!Array.isArray(parsed)) {
|
|
27618
27960
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
27619
27961
|
return null;
|
|
@@ -27637,7 +27979,15 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
|
|
|
27637
27979
|
function isPublicHost(host) {
|
|
27638
27980
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
27639
27981
|
}
|
|
27640
|
-
var
|
|
27982
|
+
var app64 = new Hono();
|
|
27983
|
+
function resolveChannelOverride(accountId, channel, senderId) {
|
|
27984
|
+
try {
|
|
27985
|
+
const dir = listValidAccounts().find((a) => a.accountId === accountId)?.accountDir;
|
|
27986
|
+
return dir ? readChannelOverrideId(dir, channel, senderId) : null;
|
|
27987
|
+
} catch {
|
|
27988
|
+
return null;
|
|
27989
|
+
}
|
|
27990
|
+
}
|
|
27641
27991
|
var nativeFileFollowers = /* @__PURE__ */ new Map();
|
|
27642
27992
|
var webchatFileFollowers = /* @__PURE__ */ new Map();
|
|
27643
27993
|
async function fetchAccountStandingRules(accountId) {
|
|
@@ -27676,8 +28026,9 @@ async function recordPassiveDispatch(input) {
|
|
|
27676
28026
|
}
|
|
27677
28027
|
var waGateway = new WaGateway({
|
|
27678
28028
|
fetchStandingRules: fetchAccountStandingRules,
|
|
28029
|
+
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
27679
28030
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
27680
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ??
|
|
28031
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve37(process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
27681
28032
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
27682
28033
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
27683
28034
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -27692,7 +28043,7 @@ var waGateway = new WaGateway({
|
|
|
27692
28043
|
caption,
|
|
27693
28044
|
accountId,
|
|
27694
28045
|
maxyAccountId,
|
|
27695
|
-
platformRoot:
|
|
28046
|
+
platformRoot: resolve37(process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, ".."))
|
|
27696
28047
|
});
|
|
27697
28048
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
27698
28049
|
},
|
|
@@ -27744,7 +28095,17 @@ var waGateway = new WaGateway({
|
|
|
27744
28095
|
const managesAccount = effectiveAccountId !== accountId ? effectiveAccountId : "none";
|
|
27745
28096
|
console.error(`[whatsapp-native] op=account-manager-route senderId=${senderId} managesAccount=${managesAccount} effectiveAccount=${effectiveAccountId} source=gate`);
|
|
27746
28097
|
}
|
|
27747
|
-
const req = buildWaSpawnRequest({
|
|
28098
|
+
const req = buildWaSpawnRequest({
|
|
28099
|
+
accountId: effectiveAccountId,
|
|
28100
|
+
senderId,
|
|
28101
|
+
role,
|
|
28102
|
+
personId,
|
|
28103
|
+
gatewayUrl,
|
|
28104
|
+
serverPath,
|
|
28105
|
+
// Task 1789 — admin rows only: a public session's id keys on personId as
|
|
28106
|
+
// well as senderId, so the per-sender pointer is not applicable to it.
|
|
28107
|
+
overrideSessionId: role === "admin" ? resolveChannelOverride(effectiveAccountId, "whatsapp", senderId) : null
|
|
28108
|
+
});
|
|
27748
28109
|
await ensureSessionConversation({
|
|
27749
28110
|
accountId: effectiveAccountId,
|
|
27750
28111
|
sessionId: req.sessionId,
|
|
@@ -27790,13 +28151,13 @@ var waGateway = new WaGateway({
|
|
|
27790
28151
|
nativeFileFollowers.set(senderId, ac);
|
|
27791
28152
|
}
|
|
27792
28153
|
});
|
|
27793
|
-
|
|
28154
|
+
app64.route("/", waGateway.routes());
|
|
27794
28155
|
waGateway.startSweeper();
|
|
27795
28156
|
function runDuplicateSenderAudit() {
|
|
27796
28157
|
try {
|
|
27797
28158
|
const configDir2 = process.env.CLAUDE_CONFIG_DIR;
|
|
27798
28159
|
if (!configDir2) return;
|
|
27799
|
-
const projectsRoot =
|
|
28160
|
+
const projectsRoot = join44(configDir2, "projects");
|
|
27800
28161
|
const records = [];
|
|
27801
28162
|
for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
|
|
27802
28163
|
if (isSubagent || archived) continue;
|
|
@@ -27806,6 +28167,8 @@ function runDuplicateSenderAudit() {
|
|
|
27806
28167
|
records.push({ sessionId, channel: meta.channel, senderId: meta.senderId, accountId: meta.accountId });
|
|
27807
28168
|
}
|
|
27808
28169
|
logDuplicateSenderGroups(findDuplicateSenderGroups(records));
|
|
28170
|
+
const now = Date.now();
|
|
28171
|
+
logUnseatedSenders(waGateway.unseatedSenders(now, 15 * 6e4), now);
|
|
27809
28172
|
} catch (err) {
|
|
27810
28173
|
console.error(`[whatsapp-native] op=duplicate-sender-audit-error error=${err instanceof Error ? err.message : String(err)}`);
|
|
27811
28174
|
}
|
|
@@ -27912,16 +28275,26 @@ var webchatGateway = new WebchatGateway({
|
|
|
27912
28275
|
firePublicSessionEndReview: (input) => firePublicSessionEndReview(input)
|
|
27913
28276
|
});
|
|
27914
28277
|
setWebchatGateway(webchatGateway);
|
|
27915
|
-
|
|
28278
|
+
app64.route("/", webchatGateway.routes());
|
|
27916
28279
|
webchatGateway.startSweeper();
|
|
27917
28280
|
webchatGateway.startPublicReaper();
|
|
27918
28281
|
var telegramFileFollowers = /* @__PURE__ */ new Map();
|
|
27919
28282
|
var telegramGateway = new TelegramGateway({
|
|
27920
28283
|
fetchStandingRules: fetchAccountStandingRules,
|
|
28284
|
+
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "telegram", senderId),
|
|
27921
28285
|
gatewayUrl: telegramGatewayUrl(),
|
|
27922
28286
|
serverPath: telegramServerPath(),
|
|
27923
28287
|
ensureChannelSession: async ({ accountId, senderId, role, personId, chatId, gatewayUrl, serverPath }) => {
|
|
27924
|
-
const req = buildTelegramSpawnRequest({
|
|
28288
|
+
const req = buildTelegramSpawnRequest({
|
|
28289
|
+
accountId,
|
|
28290
|
+
senderId,
|
|
28291
|
+
role,
|
|
28292
|
+
personId,
|
|
28293
|
+
gatewayUrl,
|
|
28294
|
+
serverPath,
|
|
28295
|
+
// Task 1789 — admin rows only; see the WhatsApp twin.
|
|
28296
|
+
overrideSessionId: role === "admin" ? resolveChannelOverride(accountId, "telegram", senderId) : null
|
|
28297
|
+
});
|
|
27925
28298
|
const result = await managerRcSpawn(req);
|
|
27926
28299
|
if ("error" in result) {
|
|
27927
28300
|
console.error(`[telegram-native] spawn-failed senderId=${senderId} role=${role} error=${result.error} status=${result.status}`);
|
|
@@ -27946,7 +28319,7 @@ var telegramGateway = new TelegramGateway({
|
|
|
27946
28319
|
}
|
|
27947
28320
|
});
|
|
27948
28321
|
setTelegramGateway(telegramGateway);
|
|
27949
|
-
|
|
28322
|
+
app64.route("/", telegramGateway.routes());
|
|
27950
28323
|
telegramGateway.startSweeper();
|
|
27951
28324
|
var chatRoutes = createChatRoutes({
|
|
27952
28325
|
handleInbound: (input) => webchatGateway.handleInbound(input),
|
|
@@ -27980,19 +28353,19 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
|
|
|
27980
28353
|
return managed.account;
|
|
27981
28354
|
}
|
|
27982
28355
|
});
|
|
27983
|
-
|
|
27984
|
-
|
|
28356
|
+
app64.route("/api/channel/schedule-inject", scheduleInjectRoutes);
|
|
28357
|
+
app64.use("*", clientIpMiddleware);
|
|
27985
28358
|
function allowsSameOriginFraming(path3) {
|
|
27986
28359
|
return path3 === "/vnc-viewer.html" || path3.startsWith("/api/admin/attachment/") || path3.startsWith("/api/public-reader/attachment/") || path3 === "/api/admin/files/download";
|
|
27987
28360
|
}
|
|
27988
|
-
|
|
28361
|
+
app64.use("*", async (c, next) => {
|
|
27989
28362
|
await next();
|
|
27990
28363
|
c.header("X-Content-Type-Options", "nosniff");
|
|
27991
28364
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
27992
28365
|
c.header("X-Frame-Options", allowsSameOriginFraming(c.req.path) ? "SAMEORIGIN" : "DENY");
|
|
27993
28366
|
});
|
|
27994
28367
|
var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
|
|
27995
|
-
|
|
28368
|
+
app64.use("*", async (c, next) => {
|
|
27996
28369
|
if (!HTTP_LOG_PATHS.has(c.req.path)) {
|
|
27997
28370
|
await next();
|
|
27998
28371
|
return;
|
|
@@ -28010,7 +28383,7 @@ app63.use("*", async (c, next) => {
|
|
|
28010
28383
|
});
|
|
28011
28384
|
}
|
|
28012
28385
|
});
|
|
28013
|
-
|
|
28386
|
+
app64.use("*", async (c, next) => {
|
|
28014
28387
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28015
28388
|
if (isOperatorHost(host, getOperatorDomains()) || !isPublicHost(host)) {
|
|
28016
28389
|
await next();
|
|
@@ -28048,7 +28421,7 @@ function resolveRemoteAuthOpts(c) {
|
|
|
28048
28421
|
return { ...brandLoginOpts, origin };
|
|
28049
28422
|
}
|
|
28050
28423
|
var MAX_LOGIN_BODY = 8 * 1024;
|
|
28051
|
-
|
|
28424
|
+
app64.post("/__remote-auth/login", async (c) => {
|
|
28052
28425
|
const client = clientFrom(c);
|
|
28053
28426
|
const clientIp = client.ip || "unknown";
|
|
28054
28427
|
if (!requestIsTlsTerminated(c)) {
|
|
@@ -28093,7 +28466,7 @@ app63.post("/__remote-auth/login", async (c) => {
|
|
|
28093
28466
|
}
|
|
28094
28467
|
});
|
|
28095
28468
|
});
|
|
28096
|
-
|
|
28469
|
+
app64.get("/__remote-auth/logout", (c) => {
|
|
28097
28470
|
const client = clientFrom(c);
|
|
28098
28471
|
const clientIp = client.ip || "unknown";
|
|
28099
28472
|
console.error(`[remote-auth] logout ip=${clientIp}`);
|
|
@@ -28106,7 +28479,7 @@ app63.get("/__remote-auth/logout", (c) => {
|
|
|
28106
28479
|
}
|
|
28107
28480
|
});
|
|
28108
28481
|
});
|
|
28109
|
-
|
|
28482
|
+
app64.post("/__remote-auth/change-password", async (c) => {
|
|
28110
28483
|
const client = clientFrom(c);
|
|
28111
28484
|
const clientIp = client.ip || "unknown";
|
|
28112
28485
|
const rateLimited = checkRateLimit(client);
|
|
@@ -28165,13 +28538,13 @@ app63.post("/__remote-auth/change-password", async (c) => {
|
|
|
28165
28538
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "change", changeError: "Failed to save password", redirect }), 200);
|
|
28166
28539
|
}
|
|
28167
28540
|
});
|
|
28168
|
-
|
|
28541
|
+
app64.get("/__remote-auth/setup", (c) => {
|
|
28169
28542
|
if (isRemoteAuthConfigured()) {
|
|
28170
28543
|
return c.redirect("/");
|
|
28171
28544
|
}
|
|
28172
28545
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup" }), 200);
|
|
28173
28546
|
});
|
|
28174
|
-
|
|
28547
|
+
app64.post("/__remote-auth/set-initial-password", async (c) => {
|
|
28175
28548
|
if (isRemoteAuthConfigured()) {
|
|
28176
28549
|
return c.redirect("/");
|
|
28177
28550
|
}
|
|
@@ -28209,10 +28582,10 @@ app63.post("/__remote-auth/set-initial-password", async (c) => {
|
|
|
28209
28582
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
|
|
28210
28583
|
}
|
|
28211
28584
|
});
|
|
28212
|
-
|
|
28585
|
+
app64.get("/api/remote-auth/status", (c) => {
|
|
28213
28586
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
28214
28587
|
});
|
|
28215
|
-
|
|
28588
|
+
app64.post("/api/remote-auth/set-password", async (c) => {
|
|
28216
28589
|
let body;
|
|
28217
28590
|
try {
|
|
28218
28591
|
body = await c.req.json();
|
|
@@ -28251,10 +28624,10 @@ app63.post("/api/remote-auth/set-password", async (c) => {
|
|
|
28251
28624
|
return c.json({ error: "Failed to save password" }, 500);
|
|
28252
28625
|
}
|
|
28253
28626
|
});
|
|
28254
|
-
|
|
28627
|
+
app64.route("/api/_client-error", client_error_default);
|
|
28255
28628
|
console.log("[client-error-route] mounted");
|
|
28256
28629
|
var PWA_PUBLIC_PATHS = /* @__PURE__ */ new Set(["/sw.js", ...PWA_SURFACES.map((s) => s.manifestPath)]);
|
|
28257
|
-
|
|
28630
|
+
app64.use("*", async (c, next) => {
|
|
28258
28631
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28259
28632
|
const path3 = c.req.path;
|
|
28260
28633
|
if (path3 === "/favicon.ico" || path3.startsWith("/assets/") || path3.startsWith("/brand/") || // Public free/busy is read by the booking page (a separate Cloudflare Pages
|
|
@@ -28298,27 +28671,28 @@ app63.use("*", async (c, next) => {
|
|
|
28298
28671
|
}
|
|
28299
28672
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), redirect: path3 }), 200);
|
|
28300
28673
|
});
|
|
28301
|
-
|
|
28302
|
-
|
|
28303
|
-
|
|
28304
|
-
|
|
28305
|
-
|
|
28306
|
-
|
|
28307
|
-
|
|
28674
|
+
app64.route("/api/health", health_default);
|
|
28675
|
+
app64.route("/api/chat", chatRoutes);
|
|
28676
|
+
app64.route("/api/whatsapp", whatsapp_default);
|
|
28677
|
+
app64.route("/api/storage", storage_broker_default);
|
|
28678
|
+
app64.route("/api/whatsapp-reader", whatsapp_reader_default);
|
|
28679
|
+
app64.route("/api/public-reader", public_reader_default);
|
|
28680
|
+
app64.route("/api/webchat", createWebchatRoutes({
|
|
28308
28681
|
handleInbound: (input) => webchatGateway.handleInbound(input),
|
|
28309
28682
|
// Task 940 — the permission relay's pointer read + verdict write.
|
|
28310
28683
|
pendingPromptFor: (key) => webchatGateway.pendingPromptFor(key),
|
|
28311
28684
|
deliveryFailureFor: (key) => webchatGateway.deliveryFailureFor(key),
|
|
28312
28685
|
resolvePermissionVerdict: (key, requestId, behavior) => webchatGateway.resolvePermissionVerdict(key, requestId, behavior)
|
|
28313
28686
|
}));
|
|
28314
|
-
|
|
28315
|
-
|
|
28316
|
-
|
|
28317
|
-
|
|
28318
|
-
|
|
28319
|
-
|
|
28320
|
-
|
|
28321
|
-
|
|
28687
|
+
app64.route("/api/webchat/greeting", webchat_greeting_default);
|
|
28688
|
+
app64.route("/api/telegram", telegram_default);
|
|
28689
|
+
app64.route("/api/quickbooks", quickbooks_default);
|
|
28690
|
+
app64.route("/api/onboarding", onboarding_default);
|
|
28691
|
+
app64.route("/api/admin", admin_default);
|
|
28692
|
+
app64.route("/api/access", access_default);
|
|
28693
|
+
app64.route("/api/session", session_default2);
|
|
28694
|
+
app64.route("/api/calendar", calendar_public_default);
|
|
28695
|
+
app64.route("/api/portal", portal_fetch_default);
|
|
28322
28696
|
var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
|
|
28323
28697
|
var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
28324
28698
|
var IMAGE_MIME = {
|
|
@@ -28330,7 +28704,7 @@ var IMAGE_MIME = {
|
|
|
28330
28704
|
".svg": "image/svg+xml",
|
|
28331
28705
|
".ico": "image/x-icon"
|
|
28332
28706
|
};
|
|
28333
|
-
|
|
28707
|
+
app64.get("/agent-assets/:slug/:filename", (c) => {
|
|
28334
28708
|
const slug = c.req.param("slug");
|
|
28335
28709
|
const filename = c.req.param("filename");
|
|
28336
28710
|
if (!SAFE_SLUG_RE2.test(slug)) {
|
|
@@ -28346,8 +28720,8 @@ app63.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
28346
28720
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
28347
28721
|
return c.text("Not found", 404);
|
|
28348
28722
|
}
|
|
28349
|
-
const filePath =
|
|
28350
|
-
const expectedDir =
|
|
28723
|
+
const filePath = resolve37(account.accountDir, "agents", slug, "assets", filename);
|
|
28724
|
+
const expectedDir = resolve37(account.accountDir, "agents", slug, "assets");
|
|
28351
28725
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
28352
28726
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
28353
28727
|
return c.text("Forbidden", 403);
|
|
@@ -28359,13 +28733,13 @@ app63.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
28359
28733
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
28360
28734
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
28361
28735
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
28362
|
-
const body =
|
|
28736
|
+
const body = readFileSync42(filePath);
|
|
28363
28737
|
return c.body(body, 200, {
|
|
28364
28738
|
"Content-Type": contentType,
|
|
28365
28739
|
"Cache-Control": "public, max-age=3600"
|
|
28366
28740
|
});
|
|
28367
28741
|
});
|
|
28368
|
-
|
|
28742
|
+
app64.get("/generated/:filename", (c) => {
|
|
28369
28743
|
const filename = c.req.param("filename");
|
|
28370
28744
|
if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
|
|
28371
28745
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
@@ -28376,8 +28750,8 @@ app63.get("/generated/:filename", (c) => {
|
|
|
28376
28750
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
28377
28751
|
return c.text("Not found", 404);
|
|
28378
28752
|
}
|
|
28379
|
-
const filePath =
|
|
28380
|
-
const expectedDir =
|
|
28753
|
+
const filePath = resolve37(account.accountDir, "generated", filename);
|
|
28754
|
+
const expectedDir = resolve37(account.accountDir, "generated");
|
|
28381
28755
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
28382
28756
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
28383
28757
|
return c.text("Forbidden", 403);
|
|
@@ -28389,22 +28763,22 @@ app63.get("/generated/:filename", (c) => {
|
|
|
28389
28763
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
28390
28764
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
28391
28765
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
28392
|
-
const body =
|
|
28766
|
+
const body = readFileSync42(filePath);
|
|
28393
28767
|
return c.body(body, 200, {
|
|
28394
28768
|
"Content-Type": contentType,
|
|
28395
28769
|
"Cache-Control": "public, max-age=86400"
|
|
28396
28770
|
});
|
|
28397
28771
|
});
|
|
28398
|
-
|
|
28399
|
-
|
|
28400
|
-
|
|
28401
|
-
|
|
28772
|
+
app64.route("/sites", sites_default);
|
|
28773
|
+
app64.route("/listings", listings_default);
|
|
28774
|
+
app64.route("/v", visitor_event_default);
|
|
28775
|
+
app64.route("/v", visitor_consent_default);
|
|
28402
28776
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
28403
28777
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
28404
28778
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
28405
28779
|
if (BRAND_JSON_PATH && existsSync38(BRAND_JSON_PATH)) {
|
|
28406
28780
|
try {
|
|
28407
|
-
const fullBrand = JSON.parse(
|
|
28781
|
+
const fullBrand = JSON.parse(readFileSync42(BRAND_JSON_PATH, "utf-8"));
|
|
28408
28782
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
28409
28783
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
28410
28784
|
} catch {
|
|
@@ -28431,11 +28805,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
28431
28805
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
28432
28806
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
28433
28807
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
28434
|
-
(p) => existsSync38(
|
|
28808
|
+
(p) => existsSync38(resolve37(process.cwd(), "public", p.replace(/^\//, "")))
|
|
28435
28809
|
);
|
|
28436
28810
|
var SW_SOURCE = (() => {
|
|
28437
28811
|
try {
|
|
28438
|
-
return
|
|
28812
|
+
return readFileSync42(resolve37(process.cwd(), "public", "sw.js"), "utf-8");
|
|
28439
28813
|
} catch {
|
|
28440
28814
|
return null;
|
|
28441
28815
|
}
|
|
@@ -28443,9 +28817,9 @@ var SW_SOURCE = (() => {
|
|
|
28443
28817
|
function readInstalledVersion() {
|
|
28444
28818
|
try {
|
|
28445
28819
|
if (!PLATFORM_ROOT8) return "unknown";
|
|
28446
|
-
const versionFile =
|
|
28820
|
+
const versionFile = join44(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
28447
28821
|
if (!existsSync38(versionFile)) return "unknown";
|
|
28448
|
-
const content =
|
|
28822
|
+
const content = readFileSync42(versionFile, "utf-8").trim();
|
|
28449
28823
|
return content || "unknown";
|
|
28450
28824
|
} catch {
|
|
28451
28825
|
return "unknown";
|
|
@@ -28486,7 +28860,7 @@ var clientErrorReporterScript = `<script>
|
|
|
28486
28860
|
function cachedHtml(file) {
|
|
28487
28861
|
let html = htmlCache.get(file);
|
|
28488
28862
|
if (!html) {
|
|
28489
|
-
html =
|
|
28863
|
+
html = readFileSync42(resolve37(process.cwd(), "public", file), "utf-8");
|
|
28490
28864
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
28491
28865
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
28492
28866
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -28503,13 +28877,13 @@ ${clientErrorReporterScript}
|
|
|
28503
28877
|
return html;
|
|
28504
28878
|
}
|
|
28505
28879
|
function loadBrandingCache(agentSlug) {
|
|
28506
|
-
const configDir2 =
|
|
28880
|
+
const configDir2 = join44(homedir4(), BRAND.configDir);
|
|
28507
28881
|
try {
|
|
28508
28882
|
const accountId = getDefaultAccountId();
|
|
28509
28883
|
if (!accountId) return null;
|
|
28510
|
-
const cachePath =
|
|
28884
|
+
const cachePath = join44(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
28511
28885
|
if (!existsSync38(cachePath)) return null;
|
|
28512
|
-
return JSON.parse(
|
|
28886
|
+
return JSON.parse(readFileSync42(cachePath, "utf-8"));
|
|
28513
28887
|
} catch {
|
|
28514
28888
|
return null;
|
|
28515
28889
|
}
|
|
@@ -28553,7 +28927,7 @@ function brandedPublicHtml(agentSlug) {
|
|
|
28553
28927
|
function agentUnavailableHtml() {
|
|
28554
28928
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeHtml(BRAND.productName)}</title></head><body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1.5rem;color:#222"><h1 style="font-size:1.25rem">Agent unavailable</h1><p>This agent isn't available right now. If you reached this page from a saved link, the agent may have been turned off.</p></body></html>`;
|
|
28555
28929
|
}
|
|
28556
|
-
|
|
28930
|
+
app64.get("/", (c) => {
|
|
28557
28931
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28558
28932
|
const klass = classifyHost(host, getOperatorDomains(), isPublicHost);
|
|
28559
28933
|
if (klass === "operator") {
|
|
@@ -28575,12 +28949,12 @@ app63.get("/", (c) => {
|
|
|
28575
28949
|
console.log(`[host-class] host=${host} class=admin served=index.html`);
|
|
28576
28950
|
return c.html(cachedHtml("index.html"));
|
|
28577
28951
|
});
|
|
28578
|
-
|
|
28952
|
+
app64.get("/public", (c) => {
|
|
28579
28953
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28580
28954
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28581
28955
|
return c.html(cachedHtml("public.html"));
|
|
28582
28956
|
});
|
|
28583
|
-
|
|
28957
|
+
app64.get("/public-chat", (c) => {
|
|
28584
28958
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28585
28959
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28586
28960
|
return c.html(cachedHtml("public.html"));
|
|
@@ -28599,12 +28973,12 @@ async function logViewerFetch(c, next) {
|
|
|
28599
28973
|
duration_ms: Date.now() - start
|
|
28600
28974
|
});
|
|
28601
28975
|
}
|
|
28602
|
-
|
|
28603
|
-
|
|
28604
|
-
|
|
28976
|
+
app64.use("/vnc-viewer.html", logViewerFetch);
|
|
28977
|
+
app64.use("/vnc-popout.html", logViewerFetch);
|
|
28978
|
+
app64.get("/vnc-popout.html", (c) => {
|
|
28605
28979
|
let html = htmlCache.get("vnc-popout.html");
|
|
28606
28980
|
if (!html) {
|
|
28607
|
-
html =
|
|
28981
|
+
html = readFileSync42(resolve37(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
28608
28982
|
const name = escapeHtml(BRAND.productName);
|
|
28609
28983
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
28610
28984
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -28614,7 +28988,7 @@ app63.get("/vnc-popout.html", (c) => {
|
|
|
28614
28988
|
}
|
|
28615
28989
|
return c.html(html);
|
|
28616
28990
|
});
|
|
28617
|
-
|
|
28991
|
+
app64.post("/api/vnc/client-event", async (c) => {
|
|
28618
28992
|
let body;
|
|
28619
28993
|
try {
|
|
28620
28994
|
body = await c.req.json();
|
|
@@ -28635,11 +29009,11 @@ app63.post("/api/vnc/client-event", async (c) => {
|
|
|
28635
29009
|
});
|
|
28636
29010
|
return c.json({ ok: true });
|
|
28637
29011
|
});
|
|
28638
|
-
|
|
29012
|
+
app64.get("/g/:slug", (c) => {
|
|
28639
29013
|
return c.html(brandedPublicHtml(resolveDefaultSlug() ?? void 0));
|
|
28640
29014
|
});
|
|
28641
29015
|
for (const pwa of PWA_SURFACES) {
|
|
28642
|
-
|
|
29016
|
+
app64.get(pwa.manifestPath, (c) => {
|
|
28643
29017
|
const manifest = buildManifest(pwa, {
|
|
28644
29018
|
productName: BRAND.productName,
|
|
28645
29019
|
appIcon192: brandAppIcon192Path,
|
|
@@ -28655,7 +29029,7 @@ for (const pwa of PWA_SURFACES) {
|
|
|
28655
29029
|
return c.body(JSON.stringify(manifest));
|
|
28656
29030
|
});
|
|
28657
29031
|
}
|
|
28658
|
-
|
|
29032
|
+
app64.get("/sw.js", (c) => {
|
|
28659
29033
|
if (SW_SOURCE == null) {
|
|
28660
29034
|
console.error("[pwa] op=sw status=500 reason=sw-source-missing");
|
|
28661
29035
|
return c.text("Service worker unavailable", 500);
|
|
@@ -28664,12 +29038,12 @@ app63.get("/sw.js", (c) => {
|
|
|
28664
29038
|
console.log(`[pwa] op=sw status=200 ct=${SW_CONTENT_TYPE}`);
|
|
28665
29039
|
return c.body(SW_SOURCE);
|
|
28666
29040
|
});
|
|
28667
|
-
|
|
29041
|
+
app64.get("/graph", (c) => {
|
|
28668
29042
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28669
29043
|
if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
|
|
28670
29044
|
return c.html(cachedHtml("graph.html"));
|
|
28671
29045
|
});
|
|
28672
|
-
|
|
29046
|
+
app64.get("/chat", (c) => {
|
|
28673
29047
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28674
29048
|
if (isOperatorHost(host, getOperatorDomains())) {
|
|
28675
29049
|
console.log(`[host-class] host=${host} class=operator served=operator.html`);
|
|
@@ -28678,32 +29052,32 @@ app63.get("/chat", (c) => {
|
|
|
28678
29052
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28679
29053
|
return c.html(cachedHtml("chat.html"));
|
|
28680
29054
|
});
|
|
28681
|
-
|
|
29055
|
+
app64.get("/data", (c) => {
|
|
28682
29056
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28683
29057
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28684
29058
|
return c.html(cachedHtml("data.html"));
|
|
28685
29059
|
});
|
|
28686
|
-
|
|
29060
|
+
app64.get("/tasks", (c) => {
|
|
28687
29061
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28688
29062
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28689
29063
|
return c.html(cachedHtml("tasks.html"));
|
|
28690
29064
|
});
|
|
28691
|
-
|
|
29065
|
+
app64.get("/activity", (c) => {
|
|
28692
29066
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28693
29067
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28694
29068
|
return c.html(cachedHtml("activity.html"));
|
|
28695
29069
|
});
|
|
28696
|
-
|
|
29070
|
+
app64.get("/calendar", (c) => {
|
|
28697
29071
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28698
29072
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28699
29073
|
return c.html(cachedHtml("calendar.html"));
|
|
28700
29074
|
});
|
|
28701
|
-
|
|
29075
|
+
app64.get("/browser", (c) => {
|
|
28702
29076
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28703
29077
|
if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
|
|
28704
29078
|
return c.html(cachedHtml("browser.html"));
|
|
28705
29079
|
});
|
|
28706
|
-
|
|
29080
|
+
app64.get("/:slug", async (c, next) => {
|
|
28707
29081
|
const slug = c.req.param("slug");
|
|
28708
29082
|
if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
|
|
28709
29083
|
const account = resolveAccount();
|
|
@@ -28718,13 +29092,13 @@ app63.get("/:slug", async (c, next) => {
|
|
|
28718
29092
|
await next();
|
|
28719
29093
|
});
|
|
28720
29094
|
if (brandFaviconPath !== "/favicon.ico") {
|
|
28721
|
-
|
|
29095
|
+
app64.get("/favicon.ico", (c) => {
|
|
28722
29096
|
c.header("Cache-Control", "public, max-age=300");
|
|
28723
29097
|
return c.redirect(brandFaviconPath, 302);
|
|
28724
29098
|
});
|
|
28725
29099
|
}
|
|
28726
|
-
|
|
28727
|
-
|
|
29100
|
+
app64.use("/*", serveStatic({ root: "./public" }));
|
|
29101
|
+
app64.all("*", (c) => {
|
|
28728
29102
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28729
29103
|
const path3 = c.req.path;
|
|
28730
29104
|
if (isPublicHost(host)) {
|
|
@@ -28738,11 +29112,11 @@ app63.all("*", (c) => {
|
|
|
28738
29112
|
});
|
|
28739
29113
|
var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
|
|
28740
29114
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
28741
|
-
var httpServer = serve({ fetch:
|
|
29115
|
+
var httpServer = serve({ fetch: app64.fetch, port, hostname });
|
|
28742
29116
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
28743
29117
|
{
|
|
28744
|
-
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28745
|
-
const reconcileScript =
|
|
29118
|
+
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29119
|
+
const reconcileScript = resolve37(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
28746
29120
|
const RECONCILE_INTERVAL_MS = 12e4;
|
|
28747
29121
|
const runReconcile = (ctx) => {
|
|
28748
29122
|
if (!existsSync38(reconcileScript)) return;
|
|
@@ -28767,8 +29141,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28767
29141
|
});
|
|
28768
29142
|
}
|
|
28769
29143
|
{
|
|
28770
|
-
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28771
|
-
const outlookScript =
|
|
29144
|
+
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29145
|
+
const outlookScript = resolve37(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
|
|
28772
29146
|
const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
|
|
28773
29147
|
const runOutlookComplete = (ctx) => {
|
|
28774
29148
|
if (!existsSync38(outlookScript)) return;
|
|
@@ -28793,8 +29167,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28793
29167
|
});
|
|
28794
29168
|
}
|
|
28795
29169
|
{
|
|
28796
|
-
const auditRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28797
|
-
const strandedAccountsDir =
|
|
29170
|
+
const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29171
|
+
const strandedAccountsDir = resolve37(auditRoot, "..", "data/accounts");
|
|
28798
29172
|
const STRANDED_AUDIT_INTERVAL_MS = 3e5;
|
|
28799
29173
|
const STRANDED_AGE_MS = 16 * 6e4;
|
|
28800
29174
|
const STRANDED_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -28804,7 +29178,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28804
29178
|
const now = Date.now();
|
|
28805
29179
|
for (const name of readdirSync26(strandedAccountsDir)) {
|
|
28806
29180
|
if (!STRANDED_UUID_RE.test(name)) continue;
|
|
28807
|
-
const pendingPath2 =
|
|
29181
|
+
const pendingPath2 = resolve37(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
|
|
28808
29182
|
if (!existsSync38(pendingPath2)) continue;
|
|
28809
29183
|
const ageMs = now - statSync17(pendingPath2).mtimeMs;
|
|
28810
29184
|
if (ageMs > STRANDED_AGE_MS) {
|
|
@@ -28823,8 +29197,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28823
29197
|
});
|
|
28824
29198
|
}
|
|
28825
29199
|
{
|
|
28826
|
-
const googleRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28827
|
-
const googleAccountsDir =
|
|
29200
|
+
const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29201
|
+
const googleAccountsDir = resolve37(googleRoot, "..", "data/accounts");
|
|
28828
29202
|
const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
|
|
28829
29203
|
const runGooglePendingAuditSafe = () => {
|
|
28830
29204
|
try {
|
|
@@ -28839,7 +29213,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28839
29213
|
firstRunDelayMs: 27e3,
|
|
28840
29214
|
run: runGooglePendingAuditSafe
|
|
28841
29215
|
});
|
|
28842
|
-
const googleAuditScript =
|
|
29216
|
+
const googleAuditScript = resolve37(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
|
|
28843
29217
|
const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
|
|
28844
29218
|
const runGoogleAccountAudit = (ctx) => {
|
|
28845
29219
|
if (!existsSync38(googleAuditScript)) return;
|
|
@@ -28867,7 +29241,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28867
29241
|
});
|
|
28868
29242
|
}
|
|
28869
29243
|
{
|
|
28870
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29244
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
28871
29245
|
const STORAGE_AUDIT_INTERVAL_MS = 3e5;
|
|
28872
29246
|
const runStorageAuditSafe = () => runStorageAudit(auditPlatformRoot).catch((err) => {
|
|
28873
29247
|
console.error(`[storage-audit] error="${err.message}"`);
|
|
@@ -28901,8 +29275,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28901
29275
|
});
|
|
28902
29276
|
}
|
|
28903
29277
|
{
|
|
28904
|
-
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28905
|
-
const publishScript =
|
|
29278
|
+
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29279
|
+
const publishScript = resolve37(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
28906
29280
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
28907
29281
|
const currentAccount = () => {
|
|
28908
29282
|
try {
|
|
@@ -28932,21 +29306,21 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28932
29306
|
}
|
|
28933
29307
|
};
|
|
28934
29308
|
const auditSnapshotAge = (account) => {
|
|
28935
|
-
const availPath =
|
|
29309
|
+
const availPath = resolve37(account.accountDir, "calendar-availability.json");
|
|
28936
29310
|
let hasBookingSite = false;
|
|
28937
29311
|
try {
|
|
28938
29312
|
if (existsSync38(availPath)) {
|
|
28939
|
-
const cfg = JSON.parse(
|
|
29313
|
+
const cfg = JSON.parse(readFileSync42(availPath, "utf-8"));
|
|
28940
29314
|
hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
28941
29315
|
}
|
|
28942
29316
|
} catch {
|
|
28943
29317
|
}
|
|
28944
29318
|
if (!hasBookingSite) return;
|
|
28945
|
-
const statePath =
|
|
29319
|
+
const statePath = resolve37(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
28946
29320
|
let lastSuccessAt = null;
|
|
28947
29321
|
if (existsSync38(statePath)) {
|
|
28948
29322
|
try {
|
|
28949
|
-
const rec = JSON.parse(
|
|
29323
|
+
const rec = JSON.parse(readFileSync42(statePath, "utf-8"));
|
|
28950
29324
|
lastSuccessAt = rec.lastSuccessAt ?? null;
|
|
28951
29325
|
} catch {
|
|
28952
29326
|
console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
|
|
@@ -28974,8 +29348,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28974
29348
|
startTimeEntryCensus(() => getSession());
|
|
28975
29349
|
startLedgerCensus(() => getSession());
|
|
28976
29350
|
{
|
|
28977
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28978
|
-
const auditScript =
|
|
29351
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29352
|
+
const auditScript = resolve37(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
28979
29353
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
28980
29354
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
28981
29355
|
const runConnectorAudit = (ctx) => {
|
|
@@ -29047,6 +29421,7 @@ var SUBAPP_MANIFEST = [
|
|
|
29047
29421
|
{ prefix: "/api/access", file: "server/routes/access/index.ts", subapp: access_default },
|
|
29048
29422
|
{ prefix: "/api/session", file: "server/routes/session.ts", subapp: session_default2 },
|
|
29049
29423
|
{ prefix: "/api/calendar", file: "server/routes/calendar-public.ts", subapp: calendar_public_default },
|
|
29424
|
+
{ prefix: "/api/portal", file: "server/routes/portal-fetch.ts", subapp: portal_fetch_default },
|
|
29050
29425
|
{ prefix: "/api/_client-error", file: "server/routes/client-error.ts", subapp: client_error_default },
|
|
29051
29426
|
{ prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default },
|
|
29052
29427
|
{ prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default },
|
|
@@ -29058,7 +29433,7 @@ for (const m of SUBAPP_MANIFEST) {
|
|
|
29058
29433
|
}
|
|
29059
29434
|
try {
|
|
29060
29435
|
const registered = [];
|
|
29061
|
-
for (const r of
|
|
29436
|
+
for (const r of app64.routes ?? []) {
|
|
29062
29437
|
if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
|
|
29063
29438
|
if (AGENT_SLUG_PATTERN.test(r.path)) {
|
|
29064
29439
|
registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
|
|
@@ -29102,7 +29477,7 @@ async function runAdminUserReconcileTick() {
|
|
|
29102
29477
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
29103
29478
|
return;
|
|
29104
29479
|
}
|
|
29105
|
-
const usersRaw =
|
|
29480
|
+
const usersRaw = readFileSync42(USERS_FILE, "utf-8").trim();
|
|
29106
29481
|
if (!usersRaw) {
|
|
29107
29482
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
29108
29483
|
return;
|
|
@@ -29150,7 +29525,7 @@ registerLoop({
|
|
|
29150
29525
|
var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
29151
29526
|
function countUsersRows() {
|
|
29152
29527
|
if (!existsSync38(USERS_FILE)) return 0;
|
|
29153
|
-
const raw =
|
|
29528
|
+
const raw = readFileSync42(USERS_FILE, "utf-8").trim();
|
|
29154
29529
|
if (!raw) return 0;
|
|
29155
29530
|
const users = JSON.parse(raw);
|
|
29156
29531
|
return users.filter((u) => typeof u.userId === "string").length;
|
|
@@ -29283,7 +29658,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
29283
29658
|
}
|
|
29284
29659
|
init({
|
|
29285
29660
|
configDir: configDirForWhatsApp,
|
|
29286
|
-
platformRoot:
|
|
29661
|
+
platformRoot: resolve37(process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..")),
|
|
29287
29662
|
accountConfig: bootAccountConfig,
|
|
29288
29663
|
onMessage: async (msg) => {
|
|
29289
29664
|
if (msg.isOwnerMirror) {
|