@rubytech/create-maxy-code 0.1.504 → 0.1.506
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/lib/account-schema-regions/dist/index.d.ts +60 -0
- package/payload/platform/lib/account-schema-regions/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/account-schema-regions/dist/index.js +101 -0
- package/payload/platform/lib/account-schema-regions/dist/index.js.map +1 -0
- package/payload/platform/lib/account-schema-regions/package.json +7 -0
- package/payload/platform/lib/account-schema-regions/src/index.ts +121 -0
- package/payload/platform/lib/account-schema-regions/tsconfig.json +9 -0
- package/payload/platform/lib/models/dist/index.d.ts +5 -5
- package/payload/platform/lib/models/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/models/dist/index.js +7 -7
- package/payload/platform/lib/models/dist/index.js.map +1 -1
- package/payload/platform/lib/models/src/index.ts +7 -7
- package/payload/platform/package.json +3 -3
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +83 -28
- package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +5 -5
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
- package/payload/platform/plugins/docs/references/admin-session.md +1 -1
- package/payload/platform/plugins/docs/references/admin-ui.md +82 -27
- package/payload/platform/plugins/work/mcp/dist/__tests__/session-metering.test.js +2 -2
- package/payload/platform/plugins/work/mcp/dist/__tests__/session-metering.test.js.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/session-metering.js +2 -2
- package/payload/platform/plugins/work/mcp/dist/tools/session-metering.js.map +1 -1
- package/payload/platform/scripts/check-no-esm-require.mjs +5 -0
- package/payload/platform/scripts/logs-read-jsonl.test.sh +1 -1
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts +1 -1
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js +50 -3
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +2 -2
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pricing.d.ts +19 -11
- package/payload/platform/services/claude-session-manager/dist/pricing.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pricing.js +18 -15
- package/payload/platform/services/claude-session-manager/dist/pricing.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-metering.js +3 -3
- package/payload/platform/services/claude-session-manager/dist/session-metering.js.map +1 -1
- package/payload/platform/templates/account.json +1 -1
- package/payload/platform/templates/specialists/agents/coding-assistant.md +1 -1
- package/payload/platform/templates/specialists/agents/content-producer.md +1 -1
- package/payload/platform/templates/specialists/agents/librarian.md +1 -1
- package/payload/platform/templates/specialists/agents/research-assistant.md +1 -1
- package/payload/server/{chunk-HN4HFTXE.js → chunk-5ADBNEJA.js} +1410 -1409
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/activity.html +5 -5
- package/payload/server/public/agents.html +4 -4
- package/payload/server/public/assets/{AdminLoginScreens-DWMR4KtI.js → AdminLoginScreens-D5KOQA8S.js} +1 -1
- package/payload/server/public/assets/AdminShell-C8wazpnz.js +2 -0
- package/payload/server/public/assets/{activity-DH3z4cv3.js → activity-CFqkDhTs.js} +1 -1
- package/payload/server/public/assets/admin-B-EGUWS4.js +1 -0
- package/payload/server/public/assets/agents-DPulfmrd.js +1 -0
- package/payload/server/public/assets/{browser-C_MJYN5k.js → browser-T_5xbnJh.js} +1 -1
- package/payload/server/public/assets/{calendar-DA0h7dqW.js → calendar-CKjuiG8c.js} +1 -1
- package/payload/server/public/assets/chat-DWLYAA70.js +1 -0
- package/payload/server/public/assets/chevron-left-DBMCTKlT.js +1 -0
- package/payload/server/public/assets/chevron-right-SBAr6zzc.js +1 -0
- package/payload/server/public/assets/clock-kkzt7Scf.js +1 -0
- package/payload/server/public/assets/data-CV8N9qcy.js +1 -0
- package/payload/server/public/assets/{file-text-RCoSeOpD.js → file-text-c6cA4Evd.js} +1 -1
- package/payload/server/public/assets/graph-DIk-o45J.js +51 -0
- package/payload/server/public/assets/{graph-labels-DJV0aIG5.js → graph-labels-5bjxhC_p.js} +1 -1
- package/payload/server/public/assets/{maximize-2-DcESsKbu.js → maximize-2-BCQkFb1_.js} +1 -1
- package/payload/server/public/assets/{operator-Dlkw1sXJ.js → operator-0UnNo2ms.js} +1 -1
- package/payload/server/public/assets/page-HVtUfub2.js +32 -0
- package/payload/server/public/assets/page-JJa2xdzf.js +1 -0
- package/payload/server/public/assets/{public-C-UHWk_S.js → public-CKEYyIPn.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-C1nRSAzP.js → rotate-ccw-dTpO5dhL.js} +1 -1
- package/payload/server/public/assets/{routines-qdAZ0sA4.js → routines-C6P1-Dto.js} +1 -1
- package/payload/server/public/assets/{skills-B78wr822.js → skills-ClrpVqlz.js} +1 -1
- package/payload/server/public/assets/tasks-C8SNhSFC.js +1 -0
- package/payload/server/public/assets/{time-entry-format-BhlltPP8.js → time-entry-format-CCO6N6XZ.js} +1 -1
- package/payload/server/public/assets/{triangle-alert-rEVGyIhi.js → triangle-alert-CHjdKpag.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-DzpXjm9d.js → useCopyFeedback-BTUu3-l8.js} +1 -1
- package/payload/server/public/assets/{useSubAccountSwitcher-DVmWZxar.js → useSubAccountSwitcher-DSW9vDi2.js} +1 -1
- package/payload/server/public/assets/{useSubAccountSwitcher-wP8cOzPj.css → useSubAccountSwitcher-DyrH8DA8.css} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-CRRmPjYr.js → useVoiceRecorder-BeNqxmm2.js} +1 -1
- package/payload/server/public/assets/{wrench-oeLti270.js → wrench-CeNnc7oO.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +7 -7
- package/payload/server/public/chat.html +13 -13
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +9 -9
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +13 -13
- package/payload/server/public/routines.html +6 -6
- package/payload/server/public/skills.html +5 -5
- package/payload/server/public/tasks.html +6 -6
- package/payload/server/server.js +1395 -812
- package/payload/server/public/assets/AdminShell-C8hBpL9K.js +0 -2
- package/payload/server/public/assets/admin-aaGhd2Yh.js +0 -1
- package/payload/server/public/assets/agents-SPu-clsf.js +0 -1
- package/payload/server/public/assets/chat-Bg7dlM0I.js +0 -1
- package/payload/server/public/assets/chevron-left-BoDA12H8.js +0 -1
- package/payload/server/public/assets/chevron-right-DXjrO6qA.js +0 -1
- package/payload/server/public/assets/clock-BceBgZP1.js +0 -1
- package/payload/server/public/assets/data-rNLaurgY.js +0 -1
- package/payload/server/public/assets/graph-Dm-YZeNP.js +0 -51
- package/payload/server/public/assets/page-Dl-mgQUu.js +0 -32
- package/payload/server/public/assets/page-DoyJrk8E.js +0 -1
- package/payload/server/public/assets/tasks-DbIsS-vW.js +0 -1
package/payload/server/server.js
CHANGED
|
@@ -91,6 +91,7 @@ import {
|
|
|
91
91
|
requirePortEnv,
|
|
92
92
|
resolveAccount,
|
|
93
93
|
resolveAgentConfig,
|
|
94
|
+
resolveCacheKey,
|
|
94
95
|
resolveCallback,
|
|
95
96
|
resolveClientIp,
|
|
96
97
|
resolveDefaultAgentSlug,
|
|
@@ -117,7 +118,7 @@ import {
|
|
|
117
118
|
vncLog,
|
|
118
119
|
walkPremiumBundles,
|
|
119
120
|
writeAdminUserAndPerson
|
|
120
|
-
} from "./chunk-
|
|
121
|
+
} from "./chunk-5ADBNEJA.js";
|
|
121
122
|
import {
|
|
122
123
|
D1_MAX_SQL_STATEMENT_BYTES,
|
|
123
124
|
D1_QUERY_MAX_BODY_BYTES,
|
|
@@ -217,8 +218,69 @@ ${lines.join("\n")}`;
|
|
|
217
218
|
}
|
|
218
219
|
});
|
|
219
220
|
|
|
220
|
-
// ../lib/
|
|
221
|
+
// ../lib/account-schema-regions/dist/index.js
|
|
221
222
|
var require_dist2 = __commonJS({
|
|
223
|
+
"../lib/account-schema-regions/dist/index.js"(exports) {
|
|
224
|
+
"use strict";
|
|
225
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
226
|
+
exports.HOME_FIXED = void 0;
|
|
227
|
+
exports.parseSchemaRegions = parseSchemaRegions2;
|
|
228
|
+
exports.HOME_FIXED = Object.freeze(["projects", "contacts", "documents"]);
|
|
229
|
+
var ONT_START2 = "<!-- ontology-buckets:start -->";
|
|
230
|
+
var ONT_END2 = "<!-- ontology-buckets:end -->";
|
|
231
|
+
var PLUGIN_START = "<!-- plugin-owned-dirs:start -->";
|
|
232
|
+
var PLUGIN_END = "<!-- plugin-owned-dirs:end -->";
|
|
233
|
+
var ALLOWED_FENCE = "```allowed-top-level";
|
|
234
|
+
var DIR_LINE = /^-\s+`([^`/]+)\/`/;
|
|
235
|
+
function regionLines(text, start, end) {
|
|
236
|
+
const from = text.indexOf(start);
|
|
237
|
+
if (from === -1)
|
|
238
|
+
return [];
|
|
239
|
+
const to = text.indexOf(end, from);
|
|
240
|
+
if (to === -1)
|
|
241
|
+
return [];
|
|
242
|
+
return text.slice(from + start.length, to).split("\n");
|
|
243
|
+
}
|
|
244
|
+
function dirsIn(lines) {
|
|
245
|
+
const out = [];
|
|
246
|
+
for (const line of lines) {
|
|
247
|
+
const m = DIR_LINE.exec(line.trim());
|
|
248
|
+
if (m && !out.includes(m[1]))
|
|
249
|
+
out.push(m[1]);
|
|
250
|
+
}
|
|
251
|
+
return out;
|
|
252
|
+
}
|
|
253
|
+
function fenceEntries(text) {
|
|
254
|
+
const from = text.indexOf(ALLOWED_FENCE);
|
|
255
|
+
if (from === -1)
|
|
256
|
+
return [];
|
|
257
|
+
const bodyStart = text.indexOf("\n", from);
|
|
258
|
+
if (bodyStart === -1)
|
|
259
|
+
return [];
|
|
260
|
+
const to = text.indexOf("```", bodyStart);
|
|
261
|
+
if (to === -1)
|
|
262
|
+
return [];
|
|
263
|
+
return text.slice(bodyStart + 1, to).split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
264
|
+
}
|
|
265
|
+
function parseSchemaRegions2(schemaMd) {
|
|
266
|
+
if (schemaMd === null || schemaMd.length === 0) {
|
|
267
|
+
return { ontologyRoots: [], pluginOwned: [], allowedTopLevel: [], parsed: false, reason: "schema-absent" };
|
|
268
|
+
}
|
|
269
|
+
const allowedTopLevel = fenceEntries(schemaMd);
|
|
270
|
+
const parsed = allowedTopLevel.length > 0;
|
|
271
|
+
return {
|
|
272
|
+
ontologyRoots: dirsIn(regionLines(schemaMd, ONT_START2, ONT_END2)),
|
|
273
|
+
pluginOwned: dirsIn(regionLines(schemaMd, PLUGIN_START, PLUGIN_END)),
|
|
274
|
+
allowedTopLevel,
|
|
275
|
+
parsed,
|
|
276
|
+
reason: parsed ? "ok" : "fence-absent"
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// ../lib/graph-trash/dist/index.js
|
|
283
|
+
var require_dist3 = __commonJS({
|
|
222
284
|
"../lib/graph-trash/dist/index.js"(exports) {
|
|
223
285
|
"use strict";
|
|
224
286
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -499,7 +561,7 @@ var require_dist2 = __commonJS({
|
|
|
499
561
|
});
|
|
500
562
|
|
|
501
563
|
// ../lib/graph-style/dist/index.js
|
|
502
|
-
var
|
|
564
|
+
var require_dist4 = __commonJS({
|
|
503
565
|
"../lib/graph-style/dist/index.js"(exports) {
|
|
504
566
|
"use strict";
|
|
505
567
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -1228,7 +1290,7 @@ var require_conversation_provenance = __commonJS({
|
|
|
1228
1290
|
"use strict";
|
|
1229
1291
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1230
1292
|
exports.injectConversationProvenance = injectConversationProvenance;
|
|
1231
|
-
var index_js_1 =
|
|
1293
|
+
var index_js_1 = require_dist5();
|
|
1232
1294
|
async function injectConversationProvenance(params) {
|
|
1233
1295
|
const { session, relationships, accountId, writeLabels, conversationNodeId, logNamespace, tool } = params;
|
|
1234
1296
|
const original = [...relationships];
|
|
@@ -1278,7 +1340,7 @@ var require_conversation_provenance = __commonJS({
|
|
|
1278
1340
|
});
|
|
1279
1341
|
|
|
1280
1342
|
// ../lib/graph-write/dist/index.js
|
|
1281
|
-
var
|
|
1343
|
+
var require_dist5 = __commonJS({
|
|
1282
1344
|
"../lib/graph-write/dist/index.js"(exports) {
|
|
1283
1345
|
"use strict";
|
|
1284
1346
|
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -1756,194 +1818,138 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1756
1818
|
import { readFileSync as readFileSync47, existsSync as existsSync46, watchFile, readdirSync as readdirSync28, statSync as statSync20 } from "fs";
|
|
1757
1819
|
import { spawn as spawn3 } from "child_process";
|
|
1758
1820
|
import { createHash as createHash7 } from "crypto";
|
|
1759
|
-
import { resolve as
|
|
1821
|
+
import { resolve as resolve45, join as join47, basename as basename14 } from "path";
|
|
1760
1822
|
import { homedir as homedir4 } from "os";
|
|
1761
|
-
import { monitorEventLoopDelay } from "perf_hooks";
|
|
1762
1823
|
|
|
1763
|
-
//
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
return
|
|
1782
|
-
}
|
|
1783
|
-
function
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
const undiciFrame = /node_modules[\\/]undici|node:internal[\\/]deps[\\/]undici/.test(stack);
|
|
1791
|
-
if (undiciFrame && inFlight > 0) {
|
|
1792
|
-
containedCount += 1;
|
|
1793
|
-
console.error(
|
|
1794
|
-
`${TAG} op=contained channel=${channel} inFlight=${inFlight} err=${err instanceof Error ? err.message : String(err)} cause=${causeCode(err)}
|
|
1795
|
-
${stack}`
|
|
1796
|
-
);
|
|
1797
|
-
return true;
|
|
1798
|
-
}
|
|
1799
|
-
console.error(
|
|
1800
|
-
`${TAG} op=escaped channel=${channel} inFlight=${inFlight} undiciFrame=${undiciFrame} err=${err instanceof Error ? err.message : String(err)} cause=${causeCode(err)} contained=false`
|
|
1824
|
+
// server/lib/route-cache-watchers.ts
|
|
1825
|
+
import { watch } from "fs";
|
|
1826
|
+
import { resolve as resolve2 } from "path";
|
|
1827
|
+
|
|
1828
|
+
// server/lib/route-cache.ts
|
|
1829
|
+
var BACKSTOP_MS = 3e4;
|
|
1830
|
+
var NOTIFY_COALESCE_MS = 1e3;
|
|
1831
|
+
var RECOMPUTE_FLOOR_FACTOR = 10;
|
|
1832
|
+
var MIN_RECOMPUTE_FLOOR_MS = 1e3;
|
|
1833
|
+
var MAX_RECOMPUTE_FLOOR_MS = 1e4;
|
|
1834
|
+
var entries = /* @__PURE__ */ new Map();
|
|
1835
|
+
var inFlight = /* @__PURE__ */ new Map();
|
|
1836
|
+
var keyGenerations = /* @__PURE__ */ new Map();
|
|
1837
|
+
var resourceGenerations = /* @__PURE__ */ new Map();
|
|
1838
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
1839
|
+
var pendingNotifications = /* @__PURE__ */ new Map();
|
|
1840
|
+
var notifyTimer = null;
|
|
1841
|
+
function keyOf(resource, accountId) {
|
|
1842
|
+
return `${resource}::${accountId}`;
|
|
1843
|
+
}
|
|
1844
|
+
function generationOf(resource, key) {
|
|
1845
|
+
return (keyGenerations.get(key) ?? 0) + (resourceGenerations.get(resource) ?? 0);
|
|
1846
|
+
}
|
|
1847
|
+
function recomputeFloorMs(entry) {
|
|
1848
|
+
return Math.min(
|
|
1849
|
+
Math.max(entry.computeMs * RECOMPUTE_FLOOR_FACTOR, MIN_RECOMPUTE_FLOOR_MS),
|
|
1850
|
+
MAX_RECOMPUTE_FLOOR_MS
|
|
1801
1851
|
);
|
|
1802
|
-
return false;
|
|
1803
|
-
}
|
|
1804
|
-
var installed = false;
|
|
1805
|
-
function guardInstalled() {
|
|
1806
|
-
return installed;
|
|
1807
|
-
}
|
|
1808
|
-
function installMediaDownloadGuard() {
|
|
1809
|
-
if (installed) return;
|
|
1810
|
-
installed = true;
|
|
1811
|
-
process.on("uncaughtException", (err) => {
|
|
1812
|
-
if (handleEscapedMediaError(err, "uncaughtException")) return;
|
|
1813
|
-
process.stderr.write(`${err instanceof Error ? err.stack ?? err.message : String(err)}
|
|
1814
|
-
`);
|
|
1815
|
-
process.exit(1);
|
|
1816
|
-
});
|
|
1817
|
-
process.on("unhandledRejection", (reason) => {
|
|
1818
|
-
if (handleEscapedMediaError(reason, "unhandledRejection")) return;
|
|
1819
|
-
process.stderr.write(
|
|
1820
|
-
`Unhandled rejection: ${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}
|
|
1821
|
-
`
|
|
1822
|
-
);
|
|
1823
|
-
process.exit(1);
|
|
1824
|
-
});
|
|
1825
|
-
console.error(`${TAG} op=installed`);
|
|
1826
1852
|
}
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
const root = LEDGER_ROOT();
|
|
1840
|
-
if (!ensuredRoots.has(root)) {
|
|
1841
|
-
if (!existsSync2(root)) mkdirSync(root, { recursive: true });
|
|
1842
|
-
ensuredRoots.add(root);
|
|
1853
|
+
async function getOrCompute(resource, accountId, compute) {
|
|
1854
|
+
const key = keyOf(resource, accountId);
|
|
1855
|
+
const entry = entries.get(key);
|
|
1856
|
+
if (entry !== void 0) {
|
|
1857
|
+
const age = Date.now() - entry.computedAt;
|
|
1858
|
+
if (!entry.dirty && age <= BACKSTOP_MS) {
|
|
1859
|
+
return { value: entry.value, state: "hit" };
|
|
1860
|
+
}
|
|
1861
|
+
const sinceAttempt = Date.now() - entry.lastAttemptAt;
|
|
1862
|
+
if (entry.dirty && sinceAttempt < recomputeFloorMs(entry) && age <= BACKSTOP_MS) {
|
|
1863
|
+
return { value: entry.value, state: "dirty-floor" };
|
|
1864
|
+
}
|
|
1843
1865
|
}
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1866
|
+
const state = entry === void 0 ? "miss" : "stale-backstop";
|
|
1867
|
+
const existing = inFlight.get(key);
|
|
1868
|
+
if (existing) return { value: await existing, state };
|
|
1869
|
+
const startedGeneration = generationOf(resource, key);
|
|
1870
|
+
const startedAt = Date.now();
|
|
1871
|
+
const pending = compute();
|
|
1872
|
+
inFlight.set(key, pending);
|
|
1873
|
+
try {
|
|
1874
|
+
const value = await pending;
|
|
1875
|
+
const finishedAt = Date.now();
|
|
1876
|
+
const computeMs = finishedAt - startedAt;
|
|
1877
|
+
if (value !== null && generationOf(resource, key) === startedGeneration) {
|
|
1878
|
+
entries.set(key, {
|
|
1879
|
+
value,
|
|
1880
|
+
computedAt: finishedAt,
|
|
1881
|
+
computeMs,
|
|
1882
|
+
dirty: false,
|
|
1883
|
+
lastAttemptAt: finishedAt
|
|
1884
|
+
});
|
|
1885
|
+
} else {
|
|
1886
|
+
const prior = entries.get(key);
|
|
1887
|
+
if (prior) {
|
|
1888
|
+
prior.lastAttemptAt = finishedAt;
|
|
1889
|
+
prior.computeMs = computeMs;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
return { value, state };
|
|
1893
|
+
} finally {
|
|
1894
|
+
inFlight.delete(key);
|
|
1859
1895
|
}
|
|
1860
1896
|
}
|
|
1861
|
-
function
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
};
|
|
1868
|
-
appendFileSync(ledgerFilePath(entry.account), JSON.stringify(row) + "\n");
|
|
1869
|
-
} catch (err) {
|
|
1870
|
-
console.error(
|
|
1871
|
-
`${TAG2} FAIL op=decline msgId=${entry.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
1872
|
-
);
|
|
1897
|
+
function flushNotifications() {
|
|
1898
|
+
notifyTimer = null;
|
|
1899
|
+
const batch = [...pendingNotifications.values()];
|
|
1900
|
+
pendingNotifications.clear();
|
|
1901
|
+
for (const { resource, accountId } of batch) {
|
|
1902
|
+
for (const fn of listeners) fn(resource, accountId);
|
|
1873
1903
|
}
|
|
1874
1904
|
}
|
|
1875
|
-
function
|
|
1876
|
-
const
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
if (!line.trim()) continue;
|
|
1881
|
-
try {
|
|
1882
|
-
const row = JSON.parse(line);
|
|
1883
|
-
if (typeof row.declined === "string") out.add(row.declined);
|
|
1884
|
-
} catch {
|
|
1905
|
+
function invalidate(resource, accountId, source = "action") {
|
|
1906
|
+
const markOrDrop = (key) => {
|
|
1907
|
+
if (source === "action") {
|
|
1908
|
+
entries.delete(key);
|
|
1909
|
+
return;
|
|
1885
1910
|
}
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
}
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
for (const line of readFileSync(path3, "utf8").split("\n")) {
|
|
1894
|
-
if (!line.trim()) continue;
|
|
1895
|
-
try {
|
|
1896
|
-
const row = JSON.parse(line);
|
|
1897
|
-
if (row && typeof row.msgId === "string") out.push(row);
|
|
1898
|
-
} catch {
|
|
1911
|
+
const entry = entries.get(key);
|
|
1912
|
+
if (entry) entry.dirty = true;
|
|
1913
|
+
};
|
|
1914
|
+
if (accountId === null) {
|
|
1915
|
+
resourceGenerations.set(resource, (resourceGenerations.get(resource) ?? 0) + 1);
|
|
1916
|
+
for (const key of [...entries.keys()]) {
|
|
1917
|
+
if (key.startsWith(`${resource}::`)) markOrDrop(key);
|
|
1899
1918
|
}
|
|
1919
|
+
} else {
|
|
1920
|
+
const key = keyOf(resource, accountId);
|
|
1921
|
+
keyGenerations.set(key, (keyGenerations.get(key) ?? 0) + 1);
|
|
1922
|
+
markOrDrop(key);
|
|
1900
1923
|
}
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
if (!existsSync2(path3)) return 0;
|
|
1906
|
-
const lines = readFileSync(path3, "utf8").split("\n").filter((l) => l.trim());
|
|
1907
|
-
const cutoff = Date.now() - olderThanMs;
|
|
1908
|
-
const kept = lines.filter((line) => {
|
|
1909
|
-
try {
|
|
1910
|
-
const row = JSON.parse(line);
|
|
1911
|
-
const t = typeof row.ts === "string" ? Date.parse(row.ts) : NaN;
|
|
1912
|
-
return Number.isFinite(t) && t >= cutoff;
|
|
1913
|
-
} catch {
|
|
1914
|
-
return false;
|
|
1915
|
-
}
|
|
1916
|
-
});
|
|
1917
|
-
if (kept.length === lines.length) return 0;
|
|
1918
|
-
writeFileSync(path3, kept.join("\n") + (kept.length ? "\n" : ""));
|
|
1919
|
-
return lines.length - kept.length;
|
|
1920
|
-
}
|
|
1921
|
-
function listLedgerAccounts() {
|
|
1922
|
-
const root = LEDGER_ROOT();
|
|
1923
|
-
if (!existsSync2(root)) return [];
|
|
1924
|
-
try {
|
|
1925
|
-
return readdirSync(root).filter((f) => f.endsWith(".jsonl")).map((f) => f.slice(0, -".jsonl".length));
|
|
1926
|
-
} catch {
|
|
1927
|
-
return [];
|
|
1924
|
+
pendingNotifications.set(`${resource}::${accountId ?? "*"}`, { resource, accountId });
|
|
1925
|
+
if (notifyTimer === null) {
|
|
1926
|
+
notifyTimer = setTimeout(flushNotifications, NOTIFY_COALESCE_MS);
|
|
1927
|
+
if (typeof notifyTimer === "object" && "unref" in notifyTimer) notifyTimer.unref();
|
|
1928
1928
|
}
|
|
1929
1929
|
}
|
|
1930
|
+
function onInvalidate(fn) {
|
|
1931
|
+
listeners.add(fn);
|
|
1932
|
+
return () => {
|
|
1933
|
+
listeners.delete(fn);
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1930
1936
|
|
|
1931
1937
|
// app/lib/whatsapp/message-store.ts
|
|
1932
|
-
import { existsSync as
|
|
1933
|
-
import { dirname, join as
|
|
1938
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, appendFileSync, readdirSync, statSync as statSync2 } from "fs";
|
|
1939
|
+
import { dirname, join as join2 } from "path";
|
|
1934
1940
|
function isReactionRecord(r) {
|
|
1935
1941
|
return r.kind === "reaction";
|
|
1936
1942
|
}
|
|
1937
|
-
var STORE_ROOT = () =>
|
|
1943
|
+
var STORE_ROOT = () => join2(process.env.MAXY_DIR_OVERRIDE ?? MAXY_DIR, "data", "whatsapp-messages");
|
|
1938
1944
|
function conversationFile(platformAccountId, remoteJid) {
|
|
1939
|
-
return
|
|
1945
|
+
return join2(STORE_ROOT(), platformAccountId, `${remoteJid}.jsonl`);
|
|
1940
1946
|
}
|
|
1941
1947
|
function listStoredAccountIds() {
|
|
1942
1948
|
const root = STORE_ROOT();
|
|
1943
|
-
if (!
|
|
1944
|
-
return
|
|
1949
|
+
if (!existsSync2(root)) return [];
|
|
1950
|
+
return readdirSync(root).filter((name) => {
|
|
1945
1951
|
try {
|
|
1946
|
-
return statSync2(
|
|
1952
|
+
return statSync2(join2(root, name)).isDirectory();
|
|
1947
1953
|
} catch {
|
|
1948
1954
|
return false;
|
|
1949
1955
|
}
|
|
@@ -1970,7 +1976,7 @@ var byDateSentAsc = (a, b) => a.dateSent.localeCompare(b.dateSent);
|
|
|
1970
1976
|
function appendMessage(platformAccountId, record, altDedupeRemoteJids = []) {
|
|
1971
1977
|
const file = conversationFile(platformAccountId, record.remoteJid);
|
|
1972
1978
|
try {
|
|
1973
|
-
|
|
1979
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
1974
1980
|
} catch (err) {
|
|
1975
1981
|
const reason = err instanceof Error ? err.message : String(err);
|
|
1976
1982
|
console.error(`[whatsapp-message-store] FAIL op=append reason=${reason.slice(0, 100)} messageId=${record.messageId}`);
|
|
@@ -1979,8 +1985,8 @@ function appendMessage(platformAccountId, record, altDedupeRemoteJids = []) {
|
|
|
1979
1985
|
try {
|
|
1980
1986
|
const filesToCheck = [file, ...altDedupeRemoteJids.filter((jid) => jid && jid !== record.remoteJid).map((jid) => conversationFile(platformAccountId, jid))];
|
|
1981
1987
|
for (const candidate of new Set(filesToCheck)) {
|
|
1982
|
-
if (!
|
|
1983
|
-
const existing = parseLines(
|
|
1988
|
+
if (!existsSync2(candidate)) continue;
|
|
1989
|
+
const existing = parseLines(readFileSync(candidate, "utf8"));
|
|
1984
1990
|
if (existing.some((r) => r.messageId === record.messageId)) {
|
|
1985
1991
|
return { created: false };
|
|
1986
1992
|
}
|
|
@@ -1991,7 +1997,7 @@ function appendMessage(platformAccountId, record, altDedupeRemoteJids = []) {
|
|
|
1991
1997
|
return { created: false };
|
|
1992
1998
|
}
|
|
1993
1999
|
try {
|
|
1994
|
-
|
|
2000
|
+
appendFileSync(file, JSON.stringify(record) + "\n");
|
|
1995
2001
|
} catch (err) {
|
|
1996
2002
|
const reason = err instanceof Error ? err.message : String(err);
|
|
1997
2003
|
console.error(`[whatsapp-message-store] FAIL op=append reason=${reason.slice(0, 100)} messageId=${record.messageId}`);
|
|
@@ -2002,8 +2008,8 @@ function appendMessage(platformAccountId, record, altDedupeRemoteJids = []) {
|
|
|
2002
2008
|
function readConversation(platformAccountId, remoteJid) {
|
|
2003
2009
|
const file = conversationFile(platformAccountId, remoteJid);
|
|
2004
2010
|
try {
|
|
2005
|
-
if (!
|
|
2006
|
-
return parseLines(
|
|
2011
|
+
if (!existsSync2(file)) return [];
|
|
2012
|
+
return parseLines(readFileSync(file, "utf8")).sort(byDateSentAsc);
|
|
2007
2013
|
} catch (err) {
|
|
2008
2014
|
const reason = err instanceof Error ? err.message : String(err);
|
|
2009
2015
|
console.error(`[whatsapp-message-store] FAIL op=read reason=${reason.slice(0, 100)} remoteJid=${remoteJid}`);
|
|
@@ -2013,8 +2019,8 @@ function readConversation(platformAccountId, remoteJid) {
|
|
|
2013
2019
|
function appendReaction(platformAccountId, record) {
|
|
2014
2020
|
const file = conversationFile(platformAccountId, record.remoteJid);
|
|
2015
2021
|
try {
|
|
2016
|
-
|
|
2017
|
-
|
|
2022
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
2023
|
+
appendFileSync(file, JSON.stringify(record) + "\n");
|
|
2018
2024
|
} catch (err) {
|
|
2019
2025
|
const reason = err instanceof Error ? err.message : String(err);
|
|
2020
2026
|
console.error(
|
|
@@ -2027,9 +2033,9 @@ function appendReaction(platformAccountId, record) {
|
|
|
2027
2033
|
function readReactions(platformAccountId, remoteJid) {
|
|
2028
2034
|
const file = conversationFile(platformAccountId, remoteJid);
|
|
2029
2035
|
try {
|
|
2030
|
-
if (!
|
|
2036
|
+
if (!existsSync2(file)) return [];
|
|
2031
2037
|
const out = [];
|
|
2032
|
-
for (const line of
|
|
2038
|
+
for (const line of readFileSync(file, "utf8").split("\n")) {
|
|
2033
2039
|
if (!line.trim()) continue;
|
|
2034
2040
|
try {
|
|
2035
2041
|
const rec = JSON.parse(line);
|
|
@@ -2073,18 +2079,18 @@ function summariseRecords(records) {
|
|
|
2073
2079
|
};
|
|
2074
2080
|
}
|
|
2075
2081
|
function readConversationSummaries(platformAccountId) {
|
|
2076
|
-
const dir =
|
|
2082
|
+
const dir = join2(STORE_ROOT(), platformAccountId);
|
|
2077
2083
|
const out = /* @__PURE__ */ new Map();
|
|
2078
2084
|
const seen = /* @__PURE__ */ new Set();
|
|
2079
2085
|
let fullReads = 0;
|
|
2080
2086
|
let cacheHits = 0;
|
|
2081
2087
|
let bytesRead = 0;
|
|
2082
2088
|
try {
|
|
2083
|
-
if (!
|
|
2084
|
-
for (const entry of
|
|
2089
|
+
if (!existsSync2(dir)) return out;
|
|
2090
|
+
for (const entry of readdirSync(dir)) {
|
|
2085
2091
|
if (!entry.endsWith(".jsonl")) continue;
|
|
2086
2092
|
const remoteJid = entry.slice(0, -".jsonl".length);
|
|
2087
|
-
const file =
|
|
2093
|
+
const file = join2(dir, entry);
|
|
2088
2094
|
seen.add(file);
|
|
2089
2095
|
let size;
|
|
2090
2096
|
let mtimeMs;
|
|
@@ -2103,7 +2109,7 @@ function readConversationSummaries(platformAccountId) {
|
|
|
2103
2109
|
}
|
|
2104
2110
|
let summary;
|
|
2105
2111
|
try {
|
|
2106
|
-
const raw =
|
|
2112
|
+
const raw = readFileSync(file, "utf8");
|
|
2107
2113
|
bytesRead += Buffer.byteLength(raw);
|
|
2108
2114
|
fullReads++;
|
|
2109
2115
|
summary = summariseRecords(parseLines(raw));
|
|
@@ -2129,11 +2135,11 @@ function readConversationSummaries(platformAccountId) {
|
|
|
2129
2135
|
return out;
|
|
2130
2136
|
}
|
|
2131
2137
|
function readAllConversations(platformAccountId) {
|
|
2132
|
-
const dir =
|
|
2138
|
+
const dir = join2(STORE_ROOT(), platformAccountId);
|
|
2133
2139
|
const out = /* @__PURE__ */ new Map();
|
|
2134
2140
|
try {
|
|
2135
|
-
if (!
|
|
2136
|
-
for (const entry of
|
|
2141
|
+
if (!existsSync2(dir)) return out;
|
|
2142
|
+
for (const entry of readdirSync(dir)) {
|
|
2137
2143
|
if (!entry.endsWith(".jsonl")) continue;
|
|
2138
2144
|
const remoteJid = entry.slice(0, -".jsonl".length);
|
|
2139
2145
|
out.set(remoteJid, readConversation(platformAccountId, remoteJid));
|
|
@@ -2145,6 +2151,303 @@ function readAllConversations(platformAccountId) {
|
|
|
2145
2151
|
return out;
|
|
2146
2152
|
}
|
|
2147
2153
|
|
|
2154
|
+
// ../lib/account-enumeration/src/index.ts
|
|
2155
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync2 } from "fs";
|
|
2156
|
+
import { resolve } from "path";
|
|
2157
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2158
|
+
var cache = /* @__PURE__ */ new Map();
|
|
2159
|
+
function enumerateValidAccountIds(accountsDir) {
|
|
2160
|
+
const cached3 = cache.get(accountsDir);
|
|
2161
|
+
if (cached3 !== void 0) return cached3;
|
|
2162
|
+
let names;
|
|
2163
|
+
try {
|
|
2164
|
+
names = readdirSync2(accountsDir);
|
|
2165
|
+
} catch (err) {
|
|
2166
|
+
if (err.code === "ENOENT") {
|
|
2167
|
+
cache.set(accountsDir, []);
|
|
2168
|
+
return [];
|
|
2169
|
+
}
|
|
2170
|
+
throw err;
|
|
2171
|
+
}
|
|
2172
|
+
const valid = [];
|
|
2173
|
+
for (const name of names) {
|
|
2174
|
+
if (!UUID_RE.test(name)) continue;
|
|
2175
|
+
const configPath2 = resolve(accountsDir, name, "account.json");
|
|
2176
|
+
try {
|
|
2177
|
+
JSON.parse(readFileSync2(configPath2, "utf-8"));
|
|
2178
|
+
valid.push(name);
|
|
2179
|
+
} catch (err) {
|
|
2180
|
+
const code = err.code;
|
|
2181
|
+
if (code === "ENOENT") continue;
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
cache.set(accountsDir, valid);
|
|
2185
|
+
return valid;
|
|
2186
|
+
}
|
|
2187
|
+
function resolveHouseOrSoleAccountId(accountsDir) {
|
|
2188
|
+
const valid = enumerateValidAccountIds(accountsDir);
|
|
2189
|
+
const houses = [];
|
|
2190
|
+
for (const id of valid) {
|
|
2191
|
+
const configPath2 = resolve(accountsDir, id, "account.json");
|
|
2192
|
+
try {
|
|
2193
|
+
const cfg = JSON.parse(readFileSync2(configPath2, "utf-8"));
|
|
2194
|
+
if (cfg.role === "house") houses.push(id);
|
|
2195
|
+
} catch {
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
if (houses.length === 1) return houses[0];
|
|
2199
|
+
if (houses.length === 0 && valid.length === 1) return valid[0];
|
|
2200
|
+
throw new Error(
|
|
2201
|
+
`[account-registry] resolveHouseOrSoleAccountId: houses=${houses.length} total=${valid.length} under ${accountsDir} \u2014 expected exactly one role:"house" (or a single unlabelled account). Loud-fail rather than picking one silently.`
|
|
2202
|
+
);
|
|
2203
|
+
}
|
|
2204
|
+
function getAccountsDirFromEnv() {
|
|
2205
|
+
const root = process.env.MAXY_PLATFORM_ROOT;
|
|
2206
|
+
if (!root) {
|
|
2207
|
+
throw new Error(
|
|
2208
|
+
"[graph-write] MAXY_PLATFORM_ROOT not set \u2014 cannot enforce accountId gate. Set MAXY_PLATFORM_ROOT in the spawning process or pass `accountsDir` explicitly."
|
|
2209
|
+
);
|
|
2210
|
+
}
|
|
2211
|
+
return resolve(root, "..", "data/accounts");
|
|
2212
|
+
}
|
|
2213
|
+
function validateAccountIdEnv(envValue, diskIds) {
|
|
2214
|
+
if (!envValue) {
|
|
2215
|
+
return { ok: false, reason: "missing", envId: null, diskIds };
|
|
2216
|
+
}
|
|
2217
|
+
if (diskIds.length === 0) {
|
|
2218
|
+
return { ok: false, reason: "no-on-disk-account", envId: envValue, diskIds };
|
|
2219
|
+
}
|
|
2220
|
+
if (!diskIds.includes(envValue)) {
|
|
2221
|
+
return { ok: false, reason: "mismatch", envId: envValue, diskIds };
|
|
2222
|
+
}
|
|
2223
|
+
return { ok: true, envId: envValue, diskIds };
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
// server/lib/route-cache-watchers.ts
|
|
2227
|
+
function watchDirInto(dir, resource, accountId) {
|
|
2228
|
+
try {
|
|
2229
|
+
const w = watch(dir, { recursive: true }, () => {
|
|
2230
|
+
invalidate(resource, accountId, "watcher");
|
|
2231
|
+
});
|
|
2232
|
+
w.on("error", (err) => {
|
|
2233
|
+
console.error(`[admin-events] op=watch-failed resource=${resource} reason=${String(err)}`);
|
|
2234
|
+
});
|
|
2235
|
+
return { stop: () => {
|
|
2236
|
+
w.close();
|
|
2237
|
+
}, attached: true };
|
|
2238
|
+
} catch (err) {
|
|
2239
|
+
console.error(`[admin-events] op=watch-failed resource=${resource} reason=${String(err)}`);
|
|
2240
|
+
return { stop: () => {
|
|
2241
|
+
}, attached: false };
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
var perAccountAttached = /* @__PURE__ */ new Set();
|
|
2245
|
+
function ensureAccountWatchers(accountId, accountDir) {
|
|
2246
|
+
for (const [dir, resource] of [
|
|
2247
|
+
[resolve2(accountDir, "plugins"), "skills"],
|
|
2248
|
+
[resolve2(accountDir, "agents", "admin"), "sidebar-artefacts"],
|
|
2249
|
+
[resolve2(accountDir, "specialists", "agents"), "sidebar-artefacts"]
|
|
2250
|
+
]) {
|
|
2251
|
+
if (perAccountAttached.has(dir)) continue;
|
|
2252
|
+
const { attached: attached2 } = watchDirInto(dir, resource, accountId);
|
|
2253
|
+
if (attached2) perAccountAttached.add(dir);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
var attached = false;
|
|
2257
|
+
function attachRouteCacheWatchers() {
|
|
2258
|
+
if (attached) return;
|
|
2259
|
+
attached = true;
|
|
2260
|
+
const configDir2 = process.env.CLAUDE_CONFIG_DIR;
|
|
2261
|
+
if (configDir2) {
|
|
2262
|
+
watchDirInto(resolve2(configDir2, "projects"), "sidebar-sessions", null);
|
|
2263
|
+
} else {
|
|
2264
|
+
console.error("[admin-events] op=watch-failed resource=sidebar-sessions reason=CLAUDE_CONFIG_DIR-unset");
|
|
2265
|
+
}
|
|
2266
|
+
watchDirInto(resolve2(PLATFORM_ROOT, "templates", "specialists", "agents"), "sidebar-artefacts", null);
|
|
2267
|
+
let houseId = null;
|
|
2268
|
+
try {
|
|
2269
|
+
houseId = resolveHouseOrSoleAccountId(ACCOUNTS_DIR);
|
|
2270
|
+
} catch {
|
|
2271
|
+
houseId = null;
|
|
2272
|
+
}
|
|
2273
|
+
if (houseId) {
|
|
2274
|
+
watchDirInto(STORE_ROOT(), "conversations-all", houseId);
|
|
2275
|
+
} else {
|
|
2276
|
+
console.error("[admin-events] op=watch-failed resource=conversations-all reason=house-account-unresolved");
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
// server/index.ts
|
|
2281
|
+
import { monitorEventLoopDelay } from "perf_hooks";
|
|
2282
|
+
|
|
2283
|
+
// app/lib/whatsapp/inbound/media-download-guard.ts
|
|
2284
|
+
var TAG = "[media-guard]";
|
|
2285
|
+
var inFlight2 = 0;
|
|
2286
|
+
var containedCount = 0;
|
|
2287
|
+
function containedSinceBoot() {
|
|
2288
|
+
return containedCount;
|
|
2289
|
+
}
|
|
2290
|
+
function beginMediaDownload() {
|
|
2291
|
+
inFlight2 += 1;
|
|
2292
|
+
}
|
|
2293
|
+
function endMediaDownload() {
|
|
2294
|
+
if (inFlight2 > 0) inFlight2 -= 1;
|
|
2295
|
+
}
|
|
2296
|
+
function mediaDownloadsInFlight() {
|
|
2297
|
+
return inFlight2;
|
|
2298
|
+
}
|
|
2299
|
+
function stackOf(err) {
|
|
2300
|
+
if (err instanceof Error) return err.stack ?? err.message;
|
|
2301
|
+
return String(err);
|
|
2302
|
+
}
|
|
2303
|
+
function causeCode(err) {
|
|
2304
|
+
const cause = err?.cause;
|
|
2305
|
+
const code = cause?.code;
|
|
2306
|
+
return typeof code === "string" ? code : "none";
|
|
2307
|
+
}
|
|
2308
|
+
function handleEscapedMediaError(err, channel) {
|
|
2309
|
+
const stack = stackOf(err);
|
|
2310
|
+
const undiciFrame = /node_modules[\\/]undici|node:internal[\\/]deps[\\/]undici/.test(stack);
|
|
2311
|
+
if (undiciFrame && inFlight2 > 0) {
|
|
2312
|
+
containedCount += 1;
|
|
2313
|
+
console.error(
|
|
2314
|
+
`${TAG} op=contained channel=${channel} inFlight=${inFlight2} err=${err instanceof Error ? err.message : String(err)} cause=${causeCode(err)}
|
|
2315
|
+
${stack}`
|
|
2316
|
+
);
|
|
2317
|
+
return true;
|
|
2318
|
+
}
|
|
2319
|
+
console.error(
|
|
2320
|
+
`${TAG} op=escaped channel=${channel} inFlight=${inFlight2} undiciFrame=${undiciFrame} err=${err instanceof Error ? err.message : String(err)} cause=${causeCode(err)} contained=false`
|
|
2321
|
+
);
|
|
2322
|
+
return false;
|
|
2323
|
+
}
|
|
2324
|
+
var installed = false;
|
|
2325
|
+
function guardInstalled() {
|
|
2326
|
+
return installed;
|
|
2327
|
+
}
|
|
2328
|
+
function installMediaDownloadGuard() {
|
|
2329
|
+
if (installed) return;
|
|
2330
|
+
installed = true;
|
|
2331
|
+
process.on("uncaughtException", (err) => {
|
|
2332
|
+
if (handleEscapedMediaError(err, "uncaughtException")) return;
|
|
2333
|
+
process.stderr.write(`${err instanceof Error ? err.stack ?? err.message : String(err)}
|
|
2334
|
+
`);
|
|
2335
|
+
process.exit(1);
|
|
2336
|
+
});
|
|
2337
|
+
process.on("unhandledRejection", (reason) => {
|
|
2338
|
+
if (handleEscapedMediaError(reason, "unhandledRejection")) return;
|
|
2339
|
+
process.stderr.write(
|
|
2340
|
+
`Unhandled rejection: ${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}
|
|
2341
|
+
`
|
|
2342
|
+
);
|
|
2343
|
+
process.exit(1);
|
|
2344
|
+
});
|
|
2345
|
+
console.error(`${TAG} op=installed`);
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
// app/lib/whatsapp/ingest-audit.ts
|
|
2349
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
|
|
2350
|
+
import { join as join4 } from "path";
|
|
2351
|
+
|
|
2352
|
+
// app/lib/whatsapp/ingest-ledger.ts
|
|
2353
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, appendFileSync as appendFileSync2, readFileSync as readFileSync3, writeFileSync, readdirSync as readdirSync3 } from "fs";
|
|
2354
|
+
import { join as join3 } from "path";
|
|
2355
|
+
var TAG2 = "[wa-ingest-ledger]";
|
|
2356
|
+
var LEDGER_ROOT = () => join3(process.env.MAXY_DIR_OVERRIDE ?? MAXY_DIR, "data", "whatsapp-ingest-ledger");
|
|
2357
|
+
var ensuredRoots = /* @__PURE__ */ new Set();
|
|
2358
|
+
function ledgerFilePath(account) {
|
|
2359
|
+
const root = LEDGER_ROOT();
|
|
2360
|
+
if (!ensuredRoots.has(root)) {
|
|
2361
|
+
if (!existsSync3(root)) mkdirSync2(root, { recursive: true });
|
|
2362
|
+
ensuredRoots.add(root);
|
|
2363
|
+
}
|
|
2364
|
+
return join3(root, `${account}.jsonl`);
|
|
2365
|
+
}
|
|
2366
|
+
function appendStanza(entry) {
|
|
2367
|
+
try {
|
|
2368
|
+
const row = {
|
|
2369
|
+
msgId: entry.msgId,
|
|
2370
|
+
from: entry.from,
|
|
2371
|
+
type: entry.type,
|
|
2372
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
2373
|
+
};
|
|
2374
|
+
appendFileSync2(ledgerFilePath(entry.account), JSON.stringify(row) + "\n");
|
|
2375
|
+
} catch (err) {
|
|
2376
|
+
console.error(
|
|
2377
|
+
`${TAG2} FAIL op=append msgId=${entry.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
function markDeclined(entry) {
|
|
2382
|
+
try {
|
|
2383
|
+
const row = {
|
|
2384
|
+
declined: entry.msgId,
|
|
2385
|
+
reason: entry.reason,
|
|
2386
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
2387
|
+
};
|
|
2388
|
+
appendFileSync2(ledgerFilePath(entry.account), JSON.stringify(row) + "\n");
|
|
2389
|
+
} catch (err) {
|
|
2390
|
+
console.error(
|
|
2391
|
+
`${TAG2} FAIL op=decline msgId=${entry.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
2392
|
+
);
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
function readDeclined(account) {
|
|
2396
|
+
const path3 = ledgerFilePath(account);
|
|
2397
|
+
const out = /* @__PURE__ */ new Set();
|
|
2398
|
+
if (!existsSync3(path3)) return out;
|
|
2399
|
+
for (const line of readFileSync3(path3, "utf8").split("\n")) {
|
|
2400
|
+
if (!line.trim()) continue;
|
|
2401
|
+
try {
|
|
2402
|
+
const row = JSON.parse(line);
|
|
2403
|
+
if (typeof row.declined === "string") out.add(row.declined);
|
|
2404
|
+
} catch {
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
return out;
|
|
2408
|
+
}
|
|
2409
|
+
function readLedger(account) {
|
|
2410
|
+
const path3 = ledgerFilePath(account);
|
|
2411
|
+
if (!existsSync3(path3)) return [];
|
|
2412
|
+
const out = [];
|
|
2413
|
+
for (const line of readFileSync3(path3, "utf8").split("\n")) {
|
|
2414
|
+
if (!line.trim()) continue;
|
|
2415
|
+
try {
|
|
2416
|
+
const row = JSON.parse(line);
|
|
2417
|
+
if (row && typeof row.msgId === "string") out.push(row);
|
|
2418
|
+
} catch {
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
return out;
|
|
2422
|
+
}
|
|
2423
|
+
function pruneLedger(account, olderThanMs) {
|
|
2424
|
+
const path3 = ledgerFilePath(account);
|
|
2425
|
+
if (!existsSync3(path3)) return 0;
|
|
2426
|
+
const lines = readFileSync3(path3, "utf8").split("\n").filter((l) => l.trim());
|
|
2427
|
+
const cutoff = Date.now() - olderThanMs;
|
|
2428
|
+
const kept = lines.filter((line) => {
|
|
2429
|
+
try {
|
|
2430
|
+
const row = JSON.parse(line);
|
|
2431
|
+
const t = typeof row.ts === "string" ? Date.parse(row.ts) : NaN;
|
|
2432
|
+
return Number.isFinite(t) && t >= cutoff;
|
|
2433
|
+
} catch {
|
|
2434
|
+
return false;
|
|
2435
|
+
}
|
|
2436
|
+
});
|
|
2437
|
+
if (kept.length === lines.length) return 0;
|
|
2438
|
+
writeFileSync(path3, kept.join("\n") + (kept.length ? "\n" : ""));
|
|
2439
|
+
return lines.length - kept.length;
|
|
2440
|
+
}
|
|
2441
|
+
function listLedgerAccounts() {
|
|
2442
|
+
const root = LEDGER_ROOT();
|
|
2443
|
+
if (!existsSync3(root)) return [];
|
|
2444
|
+
try {
|
|
2445
|
+
return readdirSync3(root).filter((f) => f.endsWith(".jsonl")).map((f) => f.slice(0, -".jsonl".length));
|
|
2446
|
+
} catch {
|
|
2447
|
+
return [];
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2148
2451
|
// app/lib/whatsapp/ingest-audit.ts
|
|
2149
2452
|
var TAG3 = "[wa-ingest-audit]";
|
|
2150
2453
|
var DEFAULT_GRACE_MS = 10 * 60 * 1e3;
|
|
@@ -2164,7 +2467,7 @@ function readAllConversationsCached(accountId) {
|
|
|
2164
2467
|
const out = /* @__PURE__ */ new Map();
|
|
2165
2468
|
let dir;
|
|
2166
2469
|
try {
|
|
2167
|
-
dir =
|
|
2470
|
+
dir = readdirSync4(join4(STORE_ROOT(), accountId));
|
|
2168
2471
|
} catch {
|
|
2169
2472
|
return out;
|
|
2170
2473
|
}
|
|
@@ -2184,7 +2487,7 @@ function readAllConversationsCached(accountId) {
|
|
|
2184
2487
|
}
|
|
2185
2488
|
const parsed = [];
|
|
2186
2489
|
try {
|
|
2187
|
-
for (const line of
|
|
2490
|
+
for (const line of readFileSync4(path3, "utf8").split("\n")) {
|
|
2188
2491
|
if (!line.trim()) continue;
|
|
2189
2492
|
try {
|
|
2190
2493
|
const row = JSON.parse(line);
|
|
@@ -2347,19 +2650,19 @@ function pwaCensus(d) {
|
|
|
2347
2650
|
}
|
|
2348
2651
|
|
|
2349
2652
|
// server/webchat-descriptor.ts
|
|
2350
|
-
import { resolve, join as join5 } from "path";
|
|
2653
|
+
import { resolve as resolve3, join as join5 } from "path";
|
|
2351
2654
|
function webchatGatewayUrl() {
|
|
2352
2655
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
2353
2656
|
}
|
|
2354
2657
|
function webchatServerPath() {
|
|
2355
|
-
return process.env.MAXY_WEBCHAT_CHANNEL_SERVER_PATH ??
|
|
2658
|
+
return process.env.MAXY_WEBCHAT_CHANNEL_SERVER_PATH ?? resolve3(process.env.MAXY_PLATFORM_ROOT ?? join5(__dirname, ".."), "services/webchat-channel/dist/server.js");
|
|
2356
2659
|
}
|
|
2357
2660
|
function webchatChannelDescriptor(key) {
|
|
2358
2661
|
return { key, gatewayUrl: webchatGatewayUrl(), serverPath: webchatServerPath() };
|
|
2359
2662
|
}
|
|
2360
2663
|
|
|
2361
2664
|
// app/lib/claude-auth.ts
|
|
2362
|
-
import { readFileSync as
|
|
2665
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, renameSync, unlinkSync } from "fs";
|
|
2363
2666
|
import { sep } from "path";
|
|
2364
2667
|
import { lock } from "proper-lockfile";
|
|
2365
2668
|
if (!CLAUDE_CREDENTIALS_FILE.startsWith(MAXY_DIR + sep)) {
|
|
@@ -2376,7 +2679,7 @@ var REFRESH_NOT_FIRING_THRESHOLD = 2;
|
|
|
2376
2679
|
function readCredentials() {
|
|
2377
2680
|
let raw;
|
|
2378
2681
|
try {
|
|
2379
|
-
raw =
|
|
2682
|
+
raw = readFileSync5(CLAUDE_CREDENTIALS_FILE, "utf-8");
|
|
2380
2683
|
} catch {
|
|
2381
2684
|
return null;
|
|
2382
2685
|
}
|
|
@@ -2404,7 +2707,7 @@ function writeOAuthCredentials(tokens) {
|
|
|
2404
2707
|
try {
|
|
2405
2708
|
let fileData = {};
|
|
2406
2709
|
try {
|
|
2407
|
-
const rawFile =
|
|
2710
|
+
const rawFile = readFileSync5(CLAUDE_CREDENTIALS_FILE, "utf-8");
|
|
2408
2711
|
fileData = JSON.parse(rawFile);
|
|
2409
2712
|
} catch {
|
|
2410
2713
|
}
|
|
@@ -2634,9 +2937,9 @@ import { networkInterfaces } from "os";
|
|
|
2634
2937
|
function getLanIp() {
|
|
2635
2938
|
const nets = networkInterfaces();
|
|
2636
2939
|
let fallback = null;
|
|
2637
|
-
for (const
|
|
2638
|
-
if (!
|
|
2639
|
-
for (const entry of
|
|
2940
|
+
for (const entries2 of Object.values(nets)) {
|
|
2941
|
+
if (!entries2) continue;
|
|
2942
|
+
for (const entry of entries2) {
|
|
2640
2943
|
if (entry.family !== "IPv4" || entry.internal) continue;
|
|
2641
2944
|
if (entry.address.startsWith("192.168.") || entry.address.startsWith("10.")) {
|
|
2642
2945
|
return entry.address;
|
|
@@ -2880,8 +3183,8 @@ function discardAuthDirSync(authDir) {
|
|
|
2880
3183
|
function listCredentialAccountIds(configDir2) {
|
|
2881
3184
|
const credsRoot = path.join(os.homedir(), configDir2, "credentials", "whatsapp");
|
|
2882
3185
|
try {
|
|
2883
|
-
const
|
|
2884
|
-
return
|
|
3186
|
+
const entries2 = fsSync.readdirSync(credsRoot, { withFileTypes: true });
|
|
3187
|
+
return entries2.filter((e) => e.isDirectory() && hasCredsSync(path.join(credsRoot, e.name))).map((e) => e.name);
|
|
2885
3188
|
} catch {
|
|
2886
3189
|
return [];
|
|
2887
3190
|
}
|
|
@@ -2910,7 +3213,7 @@ var SENTINEL_BASENAME = "baileys-debug-until";
|
|
|
2910
3213
|
var STAT_INTERVAL_MS = 1e3;
|
|
2911
3214
|
var VALID_LEVELS = /* @__PURE__ */ new Set(["trace", "debug", "info"]);
|
|
2912
3215
|
var INACTIVE = { active: false, level: "warn" };
|
|
2913
|
-
var
|
|
3216
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
2914
3217
|
function sentinelPath(authDir) {
|
|
2915
3218
|
return path2.join(authDir, SENTINEL_BASENAME);
|
|
2916
3219
|
}
|
|
@@ -2937,7 +3240,7 @@ function readSentinel(authDir, nowMs) {
|
|
|
2937
3240
|
}
|
|
2938
3241
|
function resolveDebugWindow(authDir, nowMs = Date.now()) {
|
|
2939
3242
|
if (!authDir) return INACTIVE;
|
|
2940
|
-
const prev =
|
|
3243
|
+
const prev = cache2.get(authDir);
|
|
2941
3244
|
if (prev && nowMs - prev.checkedAtMs < STAT_INTERVAL_MS) {
|
|
2942
3245
|
return prev.state;
|
|
2943
3246
|
}
|
|
@@ -2952,7 +3255,7 @@ function resolveDebugWindow(authDir, nowMs = Date.now()) {
|
|
|
2952
3255
|
`${TAG5} op=baileys-debug-window-close account=${path2.basename(authDir)}`
|
|
2953
3256
|
);
|
|
2954
3257
|
}
|
|
2955
|
-
|
|
3258
|
+
cache2.set(authDir, { checkedAtMs: nowMs, state });
|
|
2956
3259
|
return state;
|
|
2957
3260
|
}
|
|
2958
3261
|
|
|
@@ -3030,7 +3333,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
3030
3333
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
3031
3334
|
console.error(`${TAG6} draining credential save queue\u2026`);
|
|
3032
3335
|
const timer = new Promise(
|
|
3033
|
-
(
|
|
3336
|
+
(resolve46) => setTimeout(() => resolve46("timeout"), timeoutMs)
|
|
3034
3337
|
);
|
|
3035
3338
|
const result = await Promise.race([
|
|
3036
3339
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -3207,11 +3510,11 @@ async function createWaSocket(opts) {
|
|
|
3207
3510
|
return sock;
|
|
3208
3511
|
}
|
|
3209
3512
|
async function waitForConnection(sock) {
|
|
3210
|
-
return new Promise((
|
|
3513
|
+
return new Promise((resolve46, reject) => {
|
|
3211
3514
|
const handler = (update) => {
|
|
3212
3515
|
if (update.connection === "open") {
|
|
3213
3516
|
sock.ev.off("connection.update", handler);
|
|
3214
|
-
|
|
3517
|
+
resolve46();
|
|
3215
3518
|
}
|
|
3216
3519
|
if (update.connection === "close") {
|
|
3217
3520
|
sock.ev.off("connection.update", handler);
|
|
@@ -3319,7 +3622,7 @@ function isTimeoutError(err) {
|
|
|
3319
3622
|
return Boolean(err?.__maxyTimeout);
|
|
3320
3623
|
}
|
|
3321
3624
|
function withTimeout(label, promise, timeoutMs) {
|
|
3322
|
-
return new Promise((
|
|
3625
|
+
return new Promise((resolve46, reject) => {
|
|
3323
3626
|
const timer = setTimeout(() => {
|
|
3324
3627
|
const err = new Error(`${label} timed out after ${timeoutMs}ms`);
|
|
3325
3628
|
err.__maxyTimeout = true;
|
|
@@ -3329,7 +3632,7 @@ function withTimeout(label, promise, timeoutMs) {
|
|
|
3329
3632
|
promise.then(
|
|
3330
3633
|
(value) => {
|
|
3331
3634
|
clearTimeout(timer);
|
|
3332
|
-
|
|
3635
|
+
resolve46(value);
|
|
3333
3636
|
},
|
|
3334
3637
|
(err) => {
|
|
3335
3638
|
clearTimeout(timer);
|
|
@@ -3559,7 +3862,7 @@ async function resolveCanonicalConversationJid(remoteJid, lidMapping) {
|
|
|
3559
3862
|
}
|
|
3560
3863
|
|
|
3561
3864
|
// app/lib/whatsapp/inbound/lid-map.ts
|
|
3562
|
-
import { existsSync as existsSync4, readFileSync as
|
|
3865
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
3563
3866
|
function resolveViaLidMap(map, senderPhone) {
|
|
3564
3867
|
const key = normalizeE164(senderPhone);
|
|
3565
3868
|
if (!key) return null;
|
|
@@ -3577,7 +3880,7 @@ function lidCaptureFor(senderJid, senderPhone) {
|
|
|
3577
3880
|
function readLidMap() {
|
|
3578
3881
|
try {
|
|
3579
3882
|
if (!existsSync4(LID_MAP_FILE)) return null;
|
|
3580
|
-
const raw =
|
|
3883
|
+
const raw = readFileSync6(LID_MAP_FILE, "utf-8").trim();
|
|
3581
3884
|
if (!raw) return null;
|
|
3582
3885
|
const parsed = JSON.parse(raw);
|
|
3583
3886
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
@@ -3836,16 +4139,16 @@ function decideDispatch(conn, payload, deps) {
|
|
|
3836
4139
|
|
|
3837
4140
|
// app/lib/access-gate.ts
|
|
3838
4141
|
import neo4j from "neo4j-driver";
|
|
3839
|
-
import { readFileSync as
|
|
3840
|
-
import { resolve as
|
|
4142
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
4143
|
+
import { resolve as resolve4 } from "path";
|
|
3841
4144
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3842
|
-
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ??
|
|
4145
|
+
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
|
|
3843
4146
|
var driver = null;
|
|
3844
4147
|
function readPassword() {
|
|
3845
4148
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
3846
|
-
const passwordFile =
|
|
4149
|
+
const passwordFile = resolve4(PLATFORM_ROOT2, "config/.neo4j-password");
|
|
3847
4150
|
try {
|
|
3848
|
-
return
|
|
4151
|
+
return readFileSync7(passwordFile, "utf-8").trim();
|
|
3849
4152
|
} catch {
|
|
3850
4153
|
throw new Error(
|
|
3851
4154
|
`Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
|
|
@@ -4394,8 +4697,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
4394
4697
|
const { givenName, familyName } = splitName(input.pushName);
|
|
4395
4698
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
4396
4699
|
let release;
|
|
4397
|
-
const mine = new Promise((
|
|
4398
|
-
release =
|
|
4700
|
+
const mine = new Promise((resolve46) => {
|
|
4701
|
+
release = resolve46;
|
|
4399
4702
|
});
|
|
4400
4703
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
4401
4704
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -4518,95 +4821,23 @@ async function ensureWhatsAppConversation(input) {
|
|
|
4518
4821
|
);
|
|
4519
4822
|
return null;
|
|
4520
4823
|
}
|
|
4521
|
-
console.error(
|
|
4522
|
-
`${TAG12} conversation-merged cacheKey=${input.cacheKey} agentType=${input.agentType} channel=whatsapp accountId=${input.platformAccountId} connectionAccountId=${input.connectionAccountId ?? input.platformAccountId} created=${result.created} ms=${ms}`
|
|
4523
|
-
);
|
|
4524
|
-
return { sessionId: result.sessionId, created: result.created };
|
|
4525
|
-
} catch (err) {
|
|
4526
|
-
const ms = Date.now() - t0;
|
|
4527
|
-
const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
|
|
4528
|
-
console.error(
|
|
4529
|
-
`${TAG12} conversation-merged FAIL cacheKey=${input.cacheKey} reason=${reason} ms=${ms}`
|
|
4530
|
-
);
|
|
4531
|
-
return null;
|
|
4532
|
-
}
|
|
4533
|
-
}
|
|
4534
|
-
|
|
4535
|
-
// ../lib/account-enumeration/src/index.ts
|
|
4536
|
-
import { readdirSync as readdirSync4, readFileSync as readFileSync7 } from "fs";
|
|
4537
|
-
import { resolve as resolve3 } from "path";
|
|
4538
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4539
|
-
var cache2 = /* @__PURE__ */ new Map();
|
|
4540
|
-
function enumerateValidAccountIds(accountsDir) {
|
|
4541
|
-
const cached3 = cache2.get(accountsDir);
|
|
4542
|
-
if (cached3 !== void 0) return cached3;
|
|
4543
|
-
let names;
|
|
4544
|
-
try {
|
|
4545
|
-
names = readdirSync4(accountsDir);
|
|
4546
|
-
} catch (err) {
|
|
4547
|
-
if (err.code === "ENOENT") {
|
|
4548
|
-
cache2.set(accountsDir, []);
|
|
4549
|
-
return [];
|
|
4550
|
-
}
|
|
4551
|
-
throw err;
|
|
4552
|
-
}
|
|
4553
|
-
const valid = [];
|
|
4554
|
-
for (const name of names) {
|
|
4555
|
-
if (!UUID_RE.test(name)) continue;
|
|
4556
|
-
const configPath2 = resolve3(accountsDir, name, "account.json");
|
|
4557
|
-
try {
|
|
4558
|
-
JSON.parse(readFileSync7(configPath2, "utf-8"));
|
|
4559
|
-
valid.push(name);
|
|
4560
|
-
} catch (err) {
|
|
4561
|
-
const code = err.code;
|
|
4562
|
-
if (code === "ENOENT") continue;
|
|
4563
|
-
}
|
|
4564
|
-
}
|
|
4565
|
-
cache2.set(accountsDir, valid);
|
|
4566
|
-
return valid;
|
|
4567
|
-
}
|
|
4568
|
-
function resolveHouseOrSoleAccountId(accountsDir) {
|
|
4569
|
-
const valid = enumerateValidAccountIds(accountsDir);
|
|
4570
|
-
const houses = [];
|
|
4571
|
-
for (const id of valid) {
|
|
4572
|
-
const configPath2 = resolve3(accountsDir, id, "account.json");
|
|
4573
|
-
try {
|
|
4574
|
-
const cfg = JSON.parse(readFileSync7(configPath2, "utf-8"));
|
|
4575
|
-
if (cfg.role === "house") houses.push(id);
|
|
4576
|
-
} catch {
|
|
4577
|
-
}
|
|
4578
|
-
}
|
|
4579
|
-
if (houses.length === 1) return houses[0];
|
|
4580
|
-
if (houses.length === 0 && valid.length === 1) return valid[0];
|
|
4581
|
-
throw new Error(
|
|
4582
|
-
`[account-registry] resolveHouseOrSoleAccountId: houses=${houses.length} total=${valid.length} under ${accountsDir} \u2014 expected exactly one role:"house" (or a single unlabelled account). Loud-fail rather than picking one silently.`
|
|
4583
|
-
);
|
|
4584
|
-
}
|
|
4585
|
-
function getAccountsDirFromEnv() {
|
|
4586
|
-
const root = process.env.MAXY_PLATFORM_ROOT;
|
|
4587
|
-
if (!root) {
|
|
4588
|
-
throw new Error(
|
|
4589
|
-
"[graph-write] MAXY_PLATFORM_ROOT not set \u2014 cannot enforce accountId gate. Set MAXY_PLATFORM_ROOT in the spawning process or pass `accountsDir` explicitly."
|
|
4590
|
-
);
|
|
4591
|
-
}
|
|
4592
|
-
return resolve3(root, "..", "data/accounts");
|
|
4593
|
-
}
|
|
4594
|
-
function validateAccountIdEnv(envValue, diskIds) {
|
|
4595
|
-
if (!envValue) {
|
|
4596
|
-
return { ok: false, reason: "missing", envId: null, diskIds };
|
|
4597
|
-
}
|
|
4598
|
-
if (diskIds.length === 0) {
|
|
4599
|
-
return { ok: false, reason: "no-on-disk-account", envId: envValue, diskIds };
|
|
4600
|
-
}
|
|
4601
|
-
if (!diskIds.includes(envValue)) {
|
|
4602
|
-
return { ok: false, reason: "mismatch", envId: envValue, diskIds };
|
|
4824
|
+
console.error(
|
|
4825
|
+
`${TAG12} conversation-merged cacheKey=${input.cacheKey} agentType=${input.agentType} channel=whatsapp accountId=${input.platformAccountId} connectionAccountId=${input.connectionAccountId ?? input.platformAccountId} created=${result.created} ms=${ms}`
|
|
4826
|
+
);
|
|
4827
|
+
return { sessionId: result.sessionId, created: result.created };
|
|
4828
|
+
} catch (err) {
|
|
4829
|
+
const ms = Date.now() - t0;
|
|
4830
|
+
const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
|
|
4831
|
+
console.error(
|
|
4832
|
+
`${TAG12} conversation-merged FAIL cacheKey=${input.cacheKey} reason=${reason} ms=${ms}`
|
|
4833
|
+
);
|
|
4834
|
+
return null;
|
|
4603
4835
|
}
|
|
4604
|
-
return { ok: true, envId: envValue, diskIds };
|
|
4605
4836
|
}
|
|
4606
4837
|
|
|
4607
4838
|
// app/lib/whatsapp/platform-account-id.ts
|
|
4608
4839
|
import { readFileSync as readFileSync8 } from "fs";
|
|
4609
|
-
import { resolve as
|
|
4840
|
+
import { resolve as resolve5 } from "path";
|
|
4610
4841
|
var cachedHouseId = null;
|
|
4611
4842
|
var cachedHouseAccountsDir = null;
|
|
4612
4843
|
function resolveHouseId(accountsDir) {
|
|
@@ -4617,7 +4848,7 @@ function resolveHouseId(accountsDir) {
|
|
|
4617
4848
|
return cachedHouseId;
|
|
4618
4849
|
}
|
|
4619
4850
|
function readChannelRoutingOverride(accountsDir, houseId) {
|
|
4620
|
-
const configPath2 =
|
|
4851
|
+
const configPath2 = resolve5(accountsDir, houseId, "account.json");
|
|
4621
4852
|
let raw;
|
|
4622
4853
|
try {
|
|
4623
4854
|
raw = readFileSync8(configPath2, "utf-8");
|
|
@@ -4682,14 +4913,14 @@ import {
|
|
|
4682
4913
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
4683
4914
|
import { mkdir, writeFile } from "fs/promises";
|
|
4684
4915
|
import { realpathSync as realpathSync2, existsSync as existsSync5, readFileSync as readFileSync10, readdirSync as readdirSync5 } from "fs";
|
|
4685
|
-
import { resolve as
|
|
4916
|
+
import { resolve as resolve7, extname } from "path";
|
|
4686
4917
|
|
|
4687
4918
|
// app/lib/data-path.ts
|
|
4688
4919
|
import { realpathSync } from "fs";
|
|
4689
|
-
import { resolve as
|
|
4690
|
-
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ??
|
|
4691
|
-
var DATA_ROOT =
|
|
4692
|
-
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ?
|
|
4920
|
+
import { resolve as resolve6, normalize, sep as sep2, relative, basename, join as join7 } from "path";
|
|
4921
|
+
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "../platform");
|
|
4922
|
+
var DATA_ROOT = resolve6(PLATFORM_ROOT3, "..", "data");
|
|
4923
|
+
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve6(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
4693
4924
|
var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["accounts"]);
|
|
4694
4925
|
var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4695
4926
|
function crossesForeignAccountPartition(relPath, accountId) {
|
|
@@ -4706,7 +4937,7 @@ function isWithinOwnAccountPartition(relPath, accountId) {
|
|
|
4706
4937
|
}
|
|
4707
4938
|
function resolveUnderRoot(raw, rootAbs, rootLabel) {
|
|
4708
4939
|
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
4709
|
-
const absolute =
|
|
4940
|
+
const absolute = resolve6(rootAbs, cleaned);
|
|
4710
4941
|
let rootReal;
|
|
4711
4942
|
try {
|
|
4712
4943
|
rootReal = realpathSync(rootAbs);
|
|
@@ -4928,7 +5159,7 @@ function docToText(buffer, mimeType) {
|
|
|
4928
5159
|
|
|
4929
5160
|
// app/lib/attachments.ts
|
|
4930
5161
|
function uploadsDirFor(accountId, subPath, attachmentId) {
|
|
4931
|
-
return subPath ?
|
|
5162
|
+
return subPath ? resolve7(DATA_ROOT, "accounts", accountId, "uploads", subPath, attachmentId) : resolve7(DATA_ROOT, "accounts", accountId, "uploads", attachmentId);
|
|
4932
5163
|
}
|
|
4933
5164
|
var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
4934
5165
|
"image/jpeg",
|
|
@@ -5005,14 +5236,14 @@ async function storeMediaBuffer(accountId, attachmentId, filename, mimeType, buf
|
|
|
5005
5236
|
return writeAttachment(accountId, "", filename, mimeType, buffer.byteLength, buffer, attachmentId);
|
|
5006
5237
|
}
|
|
5007
5238
|
function attachmentExists(accountId, attachmentId) {
|
|
5008
|
-
return existsSync5(
|
|
5239
|
+
return existsSync5(resolve7(uploadsDirFor(accountId, "", attachmentId), `${attachmentId}.meta.json`));
|
|
5009
5240
|
}
|
|
5010
5241
|
function resolveStoredAttachment(accountId, attachmentId) {
|
|
5011
5242
|
if (!ATTACHMENT_ID_RE.test(attachmentId)) return null;
|
|
5012
5243
|
const dir = uploadsDirFor(accountId, "", attachmentId);
|
|
5013
5244
|
let meta;
|
|
5014
5245
|
try {
|
|
5015
|
-
meta = JSON.parse(readFileSync10(
|
|
5246
|
+
meta = JSON.parse(readFileSync10(resolve7(dir, `${attachmentId}.meta.json`), "utf8"));
|
|
5016
5247
|
} catch {
|
|
5017
5248
|
return null;
|
|
5018
5249
|
}
|
|
@@ -5020,25 +5251,25 @@ function resolveStoredAttachment(accountId, attachmentId) {
|
|
|
5020
5251
|
const { filename, mimeType, sizeBytes } = meta;
|
|
5021
5252
|
if (typeof filename !== "string" || typeof mimeType !== "string") return null;
|
|
5022
5253
|
const resolvedSizeBytes = typeof sizeBytes === "number" ? sizeBytes : void 0;
|
|
5023
|
-
let
|
|
5254
|
+
let entries2;
|
|
5024
5255
|
try {
|
|
5025
|
-
|
|
5256
|
+
entries2 = readdirSync5(dir, { withFileTypes: true });
|
|
5026
5257
|
} catch {
|
|
5027
5258
|
return null;
|
|
5028
5259
|
}
|
|
5029
|
-
const dataFile =
|
|
5260
|
+
const dataFile = entries2.find((e) => e.isFile() && !e.name.endsWith(".meta.json") && !e.name.endsWith(EXTRACTED_TEXT_SUFFIX));
|
|
5030
5261
|
if (!dataFile) return null;
|
|
5031
5262
|
const readableSibling = `${attachmentId}${EXTRACTED_TEXT_SUFFIX}`;
|
|
5032
|
-
const readableTextPath =
|
|
5033
|
-
return { attachmentId, filename, mimeType, sizeBytes: resolvedSizeBytes, path:
|
|
5263
|
+
const readableTextPath = entries2.some((e) => e.isFile() && e.name === readableSibling) ? resolve7(dir, readableSibling) : void 0;
|
|
5264
|
+
return { attachmentId, filename, mimeType, sizeBytes: resolvedSizeBytes, path: resolve7(dir, dataFile.name), readableTextPath };
|
|
5034
5265
|
}
|
|
5035
5266
|
async function writeAttachment(accountId, subPath, filename, mimeType, sizeBytes, buffer, explicitId) {
|
|
5036
5267
|
const attachmentId = explicitId ?? randomUUID3();
|
|
5037
5268
|
const dir = uploadsDirFor(accountId, subPath, attachmentId);
|
|
5038
5269
|
await mkdir(dir, { recursive: true });
|
|
5039
5270
|
const ext = extname(filename) || "";
|
|
5040
|
-
const storagePath =
|
|
5041
|
-
const metaPath =
|
|
5271
|
+
const storagePath = resolve7(dir, `${attachmentId}${ext}`);
|
|
5272
|
+
const metaPath = resolve7(dir, `${attachmentId}.meta.json`);
|
|
5042
5273
|
const meta = {
|
|
5043
5274
|
attachmentId,
|
|
5044
5275
|
scope: subPath || accountId,
|
|
@@ -5060,7 +5291,7 @@ async function writeAttachment(accountId, subPath, filename, mimeType, sizeBytes
|
|
|
5060
5291
|
}
|
|
5061
5292
|
console.error(`[doc-to-text] op=convert mime=${bare} ok=${text !== null} chars=${text?.length ?? 0}`);
|
|
5062
5293
|
if (text) {
|
|
5063
|
-
const siblingPath =
|
|
5294
|
+
const siblingPath = resolve7(dir, `${attachmentId}${EXTRACTED_TEXT_SUFFIX}`);
|
|
5064
5295
|
try {
|
|
5065
5296
|
await writeFile(siblingPath, text);
|
|
5066
5297
|
readableTextPath = siblingPath;
|
|
@@ -5144,8 +5375,8 @@ async function storeComponentArtefact(accountId, attachmentId, mimeType, content
|
|
|
5144
5375
|
const dir = uploadsDirFor(accountId, "", attachmentId);
|
|
5145
5376
|
await mkdir(dir, { recursive: true });
|
|
5146
5377
|
const ext = extname(filename) || (mimeType === "text/html" ? ".html" : ".md");
|
|
5147
|
-
const storagePath =
|
|
5148
|
-
const metaPath =
|
|
5378
|
+
const storagePath = resolve7(dir, `${attachmentId}${ext}`);
|
|
5379
|
+
const metaPath = resolve7(dir, `${attachmentId}.meta.json`);
|
|
5149
5380
|
const meta = {
|
|
5150
5381
|
attachmentId,
|
|
5151
5382
|
scope: accountId,
|
|
@@ -5468,10 +5699,10 @@ function createInboundDebouncer(opts) {
|
|
|
5468
5699
|
}
|
|
5469
5700
|
|
|
5470
5701
|
// app/lib/whatsapp/inbound/combine-batch.ts
|
|
5471
|
-
function combineInboundBatch(
|
|
5472
|
-
const last =
|
|
5473
|
-
const combinedText =
|
|
5474
|
-
const media =
|
|
5702
|
+
function combineInboundBatch(entries2) {
|
|
5703
|
+
const last = entries2[entries2.length - 1];
|
|
5704
|
+
const combinedText = entries2.map((e) => e.text).filter(Boolean).join("\n");
|
|
5705
|
+
const media = entries2.flatMap((e) => e.media);
|
|
5475
5706
|
return { ...last, text: combinedText, media };
|
|
5476
5707
|
}
|
|
5477
5708
|
|
|
@@ -5718,15 +5949,15 @@ function messageStoreKey(accountId, remoteJid) {
|
|
|
5718
5949
|
return `${accountId}:${remoteJid}`;
|
|
5719
5950
|
}
|
|
5720
5951
|
function storeMessage(storeKey, entry) {
|
|
5721
|
-
let
|
|
5722
|
-
if (!
|
|
5723
|
-
|
|
5724
|
-
messageStore.set(storeKey,
|
|
5725
|
-
}
|
|
5726
|
-
|
|
5727
|
-
if (
|
|
5728
|
-
const trimmed =
|
|
5729
|
-
|
|
5952
|
+
let entries2 = messageStore.get(storeKey);
|
|
5953
|
+
if (!entries2) {
|
|
5954
|
+
entries2 = [];
|
|
5955
|
+
messageStore.set(storeKey, entries2);
|
|
5956
|
+
}
|
|
5957
|
+
entries2.push(entry);
|
|
5958
|
+
if (entries2.length > MESSAGE_STORE_MAX) {
|
|
5959
|
+
const trimmed = entries2.length - MESSAGE_STORE_MAX;
|
|
5960
|
+
entries2.splice(0, trimmed);
|
|
5730
5961
|
console.error(`${TAG17} message store trimmed for ${storeKey}: ${trimmed} oldest messages removed`);
|
|
5731
5962
|
}
|
|
5732
5963
|
}
|
|
@@ -6132,11 +6363,11 @@ async function connectWithReconnect(conn) {
|
|
|
6132
6363
|
console.error(
|
|
6133
6364
|
`${TAG17} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
6134
6365
|
);
|
|
6135
|
-
await new Promise((
|
|
6136
|
-
const timer = setTimeout(
|
|
6366
|
+
await new Promise((resolve46) => {
|
|
6367
|
+
const timer = setTimeout(resolve46, delay);
|
|
6137
6368
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
6138
6369
|
clearTimeout(timer);
|
|
6139
|
-
|
|
6370
|
+
resolve46();
|
|
6140
6371
|
}, { once: true });
|
|
6141
6372
|
});
|
|
6142
6373
|
}
|
|
@@ -6144,16 +6375,16 @@ async function connectWithReconnect(conn) {
|
|
|
6144
6375
|
}
|
|
6145
6376
|
}
|
|
6146
6377
|
function waitForDisconnectEvent(conn) {
|
|
6147
|
-
return new Promise((
|
|
6378
|
+
return new Promise((resolve46) => {
|
|
6148
6379
|
if (!conn.sock) {
|
|
6149
|
-
|
|
6380
|
+
resolve46();
|
|
6150
6381
|
return;
|
|
6151
6382
|
}
|
|
6152
6383
|
const sock = conn.sock;
|
|
6153
6384
|
const handler = (update) => {
|
|
6154
6385
|
if (update.connection === "close") {
|
|
6155
6386
|
sock.ev.off("connection.update", handler);
|
|
6156
|
-
|
|
6387
|
+
resolve46();
|
|
6157
6388
|
}
|
|
6158
6389
|
};
|
|
6159
6390
|
sock.ev.on("connection.update", handler);
|
|
@@ -6201,12 +6432,12 @@ function monitorInbound(conn) {
|
|
|
6201
6432
|
const conversationKey = payload.isGroup ? payload.groupJid : payload.senderPhone;
|
|
6202
6433
|
return `${payload.accountId}:${conversationKey ?? payload.senderPhone}:${payload.senderPhone}`;
|
|
6203
6434
|
},
|
|
6204
|
-
onFlush: (
|
|
6435
|
+
onFlush: (entries2) => {
|
|
6205
6436
|
if (!onInboundMessage) return;
|
|
6206
|
-
if (
|
|
6207
|
-
console.error(`${TAG17} debounce: combining ${
|
|
6437
|
+
if (entries2.length > 1) {
|
|
6438
|
+
console.error(`${TAG17} debounce: combining ${entries2.length} messages account=${conn.accountId} from=${entries2[0].senderPhone}`);
|
|
6208
6439
|
}
|
|
6209
|
-
dispatchInboundPayload(conn, combineInboundBatch(
|
|
6440
|
+
dispatchInboundPayload(conn, combineInboundBatch(entries2));
|
|
6210
6441
|
},
|
|
6211
6442
|
onError: (err) => {
|
|
6212
6443
|
console.error(`${TAG17} debounce flush error account=${conn.accountId}: ${String(err)}`);
|
|
@@ -6261,9 +6492,9 @@ function monitorInbound(conn) {
|
|
|
6261
6492
|
quoted: extracted.quotedMessage ? { id: extracted.quotedMessage.id, body: extracted.quotedMessage.text, sender: extracted.quotedMessage.sender } : void 0
|
|
6262
6493
|
});
|
|
6263
6494
|
}
|
|
6264
|
-
const
|
|
6495
|
+
const entries2 = messageStore.get(storeKey);
|
|
6265
6496
|
console.error(
|
|
6266
|
-
`${TAG17} stored message ${msg.key.id ?? "?"} for ${remoteJid} (type: ${upsert.type}, store size: ${
|
|
6497
|
+
`${TAG17} stored message ${msg.key.id ?? "?"} for ${remoteJid} (type: ${upsert.type}, store size: ${entries2?.length ?? 0}/${MESSAGE_STORE_MAX}) storeAccount=${persistAccountId} socketOwner=${conn.platformAccountId}`
|
|
6267
6498
|
);
|
|
6268
6499
|
recordActivity({
|
|
6269
6500
|
accountId: conn.accountId,
|
|
@@ -6627,8 +6858,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6627
6858
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
6628
6859
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
6629
6860
|
let resolvePending;
|
|
6630
|
-
const sttPending = new Promise((
|
|
6631
|
-
resolvePending =
|
|
6861
|
+
const sttPending = new Promise((resolve46) => {
|
|
6862
|
+
resolvePending = resolve46;
|
|
6632
6863
|
});
|
|
6633
6864
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
6634
6865
|
try {
|
|
@@ -6715,9 +6946,9 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6715
6946
|
import { spawnSync, execFileSync } from "child_process";
|
|
6716
6947
|
import { createConnection } from "net";
|
|
6717
6948
|
import { mkdirSync as mkdirSync3, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
|
|
6718
|
-
import { resolve as
|
|
6719
|
-
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ??
|
|
6720
|
-
var VNC_SCRIPT =
|
|
6949
|
+
import { resolve as resolve8 } from "path";
|
|
6950
|
+
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve8(process.cwd(), "..");
|
|
6951
|
+
var VNC_SCRIPT = resolve8(PLATFORM_ROOT4, "scripts/vnc.sh");
|
|
6721
6952
|
var displayMode = process.env.DISPLAY_MODE ?? "virtual";
|
|
6722
6953
|
if (displayMode === "native") {
|
|
6723
6954
|
console.log(`[vnc] DISPLAY_MODE=native \u2014 local requests use desktop display, remote requests use VNC`);
|
|
@@ -6830,7 +7061,7 @@ function ensureLogDir() {
|
|
|
6830
7061
|
mkdirSync3(LOG_DIR, { recursive: true });
|
|
6831
7062
|
}
|
|
6832
7063
|
function logPath(name) {
|
|
6833
|
-
return
|
|
7064
|
+
return resolve8(LOG_DIR, `${name}.log`);
|
|
6834
7065
|
}
|
|
6835
7066
|
async function ensureVnc() {
|
|
6836
7067
|
if (process.platform !== "linux") {
|
|
@@ -6917,7 +7148,7 @@ function killChromium() {
|
|
|
6917
7148
|
}
|
|
6918
7149
|
function writeChromiumWrapper() {
|
|
6919
7150
|
mkdirSync3(BIN_DIR, { recursive: true });
|
|
6920
|
-
const wrapperPath =
|
|
7151
|
+
const wrapperPath = resolve8(BIN_DIR, "chromium");
|
|
6921
7152
|
writeFileSync4(wrapperPath, `#!/bin/bash
|
|
6922
7153
|
LOG="${LOG_DIR}/chromium.log"
|
|
6923
7154
|
echo "==== [$(date)] chromium wrapper ====" >> "$LOG"
|
|
@@ -7017,20 +7248,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
7017
7248
|
|
|
7018
7249
|
// server/routes/health.ts
|
|
7019
7250
|
function checkPort(port2, timeoutMs = 500) {
|
|
7020
|
-
return new Promise((
|
|
7251
|
+
return new Promise((resolve46) => {
|
|
7021
7252
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
7022
7253
|
socket.setTimeout(timeoutMs);
|
|
7023
7254
|
socket.once("connect", () => {
|
|
7024
7255
|
socket.destroy();
|
|
7025
|
-
|
|
7256
|
+
resolve46(true);
|
|
7026
7257
|
});
|
|
7027
7258
|
socket.once("error", () => {
|
|
7028
7259
|
socket.destroy();
|
|
7029
|
-
|
|
7260
|
+
resolve46(false);
|
|
7030
7261
|
});
|
|
7031
7262
|
socket.once("timeout", () => {
|
|
7032
7263
|
socket.destroy();
|
|
7033
|
-
|
|
7264
|
+
resolve46(false);
|
|
7034
7265
|
});
|
|
7035
7266
|
});
|
|
7036
7267
|
}
|
|
@@ -7690,7 +7921,7 @@ function createScheduleInjectRoutes(deps) {
|
|
|
7690
7921
|
}
|
|
7691
7922
|
|
|
7692
7923
|
// server/index.ts
|
|
7693
|
-
var
|
|
7924
|
+
var import_dist9 = __toESM(require_dist(), 1);
|
|
7694
7925
|
|
|
7695
7926
|
// app/lib/whatsapp/socket-resolution-error.ts
|
|
7696
7927
|
function formatPresentKeys(presentKeys) {
|
|
@@ -7731,7 +7962,7 @@ async function sendTelegramText(botToken, chatId, text) {
|
|
|
7731
7962
|
}
|
|
7732
7963
|
|
|
7733
7964
|
// server/routes/whatsapp.ts
|
|
7734
|
-
import { join as join12, resolve as
|
|
7965
|
+
import { join as join12, resolve as resolve11 } from "path";
|
|
7735
7966
|
import { readdirSync as readdirSync7, readFileSync as readFileSync14, existsSync as existsSync8 } from "fs";
|
|
7736
7967
|
|
|
7737
7968
|
// app/lib/whatsapp/login.ts
|
|
@@ -7739,10 +7970,10 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
7739
7970
|
|
|
7740
7971
|
// app/lib/whatsapp/config-persist.ts
|
|
7741
7972
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "fs";
|
|
7742
|
-
import { resolve as
|
|
7973
|
+
import { resolve as resolve9, join as join10, dirname as dirname2, basename as basename2 } from "path";
|
|
7743
7974
|
var TAG21 = "[whatsapp:config]";
|
|
7744
7975
|
function configPath(accountDir) {
|
|
7745
|
-
return
|
|
7976
|
+
return resolve9(accountDir, "account.json");
|
|
7746
7977
|
}
|
|
7747
7978
|
function readConfig(accountDir) {
|
|
7748
7979
|
const path3 = configPath(accountDir);
|
|
@@ -7814,8 +8045,8 @@ function addAdminPhone(accountDir, phone) {
|
|
|
7814
8045
|
}
|
|
7815
8046
|
try {
|
|
7816
8047
|
const config = readConfig(accountDir);
|
|
7817
|
-
const targetId = typeof config.accountId === "string" && config.accountId ? config.accountId : basename2(
|
|
7818
|
-
const socketOwner = resolveHouseOrSoleAccountId(dirname2(
|
|
8048
|
+
const targetId = typeof config.accountId === "string" && config.accountId ? config.accountId : basename2(resolve9(accountDir));
|
|
8049
|
+
const socketOwner = resolveHouseOrSoleAccountId(dirname2(resolve9(accountDir)));
|
|
7819
8050
|
if (targetId !== socketOwner) {
|
|
7820
8051
|
console.error(`${TAG21} add-admin-phone rejected accountId=${targetId} reason=not-socket-account`);
|
|
7821
8052
|
return {
|
|
@@ -7904,7 +8135,7 @@ function purgeNonSocketAdminPhones(accountDir, socketOwnerId) {
|
|
|
7904
8135
|
const config = readConfig(accountDir);
|
|
7905
8136
|
const wa = config.whatsapp;
|
|
7906
8137
|
if (!wa || typeof wa !== "object") return { removed: [] };
|
|
7907
|
-
const targetId = typeof config.accountId === "string" && config.accountId ? config.accountId : basename2(
|
|
8138
|
+
const targetId = typeof config.accountId === "string" && config.accountId ? config.accountId : basename2(resolve9(accountDir));
|
|
7908
8139
|
if (targetId === socketOwnerId) return { removed: [] };
|
|
7909
8140
|
const adminPhones = Array.isArray(wa.adminPhones) ? wa.adminPhones.filter((p) => typeof p === "string") : [];
|
|
7910
8141
|
if (adminPhones.length === 0) return { removed: [] };
|
|
@@ -8369,8 +8600,8 @@ async function startLogin(opts) {
|
|
|
8369
8600
|
await clearAuth(authDir);
|
|
8370
8601
|
let resolveCode = null;
|
|
8371
8602
|
let rejectCode = null;
|
|
8372
|
-
const codePromise = new Promise((
|
|
8373
|
-
resolveCode =
|
|
8603
|
+
const codePromise = new Promise((resolve46, reject) => {
|
|
8604
|
+
resolveCode = resolve46;
|
|
8374
8605
|
rejectCode = reject;
|
|
8375
8606
|
});
|
|
8376
8607
|
const codeTimer = setTimeout(
|
|
@@ -8521,7 +8752,7 @@ function reconcileMessageStoreAccountKeys(input) {
|
|
|
8521
8752
|
// app/lib/whatsapp/outbound/send-document.ts
|
|
8522
8753
|
import { realpathSync as realpathSync3 } from "fs";
|
|
8523
8754
|
import { readFile, stat as stat2 } from "fs/promises";
|
|
8524
|
-
import { resolve as
|
|
8755
|
+
import { resolve as resolve10, basename as basename3 } from "path";
|
|
8525
8756
|
var TAG23 = "[whatsapp:outbound]";
|
|
8526
8757
|
var lastDocumentOutboundAt = /* @__PURE__ */ new Map();
|
|
8527
8758
|
var lastRouteDocumentOutboundAt = /* @__PURE__ */ new Map();
|
|
@@ -8548,7 +8779,7 @@ async function sendWhatsAppDocument(input) {
|
|
|
8548
8779
|
if (!maxyAccountId || !platformRoot5) {
|
|
8549
8780
|
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
8550
8781
|
}
|
|
8551
|
-
const accountDir =
|
|
8782
|
+
const accountDir = resolve10(platformRoot5, "..", "data/accounts", maxyAccountId);
|
|
8552
8783
|
let accountResolved;
|
|
8553
8784
|
try {
|
|
8554
8785
|
accountResolved = realpathSync3(accountDir);
|
|
@@ -8740,25 +8971,25 @@ function isAccountConfigured(accountDir, accountId) {
|
|
|
8740
8971
|
}
|
|
8741
8972
|
function reconcileCredsOnDisk(opts) {
|
|
8742
8973
|
const { credsRoot, accountDir, liveAccountIds, activeLoginAccountIds } = opts;
|
|
8743
|
-
const
|
|
8974
|
+
const entries2 = [];
|
|
8744
8975
|
for (const accountId of listCredsAccountIds(credsRoot)) {
|
|
8745
8976
|
if (liveAccountIds.has(accountId)) continue;
|
|
8746
8977
|
const authDir = join11(credsRoot, accountId);
|
|
8747
8978
|
const census = credsCensus(authDir);
|
|
8748
8979
|
if (census.registered && !census.hasAccountSignature) {
|
|
8749
|
-
const
|
|
8980
|
+
const inFlight3 = activeLoginAccountIds.has(accountId);
|
|
8750
8981
|
const ageMs2 = getAuthAgeMs(authDir);
|
|
8751
|
-
const abandoned = !
|
|
8982
|
+
const abandoned = !inFlight3 && ageMs2 !== null && ageMs2 >= HALF_REGISTERED_MIN_AGE_MS;
|
|
8752
8983
|
if (!abandoned) {
|
|
8753
|
-
console.error(`${TAG24} op=half-registered account=${accountId} registered=true hasAccount=false inFlight=${
|
|
8754
|
-
|
|
8984
|
+
console.error(`${TAG24} op=half-registered account=${accountId} registered=true hasAccount=false inFlight=${inFlight3} ageMs=${ageMs2 ?? "null"}`);
|
|
8985
|
+
entries2.push({ accountId, connected: false, linkedUnconfigured: true });
|
|
8755
8986
|
continue;
|
|
8756
8987
|
}
|
|
8757
8988
|
console.error(`${TAG24} op=discard-half-registered account=${accountId} registered=true hasAccount=false ageMs=${ageMs2}`);
|
|
8758
8989
|
const removed = discardAuthDirSync(authDir);
|
|
8759
8990
|
if (!removed) {
|
|
8760
8991
|
console.error(`${TAG24} op=discard-half-registered-failed account=${accountId} \u2014 residue persists`);
|
|
8761
|
-
|
|
8992
|
+
entries2.push({ accountId, connected: false, linkedUnconfigured: true });
|
|
8762
8993
|
}
|
|
8763
8994
|
continue;
|
|
8764
8995
|
}
|
|
@@ -8775,9 +9006,9 @@ function reconcileCredsOnDisk(opts) {
|
|
|
8775
9006
|
console.error(`${TAG24} self-heal FAILED account=${accountId}: ${result.error}`);
|
|
8776
9007
|
}
|
|
8777
9008
|
}
|
|
8778
|
-
|
|
9009
|
+
entries2.push({ accountId, selfPhone, connected: false, linkedUnconfigured: !configured });
|
|
8779
9010
|
}
|
|
8780
|
-
return
|
|
9011
|
+
return entries2;
|
|
8781
9012
|
}
|
|
8782
9013
|
|
|
8783
9014
|
// app/lib/whatsapp/outbound/reply-guard.ts
|
|
@@ -9165,14 +9396,14 @@ app2.post("/config", async (c) => {
|
|
|
9165
9396
|
return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
|
|
9166
9397
|
}
|
|
9167
9398
|
case "list-public-agents": {
|
|
9168
|
-
const agentsDir =
|
|
9399
|
+
const agentsDir = resolve11(account.accountDir, "agents");
|
|
9169
9400
|
const agents = [];
|
|
9170
9401
|
if (existsSync8(agentsDir)) {
|
|
9171
9402
|
try {
|
|
9172
|
-
const
|
|
9173
|
-
for (const entry of
|
|
9403
|
+
const entries2 = readdirSync7(agentsDir, { withFileTypes: true });
|
|
9404
|
+
for (const entry of entries2.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
9174
9405
|
if (!entry.isDirectory() || entry.name === "admin") continue;
|
|
9175
|
-
const configPath2 =
|
|
9406
|
+
const configPath2 = resolve11(agentsDir, entry.name, "config.json");
|
|
9176
9407
|
if (!existsSync8(configPath2)) continue;
|
|
9177
9408
|
try {
|
|
9178
9409
|
const config = JSON.parse(readFileSync14(configPath2, "utf-8"));
|
|
@@ -9664,7 +9895,7 @@ var bodyLimit = (options) => {
|
|
|
9664
9895
|
};
|
|
9665
9896
|
|
|
9666
9897
|
// server/routes/storage-broker.ts
|
|
9667
|
-
import { join as join13, resolve as
|
|
9898
|
+
import { join as join13, resolve as resolve12 } from "path";
|
|
9668
9899
|
import { readdirSync as readdirSync8, readFileSync as readFileSync15, existsSync as existsSync9, realpathSync as realpathSync4 } from "fs";
|
|
9669
9900
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
9670
9901
|
var app3 = new Hono();
|
|
@@ -9742,9 +9973,9 @@ var pagesDeployBodyLimit = bodyLimit({
|
|
|
9742
9973
|
function resolveSiteDir(account, site) {
|
|
9743
9974
|
if (!site || site === "." || site === "..") return null;
|
|
9744
9975
|
if (site.includes("/") || site.includes("\\") || site.includes("\0")) return null;
|
|
9745
|
-
const accountsDir =
|
|
9746
|
-
const base =
|
|
9747
|
-
const dir =
|
|
9976
|
+
const accountsDir = resolve12(platformRoot(), "..", "data", "accounts");
|
|
9977
|
+
const base = resolve12(accountsDir, account, "sites");
|
|
9978
|
+
const dir = resolve12(base, site);
|
|
9748
9979
|
if (dir !== base && !dir.startsWith(base + "/")) return null;
|
|
9749
9980
|
try {
|
|
9750
9981
|
const realBase = realpathSync4(base);
|
|
@@ -10177,7 +10408,7 @@ var storage_broker_default = app3;
|
|
|
10177
10408
|
async function runStorageAudit(root) {
|
|
10178
10409
|
const { reconcileStorage } = await import("./src-JN6PIAJN.js");
|
|
10179
10410
|
const cf = await makeHouseCfExec(root);
|
|
10180
|
-
const accountsDir =
|
|
10411
|
+
const accountsDir = resolve12(root, "..", "data", "accounts");
|
|
10181
10412
|
const strays = (await reconcileStorage({
|
|
10182
10413
|
cfD1: async () => (await cf.d1List()).map((d) => d.name),
|
|
10183
10414
|
cfR2: async () => (await cf.r2List()).map((b) => b.name),
|
|
@@ -10275,7 +10506,7 @@ function auditErrorText(message) {
|
|
|
10275
10506
|
function readLastPushAgeSec(platformRoot5, accountId, nowMs) {
|
|
10276
10507
|
try {
|
|
10277
10508
|
const raw = readFileSync15(
|
|
10278
|
-
|
|
10509
|
+
resolve12(platformRoot5, "..", "data", "portal-index-state.json"),
|
|
10279
10510
|
"utf-8"
|
|
10280
10511
|
);
|
|
10281
10512
|
const state = JSON.parse(raw);
|
|
@@ -10289,7 +10520,7 @@ function readLastPushAgeSec(platformRoot5, accountId, nowMs) {
|
|
|
10289
10520
|
function countConfigAbsent(platformRoot5, present) {
|
|
10290
10521
|
try {
|
|
10291
10522
|
const raw = readFileSync15(
|
|
10292
|
-
|
|
10523
|
+
resolve12(platformRoot5, "..", "data", "portal-index-state.json"),
|
|
10293
10524
|
"utf-8"
|
|
10294
10525
|
);
|
|
10295
10526
|
const state = JSON.parse(raw);
|
|
@@ -10316,7 +10547,7 @@ function discoverPortalAccounts(accountsDir) {
|
|
|
10316
10547
|
return found;
|
|
10317
10548
|
}
|
|
10318
10549
|
async function runDataPortalAudit(root) {
|
|
10319
|
-
const portals = discoverPortalAccounts(
|
|
10550
|
+
const portals = discoverPortalAccounts(resolve12(root, "..", "data", "accounts"));
|
|
10320
10551
|
const configAbsent = countConfigAbsent(root, portals);
|
|
10321
10552
|
if (portals.length === 0) {
|
|
10322
10553
|
if (configAbsent > 0) {
|
|
@@ -10386,7 +10617,7 @@ async function runDataPortalAudit(root) {
|
|
|
10386
10617
|
// server/portal-uploads-pull.ts
|
|
10387
10618
|
import { existsSync as existsSync10 } from "fs";
|
|
10388
10619
|
import { mkdir as mkdir3, readFile as readFile2, rename, stat as stat3, unlink as unlink2, writeFile as writeFile4 } from "fs/promises";
|
|
10389
|
-
import { basename as basename4, dirname as dirname4, join as join14, relative as relative2, resolve as
|
|
10620
|
+
import { basename as basename4, dirname as dirname4, join as join14, relative as relative2, resolve as resolve13, sep as sep3 } from "path";
|
|
10390
10621
|
|
|
10391
10622
|
// ../plugins/cloudflare/bin/schema-exposed-dirs.mjs
|
|
10392
10623
|
var ONT_START = "<!-- ontology-buckets:start -->";
|
|
@@ -10483,10 +10714,10 @@ function resolveDestination(accountDir, row, exposed) {
|
|
|
10483
10714
|
const segments = row.targetPath.split("/");
|
|
10484
10715
|
if (!segments.every(safeSegment)) return { ok: false, reason: "bad-target" };
|
|
10485
10716
|
if (!exposed.includes(segments[0])) return { ok: false, reason: "bad-target" };
|
|
10486
|
-
root =
|
|
10717
|
+
root = resolve13(accountDir);
|
|
10487
10718
|
dir = join14(root, ...segments);
|
|
10488
10719
|
} else {
|
|
10489
|
-
root =
|
|
10720
|
+
root = resolve13(accountDir, "uploads");
|
|
10490
10721
|
dir = join14(root, row.ownerId);
|
|
10491
10722
|
}
|
|
10492
10723
|
const final = join14(dir, row.filename);
|
|
@@ -10498,7 +10729,7 @@ function resolveDestination(accountDir, row, exposed) {
|
|
|
10498
10729
|
dir,
|
|
10499
10730
|
tmp: join14(dir, ".uploads-tmp", row.filename),
|
|
10500
10731
|
final,
|
|
10501
|
-
relative: relative2(
|
|
10732
|
+
relative: relative2(resolve13(accountDir), final).split(sep3).join("/")
|
|
10502
10733
|
};
|
|
10503
10734
|
}
|
|
10504
10735
|
function nextFreePath(final) {
|
|
@@ -10553,7 +10784,7 @@ async function pullAccount(opts) {
|
|
|
10553
10784
|
`${TAG26} op=collision account=${accountId}${idField} suffix="${basename4(finalPath)}" result=suffixed`
|
|
10554
10785
|
);
|
|
10555
10786
|
}
|
|
10556
|
-
const relPath = relative2(
|
|
10787
|
+
const relPath = relative2(resolve13(accountDir), finalPath).split(sep3).join("/");
|
|
10557
10788
|
try {
|
|
10558
10789
|
await mkdir3(dirname4(dest.tmp), { recursive: true });
|
|
10559
10790
|
await writeFile4(dest.tmp, bytes);
|
|
@@ -10613,8 +10844,8 @@ async function withdrawRow(opts) {
|
|
|
10613
10844
|
log2(`${head} removed=false result=bad-path`);
|
|
10614
10845
|
return false;
|
|
10615
10846
|
}
|
|
10616
|
-
const absolute = join14(
|
|
10617
|
-
if (!absolute.startsWith(
|
|
10847
|
+
const absolute = join14(resolve13(accountDir), ...segments);
|
|
10848
|
+
if (!absolute.startsWith(resolve13(accountDir) + sep3)) {
|
|
10618
10849
|
log2(`${head} removed=false result=bad-path`);
|
|
10619
10850
|
return false;
|
|
10620
10851
|
}
|
|
@@ -10693,7 +10924,7 @@ async function readExposedDirs(accountDir) {
|
|
|
10693
10924
|
return exposed;
|
|
10694
10925
|
}
|
|
10695
10926
|
async function runPortalUploadsPull(root) {
|
|
10696
|
-
const accountsRoot =
|
|
10927
|
+
const accountsRoot = resolve13(root, "..", "data", "accounts");
|
|
10697
10928
|
const portals = discoverPortalAccounts(accountsRoot);
|
|
10698
10929
|
if (portals.length === 0) return "portals=0 pulled=0";
|
|
10699
10930
|
const cf = await makeHouseCfExec(root);
|
|
@@ -10742,7 +10973,7 @@ async function runPortalUploadsPull(root) {
|
|
|
10742
10973
|
import { readFile as readFile3, readdir, stat as stat4 } from "fs/promises";
|
|
10743
10974
|
import { existsSync as existsSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync6 } from "fs";
|
|
10744
10975
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
10745
|
-
import { join as join15, resolve as
|
|
10976
|
+
import { join as join15, resolve as resolve14 } from "path";
|
|
10746
10977
|
import { fileURLToPath } from "url";
|
|
10747
10978
|
|
|
10748
10979
|
// ../plugins/cloudflare/bin/d1-http.mjs
|
|
@@ -10827,9 +11058,9 @@ async function readExposeFolders(accountDir) {
|
|
|
10827
11058
|
async function walkExposed(accountDir, exposed) {
|
|
10828
11059
|
const out = [];
|
|
10829
11060
|
async function walk(rel) {
|
|
10830
|
-
let
|
|
11061
|
+
let entries2;
|
|
10831
11062
|
try {
|
|
10832
|
-
|
|
11063
|
+
entries2 = await readdir(join15(accountDir, rel), { withFileTypes: true });
|
|
10833
11064
|
} catch {
|
|
10834
11065
|
return;
|
|
10835
11066
|
}
|
|
@@ -10838,7 +11069,7 @@ async function walkExposed(accountDir, exposed) {
|
|
|
10838
11069
|
out.push({ relPath: rel, sizeBytes: 0, modifiedAt: st.mtime.toISOString(), isDir: true });
|
|
10839
11070
|
} catch {
|
|
10840
11071
|
}
|
|
10841
|
-
for (const e of
|
|
11072
|
+
for (const e of entries2) {
|
|
10842
11073
|
if (e.name.startsWith(".")) continue;
|
|
10843
11074
|
const childRel = `${rel}/${e.name}`;
|
|
10844
11075
|
if (e.isDirectory()) {
|
|
@@ -10938,7 +11169,7 @@ async function pushAccount(opts) {
|
|
|
10938
11169
|
return { exposed: r.exposed, rows: rows.length, schemaPresent: true, generation: next };
|
|
10939
11170
|
}
|
|
10940
11171
|
function stateFilePath(platformRoot5) {
|
|
10941
|
-
return
|
|
11172
|
+
return resolve14(platformRoot5, "..", "data", "portal-index-state.json");
|
|
10942
11173
|
}
|
|
10943
11174
|
function readState(platformRoot5) {
|
|
10944
11175
|
try {
|
|
@@ -11005,7 +11236,7 @@ function resolveCloudflareCredentials(platformRoot5) {
|
|
|
11005
11236
|
return { cfAccountId: house.CLOUDFLARE_ACCOUNT_ID, token };
|
|
11006
11237
|
}
|
|
11007
11238
|
async function pushAllAccounts(platformRoot5) {
|
|
11008
|
-
const accountsRoot =
|
|
11239
|
+
const accountsRoot = resolve14(platformRoot5, "..", "data", "accounts");
|
|
11009
11240
|
const targets = await portalAccounts(accountsRoot);
|
|
11010
11241
|
console.error(`${TAG27} op=enumerate accountsRoot="${accountsRoot}" accounts=${targets.length}`);
|
|
11011
11242
|
if (targets.length === 0) return { accounts: 0, pushed: 0, failed: 0 };
|
|
@@ -11084,8 +11315,8 @@ async function main() {
|
|
|
11084
11315
|
]);
|
|
11085
11316
|
if (r.failed > 0) process.exit(1);
|
|
11086
11317
|
}
|
|
11087
|
-
var invokedPath = process.argv[1] ?
|
|
11088
|
-
if (invokedPath && invokedPath.endsWith("/portal-index-push.mjs") && invokedPath ===
|
|
11318
|
+
var invokedPath = process.argv[1] ? resolve14(process.argv[1]) : "";
|
|
11319
|
+
if (invokedPath && invokedPath.endsWith("/portal-index-push.mjs") && invokedPath === resolve14(fileURLToPath(import.meta.url))) {
|
|
11089
11320
|
void main();
|
|
11090
11321
|
}
|
|
11091
11322
|
|
|
@@ -11164,12 +11395,12 @@ function runReconcileLivenessAudit(statePath, nowMs, intervalMs, emit) {
|
|
|
11164
11395
|
}
|
|
11165
11396
|
|
|
11166
11397
|
// server/routes/whatsapp-reader.ts
|
|
11167
|
-
import { readFileSync as readFileSync21, watch, statSync as statSync8, openSync, readSync, closeSync, existsSync as existsSync17, readdirSync as readdirSync14, realpathSync as realpathSync6 } from "fs";
|
|
11168
|
-
import { basename as basename6, dirname as dirname7, isAbsolute, join as join21, relative as relative3, resolve as
|
|
11398
|
+
import { readFileSync as readFileSync21, watch as watch2, statSync as statSync8, openSync, readSync, closeSync, existsSync as existsSync17, readdirSync as readdirSync14, realpathSync as realpathSync6 } from "fs";
|
|
11399
|
+
import { basename as basename6, dirname as dirname7, isAbsolute, join as join21, relative as relative3, resolve as resolve17, sep as sep5 } from "path";
|
|
11169
11400
|
|
|
11170
11401
|
// server/routes/admin/sidebar-sessions.ts
|
|
11171
11402
|
import { readdirSync as readdirSync12, readFileSync as readFileSync19, statSync as statSync6 } from "fs";
|
|
11172
|
-
import { dirname as dirname5, join as join19, resolve as
|
|
11403
|
+
import { dirname as dirname5, join as join19, resolve as resolve15 } from "path";
|
|
11173
11404
|
|
|
11174
11405
|
// ../services/claude-session-manager/src/jsonl-path.ts
|
|
11175
11406
|
import { existsSync as existsSync14, readdirSync as readdirSync10 } from "fs";
|
|
@@ -11329,15 +11560,15 @@ function enumerateJsonls(projectsRoot) {
|
|
|
11329
11560
|
}
|
|
11330
11561
|
for (const slug of slugs) {
|
|
11331
11562
|
const slugDir = join19(projectsRoot, slug);
|
|
11332
|
-
let
|
|
11563
|
+
let entries2;
|
|
11333
11564
|
try {
|
|
11334
|
-
|
|
11565
|
+
entries2 = readdirSync12(slugDir, { withFileTypes: true });
|
|
11335
11566
|
} catch (err) {
|
|
11336
11567
|
const code = err.code ?? "unknown";
|
|
11337
11568
|
console.error(`[admin-sessions-list] slug-skipped slug=${slug} code=${code}`);
|
|
11338
11569
|
continue;
|
|
11339
11570
|
}
|
|
11340
|
-
for (const entry of
|
|
11571
|
+
for (const entry of entries2) {
|
|
11341
11572
|
if (entry.isFile() && SESSION_ID_RE.test(entry.name)) {
|
|
11342
11573
|
out.push({ path: join19(slugDir, entry.name), isSubagent: false, archived: false });
|
|
11343
11574
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
@@ -11520,17 +11751,30 @@ function resolveTitle(body, sessionId, userTitles) {
|
|
|
11520
11751
|
app4.get("/", requireAdminSession, async (c) => {
|
|
11521
11752
|
const cacheKey = c.var.cacheKey;
|
|
11522
11753
|
const scopeAccountId = getAccountIdForSession(cacheKey) ?? null;
|
|
11754
|
+
const start = Date.now();
|
|
11755
|
+
const { value, state } = await getOrCompute(
|
|
11756
|
+
"sidebar-sessions",
|
|
11757
|
+
scopeAccountId ?? NO_ACCOUNT_KEY,
|
|
11758
|
+
() => computeSessionList(scopeAccountId)
|
|
11759
|
+
);
|
|
11760
|
+
console.log(
|
|
11761
|
+
`[admin-sessions-list] op=list-cache cache=${state} accountId=${scopeAccountId ? scopeAccountId.slice(0, 8) : "unset"} ms=${Date.now() - start}`
|
|
11762
|
+
);
|
|
11763
|
+
return c.json(value);
|
|
11764
|
+
});
|
|
11765
|
+
var NO_ACCOUNT_KEY = "no-account";
|
|
11766
|
+
async function computeSessionList(scopeAccountId) {
|
|
11523
11767
|
const validAccounts = listValidAccounts();
|
|
11524
11768
|
const multiAccount = validAccounts.length > 1;
|
|
11525
11769
|
const configDir2 = claudeConfigDir();
|
|
11526
11770
|
if (!configDir2) {
|
|
11527
11771
|
console.error("[admin-sessions-list] CLAUDE_CONFIG_DIR not set; returning empty list");
|
|
11528
|
-
return
|
|
11772
|
+
return { sessions: [], accountId: scopeAccountId };
|
|
11529
11773
|
}
|
|
11530
11774
|
const projectsRoot = join19(configDir2, "projects");
|
|
11531
11775
|
const jsonls = enumerateJsonls(projectsRoot);
|
|
11532
11776
|
const liveSessions = await fetchLiveSessions();
|
|
11533
|
-
const accountDir = scopeAccountId ?
|
|
11777
|
+
const accountDir = scopeAccountId ? resolve15(ACCOUNTS_DIR, scopeAccountId) : null;
|
|
11534
11778
|
const userTitles = loadUserTitles(accountDir);
|
|
11535
11779
|
const account = multiAccount ? validAccounts.find((a) => a.accountId === scopeAccountId) ?? null : resolveAccount();
|
|
11536
11780
|
let channelUsers = [];
|
|
@@ -11701,8 +11945,8 @@ app4.get("/", requireAdminSession, async (c) => {
|
|
|
11701
11945
|
if (multiAccount) {
|
|
11702
11946
|
console.log(`[admin-sessions-list] untagged-accountId-rows=${untaggedCount}`);
|
|
11703
11947
|
}
|
|
11704
|
-
return
|
|
11705
|
-
}
|
|
11948
|
+
return { sessions: rows, accountId: scopeAccountId };
|
|
11949
|
+
}
|
|
11706
11950
|
var sidebar_sessions_default = app4;
|
|
11707
11951
|
|
|
11708
11952
|
// app/lib/agent-errors.ts
|
|
@@ -11983,7 +12227,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
11983
12227
|
|
|
11984
12228
|
// app/lib/whatsapp-reader/enrich-attachments.ts
|
|
11985
12229
|
import { readFileSync as readFileSync20, readdirSync as readdirSync13, existsSync as existsSync16, statSync as statSync7, realpathSync as realpathSync5 } from "fs";
|
|
11986
|
-
import { basename as basename5, dirname as dirname6, join as join20, resolve as
|
|
12230
|
+
import { basename as basename5, dirname as dirname6, join as join20, resolve as resolve16, sep as sep4 } from "path";
|
|
11987
12231
|
var ADMIN_UPLOAD_PATH = /\/uploads\/(?!public\/)([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\//gi;
|
|
11988
12232
|
function adminAttachmentIds(text) {
|
|
11989
12233
|
const ids = [];
|
|
@@ -12002,19 +12246,19 @@ function adminAttachmentsFromText(text, uploadsBase) {
|
|
|
12002
12246
|
return out;
|
|
12003
12247
|
}
|
|
12004
12248
|
function claudeUploadsRoot() {
|
|
12005
|
-
return process.env.CLAUDE_CONFIG_DIR ?
|
|
12249
|
+
return process.env.CLAUDE_CONFIG_DIR ? resolve16(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
12006
12250
|
}
|
|
12007
12251
|
var CC_MENTION_RE = /@"([^"]+)"/g;
|
|
12008
12252
|
function claudeUploadAttachmentsFromText(text, sessionId) {
|
|
12009
12253
|
const root = claudeUploadsRoot();
|
|
12010
12254
|
if (!root) return { attachments: [], strippedText: text };
|
|
12011
|
-
const sessionDir =
|
|
12255
|
+
const sessionDir = resolve16(root, sessionId);
|
|
12012
12256
|
const attachments = [];
|
|
12013
12257
|
const accepted = [];
|
|
12014
12258
|
for (const m of text.matchAll(CC_MENTION_RE)) {
|
|
12015
12259
|
let abs;
|
|
12016
12260
|
try {
|
|
12017
|
-
abs =
|
|
12261
|
+
abs = resolve16(m[1]);
|
|
12018
12262
|
} catch {
|
|
12019
12263
|
continue;
|
|
12020
12264
|
}
|
|
@@ -12067,14 +12311,14 @@ function readAttachmentMeta(dir) {
|
|
|
12067
12311
|
return render;
|
|
12068
12312
|
}
|
|
12069
12313
|
function listSessionAttachmentsInDir(dir) {
|
|
12070
|
-
let
|
|
12314
|
+
let entries2;
|
|
12071
12315
|
try {
|
|
12072
|
-
|
|
12316
|
+
entries2 = readdirSync13(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
12073
12317
|
} catch {
|
|
12074
12318
|
return [];
|
|
12075
12319
|
}
|
|
12076
12320
|
const sidecars = [];
|
|
12077
|
-
for (const name of
|
|
12321
|
+
for (const name of entries2) {
|
|
12078
12322
|
const sidecar = readSidecar(join20(dir, name), name);
|
|
12079
12323
|
if (sidecar) sidecars.push(sidecar);
|
|
12080
12324
|
}
|
|
@@ -12124,8 +12368,8 @@ function managerBase(tag) {
|
|
|
12124
12368
|
}
|
|
12125
12369
|
|
|
12126
12370
|
// server/routes/activity-scan.ts
|
|
12127
|
-
function pickActivity(
|
|
12128
|
-
const candidates =
|
|
12371
|
+
function pickActivity(entries2, grown, nowMs, concurrencyWindowMs) {
|
|
12372
|
+
const candidates = entries2.filter((e) => grown.has(e.hex));
|
|
12129
12373
|
if (candidates.length === 0) return null;
|
|
12130
12374
|
let newest = candidates[0];
|
|
12131
12375
|
for (const e of candidates) if (e.mtimeMs > newest.mtimeMs) newest = e;
|
|
@@ -12370,83 +12614,87 @@ app5.get("/conversations-all", requireAdminSession, async (c) => {
|
|
|
12370
12614
|
console.log(`[wa-reader] op=conversations-all-gate decision=deny caller=${caller2?.slice(0, 8) ?? "null"} house=${houseId?.slice(0, 8) ?? "null"}`);
|
|
12371
12615
|
return c.json({ error: "house-only" }, 403);
|
|
12372
12616
|
}
|
|
12373
|
-
const
|
|
12374
|
-
|
|
12375
|
-
|
|
12376
|
-
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
const
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12617
|
+
const { value, state } = await getOrCompute("conversations-all", houseId, async () => {
|
|
12618
|
+
const cfg = claudeConfigDir();
|
|
12619
|
+
if (!cfg) return { conversations: [] };
|
|
12620
|
+
const projectsRoot = join21(cfg, "projects");
|
|
12621
|
+
const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
|
|
12622
|
+
const validAccounts = listValidAccounts();
|
|
12623
|
+
const validIds = new Set(validAccounts.map((a) => a.accountId));
|
|
12624
|
+
const houseIds = new Set(validAccounts.filter((a) => a.config.role === "house").map((a) => a.accountId));
|
|
12625
|
+
const rawByAccount = /* @__PURE__ */ new Map();
|
|
12626
|
+
for (const { path: path3 } of enumerateJsonls(projectsRoot)) {
|
|
12627
|
+
const sessionId = basename6(path3).replace(/\.jsonl$/, "");
|
|
12628
|
+
const projectDir = dirname7(path3);
|
|
12629
|
+
const meta = readSidecarMeta(join21(projectDir, `${sessionId}.meta.json`));
|
|
12630
|
+
if (!isReaderChannelSession(meta.role, meta.channel)) continue;
|
|
12631
|
+
if (meta.accountId === null || !validIds.has(meta.accountId)) continue;
|
|
12632
|
+
let body = "";
|
|
12633
|
+
try {
|
|
12634
|
+
body = readFileSync21(path3, "utf8");
|
|
12635
|
+
} catch {
|
|
12636
|
+
}
|
|
12637
|
+
const { title } = resolveTitle(body, sessionId, userTitles);
|
|
12638
|
+
const turns = parseTranscript(body.split("\n"));
|
|
12639
|
+
const row = {
|
|
12640
|
+
sessionId,
|
|
12641
|
+
projectDir,
|
|
12642
|
+
title,
|
|
12643
|
+
startedAt: meta.startedAt ?? "",
|
|
12644
|
+
role: meta.role,
|
|
12645
|
+
channel: meta.channel,
|
|
12646
|
+
senderId: meta.senderId,
|
|
12647
|
+
lastMessageAt: lastTurnTs(turns),
|
|
12648
|
+
modelGated: isModelGated(turns),
|
|
12649
|
+
model: lastAssistantModel(body)
|
|
12650
|
+
};
|
|
12651
|
+
const list = rawByAccount.get(meta.accountId) ?? [];
|
|
12652
|
+
list.push(row);
|
|
12653
|
+
rawByAccount.set(meta.accountId, list);
|
|
12654
|
+
}
|
|
12655
|
+
let users = [];
|
|
12388
12656
|
try {
|
|
12389
|
-
|
|
12390
|
-
} catch {
|
|
12657
|
+
users = readUsersFile(USERS_FILE) ?? [];
|
|
12658
|
+
} catch (err) {
|
|
12659
|
+
console.error(`[wa-reader] op=users-file-unreadable reason=${err instanceof Error ? err.message : String(err)}`);
|
|
12660
|
+
users = [];
|
|
12391
12661
|
}
|
|
12392
|
-
const
|
|
12393
|
-
const
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12399
|
-
|
|
12400
|
-
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
|
|
12411
|
-
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
const
|
|
12420
|
-
const
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
operatorName: r.senderId ? nameBySender.get(r.senderId) ?? null : null
|
|
12429
|
-
}));
|
|
12430
|
-
const storeRows = buildStoreConversationRows(account.accountId, readConversationSummaries(account.accountId));
|
|
12431
|
-
perAccount.push({ isHouse, rows: [...named, ...storeRows] });
|
|
12432
|
-
}
|
|
12433
|
-
const houseAccount = validAccounts.find((a) => a.accountId === houseId) ?? null;
|
|
12434
|
-
const subManagerPhones = /* @__PURE__ */ new Map();
|
|
12435
|
-
if (houseAccount) {
|
|
12436
|
-
const managers = readAccountManagers(houseAccount.accountDir);
|
|
12437
|
-
for (const a of validAccounts) {
|
|
12438
|
-
if (houseIds.has(a.accountId)) continue;
|
|
12439
|
-
const phones = managerPhonesForAccount(managers, a.accountId);
|
|
12440
|
-
if (phones.length > 0) subManagerPhones.set(a.accountId, phones);
|
|
12441
|
-
}
|
|
12442
|
-
}
|
|
12443
|
-
const merged = tagAndMergeInstallConversations(perAccount);
|
|
12444
|
-
const conversations = dropHouseRowsOwnedBySubAccount(merged, subManagerPhones);
|
|
12445
|
-
const houseRows = conversations.filter((r) => r.isHouse).length;
|
|
12446
|
-
console.log(
|
|
12447
|
-
`[wa-reader] op=conversations-all accounts=${validAccounts.length} rows=${conversations.length} house=${houseRows} sub=${conversations.length - houseRows} deduped=${merged.length - conversations.length}`
|
|
12448
|
-
);
|
|
12449
|
-
return c.json({ conversations });
|
|
12662
|
+
const perAccount = [];
|
|
12663
|
+
for (const account of validAccounts) {
|
|
12664
|
+
const isHouse = houseIds.has(account.accountId);
|
|
12665
|
+
const sessionRows = selectReaderChannelSessions(rawByAccount.get(account.accountId) ?? []).filter((r) => r.channel !== "whatsapp");
|
|
12666
|
+
const admins = account.config.admins ?? [];
|
|
12667
|
+
const distinct = [...new Set(sessionRows.map((r) => r.senderId).filter((s) => !!s))];
|
|
12668
|
+
const resolved = await resolveOperatorNamesBySender(users, admins, distinct);
|
|
12669
|
+
const nameBySender = /* @__PURE__ */ new Map();
|
|
12670
|
+
for (const senderId of distinct) nameBySender.set(senderId, resolved.get(senderId)?.name ?? null);
|
|
12671
|
+
const named = sessionRows.map((r) => ({
|
|
12672
|
+
...r,
|
|
12673
|
+
operatorName: r.senderId ? nameBySender.get(r.senderId) ?? null : null
|
|
12674
|
+
}));
|
|
12675
|
+
const storeRows = buildStoreConversationRows(account.accountId, readConversationSummaries(account.accountId));
|
|
12676
|
+
perAccount.push({ isHouse, rows: [...named, ...storeRows] });
|
|
12677
|
+
}
|
|
12678
|
+
const houseAccount = validAccounts.find((a) => a.accountId === houseId) ?? null;
|
|
12679
|
+
const subManagerPhones = /* @__PURE__ */ new Map();
|
|
12680
|
+
if (houseAccount) {
|
|
12681
|
+
const managers = readAccountManagers(houseAccount.accountDir);
|
|
12682
|
+
for (const a of validAccounts) {
|
|
12683
|
+
if (houseIds.has(a.accountId)) continue;
|
|
12684
|
+
const phones = managerPhonesForAccount(managers, a.accountId);
|
|
12685
|
+
if (phones.length > 0) subManagerPhones.set(a.accountId, phones);
|
|
12686
|
+
}
|
|
12687
|
+
}
|
|
12688
|
+
const merged = tagAndMergeInstallConversations(perAccount);
|
|
12689
|
+
const conversations = dropHouseRowsOwnedBySubAccount(merged, subManagerPhones);
|
|
12690
|
+
const houseRows = conversations.filter((r) => r.isHouse).length;
|
|
12691
|
+
console.log(
|
|
12692
|
+
`[wa-reader] op=conversations-all accounts=${validAccounts.length} rows=${conversations.length} house=${houseRows} sub=${conversations.length - houseRows} deduped=${merged.length - conversations.length}`
|
|
12693
|
+
);
|
|
12694
|
+
return { conversations };
|
|
12695
|
+
});
|
|
12696
|
+
console.log(`[wa-reader] op=conversations-all cache=${state} house=${houseId.slice(0, 8)}`);
|
|
12697
|
+
return c.json(value);
|
|
12450
12698
|
});
|
|
12451
12699
|
function readFrom(path3, from) {
|
|
12452
12700
|
const end = statSync8(path3).size;
|
|
@@ -12500,7 +12748,7 @@ function enrichAdminAttachments(turn, sessionId) {
|
|
|
12500
12748
|
if (turn.kind !== "operator-inbound") return turn;
|
|
12501
12749
|
if (!turn.text.includes('@"') && adminAttachmentIds(turn.text).length === 0) return turn;
|
|
12502
12750
|
const account = resolveAccount();
|
|
12503
|
-
const store2 = account ? adminAttachmentsFromText(turn.text,
|
|
12751
|
+
const store2 = account ? adminAttachmentsFromText(turn.text, resolve17(DATA_ROOT, "accounts", account.accountId, "uploads")) : [];
|
|
12504
12752
|
const cc = claudeUploadAttachmentsFromText(turn.text, sessionId);
|
|
12505
12753
|
if (cc.attachments.length > 0) {
|
|
12506
12754
|
console.log(`[session-upload-shape] op=mention accepted=${cc.attachments.length} session=${sessionId.slice(0, 8)}`);
|
|
@@ -12569,7 +12817,7 @@ function runActivityTick(c) {
|
|
|
12569
12817
|
names = readdirSync14(c.subagentsDir).filter((n) => AGENT_JSONL_RE.test(n));
|
|
12570
12818
|
} catch {
|
|
12571
12819
|
}
|
|
12572
|
-
const
|
|
12820
|
+
const entries2 = [];
|
|
12573
12821
|
for (const name of names) {
|
|
12574
12822
|
const hex = name.replace(AGENT_JSONL_RE, "$1");
|
|
12575
12823
|
let size = 0;
|
|
@@ -12583,14 +12831,14 @@ function runActivityTick(c) {
|
|
|
12583
12831
|
}
|
|
12584
12832
|
if (!c.initialSize.has(hex)) c.initialSize.set(hex, size);
|
|
12585
12833
|
if (size > (c.initialSize.get(hex) ?? size)) c.grown.add(hex);
|
|
12586
|
-
|
|
12834
|
+
entries2.push({ hex, size, mtimeMs });
|
|
12587
12835
|
}
|
|
12588
12836
|
const openKey = openTaskKey(c.lastParentTurns);
|
|
12589
12837
|
if (openKey && c.subagentFrames === 0 && !c.reconcileLogged.has(openKey)) {
|
|
12590
12838
|
c.reconcileLogged.add(openKey);
|
|
12591
12839
|
c.log(`[webchat-activity] op=reconcile-gap key=${c.sessionKey} toolUseId=${openKey} framesEmitted=0`);
|
|
12592
12840
|
}
|
|
12593
|
-
const pick = pickActivity(
|
|
12841
|
+
const pick = pickActivity(entries2, c.grown, c.now, CONCURRENCY_WINDOW_MS);
|
|
12594
12842
|
if (!pick) return;
|
|
12595
12843
|
const meta = readSubagentMeta(c.subagentsDir, pick.hex);
|
|
12596
12844
|
if (!meta) return;
|
|
@@ -12620,11 +12868,11 @@ app5.get("/stream", requireAdminSession, (c) => {
|
|
|
12620
12868
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
12621
12869
|
const projectDir = c.req.query("projectDir") ?? "";
|
|
12622
12870
|
const cfg = claudeConfigDir();
|
|
12623
|
-
const projectsRoot = cfg ?
|
|
12871
|
+
const projectsRoot = cfg ? resolve17(join21(cfg, "projects")) : null;
|
|
12624
12872
|
if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
|
|
12625
12873
|
return c.json({ error: "bad session reference" }, 400);
|
|
12626
12874
|
}
|
|
12627
|
-
const jsonlPath =
|
|
12875
|
+
const jsonlPath = resolve17(join21(projectDir, `${sessionId}.jsonl`));
|
|
12628
12876
|
if (!jsonlPath.startsWith(projectsRoot + sep5)) {
|
|
12629
12877
|
return c.json({ error: "bad session reference" }, 400);
|
|
12630
12878
|
}
|
|
@@ -12651,7 +12899,7 @@ data: ${JSON.stringify(turn)}
|
|
|
12651
12899
|
let offset = resumeOffset(lastEventId, fileEnd0);
|
|
12652
12900
|
console.log(`[wa-stream] op=open conn=${connId} sessionId=${sessionId} channel=reader resume=${offset}`);
|
|
12653
12901
|
const sessionKey = c.req.query("session_key") ?? "";
|
|
12654
|
-
const subagentsDir =
|
|
12902
|
+
const subagentsDir = resolve17(join21(projectDir, sessionId, "subagents"));
|
|
12655
12903
|
console.log(
|
|
12656
12904
|
`[webchat-activity] op=watch-subagents key=${sessionKey} dir=${subagentsDir} exists=${existsSync17(subagentsDir)}`
|
|
12657
12905
|
);
|
|
@@ -12715,7 +12963,7 @@ data: ${JSON.stringify(obj)}
|
|
|
12715
12963
|
}
|
|
12716
12964
|
let watcher = null;
|
|
12717
12965
|
try {
|
|
12718
|
-
watcher =
|
|
12966
|
+
watcher = watch2(jsonlPath, drain);
|
|
12719
12967
|
} catch {
|
|
12720
12968
|
}
|
|
12721
12969
|
const tick = () => {
|
|
@@ -12761,7 +13009,7 @@ var DIRECTIVE_NAME_RE = /^\d+-\d+\.txt$/;
|
|
|
12761
13009
|
function directiveStoreDir(sessionId) {
|
|
12762
13010
|
const account = resolveAccount();
|
|
12763
13011
|
if (!account) return null;
|
|
12764
|
-
return
|
|
13012
|
+
return resolve17(account.accountDir, "logs", "prompt-optimiser-directives", sessionId);
|
|
12765
13013
|
}
|
|
12766
13014
|
app5.get("/directives", requireAdminSession, (c) => {
|
|
12767
13015
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
@@ -12774,7 +13022,7 @@ app5.get("/directives", requireAdminSession, (c) => {
|
|
|
12774
13022
|
} catch {
|
|
12775
13023
|
return c.json({ entries: [] });
|
|
12776
13024
|
}
|
|
12777
|
-
const
|
|
13025
|
+
const entries2 = names.map((name) => {
|
|
12778
13026
|
const [secsPart, pidPart] = name.replace(/\.txt$/, "").split("-");
|
|
12779
13027
|
const secs = Number(secsPart);
|
|
12780
13028
|
const pid = Number(pidPart);
|
|
@@ -12785,7 +13033,7 @@ app5.get("/directives", requireAdminSession, (c) => {
|
|
|
12785
13033
|
}
|
|
12786
13034
|
return { name, secs, pid, ts: Number.isFinite(secs) ? new Date(secs * 1e3).toISOString() : null, len };
|
|
12787
13035
|
}).sort((a, b) => a.secs - b.secs || a.pid - b.pid).map(({ name, ts, len }) => ({ name, ts, len }));
|
|
12788
|
-
return c.json({ entries });
|
|
13036
|
+
return c.json({ entries: entries2 });
|
|
12789
13037
|
});
|
|
12790
13038
|
app5.get("/directive", requireAdminSession, (c) => {
|
|
12791
13039
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
@@ -12795,7 +13043,7 @@ app5.get("/directive", requireAdminSession, (c) => {
|
|
|
12795
13043
|
}
|
|
12796
13044
|
const dir = directiveStoreDir(sessionId);
|
|
12797
13045
|
if (!dir) return c.json({ error: "no account" }, 404);
|
|
12798
|
-
const path3 =
|
|
13046
|
+
const path3 = resolve17(join21(dir, name));
|
|
12799
13047
|
if (!path3.startsWith(dir + sep5)) return c.json({ error: "bad reference" }, 400);
|
|
12800
13048
|
let body;
|
|
12801
13049
|
try {
|
|
@@ -12825,7 +13073,7 @@ app5.get("/store-stream", requireAdminSession, (c) => {
|
|
|
12825
13073
|
const connId = `${remoteJid.slice(0, 12)}-${process.hrtime.bigint().toString(36).slice(-6)}`;
|
|
12826
13074
|
const startedAt = Date.now();
|
|
12827
13075
|
const lastEventId = c.req.header("Last-Event-ID");
|
|
12828
|
-
const uploadsBase =
|
|
13076
|
+
const uploadsBase = resolve17(DATA_ROOT, "accounts", accountId, "uploads");
|
|
12829
13077
|
const byMsgKeyId = /* @__PURE__ */ new Map();
|
|
12830
13078
|
const reactionRecords = [];
|
|
12831
13079
|
const reactionsForTarget = (msgKeyId) => foldReactions(reactionRecords).get(msgKeyId);
|
|
@@ -12954,7 +13202,7 @@ data: ${JSON.stringify(payload)}
|
|
|
12954
13202
|
};
|
|
12955
13203
|
let watcher = null;
|
|
12956
13204
|
try {
|
|
12957
|
-
watcher =
|
|
13205
|
+
watcher = watch2(file, drain);
|
|
12958
13206
|
} catch {
|
|
12959
13207
|
}
|
|
12960
13208
|
const lifecycle = openStream(connId, "store", startedAt);
|
|
@@ -13233,8 +13481,8 @@ app5.post("/reply", requireAdminSession, async (c) => {
|
|
|
13233
13481
|
var whatsapp_reader_default = app5;
|
|
13234
13482
|
|
|
13235
13483
|
// server/routes/public-reader.ts
|
|
13236
|
-
import { statSync as statSync9, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as
|
|
13237
|
-
import { basename as basename7, dirname as dirname8, join as join22, resolve as
|
|
13484
|
+
import { statSync as statSync9, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch3, readFileSync as readFileSync22, readdirSync as readdirSync15 } from "fs";
|
|
13485
|
+
import { basename as basename7, dirname as dirname8, join as join22, resolve as resolve18, sep as sep6 } from "path";
|
|
13238
13486
|
|
|
13239
13487
|
// app/lib/whatsapp-reader/delivered-kinds.ts
|
|
13240
13488
|
var PUBLIC_DELIVERED_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -13356,7 +13604,7 @@ app6.get("/session", (c) => {
|
|
|
13356
13604
|
return c.json({ sessionId: null, projectDir: null, sessionKey });
|
|
13357
13605
|
});
|
|
13358
13606
|
function publicUploadsDir(accountId, sessionId) {
|
|
13359
|
-
return
|
|
13607
|
+
return resolve18(DATA_ROOT, "accounts", accountId, "uploads", "public", sessionId);
|
|
13360
13608
|
}
|
|
13361
13609
|
function enrichPublicAttachments(turns, sessionAttachments, cursor) {
|
|
13362
13610
|
for (const turn of turns) {
|
|
@@ -13387,11 +13635,11 @@ app6.get("/stream", (c) => {
|
|
|
13387
13635
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
13388
13636
|
const projectDir = c.req.query("projectDir") ?? "";
|
|
13389
13637
|
const cfg = claudeConfigDir();
|
|
13390
|
-
const projectsRoot = cfg ?
|
|
13638
|
+
const projectsRoot = cfg ? resolve18(join22(cfg, "projects")) : null;
|
|
13391
13639
|
if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
|
|
13392
13640
|
return c.json({ error: "bad session reference" }, 400);
|
|
13393
13641
|
}
|
|
13394
|
-
const jsonlPath =
|
|
13642
|
+
const jsonlPath = resolve18(join22(projectDir, `${sessionId}.jsonl`));
|
|
13395
13643
|
if (!jsonlPath.startsWith(projectsRoot + sep6)) {
|
|
13396
13644
|
return c.json({ error: "bad session reference" }, 400);
|
|
13397
13645
|
}
|
|
@@ -13440,7 +13688,7 @@ data: ${JSON.stringify(turn)}
|
|
|
13440
13688
|
};
|
|
13441
13689
|
let watcher = null;
|
|
13442
13690
|
try {
|
|
13443
|
-
watcher =
|
|
13691
|
+
watcher = watch3(jsonlPath, drain);
|
|
13444
13692
|
} catch {
|
|
13445
13693
|
}
|
|
13446
13694
|
const poll = setInterval(drain, 1e3);
|
|
@@ -13495,7 +13743,7 @@ app6.get("/attachment/:attachmentId", (c) => {
|
|
|
13495
13743
|
return new Response("Not found", { status: 404 });
|
|
13496
13744
|
}
|
|
13497
13745
|
const uploadsRoot = publicUploadsDir(account.accountId, sessionId);
|
|
13498
|
-
const dir =
|
|
13746
|
+
const dir = resolve18(uploadsRoot, attachmentId);
|
|
13499
13747
|
if (!dir.startsWith(uploadsRoot + sep6)) {
|
|
13500
13748
|
console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=bad-id`);
|
|
13501
13749
|
return new Response("Not found", { status: 404 });
|
|
@@ -13510,7 +13758,7 @@ app6.get("/attachment/:attachmentId", (c) => {
|
|
|
13510
13758
|
try {
|
|
13511
13759
|
dataFile = readdirSync15(dir).find((f) => !f.endsWith(".meta.json") && !f.endsWith(EXTRACTED_TEXT_SUFFIX));
|
|
13512
13760
|
if (!dataFile) throw new Error("no data file");
|
|
13513
|
-
buffer = readFileSync22(
|
|
13761
|
+
buffer = readFileSync22(resolve18(dir, dataFile));
|
|
13514
13762
|
} catch {
|
|
13515
13763
|
console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=not-found`);
|
|
13516
13764
|
return new Response("Not found", { status: 404 });
|
|
@@ -13578,7 +13826,7 @@ function writeCanonicalOverrideId(accountDir, userId, sessionId) {
|
|
|
13578
13826
|
}
|
|
13579
13827
|
|
|
13580
13828
|
// ../lib/models/src/index.ts
|
|
13581
|
-
var OPUS_MODEL = "claude-opus-
|
|
13829
|
+
var OPUS_MODEL = "claude-opus-5[1m]";
|
|
13582
13830
|
var SONNET_MODEL = "claude-sonnet-5";
|
|
13583
13831
|
var HAIKU_MODEL = "claude-haiku-4-5";
|
|
13584
13832
|
var MODEL_CONTEXT_WINDOW = {
|
|
@@ -13590,7 +13838,7 @@ function contextWindow(model) {
|
|
|
13590
13838
|
return MODEL_CONTEXT_WINDOW[model] ?? 2e5;
|
|
13591
13839
|
}
|
|
13592
13840
|
var MODEL_DISPLAY_NAMES = {
|
|
13593
|
-
[OPUS_MODEL]: "Opus
|
|
13841
|
+
[OPUS_MODEL]: "Opus 5 (1M context)",
|
|
13594
13842
|
[SONNET_MODEL]: "Sonnet 5",
|
|
13595
13843
|
[HAIKU_MODEL]: "Haiku 4.5"
|
|
13596
13844
|
};
|
|
@@ -14364,7 +14612,7 @@ ${note}` : note;
|
|
|
14364
14612
|
}
|
|
14365
14613
|
|
|
14366
14614
|
// server/routes/webchat-greeting.ts
|
|
14367
|
-
import { resolve as
|
|
14615
|
+
import { resolve as resolve19 } from "path";
|
|
14368
14616
|
|
|
14369
14617
|
// app/lib/claude-agent/specialist-roster.ts
|
|
14370
14618
|
import { existsSync as existsSync20, readFileSync as readFileSync26, readdirSync as readdirSync18 } from "fs";
|
|
@@ -14380,10 +14628,10 @@ function field(fm, name) {
|
|
|
14380
14628
|
}
|
|
14381
14629
|
function readSpecialistRoster(specialistsDir) {
|
|
14382
14630
|
if (!existsSync20(specialistsDir)) return { specialists: [], skipped: [] };
|
|
14383
|
-
const
|
|
14631
|
+
const entries2 = readdirSync18(specialistsDir);
|
|
14384
14632
|
const specialists = [];
|
|
14385
14633
|
const skipped = [];
|
|
14386
|
-
for (const file of
|
|
14634
|
+
for (const file of entries2.sort()) {
|
|
14387
14635
|
if (!file.endsWith(".md")) continue;
|
|
14388
14636
|
let raw;
|
|
14389
14637
|
try {
|
|
@@ -14469,7 +14717,7 @@ app7.get("/", requireAdminSession, async (c) => {
|
|
|
14469
14717
|
let specialists = [];
|
|
14470
14718
|
try {
|
|
14471
14719
|
const roster = readSpecialistRoster(
|
|
14472
|
-
|
|
14720
|
+
resolve19(ACCOUNTS_DIR, accountId, "specialists", "agents")
|
|
14473
14721
|
);
|
|
14474
14722
|
specialists = roster.specialists;
|
|
14475
14723
|
for (const s of roster.skipped) {
|
|
@@ -14504,7 +14752,7 @@ var webchat_greeting_default = app7;
|
|
|
14504
14752
|
|
|
14505
14753
|
// server/routes/telegram.ts
|
|
14506
14754
|
import { homedir as homedir2 } from "os";
|
|
14507
|
-
import { resolve as
|
|
14755
|
+
import { resolve as resolve20, join as join27 } from "path";
|
|
14508
14756
|
import { existsSync as existsSync21, readFileSync as readFileSync27 } from "fs";
|
|
14509
14757
|
|
|
14510
14758
|
// app/lib/telegram/gateway/instance.ts
|
|
@@ -14563,7 +14811,7 @@ function routeTelegramUpdate(input) {
|
|
|
14563
14811
|
// server/routes/telegram.ts
|
|
14564
14812
|
var TAG29 = "[telegram-inbound]";
|
|
14565
14813
|
function configDirName() {
|
|
14566
|
-
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ??
|
|
14814
|
+
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
|
|
14567
14815
|
const brandPath = join27(platformRoot5, "config", "brand.json");
|
|
14568
14816
|
if (existsSync21(brandPath)) {
|
|
14569
14817
|
try {
|
|
@@ -14575,7 +14823,7 @@ function configDirName() {
|
|
|
14575
14823
|
}
|
|
14576
14824
|
function secretPath(botType) {
|
|
14577
14825
|
const filename = botType === "admin" ? ".telegram-admin-webhook-secret" : ".telegram-webhook-secret";
|
|
14578
|
-
return
|
|
14826
|
+
return resolve20(homedir2(), configDirName(), filename);
|
|
14579
14827
|
}
|
|
14580
14828
|
var app8 = new Hono();
|
|
14581
14829
|
app8.post("/", async (c) => {
|
|
@@ -15013,14 +15261,14 @@ function computeAdminStoreDivergence(input) {
|
|
|
15013
15261
|
}
|
|
15014
15262
|
const accountUserIds = /* @__PURE__ */ new Set();
|
|
15015
15263
|
if (existsSync23(input.accountsDir)) {
|
|
15016
|
-
let
|
|
15264
|
+
let entries2;
|
|
15017
15265
|
try {
|
|
15018
|
-
|
|
15266
|
+
entries2 = readdirSync19(input.accountsDir);
|
|
15019
15267
|
} catch (err) {
|
|
15020
15268
|
result.errors.push({ source: input.accountsDir, detail: err instanceof Error ? err.message : String(err) });
|
|
15021
15269
|
return result;
|
|
15022
15270
|
}
|
|
15023
|
-
for (const entry of
|
|
15271
|
+
for (const entry of entries2) {
|
|
15024
15272
|
if (entry.startsWith(".")) continue;
|
|
15025
15273
|
const accountDir = join29(input.accountsDir, entry);
|
|
15026
15274
|
try {
|
|
@@ -15716,15 +15964,15 @@ function operatorRoleFor(config, userId) {
|
|
|
15716
15964
|
async function buildAccountOptionList(accounts, userId) {
|
|
15717
15965
|
const houseAccount = accounts.find((a) => a.config.role === "house");
|
|
15718
15966
|
console.log(`[admin-accounts] op=list count=${accounts.length} house=${houseAccount?.accountId ?? "none"}`);
|
|
15719
|
-
const
|
|
15967
|
+
const resolve46 = async (a) => {
|
|
15720
15968
|
const businessName = await fetchAccountName(a.accountId) || void 0;
|
|
15721
15969
|
return { accountId: a.accountId, businessName, role: operatorRoleFor(a.config, userId), isHouse: a.config.role === "house" };
|
|
15722
15970
|
};
|
|
15723
15971
|
const houseOptions = await Promise.all(
|
|
15724
|
-
accounts.filter((a) => a.config.role === "house").map(
|
|
15972
|
+
accounts.filter((a) => a.config.role === "house").map(resolve46)
|
|
15725
15973
|
);
|
|
15726
15974
|
const nonHouseOptions = await Promise.all(
|
|
15727
|
-
accounts.filter((a) => a.config.role !== "house").map(
|
|
15975
|
+
accounts.filter((a) => a.config.role !== "house").map(resolve46)
|
|
15728
15976
|
);
|
|
15729
15977
|
const sortKey = (o) => (o.businessName || o.accountId).toLowerCase();
|
|
15730
15978
|
nonHouseOptions.sort((x, y) => {
|
|
@@ -15858,7 +16106,7 @@ var accounts_default = app13;
|
|
|
15858
16106
|
|
|
15859
16107
|
// server/routes/admin/logs.ts
|
|
15860
16108
|
import { existsSync as existsSync27, readdirSync as readdirSync20, readFileSync as readFileSync31, statSync as statSync13 } from "fs";
|
|
15861
|
-
import { resolve as
|
|
16109
|
+
import { resolve as resolve21, basename as basename9 } from "path";
|
|
15862
16110
|
|
|
15863
16111
|
// app/lib/logs-read-resolve.ts
|
|
15864
16112
|
import { existsSync as existsSync26 } from "fs";
|
|
@@ -15885,7 +16133,7 @@ app14.get("/", async (c) => {
|
|
|
15885
16133
|
const cacheKeyParam = c.req.query("cacheKey");
|
|
15886
16134
|
const download = c.req.query("download") === "1";
|
|
15887
16135
|
const account = resolveAccount();
|
|
15888
|
-
const accountLogDir = account ?
|
|
16136
|
+
const accountLogDir = account ? resolve21(account.accountDir, "logs") : null;
|
|
15889
16137
|
const logDirs = [];
|
|
15890
16138
|
if (accountLogDir) logDirs.push(accountLogDir);
|
|
15891
16139
|
logDirs.push(LOG_DIR);
|
|
@@ -15893,7 +16141,7 @@ app14.get("/", async (c) => {
|
|
|
15893
16141
|
const safe = basename9(fileParam);
|
|
15894
16142
|
const searched = [];
|
|
15895
16143
|
for (const dir of logDirs) {
|
|
15896
|
-
const filePath =
|
|
16144
|
+
const filePath = resolve21(dir, safe);
|
|
15897
16145
|
searched.push(filePath);
|
|
15898
16146
|
try {
|
|
15899
16147
|
const buffer = readFileSync31(filePath);
|
|
@@ -16005,10 +16253,10 @@ app14.get("/", async (c) => {
|
|
|
16005
16253
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
16006
16254
|
continue;
|
|
16007
16255
|
}
|
|
16008
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync13(
|
|
16256
|
+
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync13(resolve21(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
16009
16257
|
seen.add(name);
|
|
16010
16258
|
try {
|
|
16011
|
-
const content = readFileSync31(
|
|
16259
|
+
const content = readFileSync31(resolve21(dir, name));
|
|
16012
16260
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
16013
16261
|
logs[name] = tail.trim() || "(empty)";
|
|
16014
16262
|
} catch (err) {
|
|
@@ -16049,11 +16297,11 @@ var claude_info_default = app15;
|
|
|
16049
16297
|
// server/routes/admin/attachment.ts
|
|
16050
16298
|
import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
|
|
16051
16299
|
import { existsSync as existsSync28 } from "fs";
|
|
16052
|
-
import { resolve as
|
|
16300
|
+
import { resolve as resolve22 } from "path";
|
|
16053
16301
|
var app16 = new Hono();
|
|
16054
16302
|
var ATTACHMENT_ID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
16055
16303
|
function uploadsDirFor2(accountId, attachmentId) {
|
|
16056
|
-
return
|
|
16304
|
+
return resolve22(DATA_ROOT, "accounts", accountId, "uploads", attachmentId);
|
|
16057
16305
|
}
|
|
16058
16306
|
app16.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
16059
16307
|
const attachmentId = c.req.param("attachmentId");
|
|
@@ -16090,7 +16338,7 @@ app16.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
16090
16338
|
if (!found) {
|
|
16091
16339
|
return new Response("Not found", { status: 404 });
|
|
16092
16340
|
}
|
|
16093
|
-
const metaPath =
|
|
16341
|
+
const metaPath = resolve22(dir, `${attachmentId}.meta.json`);
|
|
16094
16342
|
if (!existsSync28(metaPath)) {
|
|
16095
16343
|
return new Response("Not found", { status: 404 });
|
|
16096
16344
|
}
|
|
@@ -16100,12 +16348,12 @@ app16.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
16100
16348
|
} catch {
|
|
16101
16349
|
return new Response("Not found", { status: 404 });
|
|
16102
16350
|
}
|
|
16103
|
-
const
|
|
16104
|
-
const dataFile =
|
|
16351
|
+
const entries2 = await readdir2(dir);
|
|
16352
|
+
const dataFile = entries2.find((f) => !f.endsWith(".meta.json") && !f.endsWith(EXTRACTED_TEXT_SUFFIX));
|
|
16105
16353
|
if (!dataFile) {
|
|
16106
16354
|
return new Response("Not found", { status: 404 });
|
|
16107
16355
|
}
|
|
16108
|
-
const filePath =
|
|
16356
|
+
const filePath = resolve22(dir, dataFile);
|
|
16109
16357
|
const buffer = await readFile4(filePath);
|
|
16110
16358
|
const inlineSafe = isInlineSafeMime(meta.mimeType);
|
|
16111
16359
|
const safeName = meta.filename.replace(/["\\\r\n]/g, "_");
|
|
@@ -16167,9 +16415,91 @@ app17.get("/", requireAdminSession, async (c) => {
|
|
|
16167
16415
|
var session_upload_default = app17;
|
|
16168
16416
|
|
|
16169
16417
|
// server/routes/admin/agents.ts
|
|
16170
|
-
import { resolve as
|
|
16418
|
+
import { resolve as resolve23 } from "path";
|
|
16171
16419
|
import { readdirSync as readdirSync21, readFileSync as readFileSync32, existsSync as existsSync30, rmSync as rmSync2 } from "fs";
|
|
16420
|
+
|
|
16421
|
+
// server/routes/admin/_account-scope.ts
|
|
16422
|
+
function accountDirForSession(cacheKey) {
|
|
16423
|
+
const accountId = getAccountIdForSession(cacheKey);
|
|
16424
|
+
if (!accountId) return null;
|
|
16425
|
+
const match = listValidAccounts().find((a) => a.accountId === accountId);
|
|
16426
|
+
return match ? { accountId, accountDir: match.accountDir } : null;
|
|
16427
|
+
}
|
|
16428
|
+
function validateAccountId2(accountId) {
|
|
16429
|
+
if (!accountId) return null;
|
|
16430
|
+
const match = listValidAccounts().find((a) => a.accountId === accountId);
|
|
16431
|
+
return match ? { accountId, accountDir: match.accountDir } : null;
|
|
16432
|
+
}
|
|
16433
|
+
async function authorizeAccountWrite(c, accountId) {
|
|
16434
|
+
const remote = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
16435
|
+
const peerIsLoopback = remote === "127.0.0.1" || remote === "::1" || remote === "::ffff:127.0.0.1";
|
|
16436
|
+
if (peerIsLoopback && !c.req.header("x-forwarded-for")) return { ok: true, via: "on-device" };
|
|
16437
|
+
const { fingerprint, signedSessionToken } = await resolveCacheKey(c);
|
|
16438
|
+
if (!fingerprint || !validateSession(fingerprint, "admin", signedSessionToken).ok) {
|
|
16439
|
+
return { ok: false, reason: "no-session" };
|
|
16440
|
+
}
|
|
16441
|
+
const acct = accountDirForSession(fingerprint);
|
|
16442
|
+
if (!acct || acct.accountId !== accountId) return { ok: false, reason: "account-mismatch" };
|
|
16443
|
+
return { ok: true, via: "session" };
|
|
16444
|
+
}
|
|
16445
|
+
|
|
16446
|
+
// server/routes/admin/agents.ts
|
|
16172
16447
|
var SLUG_UNSAFE = (s) => s.includes("/") || s.includes("..") || s.includes("\\");
|
|
16448
|
+
function subdirs(dir) {
|
|
16449
|
+
if (!existsSync30(dir)) return [];
|
|
16450
|
+
try {
|
|
16451
|
+
return readdirSync21(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
16452
|
+
} catch {
|
|
16453
|
+
return [];
|
|
16454
|
+
}
|
|
16455
|
+
}
|
|
16456
|
+
function splitFrontmatter(text) {
|
|
16457
|
+
const m = text.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
16458
|
+
const fm = {};
|
|
16459
|
+
if (!m) return { fm, body: text };
|
|
16460
|
+
for (const line of m[1].split(/\r?\n/)) {
|
|
16461
|
+
const kv = line.match(/^([A-Za-z][\w-]*):[ \t]*(.*)$/);
|
|
16462
|
+
if (kv) fm[kv[1]] = kv[2].trim().replace(/^["']|["']$/g, "").trim();
|
|
16463
|
+
}
|
|
16464
|
+
return { fm, body: text.slice(m[0].length) };
|
|
16465
|
+
}
|
|
16466
|
+
function listSpecialists(accountDir) {
|
|
16467
|
+
const pluginsDir = resolve23(accountDir, "plugins");
|
|
16468
|
+
const specialists = [];
|
|
16469
|
+
let specialistsSkipped = 0;
|
|
16470
|
+
for (const plugin of subdirs(pluginsDir).sort()) {
|
|
16471
|
+
const agentsDirP = resolve23(pluginsDir, plugin, "agents");
|
|
16472
|
+
if (!existsSync30(agentsDirP)) continue;
|
|
16473
|
+
let entries2;
|
|
16474
|
+
try {
|
|
16475
|
+
entries2 = readdirSync21(agentsDirP, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
16476
|
+
} catch {
|
|
16477
|
+
continue;
|
|
16478
|
+
}
|
|
16479
|
+
for (const entry of entries2) {
|
|
16480
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
16481
|
+
try {
|
|
16482
|
+
const { fm } = splitFrontmatter(readFileSync32(resolve23(agentsDirP, entry.name), "utf-8"));
|
|
16483
|
+
if (!fm.name) {
|
|
16484
|
+
specialistsSkipped++;
|
|
16485
|
+
console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${entry.name}`);
|
|
16486
|
+
continue;
|
|
16487
|
+
}
|
|
16488
|
+
specialists.push({
|
|
16489
|
+
slug: entry.name.replace(/\.md$/, ""),
|
|
16490
|
+
displayName: fm.name,
|
|
16491
|
+
kind: "specialist",
|
|
16492
|
+
plugin,
|
|
16493
|
+
description: fm.description ?? ""
|
|
16494
|
+
});
|
|
16495
|
+
} catch {
|
|
16496
|
+
specialistsSkipped++;
|
|
16497
|
+
console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${entry.name}`);
|
|
16498
|
+
}
|
|
16499
|
+
}
|
|
16500
|
+
}
|
|
16501
|
+
return { specialists, specialistsSkipped };
|
|
16502
|
+
}
|
|
16173
16503
|
var OWNED_DOCS = [
|
|
16174
16504
|
{ file: "IDENTITY.md", key: "identity" },
|
|
16175
16505
|
{ file: "SOUL.md", key: "soul" },
|
|
@@ -16177,46 +16507,76 @@ var OWNED_DOCS = [
|
|
|
16177
16507
|
{ file: "KNOWLEDGE-SUMMARY.md", key: "knowledgeSummary" }
|
|
16178
16508
|
];
|
|
16179
16509
|
var app18 = new Hono();
|
|
16180
|
-
app18.get("/", (c) => {
|
|
16181
|
-
const account =
|
|
16182
|
-
if (!account)
|
|
16183
|
-
|
|
16184
|
-
|
|
16510
|
+
app18.get("/", requireAdminSession, (c) => {
|
|
16511
|
+
const account = accountDirForSession(c.var.cacheKey);
|
|
16512
|
+
if (!account) {
|
|
16513
|
+
console.error("[admin/agents] op=list auth-rejected reason=no-account-for-session");
|
|
16514
|
+
return c.json({ error: "Account not found for session" }, 401);
|
|
16515
|
+
}
|
|
16185
16516
|
const agents = [];
|
|
16186
16517
|
let skipped = 0;
|
|
16187
|
-
|
|
16188
|
-
|
|
16189
|
-
|
|
16190
|
-
|
|
16191
|
-
|
|
16192
|
-
|
|
16193
|
-
|
|
16194
|
-
|
|
16195
|
-
|
|
16196
|
-
|
|
16197
|
-
|
|
16198
|
-
|
|
16199
|
-
|
|
16200
|
-
|
|
16201
|
-
|
|
16202
|
-
|
|
16203
|
-
|
|
16518
|
+
const agentsDir = resolve23(account.accountDir, "agents");
|
|
16519
|
+
if (existsSync30(agentsDir)) {
|
|
16520
|
+
try {
|
|
16521
|
+
const entries2 = readdirSync21(agentsDir, { withFileTypes: true });
|
|
16522
|
+
for (const entry of entries2.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
16523
|
+
if (!entry.isDirectory()) continue;
|
|
16524
|
+
if (entry.name === "admin") continue;
|
|
16525
|
+
const configPath2 = resolve23(agentsDir, entry.name, "config.json");
|
|
16526
|
+
if (!existsSync30(configPath2)) continue;
|
|
16527
|
+
try {
|
|
16528
|
+
const config = JSON.parse(readFileSync32(configPath2, "utf-8"));
|
|
16529
|
+
agents.push({
|
|
16530
|
+
slug: entry.name,
|
|
16531
|
+
displayName: config.displayName ?? entry.name,
|
|
16532
|
+
status: config.status ?? "unknown",
|
|
16533
|
+
kind: "public"
|
|
16534
|
+
});
|
|
16535
|
+
} catch {
|
|
16536
|
+
skipped++;
|
|
16537
|
+
console.error(`[admin/agents] failed to parse config.json for agent "${entry.name}" \u2014 skipping`);
|
|
16538
|
+
}
|
|
16204
16539
|
}
|
|
16540
|
+
} catch (err) {
|
|
16541
|
+
console.error(`[admin/agents] failed to scan agents directory: ${err}`);
|
|
16205
16542
|
}
|
|
16206
|
-
} catch (err) {
|
|
16207
|
-
console.error(`[admin/agents] failed to scan agents directory: ${err}`);
|
|
16208
|
-
return c.json({ agents: [], skipped: 0 });
|
|
16209
16543
|
}
|
|
16210
|
-
|
|
16211
|
-
|
|
16544
|
+
const { specialists, specialistsSkipped } = listSpecialists(account.accountDir);
|
|
16545
|
+
console.log(`[admin/agents] op=list accountId=${account.accountId.slice(0, 8)} agents=${agents.length} specialists=${specialists.length} skipped=${skipped} specialistsSkipped=${specialistsSkipped}`);
|
|
16546
|
+
return c.json({ agents: [...agents, ...specialists], accountId: account.accountId, skipped, specialistsSkipped });
|
|
16212
16547
|
});
|
|
16213
|
-
app18.get("/:slug", (c) => {
|
|
16548
|
+
app18.get("/:slug", requireAdminSession, (c) => {
|
|
16214
16549
|
const slug = c.req.param("slug");
|
|
16215
|
-
const account =
|
|
16216
|
-
if (!account)
|
|
16550
|
+
const account = accountDirForSession(c.var.cacheKey);
|
|
16551
|
+
if (!account) {
|
|
16552
|
+
console.error("[admin/agents] op=detail auth-rejected reason=no-account-for-session");
|
|
16553
|
+
return c.json({ error: "Account not found for session" }, 401);
|
|
16554
|
+
}
|
|
16217
16555
|
if (SLUG_UNSAFE(slug)) return c.json({ error: "Invalid agent slug" }, 400);
|
|
16218
|
-
const
|
|
16219
|
-
|
|
16556
|
+
const pluginParam = c.req.query("plugin");
|
|
16557
|
+
if (pluginParam) {
|
|
16558
|
+
if (SLUG_UNSAFE(pluginParam)) return c.json({ error: "Invalid plugin" }, 400);
|
|
16559
|
+
const file = resolve23(account.accountDir, "plugins", pluginParam, "agents", `${slug}.md`);
|
|
16560
|
+
if (!existsSync30(file)) return c.json({ error: "Agent not found" }, 404);
|
|
16561
|
+
const { fm, body } = splitFrontmatter(readFileSync32(file, "utf-8"));
|
|
16562
|
+
const skills = subdirs(resolve23(account.accountDir, "plugins", pluginParam, "skills"));
|
|
16563
|
+
console.log(`[admin/agents] op=detail accountId=${account.accountId.slice(0, 8)} slug=${slug} kind=specialist plugin=${pluginParam}`);
|
|
16564
|
+
return c.json({
|
|
16565
|
+
agent: {
|
|
16566
|
+
kind: "specialist",
|
|
16567
|
+
slug,
|
|
16568
|
+
displayName: fm.name ?? slug,
|
|
16569
|
+
description: fm.description ?? "",
|
|
16570
|
+
summary: fm.summary ?? "",
|
|
16571
|
+
model: fm.model ?? null,
|
|
16572
|
+
tools: fm.tools ? fm.tools.split(",").map((t) => t.trim()).filter(Boolean) : [],
|
|
16573
|
+
systemPrompt: body.trim(),
|
|
16574
|
+
skills
|
|
16575
|
+
}
|
|
16576
|
+
});
|
|
16577
|
+
}
|
|
16578
|
+
const agentDir = resolve23(account.accountDir, "agents", slug);
|
|
16579
|
+
const configPath2 = resolve23(agentDir, "config.json");
|
|
16220
16580
|
if (!existsSync30(configPath2)) return c.json({ error: "Agent not found" }, 404);
|
|
16221
16581
|
let config;
|
|
16222
16582
|
try {
|
|
@@ -16228,7 +16588,7 @@ app18.get("/:slug", (c) => {
|
|
|
16228
16588
|
const docs = {};
|
|
16229
16589
|
const present = {};
|
|
16230
16590
|
for (const { file, key } of OWNED_DOCS) {
|
|
16231
|
-
const p =
|
|
16591
|
+
const p = resolve23(agentDir, file);
|
|
16232
16592
|
try {
|
|
16233
16593
|
if (existsSync30(p)) {
|
|
16234
16594
|
docs[key] = readFileSync32(p, "utf-8");
|
|
@@ -16244,7 +16604,7 @@ app18.get("/:slug", (c) => {
|
|
|
16244
16604
|
}
|
|
16245
16605
|
}
|
|
16246
16606
|
const found = Object.keys(present).filter((k) => present[k]);
|
|
16247
|
-
console.log(`[admin/agents] op=detail slug=${slug} present=${found.join(",") || "none"}`);
|
|
16607
|
+
console.log(`[admin/agents] op=detail accountId=${account.accountId.slice(0, 8)} slug=${slug} kind=public present=${found.join(",") || "none"}`);
|
|
16248
16608
|
return c.json({
|
|
16249
16609
|
agent: {
|
|
16250
16610
|
slug,
|
|
@@ -16262,15 +16622,28 @@ app18.get("/:slug", (c) => {
|
|
|
16262
16622
|
});
|
|
16263
16623
|
app18.delete("/:slug", async (c) => {
|
|
16264
16624
|
const slug = c.req.param("slug");
|
|
16265
|
-
const account = resolveAccount();
|
|
16266
|
-
if (!account) return c.json({ error: "No account resolved" }, 400);
|
|
16267
16625
|
if (slug === "admin") {
|
|
16268
16626
|
return c.json({ error: "Cannot delete the admin agent" }, 403);
|
|
16269
16627
|
}
|
|
16270
16628
|
if (SLUG_UNSAFE(slug)) {
|
|
16271
16629
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
16272
16630
|
}
|
|
16273
|
-
const
|
|
16631
|
+
const accountIdParam = c.req.query("accountId") ?? "";
|
|
16632
|
+
if (!accountIdParam) {
|
|
16633
|
+
console.error(`[admin/agents] op=delete rejected reason=missing-accountId slug=${slug}`);
|
|
16634
|
+
return c.json({ error: "accountId is required" }, 400);
|
|
16635
|
+
}
|
|
16636
|
+
const account = validateAccountId2(accountIdParam);
|
|
16637
|
+
if (!account) {
|
|
16638
|
+
console.error(`[admin/agents] op=delete rejected reason=unknown-accountId accountId=${accountIdParam.slice(0, 8)} slug=${slug}`);
|
|
16639
|
+
return c.json({ error: "Unknown accountId" }, 400);
|
|
16640
|
+
}
|
|
16641
|
+
const authz = await authorizeAccountWrite(c, account.accountId);
|
|
16642
|
+
if (!authz.ok) {
|
|
16643
|
+
console.error(`[admin/agents] op=delete rejected reason=${authz.reason} accountId=${account.accountId.slice(0, 8)} slug=${slug}`);
|
|
16644
|
+
return c.json({ error: "Not authorized for this account" }, 403);
|
|
16645
|
+
}
|
|
16646
|
+
const agentDir = resolve23(account.accountDir, "agents", slug);
|
|
16274
16647
|
if (!existsSync30(agentDir)) {
|
|
16275
16648
|
return c.json({ error: "Agent not found" }, 404);
|
|
16276
16649
|
}
|
|
@@ -16278,29 +16651,42 @@ app18.delete("/:slug", async (c) => {
|
|
|
16278
16651
|
await deleteAgentProjection(account.accountId, slug);
|
|
16279
16652
|
} catch (err) {
|
|
16280
16653
|
const msg = err instanceof Error ? err.message : String(err);
|
|
16281
|
-
console.error(`[admin/agents]
|
|
16654
|
+
console.error(`[admin/agents] op=delete accountId=${account.accountId.slice(0, 8)} slug=${slug} outcome=failed reason=graph-cleanup-failed error="${msg}" \u2014 files preserved`);
|
|
16282
16655
|
return c.json({ error: `Graph cleanup failed: ${msg}` }, 500);
|
|
16283
16656
|
}
|
|
16284
16657
|
try {
|
|
16285
16658
|
rmSync2(agentDir, { recursive: true, force: true });
|
|
16286
|
-
console.log(`[admin/agents]
|
|
16659
|
+
console.log(`[admin/agents] op=delete accountId=${account.accountId.slice(0, 8)} slug=${slug} outcome=ok`);
|
|
16287
16660
|
return c.json({ ok: true });
|
|
16288
16661
|
} catch (err) {
|
|
16289
|
-
console.error(`[admin/agents]
|
|
16662
|
+
console.error(`[admin/agents] op=delete accountId=${account.accountId.slice(0, 8)} slug=${slug} outcome=failed reason=rm-error error="${err}"`);
|
|
16290
16663
|
return c.json({ error: "Failed to delete agent" }, 500);
|
|
16291
16664
|
}
|
|
16292
16665
|
});
|
|
16293
16666
|
app18.post("/:slug/project", async (c) => {
|
|
16294
16667
|
const slug = c.req.param("slug");
|
|
16295
|
-
const account = resolveAccount();
|
|
16296
|
-
if (!account) return c.json({ error: "No account resolved" }, 400);
|
|
16297
16668
|
if (slug === "admin") {
|
|
16298
16669
|
return c.json({ error: "Cannot project the admin agent" }, 403);
|
|
16299
16670
|
}
|
|
16300
16671
|
if (SLUG_UNSAFE(slug)) {
|
|
16301
16672
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
16302
16673
|
}
|
|
16303
|
-
const
|
|
16674
|
+
const accountIdParam = c.req.query("accountId") ?? "";
|
|
16675
|
+
if (!accountIdParam) {
|
|
16676
|
+
console.error(`[admin/agents] op=project rejected reason=missing-accountId slug=${slug}`);
|
|
16677
|
+
return c.json({ error: "accountId is required" }, 400);
|
|
16678
|
+
}
|
|
16679
|
+
const account = validateAccountId2(accountIdParam);
|
|
16680
|
+
if (!account) {
|
|
16681
|
+
console.error(`[admin/agents] op=project rejected reason=unknown-accountId accountId=${accountIdParam.slice(0, 8)} slug=${slug}`);
|
|
16682
|
+
return c.json({ error: "Unknown accountId" }, 400);
|
|
16683
|
+
}
|
|
16684
|
+
const authz = await authorizeAccountWrite(c, account.accountId);
|
|
16685
|
+
if (!authz.ok) {
|
|
16686
|
+
console.error(`[admin/agents] op=project rejected reason=${authz.reason} accountId=${account.accountId.slice(0, 8)} slug=${slug}`);
|
|
16687
|
+
return c.json({ error: "Not authorized for this account" }, 403);
|
|
16688
|
+
}
|
|
16689
|
+
const agentDir = resolve23(account.accountDir, "agents", slug);
|
|
16304
16690
|
if (!existsSync30(agentDir)) {
|
|
16305
16691
|
return c.json({ error: "Agent not found on disk" }, 404);
|
|
16306
16692
|
}
|
|
@@ -17004,7 +17390,7 @@ var sessions_default = app19;
|
|
|
17004
17390
|
|
|
17005
17391
|
// app/lib/claude-agent/spawn-context.ts
|
|
17006
17392
|
import { existsSync as existsSync32, readFileSync as readFileSync33, readdirSync as readdirSync22, statSync as statSync15 } from "fs";
|
|
17007
|
-
import { dirname as dirname12, resolve as
|
|
17393
|
+
import { dirname as dirname12, resolve as resolve24, join as join33 } from "path";
|
|
17008
17394
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
17009
17395
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
17010
17396
|
try {
|
|
@@ -17101,7 +17487,7 @@ function parseSkillPathsFromFrontmatter(fm) {
|
|
|
17101
17487
|
}
|
|
17102
17488
|
function listPluginDirs(accountPluginsDir) {
|
|
17103
17489
|
const out = /* @__PURE__ */ new Map();
|
|
17104
|
-
const platformPlugins =
|
|
17490
|
+
const platformPlugins = resolve24(PLATFORM_ROOT, "plugins");
|
|
17105
17491
|
if (existsSync32(platformPlugins)) {
|
|
17106
17492
|
for (const name of readdirSync22(platformPlugins)) {
|
|
17107
17493
|
const dir = join33(platformPlugins, name);
|
|
@@ -17111,7 +17497,7 @@ function listPluginDirs(accountPluginsDir) {
|
|
|
17111
17497
|
}
|
|
17112
17498
|
}
|
|
17113
17499
|
}
|
|
17114
|
-
const premiumRoot =
|
|
17500
|
+
const premiumRoot = resolve24(dirname12(PLATFORM_ROOT), "premium-plugins");
|
|
17115
17501
|
if (existsSync32(premiumRoot)) {
|
|
17116
17502
|
for (const bundle of readdirSync22(premiumRoot)) {
|
|
17117
17503
|
const bundlePlugins = join33(premiumRoot, bundle, "plugins");
|
|
@@ -17144,7 +17530,7 @@ function listPluginDirs(accountPluginsDir) {
|
|
|
17144
17530
|
return out;
|
|
17145
17531
|
}
|
|
17146
17532
|
function accountPluginsDirFor(accountId) {
|
|
17147
|
-
return
|
|
17533
|
+
return resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "plugins");
|
|
17148
17534
|
}
|
|
17149
17535
|
function listAccountPluginNames(accountPluginsDir) {
|
|
17150
17536
|
if (!existsSync32(accountPluginsDir)) return [];
|
|
@@ -17161,7 +17547,7 @@ function listAccountPluginNames(accountPluginsDir) {
|
|
|
17161
17547
|
}
|
|
17162
17548
|
}
|
|
17163
17549
|
function readEnabledPlugins(accountId) {
|
|
17164
|
-
const accountFile =
|
|
17550
|
+
const accountFile = resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
17165
17551
|
if (!existsSync32(accountFile)) return /* @__PURE__ */ new Set();
|
|
17166
17552
|
try {
|
|
17167
17553
|
const parsed = JSON.parse(readFileSync33(accountFile, "utf-8"));
|
|
@@ -17237,11 +17623,11 @@ ${skillFm}
|
|
|
17237
17623
|
return out;
|
|
17238
17624
|
}
|
|
17239
17625
|
function computeSpecialistDomains(accountId) {
|
|
17240
|
-
const specialistsDir =
|
|
17626
|
+
const specialistsDir = resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
|
|
17241
17627
|
if (!existsSync32(specialistsDir)) return [];
|
|
17242
|
-
let
|
|
17628
|
+
let entries2;
|
|
17243
17629
|
try {
|
|
17244
|
-
|
|
17630
|
+
entries2 = readdirSync22(specialistsDir);
|
|
17245
17631
|
} catch {
|
|
17246
17632
|
return [];
|
|
17247
17633
|
}
|
|
@@ -17263,7 +17649,7 @@ function computeSpecialistDomains(accountId) {
|
|
|
17263
17649
|
return map.get(tool);
|
|
17264
17650
|
};
|
|
17265
17651
|
const out = [];
|
|
17266
|
-
for (const file of
|
|
17652
|
+
for (const file of entries2.sort()) {
|
|
17267
17653
|
if (!file.endsWith(".md")) continue;
|
|
17268
17654
|
const fm = readFrontmatter(join33(specialistsDir, file));
|
|
17269
17655
|
if (!fm) continue;
|
|
@@ -17283,7 +17669,7 @@ ${fm}
|
|
|
17283
17669
|
return out;
|
|
17284
17670
|
}
|
|
17285
17671
|
function computeDormantPlugins(accountId) {
|
|
17286
|
-
const accountFile =
|
|
17672
|
+
const accountFile = resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
17287
17673
|
if (!existsSync32(accountFile)) return [];
|
|
17288
17674
|
let enabled;
|
|
17289
17675
|
try {
|
|
@@ -17298,18 +17684,18 @@ function computeDormantPlugins(accountId) {
|
|
|
17298
17684
|
);
|
|
17299
17685
|
return [];
|
|
17300
17686
|
}
|
|
17301
|
-
const pluginsDir =
|
|
17687
|
+
const pluginsDir = resolve24(PLATFORM_ROOT, "plugins");
|
|
17302
17688
|
if (!existsSync32(pluginsDir)) return [];
|
|
17303
|
-
let
|
|
17689
|
+
let entries2;
|
|
17304
17690
|
try {
|
|
17305
|
-
|
|
17691
|
+
entries2 = readdirSync22(pluginsDir);
|
|
17306
17692
|
} catch {
|
|
17307
17693
|
return [];
|
|
17308
17694
|
}
|
|
17309
17695
|
const out = [];
|
|
17310
|
-
for (const name of
|
|
17696
|
+
for (const name of entries2) {
|
|
17311
17697
|
if (enabled.has(name)) continue;
|
|
17312
|
-
const manifestPath =
|
|
17698
|
+
const manifestPath = resolve24(pluginsDir, name, "PLUGIN.md");
|
|
17313
17699
|
if (!existsSync32(manifestPath)) continue;
|
|
17314
17700
|
let manifest;
|
|
17315
17701
|
try {
|
|
@@ -17328,7 +17714,7 @@ function computeDormantPlugins(accountId) {
|
|
|
17328
17714
|
|
|
17329
17715
|
// server/routes/admin/claude-sessions.ts
|
|
17330
17716
|
import { existsSync as existsSync33, readFileSync as readFileSync34 } from "fs";
|
|
17331
|
-
import { resolve as
|
|
17717
|
+
import { resolve as resolve25 } from "path";
|
|
17332
17718
|
function readTunnelState(brandConfigDir) {
|
|
17333
17719
|
const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
|
|
17334
17720
|
if (!existsSync33(statePath)) return null;
|
|
@@ -17344,10 +17730,10 @@ function readTunnelState(brandConfigDir) {
|
|
|
17344
17730
|
}
|
|
17345
17731
|
}
|
|
17346
17732
|
function resolveTunnelUrl() {
|
|
17347
|
-
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ??
|
|
17733
|
+
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
|
|
17348
17734
|
let brand;
|
|
17349
17735
|
try {
|
|
17350
|
-
brand = JSON.parse(readFileSync34(
|
|
17736
|
+
brand = JSON.parse(readFileSync34(resolve25(platformRoot5, "config", "brand.json"), "utf-8"));
|
|
17351
17737
|
} catch {
|
|
17352
17738
|
return null;
|
|
17353
17739
|
}
|
|
@@ -17586,6 +17972,77 @@ app22.post("/", async (c) => {
|
|
|
17586
17972
|
console.error(`[${event}] ${formatted}`);
|
|
17587
17973
|
return c.json({ ok: true });
|
|
17588
17974
|
});
|
|
17975
|
+
function formatEvent(resource, accountId) {
|
|
17976
|
+
return `data: ${JSON.stringify({ resource, accountId })}
|
|
17977
|
+
|
|
17978
|
+
`;
|
|
17979
|
+
}
|
|
17980
|
+
function shouldDeliver(eventAccountId, subscriberAccountId) {
|
|
17981
|
+
return eventAccountId === null || eventAccountId === subscriberAccountId;
|
|
17982
|
+
}
|
|
17983
|
+
app22.get("/", requireAdminSession, async (c) => {
|
|
17984
|
+
const accountId = getAccountIdForSession(c.var.cacheKey);
|
|
17985
|
+
if (!accountId) return c.json({ error: "Account not found for session" }, 401);
|
|
17986
|
+
const encoder = new TextEncoder();
|
|
17987
|
+
let unsubscribe = () => {
|
|
17988
|
+
};
|
|
17989
|
+
let heartbeat = null;
|
|
17990
|
+
let closed = false;
|
|
17991
|
+
const teardown = (reason) => {
|
|
17992
|
+
if (closed) return;
|
|
17993
|
+
closed = true;
|
|
17994
|
+
unsubscribe();
|
|
17995
|
+
if (heartbeat) clearInterval(heartbeat);
|
|
17996
|
+
console.log(`[admin-events] op=drop resource=all accountId=${accountId.slice(0, 8)} reason=${reason}`);
|
|
17997
|
+
};
|
|
17998
|
+
const stream2 = new ReadableStream({
|
|
17999
|
+
start(controller) {
|
|
18000
|
+
console.log(`[admin-events] op=subscribe resource=all accountId=${accountId.slice(0, 8)}`);
|
|
18001
|
+
unsubscribe = onInvalidate((resource, eventAccountId) => {
|
|
18002
|
+
if (closed) return;
|
|
18003
|
+
if (!shouldDeliver(eventAccountId, accountId)) return;
|
|
18004
|
+
try {
|
|
18005
|
+
controller.enqueue(encoder.encode(formatEvent(resource, eventAccountId)));
|
|
18006
|
+
console.log(`[admin-events] op=emit resource=${resource} accountId=${accountId.slice(0, 8)}`);
|
|
18007
|
+
} catch (err) {
|
|
18008
|
+
teardown(`enqueue-failed:${String(err)}`);
|
|
18009
|
+
try {
|
|
18010
|
+
controller.close();
|
|
18011
|
+
} catch {
|
|
18012
|
+
}
|
|
18013
|
+
}
|
|
18014
|
+
});
|
|
18015
|
+
heartbeat = setInterval(() => {
|
|
18016
|
+
try {
|
|
18017
|
+
controller.enqueue(encoder.encode(": ping\n\n"));
|
|
18018
|
+
} catch {
|
|
18019
|
+
teardown("heartbeat-enqueue-failed");
|
|
18020
|
+
try {
|
|
18021
|
+
controller.close();
|
|
18022
|
+
} catch {
|
|
18023
|
+
}
|
|
18024
|
+
}
|
|
18025
|
+
}, 2e4);
|
|
18026
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
18027
|
+
teardown("client-aborted");
|
|
18028
|
+
try {
|
|
18029
|
+
controller.close();
|
|
18030
|
+
} catch {
|
|
18031
|
+
}
|
|
18032
|
+
});
|
|
18033
|
+
},
|
|
18034
|
+
cancel() {
|
|
18035
|
+
teardown("client-closed");
|
|
18036
|
+
}
|
|
18037
|
+
});
|
|
18038
|
+
return new Response(stream2, {
|
|
18039
|
+
headers: {
|
|
18040
|
+
"Content-Type": "text/event-stream",
|
|
18041
|
+
"Cache-Control": "no-cache",
|
|
18042
|
+
Connection: "keep-alive"
|
|
18043
|
+
}
|
|
18044
|
+
});
|
|
18045
|
+
});
|
|
17589
18046
|
var events_default = app22;
|
|
17590
18047
|
|
|
17591
18048
|
// server/routes/admin/files.ts
|
|
@@ -17593,7 +18050,7 @@ import { createReadStream as createReadStream2, createWriteStream as createWrite
|
|
|
17593
18050
|
import { readdir as readdir4, readFile as readFile7, stat as stat6, mkdir as mkdir4, unlink as unlink3, rename as rename2, rmdir } from "fs/promises";
|
|
17594
18051
|
import { realpathSync as realpathSync7 } from "fs";
|
|
17595
18052
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
17596
|
-
import { basename as basename11, dirname as dirname13, join as join35, relative as relative5, resolve as
|
|
18053
|
+
import { basename as basename11, dirname as dirname13, join as join35, relative as relative5, resolve as resolve27, sep as sep8 } from "path";
|
|
17597
18054
|
import { Readable as Readable2 } from "stream";
|
|
17598
18055
|
|
|
17599
18056
|
// ../lib/graph-trash/src/index.ts
|
|
@@ -17911,7 +18368,7 @@ async function cascadeDeleteDocument(params) {
|
|
|
17911
18368
|
|
|
17912
18369
|
// app/lib/file-index.ts
|
|
17913
18370
|
import * as fsp from "fs/promises";
|
|
17914
|
-
import { resolve as
|
|
18371
|
+
import { resolve as resolve26, relative as relative4, join as join34, basename as basename10, extname as extname2, sep as sep7 } from "path";
|
|
17915
18372
|
import { tmpdir as tmpdir2 } from "os";
|
|
17916
18373
|
import { execFile as execFile2 } from "child_process";
|
|
17917
18374
|
import { promisify as promisify2 } from "util";
|
|
@@ -18064,16 +18521,16 @@ async function readDisplayName(absolute) {
|
|
|
18064
18521
|
}
|
|
18065
18522
|
}
|
|
18066
18523
|
async function walkSubtree(root, dataRoot, out) {
|
|
18067
|
-
let
|
|
18524
|
+
let entries2;
|
|
18068
18525
|
try {
|
|
18069
|
-
|
|
18526
|
+
entries2 = await fsp.readdir(root, { withFileTypes: true });
|
|
18070
18527
|
} catch (err) {
|
|
18071
18528
|
if (err.code === "ENOENT") return { clean: true };
|
|
18072
18529
|
console.error(`[file-index] walk-error dir="${relative4(dataRoot, root)}" err="${err.message}"`);
|
|
18073
18530
|
return { clean: false };
|
|
18074
18531
|
}
|
|
18075
18532
|
let clean = true;
|
|
18076
|
-
for (const entry of
|
|
18533
|
+
for (const entry of entries2) {
|
|
18077
18534
|
if (entry.isSymbolicLink()) continue;
|
|
18078
18535
|
if (entry.isDirectory() && entry.name === ".uploads-tmp") continue;
|
|
18079
18536
|
const abs = join34(root, entry.name);
|
|
@@ -18231,7 +18688,7 @@ async function reconcileFileIndex(accountId, opts) {
|
|
|
18231
18688
|
return withSession(opts, async (session) => {
|
|
18232
18689
|
const walked = [];
|
|
18233
18690
|
const subtreeRoots = [
|
|
18234
|
-
|
|
18691
|
+
resolve26(dataRoot, "accounts", accountId)
|
|
18235
18692
|
];
|
|
18236
18693
|
let clean = true;
|
|
18237
18694
|
for (const root of subtreeRoots) {
|
|
@@ -18314,7 +18771,7 @@ async function reconcileFileIndexDebounced(accountId, opts) {
|
|
|
18314
18771
|
async function indexFile(accountId, relativePath, opts) {
|
|
18315
18772
|
const dataRoot = opts?.dataRoot ?? DATA_ROOT;
|
|
18316
18773
|
const embed2 = opts?.embed ?? embed;
|
|
18317
|
-
const absolute =
|
|
18774
|
+
const absolute = resolve26(dataRoot, relativePath);
|
|
18318
18775
|
await withSession(opts, async (session) => {
|
|
18319
18776
|
const st = await fsp.stat(absolute);
|
|
18320
18777
|
const wf = {
|
|
@@ -18365,11 +18822,11 @@ function crc32(buf) {
|
|
|
18365
18822
|
for (let i = 0; i < buf.length; i++) crc = crcTable[(crc ^ buf[i]) & 255] ^ crc >>> 8;
|
|
18366
18823
|
return (crc ^ 4294967295) >>> 0;
|
|
18367
18824
|
}
|
|
18368
|
-
function buildZip(
|
|
18825
|
+
function buildZip(entries2) {
|
|
18369
18826
|
const localChunks = [];
|
|
18370
18827
|
const centralChunks = [];
|
|
18371
18828
|
let offset = 0;
|
|
18372
|
-
for (const entry of
|
|
18829
|
+
for (const entry of entries2) {
|
|
18373
18830
|
const nameBytes = Buffer.from(entry.name, "utf8");
|
|
18374
18831
|
const crc = crc32(entry.data);
|
|
18375
18832
|
const uncompressedSize = entry.data.length;
|
|
@@ -18419,14 +18876,31 @@ function buildZip(entries) {
|
|
|
18419
18876
|
eocd.writeUInt32LE(EOCD_SIG, 0);
|
|
18420
18877
|
eocd.writeUInt16LE(0, 4);
|
|
18421
18878
|
eocd.writeUInt16LE(0, 6);
|
|
18422
|
-
eocd.writeUInt16LE(
|
|
18423
|
-
eocd.writeUInt16LE(
|
|
18879
|
+
eocd.writeUInt16LE(entries2.length, 8);
|
|
18880
|
+
eocd.writeUInt16LE(entries2.length, 10);
|
|
18424
18881
|
eocd.writeUInt32LE(centralSize, 12);
|
|
18425
18882
|
eocd.writeUInt32LE(centralOffset, 16);
|
|
18426
18883
|
eocd.writeUInt16LE(0, 20);
|
|
18427
18884
|
return Buffer.concat([...localChunks, centralDir, eocd]);
|
|
18428
18885
|
}
|
|
18429
18886
|
|
|
18887
|
+
// server/lib/account-root-groups.ts
|
|
18888
|
+
var import_dist = __toESM(require_dist2(), 1);
|
|
18889
|
+
function classifyAccountRoot(schemaMd, rootEntries) {
|
|
18890
|
+
const regions = (0, import_dist.parseSchemaRegions)(schemaMd);
|
|
18891
|
+
if (!regions.parsed) {
|
|
18892
|
+
return { home: [], system: [...rootEntries], unknown: [], parsed: false, reason: regions.reason };
|
|
18893
|
+
}
|
|
18894
|
+
const homeOrder = [...regions.ontologyRoots, ...import_dist.HOME_FIXED].filter((d, i, a) => a.indexOf(d) === i);
|
|
18895
|
+
const homeSet = new Set(homeOrder);
|
|
18896
|
+
const present = new Set(rootEntries);
|
|
18897
|
+
const home = homeOrder.filter((d) => present.has(d));
|
|
18898
|
+
const system = rootEntries.filter((n) => !homeSet.has(n));
|
|
18899
|
+
const declared = /* @__PURE__ */ new Set([...regions.allowedTopLevel, ...regions.pluginOwned, ...homeSet]);
|
|
18900
|
+
const unknown = rootEntries.filter((n) => !declared.has(n));
|
|
18901
|
+
return { home, system, unknown, parsed: true, reason: "ok" };
|
|
18902
|
+
}
|
|
18903
|
+
|
|
18430
18904
|
// server/routes/admin/files.ts
|
|
18431
18905
|
var UPLOAD_TMP_DIR = ".uploads-tmp";
|
|
18432
18906
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -18443,7 +18917,7 @@ async function readMeta(absDir, baseName) {
|
|
|
18443
18917
|
}
|
|
18444
18918
|
async function readAccountNames() {
|
|
18445
18919
|
const map = /* @__PURE__ */ new Map();
|
|
18446
|
-
const accountsDir =
|
|
18920
|
+
const accountsDir = resolve27(DATA_ROOT, "accounts");
|
|
18447
18921
|
let names;
|
|
18448
18922
|
try {
|
|
18449
18923
|
names = await readdir4(accountsDir);
|
|
@@ -18452,7 +18926,7 @@ async function readAccountNames() {
|
|
|
18452
18926
|
}
|
|
18453
18927
|
for (const name of names) {
|
|
18454
18928
|
if (!UUID_RE4.test(name)) continue;
|
|
18455
|
-
const configPath2 =
|
|
18929
|
+
const configPath2 = resolve27(accountsDir, name, "account.json");
|
|
18456
18930
|
try {
|
|
18457
18931
|
const raw = await readFile7(configPath2, "utf8");
|
|
18458
18932
|
const parsed = JSON.parse(raw);
|
|
@@ -18538,8 +19012,24 @@ function buildDisplayPath(relPath, accountNames) {
|
|
|
18538
19012
|
return dn ? { name: seg, displayName: dn } : { name: seg };
|
|
18539
19013
|
});
|
|
18540
19014
|
}
|
|
18541
|
-
|
|
18542
|
-
|
|
19015
|
+
var HOME_CHILD_PREVIEW = 20;
|
|
19016
|
+
function parseGroupPath(raw, sessionAccountId) {
|
|
19017
|
+
const trimmed = raw.replace(/^\.?\/*/, "");
|
|
19018
|
+
if (trimmed === "@home" || trimmed === "@system") {
|
|
19019
|
+
return { view: trimmed.slice(1), accountRel: `accounts/${sessionAccountId}` };
|
|
19020
|
+
}
|
|
19021
|
+
const segs = trimmed.split("/").filter(Boolean);
|
|
19022
|
+
if (segs.length !== 3) return null;
|
|
19023
|
+
if (segs[0] !== "accounts" || !UUID_RE4.test(segs[1])) return null;
|
|
19024
|
+
if (segs[2] !== "@home" && segs[2] !== "@system") return null;
|
|
19025
|
+
return { view: segs[2].slice(1), accountRel: `accounts/${segs[1]}` };
|
|
19026
|
+
}
|
|
19027
|
+
function isAccountRoot(relPath) {
|
|
19028
|
+
const segs = relPath.split("/").filter(Boolean);
|
|
19029
|
+
return segs.length === 2 && segs[0] === "accounts" && UUID_RE4.test(segs[1]);
|
|
19030
|
+
}
|
|
19031
|
+
function sortDirEntries(entries2) {
|
|
19032
|
+
entries2.sort((a, b) => {
|
|
18543
19033
|
if (a.kind !== b.kind) return a.kind === "directory" ? -1 : 1;
|
|
18544
19034
|
if (a.kind === "file" && b.kind === "file") {
|
|
18545
19035
|
const byTime = b.modifiedAt.localeCompare(a.modifiedAt);
|
|
@@ -18596,6 +19086,7 @@ function isProtectedFromRename(relPath) {
|
|
|
18596
19086
|
function isValidEntryName(name) {
|
|
18597
19087
|
if (typeof name !== "string" || name.length === 0) return false;
|
|
18598
19088
|
if (name === "." || name === "..") return false;
|
|
19089
|
+
if (name.startsWith("@")) return false;
|
|
18599
19090
|
return !/[\\/\0]/.test(name);
|
|
18600
19091
|
}
|
|
18601
19092
|
function resolveOwnAccountWrite(rawPath, accountId, endpoint) {
|
|
@@ -18640,7 +19131,8 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18640
19131
|
return c.json({ error: "Account not found for session" }, 401);
|
|
18641
19132
|
}
|
|
18642
19133
|
const rawPath = c.req.query("path") ?? "";
|
|
18643
|
-
const
|
|
19134
|
+
const groupReq = parseGroupPath(rawPath, accountId);
|
|
19135
|
+
const resolution = resolveDataPath(groupReq ? groupReq.accountRel : rawPath);
|
|
18644
19136
|
if (!resolution.ok) {
|
|
18645
19137
|
if (resolution.status === 403) {
|
|
18646
19138
|
console.error(`[data] path-traversal-blocked requested="${rawPath}" resolved="${resolution.resolved ?? "n/a"}"`);
|
|
@@ -18660,7 +19152,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18660
19152
|
const names = await readdir4(absolute);
|
|
18661
19153
|
const segs = relPath.split("/").filter(Boolean);
|
|
18662
19154
|
const isAccountParent = segs.length === 1 && segs[0] === "accounts";
|
|
18663
|
-
const
|
|
19155
|
+
const entries2 = [];
|
|
18664
19156
|
for (const name of names) {
|
|
18665
19157
|
if (UUID_RE4.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
|
|
18666
19158
|
continue;
|
|
@@ -18673,7 +19165,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18673
19165
|
try {
|
|
18674
19166
|
const entryPath = join35(absolute, name);
|
|
18675
19167
|
const s = await stat6(entryPath);
|
|
18676
|
-
|
|
19168
|
+
entries2.push({
|
|
18677
19169
|
name,
|
|
18678
19170
|
kind: s.isDirectory() ? "directory" : s.isFile() ? "file" : "other",
|
|
18679
19171
|
sizeBytes: s.isDirectory() ? null : s.size,
|
|
@@ -18681,12 +19173,12 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18681
19173
|
protected: protectedEntry
|
|
18682
19174
|
});
|
|
18683
19175
|
} catch {
|
|
18684
|
-
|
|
19176
|
+
entries2.push({ name, kind: "other", sizeBytes: null, modifiedAt: (/* @__PURE__ */ new Date(0)).toISOString(), protected: protectedEntry });
|
|
18685
19177
|
}
|
|
18686
19178
|
}
|
|
18687
19179
|
const accountNames = await readAccountNames();
|
|
18688
|
-
const classes = await Promise.all(
|
|
18689
|
-
sortDirEntries(
|
|
19180
|
+
const classes = await Promise.all(entries2.map((e) => enrich(absolute, e, accountNames)));
|
|
19181
|
+
sortDirEntries(entries2);
|
|
18690
19182
|
let flattened = 0;
|
|
18691
19183
|
let keptAsDir = 0;
|
|
18692
19184
|
for (const cls of classes) {
|
|
@@ -18698,8 +19190,77 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18698
19190
|
console.error(`[data] op=uploads-flatten containers=${containers} flattened=${flattened} kept-as-dir=${keptAsDir}`);
|
|
18699
19191
|
}
|
|
18700
19192
|
const displayPath = buildDisplayPath(relPath, accountNames);
|
|
18701
|
-
|
|
18702
|
-
|
|
19193
|
+
if (groupReq || isAccountRoot(relPath)) {
|
|
19194
|
+
const acctId = relPath.split("/")[1] ?? accountId;
|
|
19195
|
+
const schemaMd = await readFile7(join35(absolute, "SCHEMA.md"), "utf8").catch(() => null);
|
|
19196
|
+
const groups = classifyAccountRoot(schemaMd, entries2.map((e) => e.name));
|
|
19197
|
+
console.error(
|
|
19198
|
+
`[data-schema] op=classify account=${acctId} status=${groups.parsed ? "ok" : "parse-failed"} reason=${groups.reason} entries=${entries2.length} home=${groups.home.join(",") || "none"} system=${groups.system.length} unknown=${groups.unknown.join(",") || "none"}`
|
|
19199
|
+
);
|
|
19200
|
+
if (groupReq && !groups.parsed) {
|
|
19201
|
+
return c.json({ path: relPath, displayPath, entries: entries2 });
|
|
19202
|
+
}
|
|
19203
|
+
if (!groupReq) {
|
|
19204
|
+
const homeSet = new Set(groups.home);
|
|
19205
|
+
for (const e of entries2) e.group = homeSet.has(e.name) ? "home" : "system";
|
|
19206
|
+
return c.json({ path: relPath, displayPath, entries: entries2 });
|
|
19207
|
+
}
|
|
19208
|
+
const groupPath = `${groupReq.accountRel}/@${groupReq.view}`;
|
|
19209
|
+
const groupDisplay = [...displayPath, { name: `@${groupReq.view}`, displayName: groupReq.view }];
|
|
19210
|
+
if (groupReq.view === "system") {
|
|
19211
|
+
const systemSet = new Set(groups.system);
|
|
19212
|
+
return c.json({
|
|
19213
|
+
path: groupPath,
|
|
19214
|
+
displayPath: groupDisplay,
|
|
19215
|
+
view: "system",
|
|
19216
|
+
entries: entries2.filter((e) => systemSet.has(e.name))
|
|
19217
|
+
});
|
|
19218
|
+
}
|
|
19219
|
+
const byName = new Map(entries2.map((e) => [e.name, e]));
|
|
19220
|
+
const homeDirs = [];
|
|
19221
|
+
const homeFiles = [];
|
|
19222
|
+
for (const name of groups.home) {
|
|
19223
|
+
if (byName.get(name)?.kind === "directory") homeDirs.push(name);
|
|
19224
|
+
else homeFiles.push(name);
|
|
19225
|
+
}
|
|
19226
|
+
if (homeFiles.length > 0) {
|
|
19227
|
+
console.error(`[data-schema] op=home-files account=${acctId} count=${homeFiles.length} names=${homeFiles.join(",")}`);
|
|
19228
|
+
}
|
|
19229
|
+
const cards = [];
|
|
19230
|
+
for (const name of homeDirs) {
|
|
19231
|
+
const base = byName.get(name);
|
|
19232
|
+
try {
|
|
19233
|
+
const dirents = await readdir4(join35(absolute, name), { withFileTypes: true });
|
|
19234
|
+
const visible = dirents.filter((d) => !d.name.startsWith("."));
|
|
19235
|
+
visible.sort((a, b) => {
|
|
19236
|
+
const ad = a.isDirectory() ? 0 : 1;
|
|
19237
|
+
const bd = b.isDirectory() ? 0 : 1;
|
|
19238
|
+
return ad !== bd ? ad - bd : a.name.localeCompare(b.name);
|
|
19239
|
+
});
|
|
19240
|
+
cards.push({
|
|
19241
|
+
...base,
|
|
19242
|
+
childCount: visible.length,
|
|
19243
|
+
children: visible.slice(0, HOME_CHILD_PREVIEW).map((d) => ({
|
|
19244
|
+
name: d.name,
|
|
19245
|
+
kind: d.isDirectory() ? "directory" : "file"
|
|
19246
|
+
}))
|
|
19247
|
+
});
|
|
19248
|
+
} catch (err) {
|
|
19249
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
19250
|
+
console.error(`[data-schema] op=card-children account=${acctId} bucket=${name} status=error reason="${message}"`);
|
|
19251
|
+
cards.push({ ...base, childCount: null, children: [] });
|
|
19252
|
+
}
|
|
19253
|
+
}
|
|
19254
|
+
return c.json({
|
|
19255
|
+
path: groupPath,
|
|
19256
|
+
displayPath: groupDisplay,
|
|
19257
|
+
view: "home",
|
|
19258
|
+
entries: cards,
|
|
19259
|
+
systemCount: groups.system.length + homeFiles.length
|
|
19260
|
+
});
|
|
19261
|
+
}
|
|
19262
|
+
console.error(`[data] file-list path="${relPath}" entries=${entries2.length}`);
|
|
19263
|
+
return c.json({ path: relPath, displayPath, entries: entries2 });
|
|
18703
19264
|
} catch (err) {
|
|
18704
19265
|
const code = err.code;
|
|
18705
19266
|
if (code === "ENOENT") {
|
|
@@ -18851,14 +19412,14 @@ app23.get("/download-zip", requireAdminSession, async (c) => {
|
|
|
18851
19412
|
if (rawPaths.length > maxFiles) {
|
|
18852
19413
|
return c.json({ error: `Too many files (max ${maxFiles})` }, 413);
|
|
18853
19414
|
}
|
|
18854
|
-
const
|
|
19415
|
+
const entries2 = [];
|
|
18855
19416
|
let total = 0;
|
|
18856
19417
|
let dirsWalked = 0;
|
|
18857
19418
|
const addFile = async (absolute, size, member) => {
|
|
18858
|
-
if (
|
|
19419
|
+
if (entries2.length >= maxFiles) return c.json({ error: `Too many files (max ${maxFiles})` }, 413);
|
|
18859
19420
|
total += size;
|
|
18860
19421
|
if (total > maxTotalBytes) return c.json({ error: "Selection too large to zip" }, 413);
|
|
18861
|
-
|
|
19422
|
+
entries2.push({ name: member, data: await readFile7(absolute) });
|
|
18862
19423
|
return null;
|
|
18863
19424
|
};
|
|
18864
19425
|
for (const rawPath of rawPaths) {
|
|
@@ -18906,8 +19467,8 @@ app23.get("/download-zip", requireAdminSession, async (c) => {
|
|
|
18906
19467
|
throw err;
|
|
18907
19468
|
}
|
|
18908
19469
|
}
|
|
18909
|
-
const archive = buildZip(
|
|
18910
|
-
console.error(`[data] file-download-zip dirs=${dirsWalked} files=${
|
|
19470
|
+
const archive = buildZip(entries2);
|
|
19471
|
+
console.error(`[data] file-download-zip dirs=${dirsWalked} files=${entries2.length} bytes=${archive.length}`);
|
|
18911
19472
|
return new Response(new Uint8Array(archive), {
|
|
18912
19473
|
status: 200,
|
|
18913
19474
|
headers: {
|
|
@@ -18948,8 +19509,8 @@ async function resolveUploadTarget(c, accountId, uid) {
|
|
|
18948
19509
|
console.error(`[data] write-scope-blocked endpoint="POST /api/admin/files/upload" path="${destRel}" account=${accountId.slice(0, 8)}\u2026`);
|
|
18949
19510
|
return { ok: false, status: 403, error: "Path is outside your account folder" };
|
|
18950
19511
|
}
|
|
18951
|
-
const destDir = relDir === "." ? base.absolute :
|
|
18952
|
-
const destPath =
|
|
19512
|
+
const destDir = relDir === "." ? base.absolute : resolve27(base.absolute, relDir);
|
|
19513
|
+
const destPath = resolve27(destDir, finalName);
|
|
18953
19514
|
if (relpath) {
|
|
18954
19515
|
const firstCreated = await mkdir4(destDir, { recursive: true });
|
|
18955
19516
|
if (firstCreated) console.error(`[data-upload] op=mkdir uid=${uid} token=${token} dir="${destRel}"`);
|
|
@@ -18966,7 +19527,7 @@ async function resolveUploadTarget(c, accountId, uid) {
|
|
|
18966
19527
|
return { ok: true, destDir, destPath, destRel, finalName, mimeType, relpath, token };
|
|
18967
19528
|
}
|
|
18968
19529
|
async function logUploadsTmpCensus(accountId) {
|
|
18969
|
-
const tmpDir =
|
|
19530
|
+
const tmpDir = resolve27(DATA_ROOT, "accounts", accountId, UPLOAD_TMP_DIR);
|
|
18970
19531
|
let live = 0;
|
|
18971
19532
|
try {
|
|
18972
19533
|
live = (await readdir4(tmpDir)).length;
|
|
@@ -18993,9 +19554,9 @@ app23.post("/upload", requireAdminSession, async (c) => {
|
|
|
18993
19554
|
if (!body) return c.json({ error: "Empty request body" }, 400);
|
|
18994
19555
|
console.error(`[data-upload] op=request uid=${uid} account=${accountId} dir="${destRel}" token=${token} rel="${relpath ?? ""}" declaredBytes=${declaredBytes}`);
|
|
18995
19556
|
const ceiling = dataUploadCeiling();
|
|
18996
|
-
const tmpDir =
|
|
19557
|
+
const tmpDir = resolve27(DATA_ROOT, "accounts", accountId, UPLOAD_TMP_DIR);
|
|
18997
19558
|
await mkdir4(tmpDir, { recursive: true });
|
|
18998
|
-
const tmpPath =
|
|
19559
|
+
const tmpPath = resolve27(tmpDir, `${uid}.part`);
|
|
18999
19560
|
const out = createWriteStream2(tmpPath);
|
|
19000
19561
|
const t0 = Date.now();
|
|
19001
19562
|
let received = 0;
|
|
@@ -19142,9 +19703,9 @@ async function handleChunkUpload(c, accountId) {
|
|
|
19142
19703
|
}
|
|
19143
19704
|
const target = await resolveUploadTarget(c, accountId, uploadId);
|
|
19144
19705
|
if (!target.ok) return c.json({ error: target.error }, target.status);
|
|
19145
|
-
const tmpDir =
|
|
19706
|
+
const tmpDir = resolve27(DATA_ROOT, "accounts", accountId, UPLOAD_TMP_DIR);
|
|
19146
19707
|
await mkdir4(tmpDir, { recursive: true });
|
|
19147
|
-
const tmpPath =
|
|
19708
|
+
const tmpPath = resolve27(tmpDir, `${uploadId}.part`);
|
|
19148
19709
|
const out = createWriteStream2(tmpPath);
|
|
19149
19710
|
const state2 = {
|
|
19150
19711
|
accountId,
|
|
@@ -19250,7 +19811,7 @@ async function evictStaleChunkUploads(ttlMs) {
|
|
|
19250
19811
|
return evicted;
|
|
19251
19812
|
}
|
|
19252
19813
|
async function sweepStaleUploadTemps() {
|
|
19253
|
-
const accountsRoot =
|
|
19814
|
+
const accountsRoot = resolve27(DATA_ROOT, "accounts");
|
|
19254
19815
|
let accounts;
|
|
19255
19816
|
try {
|
|
19256
19817
|
accounts = await readdir4(accountsRoot, { withFileTypes: true });
|
|
@@ -19260,16 +19821,16 @@ async function sweepStaleUploadTemps() {
|
|
|
19260
19821
|
let removed = 0;
|
|
19261
19822
|
for (const acc of accounts) {
|
|
19262
19823
|
if (!acc.isDirectory()) continue;
|
|
19263
|
-
const tmpDir =
|
|
19264
|
-
let
|
|
19824
|
+
const tmpDir = resolve27(accountsRoot, acc.name, UPLOAD_TMP_DIR);
|
|
19825
|
+
let entries2;
|
|
19265
19826
|
try {
|
|
19266
|
-
|
|
19827
|
+
entries2 = await readdir4(tmpDir);
|
|
19267
19828
|
} catch {
|
|
19268
19829
|
continue;
|
|
19269
19830
|
}
|
|
19270
|
-
for (const name of
|
|
19831
|
+
for (const name of entries2) {
|
|
19271
19832
|
try {
|
|
19272
|
-
await unlink3(
|
|
19833
|
+
await unlink3(resolve27(tmpDir, name));
|
|
19273
19834
|
removed++;
|
|
19274
19835
|
} catch {
|
|
19275
19836
|
}
|
|
@@ -19380,7 +19941,7 @@ app23.post("/folder", requireAdminSession, async (c) => {
|
|
|
19380
19941
|
if (!isValidEntryName(name)) return c.json({ error: "Invalid folder name" }, 400);
|
|
19381
19942
|
const parent = resolveOwnAccountWrite(rawPath, accountId, "POST /api/admin/files/folder");
|
|
19382
19943
|
if (!parent.ok) return c.json({ error: parent.error }, parent.status);
|
|
19383
|
-
const newAbs =
|
|
19944
|
+
const newAbs = resolve27(parent.absolute, name);
|
|
19384
19945
|
const newRel = `${parent.relative}/${name}`;
|
|
19385
19946
|
try {
|
|
19386
19947
|
const info = await stat6(parent.absolute);
|
|
@@ -19419,7 +19980,7 @@ app23.post("/rename", requireAdminSession, async (c) => {
|
|
|
19419
19980
|
console.error(`[data] file-rename blocked path="${src.relative}" reason="protected"`);
|
|
19420
19981
|
return c.json({ error: "Protected entry \u2014 refusing to rename" }, 403);
|
|
19421
19982
|
}
|
|
19422
|
-
const destAbs =
|
|
19983
|
+
const destAbs = resolve27(dirname13(src.absolute), newName);
|
|
19423
19984
|
const newRel = `${dirname13(src.relative)}/${newName}`.replace(/^\.\//, "");
|
|
19424
19985
|
if (isProtectedFromRename(newRel)) {
|
|
19425
19986
|
console.error(`[data] file-rename blocked path="${newRel}" reason="protected-target"`);
|
|
@@ -19450,7 +20011,7 @@ app23.post("/rename", requireAdminSession, async (c) => {
|
|
|
19450
20011
|
var files_default = app23;
|
|
19451
20012
|
|
|
19452
20013
|
// ../lib/graph-search/src/index.ts
|
|
19453
|
-
var
|
|
20014
|
+
var import_dist2 = __toESM(require_dist3());
|
|
19454
20015
|
import { int } from "neo4j-driver";
|
|
19455
20016
|
|
|
19456
20017
|
// ../lib/graph-search/src/rrf-fusion.ts
|
|
@@ -19700,7 +20261,7 @@ async function bm25Only(session, params) {
|
|
|
19700
20261
|
${scopeClause}
|
|
19701
20262
|
${agentClause}
|
|
19702
20263
|
${labelClause}
|
|
19703
|
-
AND ${(0,
|
|
20264
|
+
AND ${(0, import_dist2.notTrashed)("node")}
|
|
19704
20265
|
${kwClause}
|
|
19705
20266
|
RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
|
|
19706
20267
|
ORDER BY score DESC
|
|
@@ -19862,7 +20423,7 @@ async function hybrid(session, embed2, params) {
|
|
|
19862
20423
|
WHERE node.accountId = $accountId
|
|
19863
20424
|
${scopeClause}
|
|
19864
20425
|
${agentClause}
|
|
19865
|
-
AND ${(0,
|
|
20426
|
+
AND ${(0, import_dist2.notTrashed)("node")}
|
|
19866
20427
|
${keywordClause}
|
|
19867
20428
|
RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
|
|
19868
20429
|
ORDER BY score DESC
|
|
@@ -19961,7 +20522,7 @@ async function hybrid(session, embed2, params) {
|
|
|
19961
20522
|
const propResult = await session.run(
|
|
19962
20523
|
`MATCH (node)
|
|
19963
20524
|
WHERE node.accountId = $accountId
|
|
19964
|
-
AND ${(0,
|
|
20525
|
+
AND ${(0, import_dist2.notTrashed)("node")}
|
|
19965
20526
|
AND node.keywords IS NOT NULL
|
|
19966
20527
|
AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
|
|
19967
20528
|
${propScope.clause}
|
|
@@ -20013,7 +20574,7 @@ async function hybrid(session, embed2, params) {
|
|
|
20013
20574
|
const propResult = await session.run(
|
|
20014
20575
|
`MATCH (node)
|
|
20015
20576
|
WHERE node.accountId = $accountId
|
|
20016
|
-
AND ${(0,
|
|
20577
|
+
AND ${(0, import_dist2.notTrashed)("node")}
|
|
20017
20578
|
AND node.keywords IS NOT NULL
|
|
20018
20579
|
AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
|
|
20019
20580
|
${propScope.clause}
|
|
@@ -20120,7 +20681,7 @@ async function hybrid(session, embed2, params) {
|
|
|
20120
20681
|
`UNWIND $nodeIds AS nid
|
|
20121
20682
|
MATCH (n)-[r]-(related)
|
|
20122
20683
|
WHERE elementId(n) = nid
|
|
20123
|
-
AND ${(0,
|
|
20684
|
+
AND ${(0, import_dist2.notTrashed)("related")}
|
|
20124
20685
|
${expandScopeClause}
|
|
20125
20686
|
${expandAgentClause}
|
|
20126
20687
|
WITH nid, n, r, related
|
|
@@ -20348,8 +20909,8 @@ var graph_search_default = app24;
|
|
|
20348
20909
|
import neo4j2 from "neo4j-driver";
|
|
20349
20910
|
|
|
20350
20911
|
// app/lib/graph-labels.ts
|
|
20351
|
-
var
|
|
20352
|
-
var
|
|
20912
|
+
var import_dist3 = __toESM(require_dist4(), 1);
|
|
20913
|
+
var import_dist4 = __toESM(require_dist4(), 1);
|
|
20353
20914
|
var HIDDEN_BY_DEFAULT_LABELS = Object.freeze(
|
|
20354
20915
|
/* @__PURE__ */ new Set(["Chunk", "GraphPreference"])
|
|
20355
20916
|
);
|
|
@@ -20427,16 +20988,16 @@ var EXCLUDED_EDGE_TYPES = Object.freeze(
|
|
|
20427
20988
|
])
|
|
20428
20989
|
);
|
|
20429
20990
|
function isKnownLabel(label) {
|
|
20430
|
-
return Object.prototype.hasOwnProperty.call(
|
|
20991
|
+
return Object.prototype.hasOwnProperty.call(import_dist4.GRAPH_LABEL_COLOURS, label);
|
|
20431
20992
|
}
|
|
20432
20993
|
function isHiddenByDefault(label) {
|
|
20433
20994
|
return HIDDEN_BY_DEFAULT_LABELS.has(label);
|
|
20434
20995
|
}
|
|
20435
20996
|
|
|
20436
20997
|
// server/lib/top-level-labels.ts
|
|
20437
|
-
var
|
|
20998
|
+
var import_dist5 = __toESM(require_dist4(), 1);
|
|
20438
20999
|
import { readdirSync as readdirSync23, readFileSync as readFileSync35 } from "fs";
|
|
20439
|
-
import { join as join36, resolve as
|
|
21000
|
+
import { join as join36, resolve as resolve28 } from "path";
|
|
20440
21001
|
var STATIC_TOP_LEVEL_LABELS = Object.freeze(
|
|
20441
21002
|
/* @__PURE__ */ new Set([
|
|
20442
21003
|
// Base-schema infra (schema-base.md has no top-level section).
|
|
@@ -20542,7 +21103,7 @@ function resolveReferencesDir() {
|
|
|
20542
21103
|
if (process.env.MAXY_PLATFORM_ROOT) {
|
|
20543
21104
|
return join36(process.env.MAXY_PLATFORM_ROOT, "plugins", "memory", "references");
|
|
20544
21105
|
}
|
|
20545
|
-
return
|
|
21106
|
+
return resolve28(import.meta.dirname, "../../../plugins/memory/references");
|
|
20546
21107
|
}
|
|
20547
21108
|
function parseTableTopLevelLabels(referencesDir) {
|
|
20548
21109
|
const labels = /* @__PURE__ */ new Set();
|
|
@@ -20571,7 +21132,7 @@ function getTopLevelLabelAllowlist(opts = {}) {
|
|
|
20571
21132
|
const referencesDir = opts.referencesDir ?? resolveReferencesDir();
|
|
20572
21133
|
const { labels: derived, contributingFiles } = parseTableTopLevelLabels(referencesDir);
|
|
20573
21134
|
const excluded = [];
|
|
20574
|
-
for (const label of
|
|
21135
|
+
for (const label of import_dist5.ADDITIONAL_BASE_LABELS) {
|
|
20575
21136
|
if (derived.delete(label)) excluded.push(label);
|
|
20576
21137
|
}
|
|
20577
21138
|
const derivedCount = derived.size;
|
|
@@ -21707,7 +22268,7 @@ var graph_default_view_default = app29;
|
|
|
21707
22268
|
|
|
21708
22269
|
// server/routes/admin/sidebar-artefacts.ts
|
|
21709
22270
|
import { readdir as readdir5, stat as stat7 } from "fs/promises";
|
|
21710
|
-
import { resolve as
|
|
22271
|
+
import { resolve as resolve29, relative as relative6, isAbsolute as isAbsolute2, sep as sep9, basename as basename12 } from "path";
|
|
21711
22272
|
import { existsSync as existsSync35 } from "fs";
|
|
21712
22273
|
var LIMIT = 50;
|
|
21713
22274
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
@@ -21723,20 +22284,25 @@ app30.get("/", requireAdminSession, async (c) => {
|
|
|
21723
22284
|
return c.json({ error: "Account not found for session" }, 401);
|
|
21724
22285
|
}
|
|
21725
22286
|
const start = Date.now();
|
|
21726
|
-
|
|
21727
|
-
|
|
22287
|
+
ensureAccountWatchers(accountId, resolve29(ACCOUNTS_DIR, accountId));
|
|
22288
|
+
const { value, state } = await getOrCompute("sidebar-artefacts", accountId, async () => {
|
|
22289
|
+
const accountFiles = await fetchAccountFileArtefacts(accountId);
|
|
22290
|
+
if (accountFiles === null) return null;
|
|
22291
|
+
const accountDir = resolve29(ACCOUNTS_DIR, accountId);
|
|
22292
|
+
const agents = await fetchAgentTemplateRows(accountDir);
|
|
22293
|
+
const artefacts = [...accountFiles, ...agents].sort(
|
|
22294
|
+
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
22295
|
+
).slice(0, LIMIT);
|
|
22296
|
+
return { artefacts, accountFileCount: accountFiles.length, agentCount: agents.length };
|
|
22297
|
+
});
|
|
22298
|
+
if (value === null) {
|
|
21728
22299
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
21729
22300
|
}
|
|
21730
|
-
const accountDir = resolve28(ACCOUNTS_DIR, accountId);
|
|
21731
|
-
const agents = await fetchAgentTemplateRows(accountDir);
|
|
21732
|
-
const artefacts = [...accountFiles, ...agents].sort(
|
|
21733
|
-
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
21734
|
-
).slice(0, LIMIT);
|
|
21735
22301
|
const ms = Date.now() - start;
|
|
21736
22302
|
console.log(
|
|
21737
|
-
`[admin/sidebar-artefacts] account=${accountId} count=${artefacts.length} accountFiles=${
|
|
22303
|
+
`[admin/sidebar-artefacts] account=${accountId} count=${value.artefacts.length} accountFiles=${value.accountFileCount} agents=${value.agentCount} cache=${state} ms=${ms}`
|
|
21738
22304
|
);
|
|
21739
|
-
return c.json({ artefacts });
|
|
22305
|
+
return c.json({ artefacts: value.artefacts });
|
|
21740
22306
|
});
|
|
21741
22307
|
async function fetchAccountFileArtefacts(accountId) {
|
|
21742
22308
|
const session = getSession();
|
|
@@ -21779,8 +22345,8 @@ async function fetchAccountFileArtefacts(accountId) {
|
|
|
21779
22345
|
async function fetchAgentTemplateRows(accountDir) {
|
|
21780
22346
|
const rows = [];
|
|
21781
22347
|
for (const filename of ADMIN_AGENT_FILES) {
|
|
21782
|
-
const overridePath =
|
|
21783
|
-
const bundledPath =
|
|
22348
|
+
const overridePath = resolve29(accountDir, "agents", "admin", filename);
|
|
22349
|
+
const bundledPath = resolve29(PLATFORM_ROOT, "templates", "agents", "admin", filename);
|
|
21784
22350
|
const labelStem = filename.replace(/\.md$/, "");
|
|
21785
22351
|
const row = await readAgentTemplateRow({
|
|
21786
22352
|
id: `agent-template:admin:${filename}`,
|
|
@@ -21793,12 +22359,12 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
21793
22359
|
});
|
|
21794
22360
|
if (row) rows.push(row);
|
|
21795
22361
|
}
|
|
21796
|
-
const overrideDir =
|
|
21797
|
-
const bundledDir =
|
|
22362
|
+
const overrideDir = resolve29(accountDir, "specialists", "agents");
|
|
22363
|
+
const bundledDir = resolve29(PLATFORM_ROOT, "templates", "specialists", "agents");
|
|
21798
22364
|
const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
|
|
21799
22365
|
for (const filename of specialistNames) {
|
|
21800
|
-
const overridePath =
|
|
21801
|
-
const bundledPath =
|
|
22366
|
+
const overridePath = resolve29(overrideDir, filename);
|
|
22367
|
+
const bundledPath = resolve29(bundledDir, filename);
|
|
21802
22368
|
const row = await readAgentTemplateRow({
|
|
21803
22369
|
id: `agent-template:specialist:${filename}`,
|
|
21804
22370
|
displayName: filename.replace(/\.md$/, ""),
|
|
@@ -21817,8 +22383,8 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
21817
22383
|
for (const dir of [overrideDir, bundledDir]) {
|
|
21818
22384
|
if (!existsSync35(dir)) continue;
|
|
21819
22385
|
try {
|
|
21820
|
-
const
|
|
21821
|
-
for (const entry of
|
|
22386
|
+
const entries2 = await readdir5(dir);
|
|
22387
|
+
for (const entry of entries2) {
|
|
21822
22388
|
if (entry.endsWith(".md")) names.add(entry);
|
|
21823
22389
|
}
|
|
21824
22390
|
} catch (err) {
|
|
@@ -22284,24 +22850,24 @@ var session_rc_spawn_default = app36;
|
|
|
22284
22850
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
22285
22851
|
|
|
22286
22852
|
// server/wa-descriptor.ts
|
|
22287
|
-
import { resolve as
|
|
22853
|
+
import { resolve as resolve30, join as join38 } from "path";
|
|
22288
22854
|
function waGatewayUrl() {
|
|
22289
22855
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
22290
22856
|
}
|
|
22291
22857
|
function waServerPath() {
|
|
22292
|
-
return process.env.MAXY_WA_CHANNEL_SERVER_PATH ??
|
|
22858
|
+
return process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve30(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, ".."), "services/whatsapp-channel/dist/server.js");
|
|
22293
22859
|
}
|
|
22294
22860
|
function waChannelDescriptor(senderId) {
|
|
22295
22861
|
return { senderId, gatewayUrl: waGatewayUrl(), serverPath: waServerPath() };
|
|
22296
22862
|
}
|
|
22297
22863
|
|
|
22298
22864
|
// server/telegram-descriptor.ts
|
|
22299
|
-
import { resolve as
|
|
22865
|
+
import { resolve as resolve31, join as join39 } from "path";
|
|
22300
22866
|
function telegramGatewayUrl() {
|
|
22301
22867
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
22302
22868
|
}
|
|
22303
22869
|
function telegramServerPath() {
|
|
22304
|
-
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ??
|
|
22870
|
+
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve31(process.env.MAXY_PLATFORM_ROOT ?? join39(__dirname, ".."), "services/telegram-channel/dist/server.js");
|
|
22305
22871
|
}
|
|
22306
22872
|
function telegramChannelDescriptor(senderId) {
|
|
22307
22873
|
return { senderId, gatewayUrl: telegramGatewayUrl(), serverPath: telegramServerPath() };
|
|
@@ -22586,8 +23152,8 @@ var system_stats_default = app38;
|
|
|
22586
23152
|
|
|
22587
23153
|
// server/routes/admin/health.ts
|
|
22588
23154
|
import { existsSync as existsSync36, readFileSync as readFileSync37 } from "fs";
|
|
22589
|
-
import { resolve as
|
|
22590
|
-
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ??
|
|
23155
|
+
import { resolve as resolve32, join as join40 } from "path";
|
|
23156
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve32(process.cwd(), "..");
|
|
22591
23157
|
var brandHostname = "maxy";
|
|
22592
23158
|
var brandJsonPath = join40(PLATFORM_ROOT6, "config", "brand.json");
|
|
22593
23159
|
if (existsSync36(brandJsonPath)) {
|
|
@@ -22597,7 +23163,7 @@ if (existsSync36(brandJsonPath)) {
|
|
|
22597
23163
|
} catch {
|
|
22598
23164
|
}
|
|
22599
23165
|
}
|
|
22600
|
-
var VERSION_FILE =
|
|
23166
|
+
var VERSION_FILE = resolve32(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
|
|
22601
23167
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
22602
23168
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
22603
23169
|
function readVersion() {
|
|
@@ -24053,7 +24619,7 @@ var routines_default = app48;
|
|
|
24053
24619
|
|
|
24054
24620
|
// server/routes/admin/skills.ts
|
|
24055
24621
|
import { existsSync as existsSync38, readdirSync as readdirSync24, readFileSync as readFileSync39, rmSync as rmSync3 } from "fs";
|
|
24056
|
-
import { resolve as
|
|
24622
|
+
import { resolve as resolve33 } from "path";
|
|
24057
24623
|
var app49 = new Hono();
|
|
24058
24624
|
var NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
24059
24625
|
function splitSkillMd(text) {
|
|
@@ -24075,13 +24641,7 @@ function stripQuotes(s) {
|
|
|
24075
24641
|
if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) return s.slice(1, -1);
|
|
24076
24642
|
return s;
|
|
24077
24643
|
}
|
|
24078
|
-
function
|
|
24079
|
-
const accountId = getAccountIdForSession(cacheKey);
|
|
24080
|
-
if (!accountId) return null;
|
|
24081
|
-
const match = listValidAccounts().find((a) => a.accountId === accountId);
|
|
24082
|
-
return match ? { accountId, accountDir: match.accountDir } : null;
|
|
24083
|
-
}
|
|
24084
|
-
function subdirs(dir) {
|
|
24644
|
+
function subdirs2(dir) {
|
|
24085
24645
|
if (!existsSync38(dir)) return [];
|
|
24086
24646
|
try {
|
|
24087
24647
|
return readdirSync24(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -24095,28 +24655,32 @@ app49.get("/", requireAdminSession, async (c) => {
|
|
|
24095
24655
|
console.error('[admin:skills] op=list auth-rejected reason="no account for session"');
|
|
24096
24656
|
return c.json({ error: "Account not found for session" }, 401);
|
|
24097
24657
|
}
|
|
24098
|
-
|
|
24099
|
-
const
|
|
24100
|
-
|
|
24101
|
-
|
|
24102
|
-
pluginCount
|
|
24103
|
-
const
|
|
24104
|
-
|
|
24105
|
-
const
|
|
24106
|
-
|
|
24107
|
-
|
|
24108
|
-
|
|
24109
|
-
|
|
24110
|
-
|
|
24111
|
-
|
|
24112
|
-
|
|
24113
|
-
|
|
24658
|
+
ensureAccountWatchers(acct.accountId, acct.accountDir);
|
|
24659
|
+
const { value, state } = await getOrCompute("skills", acct.accountId, async () => {
|
|
24660
|
+
const pluginsDir = resolve33(acct.accountDir, "plugins");
|
|
24661
|
+
const skills = [];
|
|
24662
|
+
let pluginCount = 0;
|
|
24663
|
+
for (const plugin of subdirs2(pluginsDir)) {
|
|
24664
|
+
pluginCount++;
|
|
24665
|
+
const skillsDir = resolve33(pluginsDir, plugin, "skills");
|
|
24666
|
+
for (const skill of subdirs2(skillsDir)) {
|
|
24667
|
+
const skillMd = resolve33(skillsDir, skill, "SKILL.md");
|
|
24668
|
+
if (!existsSync38(skillMd)) continue;
|
|
24669
|
+
try {
|
|
24670
|
+
const { frontmatter } = splitSkillMd(readFileSync39(skillMd, "utf-8"));
|
|
24671
|
+
const refsDir = resolve33(skillsDir, skill, "references");
|
|
24672
|
+
const refCount = existsSync38(refsDir) ? readdirSync24(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).length : 0;
|
|
24673
|
+
skills.push({ plugin, skill, description: frontmatter.description, refCount });
|
|
24674
|
+
} catch (err) {
|
|
24675
|
+
console.error(`[admin:skills] op=list-skip plugin=${plugin} skill=${skill} reason="${err instanceof Error ? err.message : String(err)}"`);
|
|
24676
|
+
}
|
|
24114
24677
|
}
|
|
24115
24678
|
}
|
|
24116
|
-
|
|
24117
|
-
|
|
24118
|
-
|
|
24119
|
-
|
|
24679
|
+
skills.sort((a, b) => a.plugin.localeCompare(b.plugin) || a.skill.localeCompare(b.skill));
|
|
24680
|
+
return { skills, pluginCount };
|
|
24681
|
+
});
|
|
24682
|
+
console.log(`[admin:skills] op=list accountId=${acct.accountId.slice(0, 8)} plugins=${value.pluginCount} skills=${value.skills.length} cache=${state}`);
|
|
24683
|
+
return c.json({ skills: value.skills });
|
|
24120
24684
|
});
|
|
24121
24685
|
app49.get("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
24122
24686
|
const acct = accountDirForSession(c.var.cacheKey);
|
|
@@ -24127,14 +24691,14 @@ app49.get("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
24127
24691
|
console.error(`[admin:skills] op=get outcome=rejected reason=invalid-name plugin="${plugin}" skill="${skill}"`);
|
|
24128
24692
|
return c.json({ error: "Invalid plugin or skill name" }, 400);
|
|
24129
24693
|
}
|
|
24130
|
-
const skillDir =
|
|
24131
|
-
const skillMd =
|
|
24694
|
+
const skillDir = resolve33(acct.accountDir, "plugins", plugin, "skills", skill);
|
|
24695
|
+
const skillMd = resolve33(skillDir, "SKILL.md");
|
|
24132
24696
|
const found = existsSync38(skillMd);
|
|
24133
24697
|
console.log(`[admin:skills] op=get plugin=${plugin} skill=${skill} found=${found}`);
|
|
24134
24698
|
if (!found) return c.json({ error: "Skill not found" }, 404);
|
|
24135
24699
|
try {
|
|
24136
24700
|
const { frontmatter, body } = splitSkillMd(readFileSync39(skillMd, "utf-8"));
|
|
24137
|
-
const refsDir =
|
|
24701
|
+
const refsDir = resolve33(skillDir, "references");
|
|
24138
24702
|
const references = existsSync38(refsDir) ? readdirSync24(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).sort() : [];
|
|
24139
24703
|
return c.json({ skill: { plugin, skill, frontmatter, body, references } });
|
|
24140
24704
|
} catch (err) {
|
|
@@ -24152,16 +24716,16 @@ app49.delete("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
24152
24716
|
console.error(`[admin:skills] op=delete outcome=rejected reason=invalid-name plugin="${plugin}" skill="${skill}"`);
|
|
24153
24717
|
return c.json({ error: "Invalid plugin or skill name" }, 400);
|
|
24154
24718
|
}
|
|
24155
|
-
const pluginDir =
|
|
24156
|
-
const skillsDir =
|
|
24157
|
-
const skillDir =
|
|
24158
|
-
if (!existsSync38(
|
|
24719
|
+
const pluginDir = resolve33(acct.accountDir, "plugins", plugin);
|
|
24720
|
+
const skillsDir = resolve33(pluginDir, "skills");
|
|
24721
|
+
const skillDir = resolve33(skillsDir, skill);
|
|
24722
|
+
if (!existsSync38(resolve33(skillDir, "SKILL.md"))) {
|
|
24159
24723
|
console.error(`[admin:skills] op=delete outcome=rejected reason=not-found plugin=${plugin} skill=${skill}`);
|
|
24160
24724
|
return c.json({ error: "Skill not found" }, 404);
|
|
24161
24725
|
}
|
|
24162
24726
|
try {
|
|
24163
24727
|
rmSync3(skillDir, { recursive: true, force: true });
|
|
24164
|
-
const pluginDirRemoved =
|
|
24728
|
+
const pluginDirRemoved = subdirs2(skillsDir).length === 0;
|
|
24165
24729
|
if (pluginDirRemoved) rmSync3(pluginDir, { recursive: true, force: true });
|
|
24166
24730
|
const skillDirGone = !existsSync38(skillDir);
|
|
24167
24731
|
if (!skillDirGone) {
|
|
@@ -24207,6 +24771,15 @@ app50.get("/", requireAdminSession, async (c) => {
|
|
|
24207
24771
|
console.error(`${TAG39} op=list auth-rejected reason="no account for session"`);
|
|
24208
24772
|
return c.json({ error: "Account not found for session" }, 401);
|
|
24209
24773
|
}
|
|
24774
|
+
const cacheStart = Date.now();
|
|
24775
|
+
const cached3 = await getOrCompute("tasks-list", accountId, () => computeTaskList(accountId));
|
|
24776
|
+
console.log(`${TAG39} op=list-cache cache=${cached3.state} account=${accountId} ms=${Date.now() - cacheStart}`);
|
|
24777
|
+
if (cached3.value === null) {
|
|
24778
|
+
return c.json({ error: "tasks-list-failed" }, 500);
|
|
24779
|
+
}
|
|
24780
|
+
return c.json(cached3.value);
|
|
24781
|
+
});
|
|
24782
|
+
async function computeTaskList(accountId) {
|
|
24210
24783
|
const session = getSession();
|
|
24211
24784
|
try {
|
|
24212
24785
|
const result = await session.run(
|
|
@@ -24289,14 +24862,14 @@ app50.get("/", requireAdminSession, async (c) => {
|
|
|
24289
24862
|
console.log(
|
|
24290
24863
|
`${TAG39} op=list account=${accountId} returned=${returned} graphTotal=${graphTotal} open=${open.length} completed=${completed.length} missingTaskId=${missingTaskId} missingCreatedAt=${missingCreatedAt} assigned=${assigned}`
|
|
24291
24864
|
);
|
|
24292
|
-
return
|
|
24865
|
+
return { open, completed };
|
|
24293
24866
|
} catch (err) {
|
|
24294
24867
|
console.error(`${TAG39} op=list error="${err instanceof Error ? err.message : String(err)}"`);
|
|
24295
|
-
return
|
|
24868
|
+
return null;
|
|
24296
24869
|
} finally {
|
|
24297
24870
|
await session.close();
|
|
24298
24871
|
}
|
|
24299
|
-
}
|
|
24872
|
+
}
|
|
24300
24873
|
var tasks_list_default = app50;
|
|
24301
24874
|
|
|
24302
24875
|
// server/routes/admin/task-timer-start.ts
|
|
@@ -24340,6 +24913,7 @@ app51.post("/", requireAdminSession, async (c) => {
|
|
|
24340
24913
|
const priorStartedAt = open.records[0]?.get("startedAt");
|
|
24341
24914
|
if (priorEntryId) {
|
|
24342
24915
|
console.log(`${TAG40} op=start taskId=${taskId} accountId=${accountId} result=already-running`);
|
|
24916
|
+
invalidate("tasks-list", accountId);
|
|
24343
24917
|
return c.json({ ok: true, running: true, entryId: priorEntryId, startedAt: priorStartedAt });
|
|
24344
24918
|
}
|
|
24345
24919
|
const entryId = randomUUID14();
|
|
@@ -24350,6 +24924,7 @@ app51.post("/", requireAdminSession, async (c) => {
|
|
|
24350
24924
|
{ taskId, accountId, entryId, now }
|
|
24351
24925
|
);
|
|
24352
24926
|
console.log(`${TAG40} op=opened taskId=${taskId} entryId=${entryId} startedAt=${now}`);
|
|
24927
|
+
invalidate("tasks-list", accountId);
|
|
24353
24928
|
return c.json({ ok: true, running: true, entryId, startedAt: now });
|
|
24354
24929
|
} catch (err) {
|
|
24355
24930
|
console.error(`${TAG40} op=start error="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -24399,6 +24974,7 @@ app52.post("/", requireAdminSession, async (c) => {
|
|
|
24399
24974
|
);
|
|
24400
24975
|
const secondsLogged2 = toNum(cur.records[0]?.get("secondsLogged"));
|
|
24401
24976
|
console.log(`${TAG41} op=stop taskId=${taskId} entryId=none seconds=0 totalAfter=${secondsLogged2}`);
|
|
24977
|
+
invalidate("tasks-list", accountId);
|
|
24402
24978
|
return c.json({ ok: true, running: false, seconds: 0, secondsLogged: secondsLogged2 });
|
|
24403
24979
|
}
|
|
24404
24980
|
const seconds = secondsBetween(startedAt, now);
|
|
@@ -24423,6 +24999,7 @@ app52.post("/", requireAdminSession, async (c) => {
|
|
|
24423
24999
|
const secondsLogged = toNum(verify.records[0]?.get("secondsLogged"));
|
|
24424
25000
|
console.log(`${TAG41} op=stop taskId=${taskId} entryId=${entryId} seconds=${seconds} totalAfter=${secondsLogged}`);
|
|
24425
25001
|
console.log(`${TAG41} op=persisted taskId=${taskId} entryId=${entryId} sumEntries=${sumEntries} sumAdjustments=${sumAdjustments} secondsLogged=${secondsLogged}`);
|
|
25002
|
+
invalidate("tasks-list", accountId);
|
|
24426
25003
|
return c.json({ ok: true, running: false, seconds, secondsLogged });
|
|
24427
25004
|
} catch (err) {
|
|
24428
25005
|
console.error(`${TAG41} op=stop error="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -24466,6 +25043,7 @@ app53.post("/", requireAdminSession, async (c) => {
|
|
|
24466
25043
|
}
|
|
24467
25044
|
const secondsLogged = toNum(result.records[0].get("secondsLogged"));
|
|
24468
25045
|
console.log(`${TAG42} op=complete taskId=${taskId} status=completed secondsLogged=${secondsLogged}`);
|
|
25046
|
+
invalidate("tasks-list", accountId);
|
|
24469
25047
|
return c.json({ ok: true, status: "completed", secondsLogged });
|
|
24470
25048
|
} catch (err) {
|
|
24471
25049
|
console.error(`${TAG42} op=complete error="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -24547,6 +25125,7 @@ app54.post("/", requireAdminSession, async (c) => {
|
|
|
24547
25125
|
);
|
|
24548
25126
|
const secondsLoggedAfter = toNum(write.records[0]?.get("secondsLoggedAfter"));
|
|
24549
25127
|
console.log(`${TAG43} op=persisted adjustmentId=${adjustmentId} previousSeconds=${previousSeconds} newSeconds=${newSeconds} delta=${delta} secondsLoggedAfter=${secondsLoggedAfter}`);
|
|
25128
|
+
invalidate("tasks-list", accountId);
|
|
24550
25129
|
return c.json({ ok: true, secondsLogged: secondsLoggedAfter });
|
|
24551
25130
|
} catch (err) {
|
|
24552
25131
|
console.error(`${TAG43} op=error adjustmentId=${adjustmentId} taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -24559,16 +25138,16 @@ var task_time_adjust_default = app54;
|
|
|
24559
25138
|
|
|
24560
25139
|
// app/lib/admin-roster.ts
|
|
24561
25140
|
import { existsSync as existsSync39, readdirSync as readdirSync25, readFileSync as readFileSync40 } from "fs";
|
|
24562
|
-
import { resolve as
|
|
25141
|
+
import { resolve as resolve34 } from "path";
|
|
24563
25142
|
function readHouseAdminUserIds(accountsDir = ACCOUNTS_DIR) {
|
|
24564
|
-
let
|
|
25143
|
+
let entries2;
|
|
24565
25144
|
try {
|
|
24566
|
-
|
|
25145
|
+
entries2 = readdirSync25(accountsDir);
|
|
24567
25146
|
} catch {
|
|
24568
25147
|
return [];
|
|
24569
25148
|
}
|
|
24570
|
-
for (const entry of
|
|
24571
|
-
const cfgPath =
|
|
25149
|
+
for (const entry of entries2) {
|
|
25150
|
+
const cfgPath = resolve34(accountsDir, entry, "account.json");
|
|
24572
25151
|
if (!existsSync39(cfgPath)) continue;
|
|
24573
25152
|
try {
|
|
24574
25153
|
const cfg = JSON.parse(readFileSync40(cfgPath, "utf-8"));
|
|
@@ -24692,6 +25271,7 @@ app55.post("/", requireAdminSession, async (c) => {
|
|
|
24692
25271
|
{ taskId, accountId, now }
|
|
24693
25272
|
);
|
|
24694
25273
|
console.log(`${TAG44} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=none assigned=false source=route`);
|
|
25274
|
+
invalidate("tasks-list", accountId);
|
|
24695
25275
|
return c.json({ ok: true, assigneeUserId: null, assigneeName: null });
|
|
24696
25276
|
}
|
|
24697
25277
|
const set = await session.run(
|
|
@@ -24699,6 +25279,7 @@ app55.post("/", requireAdminSession, async (c) => {
|
|
|
24699
25279
|
MATCH (au:AdminUser {userId: $assigneeUserId})
|
|
24700
25280
|
MERGE (t)-[:ASSIGNED_TO]->(au)
|
|
24701
25281
|
SET t.updatedAt = $now
|
|
25282
|
+
WITH t, au
|
|
24702
25283
|
OPTIONAL MATCH (au)-[:OWNS]->(p:Person) WHERE p.accountId = au.accountId
|
|
24703
25284
|
RETURN au.userId AS userId, p.givenName AS givenName, p.familyName AS familyName`,
|
|
24704
25285
|
{ taskId, accountId, assigneeUserId, now }
|
|
@@ -24715,6 +25296,7 @@ app55.post("/", requireAdminSession, async (c) => {
|
|
|
24715
25296
|
console.log(
|
|
24716
25297
|
`${TAG44} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=true source=route`
|
|
24717
25298
|
);
|
|
25299
|
+
invalidate("tasks-list", accountId);
|
|
24718
25300
|
return c.json({ ok: true, assigneeUserId, assigneeName });
|
|
24719
25301
|
} catch (err) {
|
|
24720
25302
|
console.error(`${TAG44} op=assign error="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -25023,9 +25605,9 @@ var verify_token_default = app59;
|
|
|
25023
25605
|
|
|
25024
25606
|
// app/lib/access-email.ts
|
|
25025
25607
|
import { spawn as spawn2 } from "child_process";
|
|
25026
|
-
import { resolve as
|
|
25027
|
-
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ??
|
|
25028
|
-
var SEND_SCRIPT =
|
|
25608
|
+
import { resolve as resolve35 } from "path";
|
|
25609
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve35(process.cwd(), "..");
|
|
25610
|
+
var SEND_SCRIPT = resolve35(
|
|
25029
25611
|
PLATFORM_ROOT7,
|
|
25030
25612
|
"plugins",
|
|
25031
25613
|
"email",
|
|
@@ -25168,7 +25750,7 @@ var access_default = app61;
|
|
|
25168
25750
|
|
|
25169
25751
|
// server/routes/sites.ts
|
|
25170
25752
|
import { existsSync as existsSync40, readFileSync as readFileSync41, realpathSync as realpathSync8, statSync as statSync17 } from "fs";
|
|
25171
|
-
import { resolve as
|
|
25753
|
+
import { resolve as resolve36 } from "path";
|
|
25172
25754
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
25173
25755
|
var MIME = {
|
|
25174
25756
|
".html": "text/html; charset=utf-8",
|
|
@@ -25225,8 +25807,8 @@ app62.get("/:rel{.*}", (c) => {
|
|
|
25225
25807
|
}
|
|
25226
25808
|
segments.push(seg);
|
|
25227
25809
|
}
|
|
25228
|
-
const rootDir =
|
|
25229
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
25810
|
+
const rootDir = resolve36(account.accountDir, "sites");
|
|
25811
|
+
let filePath = segments.length === 0 ? rootDir : resolve36(rootDir, ...segments);
|
|
25230
25812
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
25231
25813
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
25232
25814
|
return c.text("Forbidden", 403);
|
|
@@ -25246,7 +25828,7 @@ app62.get("/:rel{.*}", (c) => {
|
|
|
25246
25828
|
return c.redirect(target, 301);
|
|
25247
25829
|
}
|
|
25248
25830
|
if (stat11?.isDirectory()) {
|
|
25249
|
-
filePath =
|
|
25831
|
+
filePath = resolve36(filePath, "index.html");
|
|
25250
25832
|
}
|
|
25251
25833
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
25252
25834
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
@@ -25892,14 +26474,14 @@ async function writeEvent(opts) {
|
|
|
25892
26474
|
var visitor_event_default = app65;
|
|
25893
26475
|
|
|
25894
26476
|
// server/routes/session.ts
|
|
25895
|
-
import { resolve as
|
|
26477
|
+
import { resolve as resolve37 } from "path";
|
|
25896
26478
|
import { existsSync as existsSync42, writeFileSync as writeFileSync15, mkdirSync as mkdirSync9 } from "fs";
|
|
25897
26479
|
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
25898
26480
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
25899
26481
|
try {
|
|
25900
|
-
const cacheDir =
|
|
26482
|
+
const cacheDir = resolve37(MAXY_DIR, "branding-cache", accountId);
|
|
25901
26483
|
mkdirSync9(cacheDir, { recursive: true });
|
|
25902
|
-
writeFileSync15(
|
|
26484
|
+
writeFileSync15(resolve37(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
25903
26485
|
} catch (err) {
|
|
25904
26486
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25905
26487
|
}
|
|
@@ -25976,8 +26558,8 @@ app66.post("/", async (c) => {
|
|
|
25976
26558
|
}
|
|
25977
26559
|
let agentConfig = null;
|
|
25978
26560
|
if (account) {
|
|
25979
|
-
const agentDir =
|
|
25980
|
-
if (!existsSync42(agentDir) || !existsSync42(
|
|
26561
|
+
const agentDir = resolve37(account.accountDir, "agents", agentSlug);
|
|
26562
|
+
if (!existsSync42(agentDir) || !existsSync42(resolve37(agentDir, "config.json"))) {
|
|
25981
26563
|
return c.json({ error: "Agent not found" }, 404);
|
|
25982
26564
|
}
|
|
25983
26565
|
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
@@ -26237,7 +26819,7 @@ var calendar_public_default = app67;
|
|
|
26237
26819
|
// server/routes/portal-fetch.ts
|
|
26238
26820
|
import { createReadStream as createReadStream3 } from "fs";
|
|
26239
26821
|
import { stat as stat8, readFile as readFile9, realpath } from "fs/promises";
|
|
26240
|
-
import { join as join44, resolve as
|
|
26822
|
+
import { join as join44, resolve as resolve38, sep as sep10 } from "path";
|
|
26241
26823
|
import { Readable as Readable3 } from "stream";
|
|
26242
26824
|
var app68 = new Hono();
|
|
26243
26825
|
var TAG49 = "[portal-fetch]";
|
|
@@ -26290,7 +26872,7 @@ ${expiresAtMs}`)
|
|
|
26290
26872
|
return diff === 0;
|
|
26291
26873
|
}
|
|
26292
26874
|
async function readAccountSecret(accountId) {
|
|
26293
|
-
const path3 =
|
|
26875
|
+
const path3 = resolve38(DATA_ROOT, "accounts", accountId, "secrets", "data-portal.env");
|
|
26294
26876
|
let text;
|
|
26295
26877
|
try {
|
|
26296
26878
|
text = await readFile9(path3, "utf8");
|
|
@@ -26326,7 +26908,7 @@ async function resolveFetchTarget(accountDir, relPath, ownerId) {
|
|
|
26326
26908
|
const exposeFolders = await readExposeFolders2(accountDir);
|
|
26327
26909
|
const { exposed } = resolveExposedDirs(schemaText, exposeFolders);
|
|
26328
26910
|
if (exposed.length === 0 && !ownerId) return { ok: false, reason: "not-exposed" };
|
|
26329
|
-
const lexical =
|
|
26911
|
+
const lexical = resolve38(accountDir, relPath);
|
|
26330
26912
|
let root;
|
|
26331
26913
|
let absolute;
|
|
26332
26914
|
try {
|
|
@@ -26375,7 +26957,7 @@ async function handle(c, headOnly) {
|
|
|
26375
26957
|
return c.json({ ok: false, error: "denied" }, 401);
|
|
26376
26958
|
}
|
|
26377
26959
|
const target = await resolveFetchTarget(
|
|
26378
|
-
|
|
26960
|
+
resolve38(DATA_ROOT, "accounts", accountId),
|
|
26379
26961
|
relPath,
|
|
26380
26962
|
owner || void 0
|
|
26381
26963
|
);
|
|
@@ -26536,14 +27118,14 @@ function formatCensusLine(f) {
|
|
|
26536
27118
|
var TAG51 = "[ledger-census]";
|
|
26537
27119
|
var DAY_MS = 864e5;
|
|
26538
27120
|
function countWriteRejects24h(logDir, now) {
|
|
26539
|
-
let
|
|
27121
|
+
let entries2;
|
|
26540
27122
|
try {
|
|
26541
|
-
|
|
27123
|
+
entries2 = readdirSync27(logDir).filter((f) => f.startsWith("mcp-ledger-") && f.endsWith(".log"));
|
|
26542
27124
|
} catch {
|
|
26543
27125
|
return 0;
|
|
26544
27126
|
}
|
|
26545
27127
|
let total = 0;
|
|
26546
|
-
for (const name of
|
|
27128
|
+
for (const name of entries2) {
|
|
26547
27129
|
const path3 = join45(logDir, name);
|
|
26548
27130
|
try {
|
|
26549
27131
|
if (now - statSync18(path3).mtimeMs > DAY_MS) continue;
|
|
@@ -26698,7 +27280,7 @@ async function runConversationAudit() {
|
|
|
26698
27280
|
}
|
|
26699
27281
|
|
|
26700
27282
|
// app/lib/graph-health.ts
|
|
26701
|
-
var
|
|
27283
|
+
var import_dist7 = __toESM(require_dist5(), 1);
|
|
26702
27284
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
26703
27285
|
function renderLabelTop(rows) {
|
|
26704
27286
|
return rows.map((b) => {
|
|
@@ -26743,7 +27325,7 @@ async function runGraphHealthTick() {
|
|
|
26743
27325
|
console.error(
|
|
26744
27326
|
`[graph-health] userprofile-multi accounts=${upAccounts} top=${upTop.length > 0 ? upTop.join(",") : "none"}`
|
|
26745
27327
|
);
|
|
26746
|
-
const indexed = [...
|
|
27328
|
+
const indexed = [...import_dist7.VECTOR_INDEXED_LABELS];
|
|
26747
27329
|
const embCount = await session.run(
|
|
26748
27330
|
`MATCH (n) WHERE n.embedding IS NULL AND any(l IN labels(n) WHERE l IN $indexed)
|
|
26749
27331
|
RETURN count(n) AS total`,
|
|
@@ -26816,7 +27398,7 @@ function startGraphHealthTimer() {
|
|
|
26816
27398
|
|
|
26817
27399
|
// app/lib/file-watcher.ts
|
|
26818
27400
|
import * as fsp2 from "fs/promises";
|
|
26819
|
-
import { resolve as
|
|
27401
|
+
import { resolve as resolve39, sep as sep11 } from "path";
|
|
26820
27402
|
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;
|
|
26821
27403
|
var DEFAULT_COALESCE_MS = 500;
|
|
26822
27404
|
var ROOTS = ["accounts"];
|
|
@@ -26835,7 +27417,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
26835
27417
|
const dropFn = opts.drop ?? dropFileIndex;
|
|
26836
27418
|
for (const r of ROOTS) {
|
|
26837
27419
|
try {
|
|
26838
|
-
await fsp2.mkdir(
|
|
27420
|
+
await fsp2.mkdir(resolve39(dataRoot, r), { recursive: true });
|
|
26839
27421
|
} catch (err) {
|
|
26840
27422
|
console.error(
|
|
26841
27423
|
`[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
|
|
@@ -26856,7 +27438,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
26856
27438
|
timers.set(relativePath, t);
|
|
26857
27439
|
}
|
|
26858
27440
|
async function runHook(relativePath, accountId) {
|
|
26859
|
-
const absolute =
|
|
27441
|
+
const absolute = resolve39(dataRoot, relativePath);
|
|
26860
27442
|
let exists = false;
|
|
26861
27443
|
try {
|
|
26862
27444
|
const st = await fsp2.stat(absolute);
|
|
@@ -26880,7 +27462,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
26880
27462
|
}
|
|
26881
27463
|
}
|
|
26882
27464
|
async function watchRoot(rootName) {
|
|
26883
|
-
const absRoot =
|
|
27465
|
+
const absRoot = resolve39(dataRoot, rootName);
|
|
26884
27466
|
try {
|
|
26885
27467
|
const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
|
|
26886
27468
|
for await (const event of iter) {
|
|
@@ -26919,18 +27501,18 @@ async function startFileWatcher(opts = {}) {
|
|
|
26919
27501
|
|
|
26920
27502
|
// app/lib/migrate-uploads.ts
|
|
26921
27503
|
import { mkdir as mkdir6, readdir as readdir6, rename as rename3, rm as rm3 } from "fs/promises";
|
|
26922
|
-
import { dirname as dirname15, relative as relative7, resolve as
|
|
27504
|
+
import { dirname as dirname15, relative as relative7, resolve as resolve40 } from "path";
|
|
26923
27505
|
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;
|
|
26924
27506
|
async function walkFiles(dir) {
|
|
26925
27507
|
const out = [];
|
|
26926
|
-
let
|
|
27508
|
+
let entries2;
|
|
26927
27509
|
try {
|
|
26928
|
-
|
|
27510
|
+
entries2 = await readdir6(dir, { withFileTypes: true });
|
|
26929
27511
|
} catch {
|
|
26930
27512
|
return out;
|
|
26931
27513
|
}
|
|
26932
|
-
for (const e of
|
|
26933
|
-
const abs =
|
|
27514
|
+
for (const e of entries2) {
|
|
27515
|
+
const abs = resolve40(dir, e.name);
|
|
26934
27516
|
if (e.isDirectory()) {
|
|
26935
27517
|
out.push(...await walkFiles(abs));
|
|
26936
27518
|
} else if (e.isFile()) {
|
|
@@ -26950,7 +27532,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
26950
27532
|
let moved = 0;
|
|
26951
27533
|
for (const oldAbs of files) {
|
|
26952
27534
|
const suffix = relative7(srcDir, oldAbs);
|
|
26953
|
-
const newAbs =
|
|
27535
|
+
const newAbs = resolve40(destDir, suffix);
|
|
26954
27536
|
await mkdir6(dirname15(newAbs), { recursive: true });
|
|
26955
27537
|
await rename3(oldAbs, newAbs);
|
|
26956
27538
|
moved++;
|
|
@@ -26977,7 +27559,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
26977
27559
|
}
|
|
26978
27560
|
async function migrateUploads(opts = {}) {
|
|
26979
27561
|
const dataRoot = opts.dataRoot ?? DATA_ROOT;
|
|
26980
|
-
const oldRoot =
|
|
27562
|
+
const oldRoot = resolve40(dataRoot, "uploads");
|
|
26981
27563
|
let topEntries;
|
|
26982
27564
|
try {
|
|
26983
27565
|
topEntries = await readdir6(oldRoot, { withFileTypes: true });
|
|
@@ -26998,8 +27580,8 @@ async function migrateUploads(opts = {}) {
|
|
|
26998
27580
|
const name = entry.name;
|
|
26999
27581
|
if (ACCOUNT_UUID_RE5.test(name)) {
|
|
27000
27582
|
moved += await relocateTree(
|
|
27001
|
-
|
|
27002
|
-
|
|
27583
|
+
resolve40(oldRoot, name),
|
|
27584
|
+
resolve40(dataRoot, "accounts", name, "uploads"),
|
|
27003
27585
|
dataRoot,
|
|
27004
27586
|
name,
|
|
27005
27587
|
session
|
|
@@ -27011,8 +27593,8 @@ async function migrateUploads(opts = {}) {
|
|
|
27011
27593
|
continue;
|
|
27012
27594
|
}
|
|
27013
27595
|
moved += await relocateTree(
|
|
27014
|
-
|
|
27015
|
-
|
|
27596
|
+
resolve40(oldRoot, "public"),
|
|
27597
|
+
resolve40(dataRoot, "accounts", installAccountId, "uploads", "public"),
|
|
27016
27598
|
dataRoot,
|
|
27017
27599
|
null,
|
|
27018
27600
|
// public uploads carry no graph nodes
|
|
@@ -27089,13 +27671,13 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
27089
27671
|
for (const slug of slugs) {
|
|
27090
27672
|
if (!slug.isDirectory()) continue;
|
|
27091
27673
|
const slugDir = join46(projectsRoot, slug.name);
|
|
27092
|
-
let
|
|
27674
|
+
let entries2;
|
|
27093
27675
|
try {
|
|
27094
|
-
|
|
27676
|
+
entries2 = await readdir7(slugDir, { withFileTypes: true });
|
|
27095
27677
|
} catch {
|
|
27096
27678
|
continue;
|
|
27097
27679
|
}
|
|
27098
|
-
for (const entry of
|
|
27680
|
+
for (const entry of entries2) {
|
|
27099
27681
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
27100
27682
|
out.push(join46(slugDir, entry.name));
|
|
27101
27683
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
@@ -28154,16 +28736,16 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
28154
28736
|
* The public /api/chat route awaits this to return the reply on the same SSE
|
|
28155
28737
|
* response, preserving the pre-756 POST→single-blob browser contract. */
|
|
28156
28738
|
awaitReply(key, timeoutMs) {
|
|
28157
|
-
return new Promise((
|
|
28739
|
+
return new Promise((resolve46) => {
|
|
28158
28740
|
const timer = setTimeout(() => {
|
|
28159
28741
|
this.replyAwaiters.delete(key);
|
|
28160
|
-
|
|
28742
|
+
resolve46({ timeout: true });
|
|
28161
28743
|
}, timeoutMs);
|
|
28162
28744
|
if (timer.unref) timer.unref();
|
|
28163
28745
|
this.replyAwaiters.set(key, (r) => {
|
|
28164
28746
|
clearTimeout(timer);
|
|
28165
28747
|
this.replyAwaiters.delete(key);
|
|
28166
|
-
|
|
28748
|
+
resolve46(r);
|
|
28167
28749
|
});
|
|
28168
28750
|
});
|
|
28169
28751
|
}
|
|
@@ -28174,11 +28756,11 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
28174
28756
|
* terminal dialog stays answerable). */
|
|
28175
28757
|
awaitPermissionVerdict(p, timeoutMs) {
|
|
28176
28758
|
const k = _WebchatGateway.promptKey(p.key, p.requestId);
|
|
28177
|
-
return new Promise((
|
|
28759
|
+
return new Promise((resolve46) => {
|
|
28178
28760
|
this.pendingPrompts.get(k)?.resolve({ timeout: true });
|
|
28179
28761
|
const timer = setTimeout(() => {
|
|
28180
28762
|
this.pendingPrompts.delete(k);
|
|
28181
|
-
|
|
28763
|
+
resolve46({ timeout: true });
|
|
28182
28764
|
}, timeoutMs);
|
|
28183
28765
|
if (timer.unref) timer.unref();
|
|
28184
28766
|
this.pendingPrompts.set(k, {
|
|
@@ -28187,7 +28769,7 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
28187
28769
|
resolve: (r) => {
|
|
28188
28770
|
clearTimeout(timer);
|
|
28189
28771
|
this.pendingPrompts.delete(k);
|
|
28190
|
-
|
|
28772
|
+
resolve46(r);
|
|
28191
28773
|
}
|
|
28192
28774
|
});
|
|
28193
28775
|
console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
|
|
@@ -28752,7 +29334,7 @@ async function fanOut(subscribers, text, onError, tag) {
|
|
|
28752
29334
|
|
|
28753
29335
|
// app/lib/webchat/file-delivery.ts
|
|
28754
29336
|
import { realpathSync as realpathSync9 } from "fs";
|
|
28755
|
-
import { resolve as
|
|
29337
|
+
import { resolve as resolve41 } from "path";
|
|
28756
29338
|
|
|
28757
29339
|
// app/lib/channel-pty-bridge/file-delivery.ts
|
|
28758
29340
|
var SEND_USER_FILE = "SendUserFile";
|
|
@@ -28853,7 +29435,7 @@ function makeWebchatSendFile(entry) {
|
|
|
28853
29435
|
console.error(`${TAG53} file-delivery reject reason=no-account sender=${entry.senderId}`);
|
|
28854
29436
|
return { ok: false, error: "no-account" };
|
|
28855
29437
|
}
|
|
28856
|
-
const accountDir =
|
|
29438
|
+
const accountDir = resolve41(platformRoot2(), "..", "data/accounts", entry.accountId);
|
|
28857
29439
|
try {
|
|
28858
29440
|
const resolved = realpathSync9(filePath);
|
|
28859
29441
|
const accountResolved = realpathSync9(accountDir);
|
|
@@ -29417,7 +29999,7 @@ function buildTelegramSpawnRequest(input) {
|
|
|
29417
29999
|
// app/lib/telegram/outbound/send-document.ts
|
|
29418
30000
|
import { realpathSync as realpathSync10 } from "fs";
|
|
29419
30001
|
import { readFile as readFile11, stat as stat10 } from "fs/promises";
|
|
29420
|
-
import { resolve as
|
|
30002
|
+
import { resolve as resolve42, basename as basename13 } from "path";
|
|
29421
30003
|
var TAG55 = "[telegram:outbound]";
|
|
29422
30004
|
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
29423
30005
|
async function sendTelegramDocument(input) {
|
|
@@ -29428,7 +30010,7 @@ async function sendTelegramDocument(input) {
|
|
|
29428
30010
|
if (!maxyAccountId || !platformRoot5) {
|
|
29429
30011
|
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
29430
30012
|
}
|
|
29431
|
-
const accountDir =
|
|
30013
|
+
const accountDir = resolve42(platformRoot5, "..", "data/accounts", maxyAccountId);
|
|
29432
30014
|
let accountResolved;
|
|
29433
30015
|
try {
|
|
29434
30016
|
accountResolved = realpathSync10(accountDir);
|
|
@@ -29720,11 +30302,11 @@ async function firePublicSessionEndReview(input) {
|
|
|
29720
30302
|
}
|
|
29721
30303
|
|
|
29722
30304
|
// app/lib/whatsapp/inbound/resolve-client-graph-owner.ts
|
|
29723
|
-
var
|
|
30305
|
+
var import_dist8 = __toESM(require_dist(), 1);
|
|
29724
30306
|
async function resolveClientOwnerUserId(accountId, deps = {}) {
|
|
29725
30307
|
const listAccounts = deps.listAccounts ?? listValidAccounts;
|
|
29726
30308
|
const getSession4 = deps.getSession ?? getSession;
|
|
29727
|
-
const resolveOwner2 = deps.resolveOwner ??
|
|
30309
|
+
const resolveOwner2 = deps.resolveOwner ?? import_dist8.resolveOwnerUserId;
|
|
29728
30310
|
const role = listAccounts().find((a) => a.accountId === accountId)?.config.role;
|
|
29729
30311
|
if (role !== "client") return null;
|
|
29730
30312
|
const session = getSession4();
|
|
@@ -29821,10 +30403,10 @@ function warnOnChannelAdminBindingDrift() {
|
|
|
29821
30403
|
|
|
29822
30404
|
// server/lib/booking-site-accounts.ts
|
|
29823
30405
|
import { existsSync as existsSync44, readFileSync as readFileSync45 } from "fs";
|
|
29824
|
-
import { resolve as
|
|
30406
|
+
import { resolve as resolve43 } from "path";
|
|
29825
30407
|
function listBookingSiteAccounts(accounts) {
|
|
29826
30408
|
return accounts.filter((a) => {
|
|
29827
|
-
const availPath =
|
|
30409
|
+
const availPath = resolve43(a.accountDir, "calendar-availability.json");
|
|
29828
30410
|
if (!existsSync44(availPath)) return false;
|
|
29829
30411
|
try {
|
|
29830
30412
|
const cfg = JSON.parse(readFileSync45(availPath, "utf-8"));
|
|
@@ -29866,7 +30448,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
29866
30448
|
// ../lib/entitlement/src/index.ts
|
|
29867
30449
|
import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
|
|
29868
30450
|
import { existsSync as existsSync45, readFileSync as readFileSync46, statSync as statSync19 } from "fs";
|
|
29869
|
-
import { resolve as
|
|
30451
|
+
import { resolve as resolve44 } from "path";
|
|
29870
30452
|
|
|
29871
30453
|
// ../lib/entitlement/src/canonicalize.ts
|
|
29872
30454
|
function canonicalize(value) {
|
|
@@ -29901,7 +30483,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
29901
30483
|
var GRACE_DAYS = 7;
|
|
29902
30484
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
29903
30485
|
function pubkeyPath(brand) {
|
|
29904
|
-
return
|
|
30486
|
+
return resolve44(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
29905
30487
|
}
|
|
29906
30488
|
var memo = null;
|
|
29907
30489
|
function memoKey(mtimeMs, account) {
|
|
@@ -29913,7 +30495,7 @@ function resolveEntitlement(brand, account) {
|
|
|
29913
30495
|
if (brand.commercialMode !== true) {
|
|
29914
30496
|
return logResolved(implicitTrust(account), null);
|
|
29915
30497
|
}
|
|
29916
|
-
const entitlementPath =
|
|
30498
|
+
const entitlementPath = resolve44(brand.configDir, "entitlement.json");
|
|
29917
30499
|
if (!existsSync45(entitlementPath)) {
|
|
29918
30500
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
29919
30501
|
}
|
|
@@ -30169,6 +30751,7 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
|
|
|
30169
30751
|
aliasDomains = updated;
|
|
30170
30752
|
console.log(`[alias-domains] reloaded ${updated.size} alias domain(s): ${[...updated].join(", ")}`);
|
|
30171
30753
|
});
|
|
30754
|
+
attachRouteCacheWatchers();
|
|
30172
30755
|
function isPublicHost(host) {
|
|
30173
30756
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
30174
30757
|
}
|
|
@@ -30186,8 +30769,8 @@ var webchatFileFollowers = /* @__PURE__ */ new Map();
|
|
|
30186
30769
|
async function fetchAccountStandingRules(accountId) {
|
|
30187
30770
|
const session = getSession();
|
|
30188
30771
|
try {
|
|
30189
|
-
const res = await (0,
|
|
30190
|
-
return { block: (0,
|
|
30772
|
+
const res = await (0, import_dist9.resolveActiveRules)(session, accountId);
|
|
30773
|
+
return { block: (0, import_dist9.formatStandingRulesBlock)(res.rules), ownerUserId: res.ownerUserId, source: res.source };
|
|
30191
30774
|
} catch (err) {
|
|
30192
30775
|
console.error(
|
|
30193
30776
|
`[preference-inject] op=fetch-failed accountId=${accountId} error=${err instanceof Error ? err.message : String(err)}`
|
|
@@ -30221,7 +30804,7 @@ var waGateway = new WaGateway({
|
|
|
30221
30804
|
fetchStandingRules: fetchAccountStandingRules,
|
|
30222
30805
|
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
30223
30806
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
30224
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ??
|
|
30807
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve45(process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
30225
30808
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
30226
30809
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
30227
30810
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -30236,7 +30819,7 @@ var waGateway = new WaGateway({
|
|
|
30236
30819
|
caption,
|
|
30237
30820
|
accountId,
|
|
30238
30821
|
maxyAccountId,
|
|
30239
|
-
platformRoot:
|
|
30822
|
+
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, ".."))
|
|
30240
30823
|
});
|
|
30241
30824
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
30242
30825
|
},
|
|
@@ -30916,8 +31499,8 @@ app69.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
30916
31499
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
30917
31500
|
return c.text("Not found", 404);
|
|
30918
31501
|
}
|
|
30919
|
-
const filePath =
|
|
30920
|
-
const expectedDir =
|
|
31502
|
+
const filePath = resolve45(account.accountDir, "agents", slug, "assets", filename);
|
|
31503
|
+
const expectedDir = resolve45(account.accountDir, "agents", slug, "assets");
|
|
30921
31504
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
30922
31505
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
30923
31506
|
return c.text("Forbidden", 403);
|
|
@@ -30946,8 +31529,8 @@ app69.get("/generated/:filename", (c) => {
|
|
|
30946
31529
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
30947
31530
|
return c.text("Not found", 404);
|
|
30948
31531
|
}
|
|
30949
|
-
const filePath =
|
|
30950
|
-
const expectedDir =
|
|
31532
|
+
const filePath = resolve45(account.accountDir, "generated", filename);
|
|
31533
|
+
const expectedDir = resolve45(account.accountDir, "generated");
|
|
30951
31534
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
30952
31535
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
30953
31536
|
return c.text("Forbidden", 403);
|
|
@@ -31001,11 +31584,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
31001
31584
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
31002
31585
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
31003
31586
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
31004
|
-
(p) => existsSync46(
|
|
31587
|
+
(p) => existsSync46(resolve45(process.cwd(), "public", p.replace(/^\//, "")))
|
|
31005
31588
|
);
|
|
31006
31589
|
var SW_SOURCE = (() => {
|
|
31007
31590
|
try {
|
|
31008
|
-
return readFileSync47(
|
|
31591
|
+
return readFileSync47(resolve45(process.cwd(), "public", "sw.js"), "utf-8");
|
|
31009
31592
|
} catch {
|
|
31010
31593
|
return null;
|
|
31011
31594
|
}
|
|
@@ -31056,7 +31639,7 @@ var clientErrorReporterScript = `<script>
|
|
|
31056
31639
|
function cachedHtml(file) {
|
|
31057
31640
|
let html = htmlCache.get(file);
|
|
31058
31641
|
if (!html) {
|
|
31059
|
-
html = readFileSync47(
|
|
31642
|
+
html = readFileSync47(resolve45(process.cwd(), "public", file), "utf-8");
|
|
31060
31643
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
31061
31644
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
31062
31645
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -31174,7 +31757,7 @@ app69.use("/vnc-popout.html", logViewerFetch);
|
|
|
31174
31757
|
app69.get("/vnc-popout.html", (c) => {
|
|
31175
31758
|
let html = htmlCache.get("vnc-popout.html");
|
|
31176
31759
|
if (!html) {
|
|
31177
|
-
html = readFileSync47(
|
|
31760
|
+
html = readFileSync47(resolve45(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
31178
31761
|
const name = escapeHtml(BRAND.productName);
|
|
31179
31762
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
31180
31763
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -31327,7 +31910,7 @@ var httpServer = serve({ fetch: app69.fetch, port, hostname });
|
|
|
31327
31910
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
31328
31911
|
{
|
|
31329
31912
|
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31330
|
-
const reconcileScript =
|
|
31913
|
+
const reconcileScript = resolve45(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
31331
31914
|
const runReconcile = (ctx) => {
|
|
31332
31915
|
if (!existsSync46(reconcileScript)) return;
|
|
31333
31916
|
try {
|
|
@@ -31368,7 +31951,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31368
31951
|
{
|
|
31369
31952
|
const DREAM_CYCLE_SWEEP_INTERVAL_MS = 36e5;
|
|
31370
31953
|
const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31371
|
-
const dreamSweepScript =
|
|
31954
|
+
const dreamSweepScript = resolve45(dreamPlatformRoot, "plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js");
|
|
31372
31955
|
registerLoop({
|
|
31373
31956
|
name: "dream-cycle-sweep",
|
|
31374
31957
|
intervalMs: DREAM_CYCLE_SWEEP_INTERVAL_MS,
|
|
@@ -31392,7 +31975,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31392
31975
|
}
|
|
31393
31976
|
{
|
|
31394
31977
|
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31395
|
-
const outlookScript =
|
|
31978
|
+
const outlookScript = resolve45(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
|
|
31396
31979
|
const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
|
|
31397
31980
|
const runOutlookComplete = (ctx) => {
|
|
31398
31981
|
if (!existsSync46(outlookScript)) return;
|
|
@@ -31418,7 +32001,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31418
32001
|
}
|
|
31419
32002
|
{
|
|
31420
32003
|
const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31421
|
-
const strandedAccountsDir =
|
|
32004
|
+
const strandedAccountsDir = resolve45(auditRoot, "..", "data/accounts");
|
|
31422
32005
|
const STRANDED_AUDIT_INTERVAL_MS = 3e5;
|
|
31423
32006
|
const STRANDED_AGE_MS = 16 * 6e4;
|
|
31424
32007
|
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;
|
|
@@ -31428,7 +32011,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31428
32011
|
const now = Date.now();
|
|
31429
32012
|
for (const name of readdirSync28(strandedAccountsDir)) {
|
|
31430
32013
|
if (!STRANDED_UUID_RE.test(name)) continue;
|
|
31431
|
-
const pendingPath2 =
|
|
32014
|
+
const pendingPath2 = resolve45(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
|
|
31432
32015
|
if (!existsSync46(pendingPath2)) continue;
|
|
31433
32016
|
const ageMs2 = now - statSync20(pendingPath2).mtimeMs;
|
|
31434
32017
|
if (ageMs2 > STRANDED_AGE_MS) {
|
|
@@ -31448,7 +32031,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31448
32031
|
}
|
|
31449
32032
|
{
|
|
31450
32033
|
const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31451
|
-
const googleAccountsDir =
|
|
32034
|
+
const googleAccountsDir = resolve45(googleRoot, "..", "data/accounts");
|
|
31452
32035
|
const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
|
|
31453
32036
|
const runGooglePendingAuditSafe = () => {
|
|
31454
32037
|
try {
|
|
@@ -31463,7 +32046,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31463
32046
|
firstRunDelayMs: 27e3,
|
|
31464
32047
|
run: runGooglePendingAuditSafe
|
|
31465
32048
|
});
|
|
31466
|
-
const googleAuditScript =
|
|
32049
|
+
const googleAuditScript = resolve45(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
|
|
31467
32050
|
const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
|
|
31468
32051
|
const runGoogleAccountAudit = (ctx) => {
|
|
31469
32052
|
if (!existsSync46(googleAuditScript)) return;
|
|
@@ -31538,7 +32121,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31538
32121
|
}
|
|
31539
32122
|
{
|
|
31540
32123
|
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31541
|
-
const publishScript =
|
|
32124
|
+
const publishScript = resolve45(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
31542
32125
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
31543
32126
|
const bookingAccounts = () => {
|
|
31544
32127
|
try {
|
|
@@ -31568,7 +32151,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31568
32151
|
}
|
|
31569
32152
|
};
|
|
31570
32153
|
const auditSnapshotAge = (account) => {
|
|
31571
|
-
const availPath =
|
|
32154
|
+
const availPath = resolve45(account.accountDir, "calendar-availability.json");
|
|
31572
32155
|
let hasBookingSite = false;
|
|
31573
32156
|
try {
|
|
31574
32157
|
if (existsSync46(availPath)) {
|
|
@@ -31578,7 +32161,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31578
32161
|
} catch {
|
|
31579
32162
|
}
|
|
31580
32163
|
if (!hasBookingSite) return;
|
|
31581
|
-
const statePath =
|
|
32164
|
+
const statePath = resolve45(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
31582
32165
|
let lastSuccessAt = null;
|
|
31583
32166
|
if (existsSync46(statePath)) {
|
|
31584
32167
|
try {
|
|
@@ -31615,7 +32198,7 @@ startTimeEntryCensus(() => getSession());
|
|
|
31615
32198
|
startLedgerCensus(() => getSession());
|
|
31616
32199
|
{
|
|
31617
32200
|
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31618
|
-
const auditScript =
|
|
32201
|
+
const auditScript = resolve45(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
31619
32202
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
31620
32203
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
31621
32204
|
const runConnectorAudit = (ctx) => {
|
|
@@ -31924,7 +32507,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
31924
32507
|
}
|
|
31925
32508
|
init({
|
|
31926
32509
|
configDir: configDirForWhatsApp,
|
|
31927
|
-
platformRoot:
|
|
32510
|
+
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..")),
|
|
31928
32511
|
accountConfig: bootAccountConfig,
|
|
31929
32512
|
onMessage: async (msg) => {
|
|
31930
32513
|
if (isObserveAccount(msg.accountId)) {
|