@rubytech/create-maxy-code 0.1.505 → 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/package.json +3 -3
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +59 -17
- package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +3 -3
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +6 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +58 -16
- package/payload/platform/scripts/check-no-esm-require.mjs +5 -0
- 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/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-DeWt3H7l.js → AdminLoginScreens-D5KOQA8S.js} +1 -1
- package/payload/server/public/assets/{AdminShell-u32d8puE.js → AdminShell-C8wazpnz.js} +2 -2
- package/payload/server/public/assets/{activity-DjP45-DD.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-ULvl_K5x.js → browser-T_5xbnJh.js} +1 -1
- package/payload/server/public/assets/{calendar-CGMrT6WZ.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-C5K6DMcK.js → file-text-c6cA4Evd.js} +1 -1
- package/payload/server/public/assets/{graph-CO6e1bgD.js → graph-DIk-o45J.js} +2 -2
- package/payload/server/public/assets/{graph-labels-iliQjlfE.js → graph-labels-5bjxhC_p.js} +1 -1
- package/payload/server/public/assets/{maximize-2-D-64XK6V.js → maximize-2-BCQkFb1_.js} +1 -1
- package/payload/server/public/assets/{operator-DyXgogJ1.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-B9NVDBxW.js → public-CKEYyIPn.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-P35h7ogS.js → rotate-ccw-dTpO5dhL.js} +1 -1
- package/payload/server/public/assets/{routines-BeiynLF7.js → routines-C6P1-Dto.js} +1 -1
- package/payload/server/public/assets/{skills-C5H7ohLp.js → skills-ClrpVqlz.js} +1 -1
- package/payload/server/public/assets/tasks-C8SNhSFC.js +1 -0
- package/payload/server/public/assets/{time-entry-format-CDhHCMrW.js → time-entry-format-CCO6N6XZ.js} +1 -1
- package/payload/server/public/assets/{triangle-alert-OgY71rDV.js → triangle-alert-CHjdKpag.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-jOGmWwa3.js → useCopyFeedback-BTUu3-l8.js} +1 -1
- package/payload/server/public/assets/{useSubAccountSwitcher-6AFuRawP.js → useSubAccountSwitcher-DSW9vDi2.js} +1 -1
- package/payload/server/public/assets/{useSubAccountSwitcher-AFSwtthL.css → useSubAccountSwitcher-DyrH8DA8.css} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-CnE3fhfV.js → useVoiceRecorder-BeNqxmm2.js} +1 -1
- package/payload/server/public/assets/{wrench-B1hsCj0R.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 +1329 -826
- package/payload/server/public/assets/admin-Bo-Y6Vjx.js +0 -1
- package/payload/server/public/assets/agents-C80aCXd8.js +0 -1
- package/payload/server/public/assets/chat-DnWXaWD4.js +0 -1
- package/payload/server/public/assets/chevron-left-BB5Iljw-.js +0 -1
- package/payload/server/public/assets/chevron-right-DijU96_j.js +0 -1
- package/payload/server/public/assets/clock-DsmnhhJh.js +0 -1
- package/payload/server/public/assets/data-JPlhOLGC.js +0 -1
- package/payload/server/public/assets/page-DrbiAFS-.js +0 -32
- package/payload/server/public/assets/page-WeM2ry61.js +0 -1
- package/payload/server/public/assets/tasks-CI74_b6H.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);
|
|
@@ -4489,124 +4792,52 @@ function splitName(pushName) {
|
|
|
4489
4792
|
const trimmed = pushName.trim();
|
|
4490
4793
|
if (!trimmed) return { givenName: null, familyName: null };
|
|
4491
4794
|
const firstSpace = trimmed.search(/\s/);
|
|
4492
|
-
if (firstSpace === -1) return { givenName: trimmed, familyName: null };
|
|
4493
|
-
return {
|
|
4494
|
-
givenName: trimmed.slice(0, firstSpace).trim(),
|
|
4495
|
-
familyName: trimmed.slice(firstSpace + 1).trim() || null
|
|
4496
|
-
};
|
|
4497
|
-
}
|
|
4498
|
-
function sanitizeReason(err) {
|
|
4499
|
-
if (!(err instanceof Error)) return String(err).slice(0, 200);
|
|
4500
|
-
const msg = err.message.replace(/bolt:\/\/[^\s)]+/g, "bolt://<redacted>").replace(/\b(neo4j|admin|password)=[^\s)]+/gi, "$1=<redacted>").slice(0, 200);
|
|
4501
|
-
return `${err.name}:${msg}`;
|
|
4502
|
-
}
|
|
4503
|
-
|
|
4504
|
-
// app/lib/whatsapp/ensure-conversation.ts
|
|
4505
|
-
var TAG12 = "[whatsapp-persist]";
|
|
4506
|
-
async function ensureWhatsAppConversation(input) {
|
|
4507
|
-
const t0 = Date.now();
|
|
4508
|
-
try {
|
|
4509
|
-
const result = await ensureConversation(
|
|
4510
|
-
input.platformAccountId,
|
|
4511
|
-
input.agentType,
|
|
4512
|
-
input.cacheKey
|
|
4513
|
-
);
|
|
4514
|
-
const ms = Date.now() - t0;
|
|
4515
|
-
if (!result.sessionId) {
|
|
4516
|
-
console.error(
|
|
4517
|
-
`${TAG12} conversation-merged FAIL cacheKey=${input.cacheKey} reason=null-sessionId ms=${ms}`
|
|
4518
|
-
);
|
|
4519
|
-
return null;
|
|
4520
|
-
}
|
|
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
|
-
);
|
|
4795
|
+
if (firstSpace === -1) return { givenName: trimmed, familyName: null };
|
|
4796
|
+
return {
|
|
4797
|
+
givenName: trimmed.slice(0, firstSpace).trim(),
|
|
4798
|
+
familyName: trimmed.slice(firstSpace + 1).trim() || null
|
|
4799
|
+
};
|
|
4584
4800
|
}
|
|
4585
|
-
function
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
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");
|
|
4801
|
+
function sanitizeReason(err) {
|
|
4802
|
+
if (!(err instanceof Error)) return String(err).slice(0, 200);
|
|
4803
|
+
const msg = err.message.replace(/bolt:\/\/[^\s)]+/g, "bolt://<redacted>").replace(/\b(neo4j|admin|password)=[^\s)]+/gi, "$1=<redacted>").slice(0, 200);
|
|
4804
|
+
return `${err.name}:${msg}`;
|
|
4593
4805
|
}
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4806
|
+
|
|
4807
|
+
// app/lib/whatsapp/ensure-conversation.ts
|
|
4808
|
+
var TAG12 = "[whatsapp-persist]";
|
|
4809
|
+
async function ensureWhatsAppConversation(input) {
|
|
4810
|
+
const t0 = Date.now();
|
|
4811
|
+
try {
|
|
4812
|
+
const result = await ensureConversation(
|
|
4813
|
+
input.platformAccountId,
|
|
4814
|
+
input.agentType,
|
|
4815
|
+
input.cacheKey
|
|
4816
|
+
);
|
|
4817
|
+
const ms = Date.now() - t0;
|
|
4818
|
+
if (!result.sessionId) {
|
|
4819
|
+
console.error(
|
|
4820
|
+
`${TAG12} conversation-merged FAIL cacheKey=${input.cacheKey} reason=null-sessionId ms=${ms}`
|
|
4821
|
+
);
|
|
4822
|
+
return null;
|
|
4823
|
+
}
|
|
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 });
|
|
@@ -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,8 +16415,35 @@ 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("\\");
|
|
16173
16448
|
function subdirs(dir) {
|
|
16174
16449
|
if (!existsSync30(dir)) return [];
|
|
@@ -16189,22 +16464,22 @@ function splitFrontmatter(text) {
|
|
|
16189
16464
|
return { fm, body: text.slice(m[0].length) };
|
|
16190
16465
|
}
|
|
16191
16466
|
function listSpecialists(accountDir) {
|
|
16192
|
-
const pluginsDir =
|
|
16467
|
+
const pluginsDir = resolve23(accountDir, "plugins");
|
|
16193
16468
|
const specialists = [];
|
|
16194
16469
|
let specialistsSkipped = 0;
|
|
16195
16470
|
for (const plugin of subdirs(pluginsDir).sort()) {
|
|
16196
|
-
const agentsDirP =
|
|
16471
|
+
const agentsDirP = resolve23(pluginsDir, plugin, "agents");
|
|
16197
16472
|
if (!existsSync30(agentsDirP)) continue;
|
|
16198
|
-
let
|
|
16473
|
+
let entries2;
|
|
16199
16474
|
try {
|
|
16200
|
-
|
|
16475
|
+
entries2 = readdirSync21(agentsDirP, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
16201
16476
|
} catch {
|
|
16202
16477
|
continue;
|
|
16203
16478
|
}
|
|
16204
|
-
for (const entry of
|
|
16479
|
+
for (const entry of entries2) {
|
|
16205
16480
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
16206
16481
|
try {
|
|
16207
|
-
const { fm } = splitFrontmatter(readFileSync32(
|
|
16482
|
+
const { fm } = splitFrontmatter(readFileSync32(resolve23(agentsDirP, entry.name), "utf-8"));
|
|
16208
16483
|
if (!fm.name) {
|
|
16209
16484
|
specialistsSkipped++;
|
|
16210
16485
|
console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${entry.name}`);
|
|
@@ -16232,19 +16507,22 @@ var OWNED_DOCS = [
|
|
|
16232
16507
|
{ file: "KNOWLEDGE-SUMMARY.md", key: "knowledgeSummary" }
|
|
16233
16508
|
];
|
|
16234
16509
|
var app18 = new Hono();
|
|
16235
|
-
app18.get("/", (c) => {
|
|
16236
|
-
const account =
|
|
16237
|
-
if (!account)
|
|
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
|
+
}
|
|
16238
16516
|
const agents = [];
|
|
16239
16517
|
let skipped = 0;
|
|
16240
|
-
const agentsDir =
|
|
16518
|
+
const agentsDir = resolve23(account.accountDir, "agents");
|
|
16241
16519
|
if (existsSync30(agentsDir)) {
|
|
16242
16520
|
try {
|
|
16243
|
-
const
|
|
16244
|
-
for (const entry of
|
|
16521
|
+
const entries2 = readdirSync21(agentsDir, { withFileTypes: true });
|
|
16522
|
+
for (const entry of entries2.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
16245
16523
|
if (!entry.isDirectory()) continue;
|
|
16246
16524
|
if (entry.name === "admin") continue;
|
|
16247
|
-
const configPath2 =
|
|
16525
|
+
const configPath2 = resolve23(agentsDir, entry.name, "config.json");
|
|
16248
16526
|
if (!existsSync30(configPath2)) continue;
|
|
16249
16527
|
try {
|
|
16250
16528
|
const config = JSON.parse(readFileSync32(configPath2, "utf-8"));
|
|
@@ -16264,22 +16542,25 @@ app18.get("/", (c) => {
|
|
|
16264
16542
|
}
|
|
16265
16543
|
}
|
|
16266
16544
|
const { specialists, specialistsSkipped } = listSpecialists(account.accountDir);
|
|
16267
|
-
console.log(`[admin/agents] op=list agents=${agents.length} specialists=${specialists.length} skipped=${skipped} specialistsSkipped=${specialistsSkipped}`);
|
|
16268
|
-
return c.json({ agents: [...agents, ...specialists], skipped, specialistsSkipped });
|
|
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 });
|
|
16269
16547
|
});
|
|
16270
|
-
app18.get("/:slug", (c) => {
|
|
16548
|
+
app18.get("/:slug", requireAdminSession, (c) => {
|
|
16271
16549
|
const slug = c.req.param("slug");
|
|
16272
|
-
const account =
|
|
16273
|
-
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
|
+
}
|
|
16274
16555
|
if (SLUG_UNSAFE(slug)) return c.json({ error: "Invalid agent slug" }, 400);
|
|
16275
16556
|
const pluginParam = c.req.query("plugin");
|
|
16276
16557
|
if (pluginParam) {
|
|
16277
16558
|
if (SLUG_UNSAFE(pluginParam)) return c.json({ error: "Invalid plugin" }, 400);
|
|
16278
|
-
const file =
|
|
16559
|
+
const file = resolve23(account.accountDir, "plugins", pluginParam, "agents", `${slug}.md`);
|
|
16279
16560
|
if (!existsSync30(file)) return c.json({ error: "Agent not found" }, 404);
|
|
16280
16561
|
const { fm, body } = splitFrontmatter(readFileSync32(file, "utf-8"));
|
|
16281
|
-
const skills = subdirs(
|
|
16282
|
-
console.log(`[admin/agents] op=detail slug=${slug} kind=specialist plugin=${pluginParam}`);
|
|
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}`);
|
|
16283
16564
|
return c.json({
|
|
16284
16565
|
agent: {
|
|
16285
16566
|
kind: "specialist",
|
|
@@ -16294,8 +16575,8 @@ app18.get("/:slug", (c) => {
|
|
|
16294
16575
|
}
|
|
16295
16576
|
});
|
|
16296
16577
|
}
|
|
16297
|
-
const agentDir =
|
|
16298
|
-
const configPath2 =
|
|
16578
|
+
const agentDir = resolve23(account.accountDir, "agents", slug);
|
|
16579
|
+
const configPath2 = resolve23(agentDir, "config.json");
|
|
16299
16580
|
if (!existsSync30(configPath2)) return c.json({ error: "Agent not found" }, 404);
|
|
16300
16581
|
let config;
|
|
16301
16582
|
try {
|
|
@@ -16307,7 +16588,7 @@ app18.get("/:slug", (c) => {
|
|
|
16307
16588
|
const docs = {};
|
|
16308
16589
|
const present = {};
|
|
16309
16590
|
for (const { file, key } of OWNED_DOCS) {
|
|
16310
|
-
const p =
|
|
16591
|
+
const p = resolve23(agentDir, file);
|
|
16311
16592
|
try {
|
|
16312
16593
|
if (existsSync30(p)) {
|
|
16313
16594
|
docs[key] = readFileSync32(p, "utf-8");
|
|
@@ -16323,7 +16604,7 @@ app18.get("/:slug", (c) => {
|
|
|
16323
16604
|
}
|
|
16324
16605
|
}
|
|
16325
16606
|
const found = Object.keys(present).filter((k) => present[k]);
|
|
16326
|
-
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"}`);
|
|
16327
16608
|
return c.json({
|
|
16328
16609
|
agent: {
|
|
16329
16610
|
slug,
|
|
@@ -16341,15 +16622,28 @@ app18.get("/:slug", (c) => {
|
|
|
16341
16622
|
});
|
|
16342
16623
|
app18.delete("/:slug", async (c) => {
|
|
16343
16624
|
const slug = c.req.param("slug");
|
|
16344
|
-
const account = resolveAccount();
|
|
16345
|
-
if (!account) return c.json({ error: "No account resolved" }, 400);
|
|
16346
16625
|
if (slug === "admin") {
|
|
16347
16626
|
return c.json({ error: "Cannot delete the admin agent" }, 403);
|
|
16348
16627
|
}
|
|
16349
16628
|
if (SLUG_UNSAFE(slug)) {
|
|
16350
16629
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
16351
16630
|
}
|
|
16352
|
-
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);
|
|
16353
16647
|
if (!existsSync30(agentDir)) {
|
|
16354
16648
|
return c.json({ error: "Agent not found" }, 404);
|
|
16355
16649
|
}
|
|
@@ -16357,29 +16651,42 @@ app18.delete("/:slug", async (c) => {
|
|
|
16357
16651
|
await deleteAgentProjection(account.accountId, slug);
|
|
16358
16652
|
} catch (err) {
|
|
16359
16653
|
const msg = err instanceof Error ? err.message : String(err);
|
|
16360
|
-
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`);
|
|
16361
16655
|
return c.json({ error: `Graph cleanup failed: ${msg}` }, 500);
|
|
16362
16656
|
}
|
|
16363
16657
|
try {
|
|
16364
16658
|
rmSync2(agentDir, { recursive: true, force: true });
|
|
16365
|
-
console.log(`[admin/agents]
|
|
16659
|
+
console.log(`[admin/agents] op=delete accountId=${account.accountId.slice(0, 8)} slug=${slug} outcome=ok`);
|
|
16366
16660
|
return c.json({ ok: true });
|
|
16367
16661
|
} catch (err) {
|
|
16368
|
-
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}"`);
|
|
16369
16663
|
return c.json({ error: "Failed to delete agent" }, 500);
|
|
16370
16664
|
}
|
|
16371
16665
|
});
|
|
16372
16666
|
app18.post("/:slug/project", async (c) => {
|
|
16373
16667
|
const slug = c.req.param("slug");
|
|
16374
|
-
const account = resolveAccount();
|
|
16375
|
-
if (!account) return c.json({ error: "No account resolved" }, 400);
|
|
16376
16668
|
if (slug === "admin") {
|
|
16377
16669
|
return c.json({ error: "Cannot project the admin agent" }, 403);
|
|
16378
16670
|
}
|
|
16379
16671
|
if (SLUG_UNSAFE(slug)) {
|
|
16380
16672
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
16381
16673
|
}
|
|
16382
|
-
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);
|
|
16383
16690
|
if (!existsSync30(agentDir)) {
|
|
16384
16691
|
return c.json({ error: "Agent not found on disk" }, 404);
|
|
16385
16692
|
}
|
|
@@ -17083,7 +17390,7 @@ var sessions_default = app19;
|
|
|
17083
17390
|
|
|
17084
17391
|
// app/lib/claude-agent/spawn-context.ts
|
|
17085
17392
|
import { existsSync as existsSync32, readFileSync as readFileSync33, readdirSync as readdirSync22, statSync as statSync15 } from "fs";
|
|
17086
|
-
import { dirname as dirname12, resolve as
|
|
17393
|
+
import { dirname as dirname12, resolve as resolve24, join as join33 } from "path";
|
|
17087
17394
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
17088
17395
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
17089
17396
|
try {
|
|
@@ -17180,7 +17487,7 @@ function parseSkillPathsFromFrontmatter(fm) {
|
|
|
17180
17487
|
}
|
|
17181
17488
|
function listPluginDirs(accountPluginsDir) {
|
|
17182
17489
|
const out = /* @__PURE__ */ new Map();
|
|
17183
|
-
const platformPlugins =
|
|
17490
|
+
const platformPlugins = resolve24(PLATFORM_ROOT, "plugins");
|
|
17184
17491
|
if (existsSync32(platformPlugins)) {
|
|
17185
17492
|
for (const name of readdirSync22(platformPlugins)) {
|
|
17186
17493
|
const dir = join33(platformPlugins, name);
|
|
@@ -17190,7 +17497,7 @@ function listPluginDirs(accountPluginsDir) {
|
|
|
17190
17497
|
}
|
|
17191
17498
|
}
|
|
17192
17499
|
}
|
|
17193
|
-
const premiumRoot =
|
|
17500
|
+
const premiumRoot = resolve24(dirname12(PLATFORM_ROOT), "premium-plugins");
|
|
17194
17501
|
if (existsSync32(premiumRoot)) {
|
|
17195
17502
|
for (const bundle of readdirSync22(premiumRoot)) {
|
|
17196
17503
|
const bundlePlugins = join33(premiumRoot, bundle, "plugins");
|
|
@@ -17223,7 +17530,7 @@ function listPluginDirs(accountPluginsDir) {
|
|
|
17223
17530
|
return out;
|
|
17224
17531
|
}
|
|
17225
17532
|
function accountPluginsDirFor(accountId) {
|
|
17226
|
-
return
|
|
17533
|
+
return resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "plugins");
|
|
17227
17534
|
}
|
|
17228
17535
|
function listAccountPluginNames(accountPluginsDir) {
|
|
17229
17536
|
if (!existsSync32(accountPluginsDir)) return [];
|
|
@@ -17240,7 +17547,7 @@ function listAccountPluginNames(accountPluginsDir) {
|
|
|
17240
17547
|
}
|
|
17241
17548
|
}
|
|
17242
17549
|
function readEnabledPlugins(accountId) {
|
|
17243
|
-
const accountFile =
|
|
17550
|
+
const accountFile = resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
17244
17551
|
if (!existsSync32(accountFile)) return /* @__PURE__ */ new Set();
|
|
17245
17552
|
try {
|
|
17246
17553
|
const parsed = JSON.parse(readFileSync33(accountFile, "utf-8"));
|
|
@@ -17316,11 +17623,11 @@ ${skillFm}
|
|
|
17316
17623
|
return out;
|
|
17317
17624
|
}
|
|
17318
17625
|
function computeSpecialistDomains(accountId) {
|
|
17319
|
-
const specialistsDir =
|
|
17626
|
+
const specialistsDir = resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
|
|
17320
17627
|
if (!existsSync32(specialistsDir)) return [];
|
|
17321
|
-
let
|
|
17628
|
+
let entries2;
|
|
17322
17629
|
try {
|
|
17323
|
-
|
|
17630
|
+
entries2 = readdirSync22(specialistsDir);
|
|
17324
17631
|
} catch {
|
|
17325
17632
|
return [];
|
|
17326
17633
|
}
|
|
@@ -17342,7 +17649,7 @@ function computeSpecialistDomains(accountId) {
|
|
|
17342
17649
|
return map.get(tool);
|
|
17343
17650
|
};
|
|
17344
17651
|
const out = [];
|
|
17345
|
-
for (const file of
|
|
17652
|
+
for (const file of entries2.sort()) {
|
|
17346
17653
|
if (!file.endsWith(".md")) continue;
|
|
17347
17654
|
const fm = readFrontmatter(join33(specialistsDir, file));
|
|
17348
17655
|
if (!fm) continue;
|
|
@@ -17362,7 +17669,7 @@ ${fm}
|
|
|
17362
17669
|
return out;
|
|
17363
17670
|
}
|
|
17364
17671
|
function computeDormantPlugins(accountId) {
|
|
17365
|
-
const accountFile =
|
|
17672
|
+
const accountFile = resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
17366
17673
|
if (!existsSync32(accountFile)) return [];
|
|
17367
17674
|
let enabled;
|
|
17368
17675
|
try {
|
|
@@ -17377,18 +17684,18 @@ function computeDormantPlugins(accountId) {
|
|
|
17377
17684
|
);
|
|
17378
17685
|
return [];
|
|
17379
17686
|
}
|
|
17380
|
-
const pluginsDir =
|
|
17687
|
+
const pluginsDir = resolve24(PLATFORM_ROOT, "plugins");
|
|
17381
17688
|
if (!existsSync32(pluginsDir)) return [];
|
|
17382
|
-
let
|
|
17689
|
+
let entries2;
|
|
17383
17690
|
try {
|
|
17384
|
-
|
|
17691
|
+
entries2 = readdirSync22(pluginsDir);
|
|
17385
17692
|
} catch {
|
|
17386
17693
|
return [];
|
|
17387
17694
|
}
|
|
17388
17695
|
const out = [];
|
|
17389
|
-
for (const name of
|
|
17696
|
+
for (const name of entries2) {
|
|
17390
17697
|
if (enabled.has(name)) continue;
|
|
17391
|
-
const manifestPath =
|
|
17698
|
+
const manifestPath = resolve24(pluginsDir, name, "PLUGIN.md");
|
|
17392
17699
|
if (!existsSync32(manifestPath)) continue;
|
|
17393
17700
|
let manifest;
|
|
17394
17701
|
try {
|
|
@@ -17407,7 +17714,7 @@ function computeDormantPlugins(accountId) {
|
|
|
17407
17714
|
|
|
17408
17715
|
// server/routes/admin/claude-sessions.ts
|
|
17409
17716
|
import { existsSync as existsSync33, readFileSync as readFileSync34 } from "fs";
|
|
17410
|
-
import { resolve as
|
|
17717
|
+
import { resolve as resolve25 } from "path";
|
|
17411
17718
|
function readTunnelState(brandConfigDir) {
|
|
17412
17719
|
const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
|
|
17413
17720
|
if (!existsSync33(statePath)) return null;
|
|
@@ -17423,10 +17730,10 @@ function readTunnelState(brandConfigDir) {
|
|
|
17423
17730
|
}
|
|
17424
17731
|
}
|
|
17425
17732
|
function resolveTunnelUrl() {
|
|
17426
|
-
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(), "..");
|
|
17427
17734
|
let brand;
|
|
17428
17735
|
try {
|
|
17429
|
-
brand = JSON.parse(readFileSync34(
|
|
17736
|
+
brand = JSON.parse(readFileSync34(resolve25(platformRoot5, "config", "brand.json"), "utf-8"));
|
|
17430
17737
|
} catch {
|
|
17431
17738
|
return null;
|
|
17432
17739
|
}
|
|
@@ -17665,6 +17972,77 @@ app22.post("/", async (c) => {
|
|
|
17665
17972
|
console.error(`[${event}] ${formatted}`);
|
|
17666
17973
|
return c.json({ ok: true });
|
|
17667
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
|
+
});
|
|
17668
18046
|
var events_default = app22;
|
|
17669
18047
|
|
|
17670
18048
|
// server/routes/admin/files.ts
|
|
@@ -17672,7 +18050,7 @@ import { createReadStream as createReadStream2, createWriteStream as createWrite
|
|
|
17672
18050
|
import { readdir as readdir4, readFile as readFile7, stat as stat6, mkdir as mkdir4, unlink as unlink3, rename as rename2, rmdir } from "fs/promises";
|
|
17673
18051
|
import { realpathSync as realpathSync7 } from "fs";
|
|
17674
18052
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
17675
|
-
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";
|
|
17676
18054
|
import { Readable as Readable2 } from "stream";
|
|
17677
18055
|
|
|
17678
18056
|
// ../lib/graph-trash/src/index.ts
|
|
@@ -17990,7 +18368,7 @@ async function cascadeDeleteDocument(params) {
|
|
|
17990
18368
|
|
|
17991
18369
|
// app/lib/file-index.ts
|
|
17992
18370
|
import * as fsp from "fs/promises";
|
|
17993
|
-
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";
|
|
17994
18372
|
import { tmpdir as tmpdir2 } from "os";
|
|
17995
18373
|
import { execFile as execFile2 } from "child_process";
|
|
17996
18374
|
import { promisify as promisify2 } from "util";
|
|
@@ -18143,16 +18521,16 @@ async function readDisplayName(absolute) {
|
|
|
18143
18521
|
}
|
|
18144
18522
|
}
|
|
18145
18523
|
async function walkSubtree(root, dataRoot, out) {
|
|
18146
|
-
let
|
|
18524
|
+
let entries2;
|
|
18147
18525
|
try {
|
|
18148
|
-
|
|
18526
|
+
entries2 = await fsp.readdir(root, { withFileTypes: true });
|
|
18149
18527
|
} catch (err) {
|
|
18150
18528
|
if (err.code === "ENOENT") return { clean: true };
|
|
18151
18529
|
console.error(`[file-index] walk-error dir="${relative4(dataRoot, root)}" err="${err.message}"`);
|
|
18152
18530
|
return { clean: false };
|
|
18153
18531
|
}
|
|
18154
18532
|
let clean = true;
|
|
18155
|
-
for (const entry of
|
|
18533
|
+
for (const entry of entries2) {
|
|
18156
18534
|
if (entry.isSymbolicLink()) continue;
|
|
18157
18535
|
if (entry.isDirectory() && entry.name === ".uploads-tmp") continue;
|
|
18158
18536
|
const abs = join34(root, entry.name);
|
|
@@ -18310,7 +18688,7 @@ async function reconcileFileIndex(accountId, opts) {
|
|
|
18310
18688
|
return withSession(opts, async (session) => {
|
|
18311
18689
|
const walked = [];
|
|
18312
18690
|
const subtreeRoots = [
|
|
18313
|
-
|
|
18691
|
+
resolve26(dataRoot, "accounts", accountId)
|
|
18314
18692
|
];
|
|
18315
18693
|
let clean = true;
|
|
18316
18694
|
for (const root of subtreeRoots) {
|
|
@@ -18393,7 +18771,7 @@ async function reconcileFileIndexDebounced(accountId, opts) {
|
|
|
18393
18771
|
async function indexFile(accountId, relativePath, opts) {
|
|
18394
18772
|
const dataRoot = opts?.dataRoot ?? DATA_ROOT;
|
|
18395
18773
|
const embed2 = opts?.embed ?? embed;
|
|
18396
|
-
const absolute =
|
|
18774
|
+
const absolute = resolve26(dataRoot, relativePath);
|
|
18397
18775
|
await withSession(opts, async (session) => {
|
|
18398
18776
|
const st = await fsp.stat(absolute);
|
|
18399
18777
|
const wf = {
|
|
@@ -18444,11 +18822,11 @@ function crc32(buf) {
|
|
|
18444
18822
|
for (let i = 0; i < buf.length; i++) crc = crcTable[(crc ^ buf[i]) & 255] ^ crc >>> 8;
|
|
18445
18823
|
return (crc ^ 4294967295) >>> 0;
|
|
18446
18824
|
}
|
|
18447
|
-
function buildZip(
|
|
18825
|
+
function buildZip(entries2) {
|
|
18448
18826
|
const localChunks = [];
|
|
18449
18827
|
const centralChunks = [];
|
|
18450
18828
|
let offset = 0;
|
|
18451
|
-
for (const entry of
|
|
18829
|
+
for (const entry of entries2) {
|
|
18452
18830
|
const nameBytes = Buffer.from(entry.name, "utf8");
|
|
18453
18831
|
const crc = crc32(entry.data);
|
|
18454
18832
|
const uncompressedSize = entry.data.length;
|
|
@@ -18498,14 +18876,31 @@ function buildZip(entries) {
|
|
|
18498
18876
|
eocd.writeUInt32LE(EOCD_SIG, 0);
|
|
18499
18877
|
eocd.writeUInt16LE(0, 4);
|
|
18500
18878
|
eocd.writeUInt16LE(0, 6);
|
|
18501
|
-
eocd.writeUInt16LE(
|
|
18502
|
-
eocd.writeUInt16LE(
|
|
18879
|
+
eocd.writeUInt16LE(entries2.length, 8);
|
|
18880
|
+
eocd.writeUInt16LE(entries2.length, 10);
|
|
18503
18881
|
eocd.writeUInt32LE(centralSize, 12);
|
|
18504
18882
|
eocd.writeUInt32LE(centralOffset, 16);
|
|
18505
18883
|
eocd.writeUInt16LE(0, 20);
|
|
18506
18884
|
return Buffer.concat([...localChunks, centralDir, eocd]);
|
|
18507
18885
|
}
|
|
18508
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
|
+
|
|
18509
18904
|
// server/routes/admin/files.ts
|
|
18510
18905
|
var UPLOAD_TMP_DIR = ".uploads-tmp";
|
|
18511
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;
|
|
@@ -18522,7 +18917,7 @@ async function readMeta(absDir, baseName) {
|
|
|
18522
18917
|
}
|
|
18523
18918
|
async function readAccountNames() {
|
|
18524
18919
|
const map = /* @__PURE__ */ new Map();
|
|
18525
|
-
const accountsDir =
|
|
18920
|
+
const accountsDir = resolve27(DATA_ROOT, "accounts");
|
|
18526
18921
|
let names;
|
|
18527
18922
|
try {
|
|
18528
18923
|
names = await readdir4(accountsDir);
|
|
@@ -18531,7 +18926,7 @@ async function readAccountNames() {
|
|
|
18531
18926
|
}
|
|
18532
18927
|
for (const name of names) {
|
|
18533
18928
|
if (!UUID_RE4.test(name)) continue;
|
|
18534
|
-
const configPath2 =
|
|
18929
|
+
const configPath2 = resolve27(accountsDir, name, "account.json");
|
|
18535
18930
|
try {
|
|
18536
18931
|
const raw = await readFile7(configPath2, "utf8");
|
|
18537
18932
|
const parsed = JSON.parse(raw);
|
|
@@ -18617,8 +19012,24 @@ function buildDisplayPath(relPath, accountNames) {
|
|
|
18617
19012
|
return dn ? { name: seg, displayName: dn } : { name: seg };
|
|
18618
19013
|
});
|
|
18619
19014
|
}
|
|
18620
|
-
|
|
18621
|
-
|
|
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) => {
|
|
18622
19033
|
if (a.kind !== b.kind) return a.kind === "directory" ? -1 : 1;
|
|
18623
19034
|
if (a.kind === "file" && b.kind === "file") {
|
|
18624
19035
|
const byTime = b.modifiedAt.localeCompare(a.modifiedAt);
|
|
@@ -18675,6 +19086,7 @@ function isProtectedFromRename(relPath) {
|
|
|
18675
19086
|
function isValidEntryName(name) {
|
|
18676
19087
|
if (typeof name !== "string" || name.length === 0) return false;
|
|
18677
19088
|
if (name === "." || name === "..") return false;
|
|
19089
|
+
if (name.startsWith("@")) return false;
|
|
18678
19090
|
return !/[\\/\0]/.test(name);
|
|
18679
19091
|
}
|
|
18680
19092
|
function resolveOwnAccountWrite(rawPath, accountId, endpoint) {
|
|
@@ -18719,7 +19131,8 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18719
19131
|
return c.json({ error: "Account not found for session" }, 401);
|
|
18720
19132
|
}
|
|
18721
19133
|
const rawPath = c.req.query("path") ?? "";
|
|
18722
|
-
const
|
|
19134
|
+
const groupReq = parseGroupPath(rawPath, accountId);
|
|
19135
|
+
const resolution = resolveDataPath(groupReq ? groupReq.accountRel : rawPath);
|
|
18723
19136
|
if (!resolution.ok) {
|
|
18724
19137
|
if (resolution.status === 403) {
|
|
18725
19138
|
console.error(`[data] path-traversal-blocked requested="${rawPath}" resolved="${resolution.resolved ?? "n/a"}"`);
|
|
@@ -18739,7 +19152,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18739
19152
|
const names = await readdir4(absolute);
|
|
18740
19153
|
const segs = relPath.split("/").filter(Boolean);
|
|
18741
19154
|
const isAccountParent = segs.length === 1 && segs[0] === "accounts";
|
|
18742
|
-
const
|
|
19155
|
+
const entries2 = [];
|
|
18743
19156
|
for (const name of names) {
|
|
18744
19157
|
if (UUID_RE4.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
|
|
18745
19158
|
continue;
|
|
@@ -18752,7 +19165,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18752
19165
|
try {
|
|
18753
19166
|
const entryPath = join35(absolute, name);
|
|
18754
19167
|
const s = await stat6(entryPath);
|
|
18755
|
-
|
|
19168
|
+
entries2.push({
|
|
18756
19169
|
name,
|
|
18757
19170
|
kind: s.isDirectory() ? "directory" : s.isFile() ? "file" : "other",
|
|
18758
19171
|
sizeBytes: s.isDirectory() ? null : s.size,
|
|
@@ -18760,12 +19173,12 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18760
19173
|
protected: protectedEntry
|
|
18761
19174
|
});
|
|
18762
19175
|
} catch {
|
|
18763
|
-
|
|
19176
|
+
entries2.push({ name, kind: "other", sizeBytes: null, modifiedAt: (/* @__PURE__ */ new Date(0)).toISOString(), protected: protectedEntry });
|
|
18764
19177
|
}
|
|
18765
19178
|
}
|
|
18766
19179
|
const accountNames = await readAccountNames();
|
|
18767
|
-
const classes = await Promise.all(
|
|
18768
|
-
sortDirEntries(
|
|
19180
|
+
const classes = await Promise.all(entries2.map((e) => enrich(absolute, e, accountNames)));
|
|
19181
|
+
sortDirEntries(entries2);
|
|
18769
19182
|
let flattened = 0;
|
|
18770
19183
|
let keptAsDir = 0;
|
|
18771
19184
|
for (const cls of classes) {
|
|
@@ -18777,8 +19190,77 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18777
19190
|
console.error(`[data] op=uploads-flatten containers=${containers} flattened=${flattened} kept-as-dir=${keptAsDir}`);
|
|
18778
19191
|
}
|
|
18779
19192
|
const displayPath = buildDisplayPath(relPath, accountNames);
|
|
18780
|
-
|
|
18781
|
-
|
|
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 });
|
|
18782
19264
|
} catch (err) {
|
|
18783
19265
|
const code = err.code;
|
|
18784
19266
|
if (code === "ENOENT") {
|
|
@@ -18930,14 +19412,14 @@ app23.get("/download-zip", requireAdminSession, async (c) => {
|
|
|
18930
19412
|
if (rawPaths.length > maxFiles) {
|
|
18931
19413
|
return c.json({ error: `Too many files (max ${maxFiles})` }, 413);
|
|
18932
19414
|
}
|
|
18933
|
-
const
|
|
19415
|
+
const entries2 = [];
|
|
18934
19416
|
let total = 0;
|
|
18935
19417
|
let dirsWalked = 0;
|
|
18936
19418
|
const addFile = async (absolute, size, member) => {
|
|
18937
|
-
if (
|
|
19419
|
+
if (entries2.length >= maxFiles) return c.json({ error: `Too many files (max ${maxFiles})` }, 413);
|
|
18938
19420
|
total += size;
|
|
18939
19421
|
if (total > maxTotalBytes) return c.json({ error: "Selection too large to zip" }, 413);
|
|
18940
|
-
|
|
19422
|
+
entries2.push({ name: member, data: await readFile7(absolute) });
|
|
18941
19423
|
return null;
|
|
18942
19424
|
};
|
|
18943
19425
|
for (const rawPath of rawPaths) {
|
|
@@ -18985,8 +19467,8 @@ app23.get("/download-zip", requireAdminSession, async (c) => {
|
|
|
18985
19467
|
throw err;
|
|
18986
19468
|
}
|
|
18987
19469
|
}
|
|
18988
|
-
const archive = buildZip(
|
|
18989
|
-
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}`);
|
|
18990
19472
|
return new Response(new Uint8Array(archive), {
|
|
18991
19473
|
status: 200,
|
|
18992
19474
|
headers: {
|
|
@@ -19027,8 +19509,8 @@ async function resolveUploadTarget(c, accountId, uid) {
|
|
|
19027
19509
|
console.error(`[data] write-scope-blocked endpoint="POST /api/admin/files/upload" path="${destRel}" account=${accountId.slice(0, 8)}\u2026`);
|
|
19028
19510
|
return { ok: false, status: 403, error: "Path is outside your account folder" };
|
|
19029
19511
|
}
|
|
19030
|
-
const destDir = relDir === "." ? base.absolute :
|
|
19031
|
-
const destPath =
|
|
19512
|
+
const destDir = relDir === "." ? base.absolute : resolve27(base.absolute, relDir);
|
|
19513
|
+
const destPath = resolve27(destDir, finalName);
|
|
19032
19514
|
if (relpath) {
|
|
19033
19515
|
const firstCreated = await mkdir4(destDir, { recursive: true });
|
|
19034
19516
|
if (firstCreated) console.error(`[data-upload] op=mkdir uid=${uid} token=${token} dir="${destRel}"`);
|
|
@@ -19045,7 +19527,7 @@ async function resolveUploadTarget(c, accountId, uid) {
|
|
|
19045
19527
|
return { ok: true, destDir, destPath, destRel, finalName, mimeType, relpath, token };
|
|
19046
19528
|
}
|
|
19047
19529
|
async function logUploadsTmpCensus(accountId) {
|
|
19048
|
-
const tmpDir =
|
|
19530
|
+
const tmpDir = resolve27(DATA_ROOT, "accounts", accountId, UPLOAD_TMP_DIR);
|
|
19049
19531
|
let live = 0;
|
|
19050
19532
|
try {
|
|
19051
19533
|
live = (await readdir4(tmpDir)).length;
|
|
@@ -19072,9 +19554,9 @@ app23.post("/upload", requireAdminSession, async (c) => {
|
|
|
19072
19554
|
if (!body) return c.json({ error: "Empty request body" }, 400);
|
|
19073
19555
|
console.error(`[data-upload] op=request uid=${uid} account=${accountId} dir="${destRel}" token=${token} rel="${relpath ?? ""}" declaredBytes=${declaredBytes}`);
|
|
19074
19556
|
const ceiling = dataUploadCeiling();
|
|
19075
|
-
const tmpDir =
|
|
19557
|
+
const tmpDir = resolve27(DATA_ROOT, "accounts", accountId, UPLOAD_TMP_DIR);
|
|
19076
19558
|
await mkdir4(tmpDir, { recursive: true });
|
|
19077
|
-
const tmpPath =
|
|
19559
|
+
const tmpPath = resolve27(tmpDir, `${uid}.part`);
|
|
19078
19560
|
const out = createWriteStream2(tmpPath);
|
|
19079
19561
|
const t0 = Date.now();
|
|
19080
19562
|
let received = 0;
|
|
@@ -19221,9 +19703,9 @@ async function handleChunkUpload(c, accountId) {
|
|
|
19221
19703
|
}
|
|
19222
19704
|
const target = await resolveUploadTarget(c, accountId, uploadId);
|
|
19223
19705
|
if (!target.ok) return c.json({ error: target.error }, target.status);
|
|
19224
|
-
const tmpDir =
|
|
19706
|
+
const tmpDir = resolve27(DATA_ROOT, "accounts", accountId, UPLOAD_TMP_DIR);
|
|
19225
19707
|
await mkdir4(tmpDir, { recursive: true });
|
|
19226
|
-
const tmpPath =
|
|
19708
|
+
const tmpPath = resolve27(tmpDir, `${uploadId}.part`);
|
|
19227
19709
|
const out = createWriteStream2(tmpPath);
|
|
19228
19710
|
const state2 = {
|
|
19229
19711
|
accountId,
|
|
@@ -19329,7 +19811,7 @@ async function evictStaleChunkUploads(ttlMs) {
|
|
|
19329
19811
|
return evicted;
|
|
19330
19812
|
}
|
|
19331
19813
|
async function sweepStaleUploadTemps() {
|
|
19332
|
-
const accountsRoot =
|
|
19814
|
+
const accountsRoot = resolve27(DATA_ROOT, "accounts");
|
|
19333
19815
|
let accounts;
|
|
19334
19816
|
try {
|
|
19335
19817
|
accounts = await readdir4(accountsRoot, { withFileTypes: true });
|
|
@@ -19339,16 +19821,16 @@ async function sweepStaleUploadTemps() {
|
|
|
19339
19821
|
let removed = 0;
|
|
19340
19822
|
for (const acc of accounts) {
|
|
19341
19823
|
if (!acc.isDirectory()) continue;
|
|
19342
|
-
const tmpDir =
|
|
19343
|
-
let
|
|
19824
|
+
const tmpDir = resolve27(accountsRoot, acc.name, UPLOAD_TMP_DIR);
|
|
19825
|
+
let entries2;
|
|
19344
19826
|
try {
|
|
19345
|
-
|
|
19827
|
+
entries2 = await readdir4(tmpDir);
|
|
19346
19828
|
} catch {
|
|
19347
19829
|
continue;
|
|
19348
19830
|
}
|
|
19349
|
-
for (const name of
|
|
19831
|
+
for (const name of entries2) {
|
|
19350
19832
|
try {
|
|
19351
|
-
await unlink3(
|
|
19833
|
+
await unlink3(resolve27(tmpDir, name));
|
|
19352
19834
|
removed++;
|
|
19353
19835
|
} catch {
|
|
19354
19836
|
}
|
|
@@ -19459,7 +19941,7 @@ app23.post("/folder", requireAdminSession, async (c) => {
|
|
|
19459
19941
|
if (!isValidEntryName(name)) return c.json({ error: "Invalid folder name" }, 400);
|
|
19460
19942
|
const parent = resolveOwnAccountWrite(rawPath, accountId, "POST /api/admin/files/folder");
|
|
19461
19943
|
if (!parent.ok) return c.json({ error: parent.error }, parent.status);
|
|
19462
|
-
const newAbs =
|
|
19944
|
+
const newAbs = resolve27(parent.absolute, name);
|
|
19463
19945
|
const newRel = `${parent.relative}/${name}`;
|
|
19464
19946
|
try {
|
|
19465
19947
|
const info = await stat6(parent.absolute);
|
|
@@ -19498,7 +19980,7 @@ app23.post("/rename", requireAdminSession, async (c) => {
|
|
|
19498
19980
|
console.error(`[data] file-rename blocked path="${src.relative}" reason="protected"`);
|
|
19499
19981
|
return c.json({ error: "Protected entry \u2014 refusing to rename" }, 403);
|
|
19500
19982
|
}
|
|
19501
|
-
const destAbs =
|
|
19983
|
+
const destAbs = resolve27(dirname13(src.absolute), newName);
|
|
19502
19984
|
const newRel = `${dirname13(src.relative)}/${newName}`.replace(/^\.\//, "");
|
|
19503
19985
|
if (isProtectedFromRename(newRel)) {
|
|
19504
19986
|
console.error(`[data] file-rename blocked path="${newRel}" reason="protected-target"`);
|
|
@@ -19529,7 +20011,7 @@ app23.post("/rename", requireAdminSession, async (c) => {
|
|
|
19529
20011
|
var files_default = app23;
|
|
19530
20012
|
|
|
19531
20013
|
// ../lib/graph-search/src/index.ts
|
|
19532
|
-
var
|
|
20014
|
+
var import_dist2 = __toESM(require_dist3());
|
|
19533
20015
|
import { int } from "neo4j-driver";
|
|
19534
20016
|
|
|
19535
20017
|
// ../lib/graph-search/src/rrf-fusion.ts
|
|
@@ -19779,7 +20261,7 @@ async function bm25Only(session, params) {
|
|
|
19779
20261
|
${scopeClause}
|
|
19780
20262
|
${agentClause}
|
|
19781
20263
|
${labelClause}
|
|
19782
|
-
AND ${(0,
|
|
20264
|
+
AND ${(0, import_dist2.notTrashed)("node")}
|
|
19783
20265
|
${kwClause}
|
|
19784
20266
|
RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
|
|
19785
20267
|
ORDER BY score DESC
|
|
@@ -19941,7 +20423,7 @@ async function hybrid(session, embed2, params) {
|
|
|
19941
20423
|
WHERE node.accountId = $accountId
|
|
19942
20424
|
${scopeClause}
|
|
19943
20425
|
${agentClause}
|
|
19944
|
-
AND ${(0,
|
|
20426
|
+
AND ${(0, import_dist2.notTrashed)("node")}
|
|
19945
20427
|
${keywordClause}
|
|
19946
20428
|
RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
|
|
19947
20429
|
ORDER BY score DESC
|
|
@@ -20040,7 +20522,7 @@ async function hybrid(session, embed2, params) {
|
|
|
20040
20522
|
const propResult = await session.run(
|
|
20041
20523
|
`MATCH (node)
|
|
20042
20524
|
WHERE node.accountId = $accountId
|
|
20043
|
-
AND ${(0,
|
|
20525
|
+
AND ${(0, import_dist2.notTrashed)("node")}
|
|
20044
20526
|
AND node.keywords IS NOT NULL
|
|
20045
20527
|
AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
|
|
20046
20528
|
${propScope.clause}
|
|
@@ -20092,7 +20574,7 @@ async function hybrid(session, embed2, params) {
|
|
|
20092
20574
|
const propResult = await session.run(
|
|
20093
20575
|
`MATCH (node)
|
|
20094
20576
|
WHERE node.accountId = $accountId
|
|
20095
|
-
AND ${(0,
|
|
20577
|
+
AND ${(0, import_dist2.notTrashed)("node")}
|
|
20096
20578
|
AND node.keywords IS NOT NULL
|
|
20097
20579
|
AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
|
|
20098
20580
|
${propScope.clause}
|
|
@@ -20199,7 +20681,7 @@ async function hybrid(session, embed2, params) {
|
|
|
20199
20681
|
`UNWIND $nodeIds AS nid
|
|
20200
20682
|
MATCH (n)-[r]-(related)
|
|
20201
20683
|
WHERE elementId(n) = nid
|
|
20202
|
-
AND ${(0,
|
|
20684
|
+
AND ${(0, import_dist2.notTrashed)("related")}
|
|
20203
20685
|
${expandScopeClause}
|
|
20204
20686
|
${expandAgentClause}
|
|
20205
20687
|
WITH nid, n, r, related
|
|
@@ -20427,8 +20909,8 @@ var graph_search_default = app24;
|
|
|
20427
20909
|
import neo4j2 from "neo4j-driver";
|
|
20428
20910
|
|
|
20429
20911
|
// app/lib/graph-labels.ts
|
|
20430
|
-
var
|
|
20431
|
-
var
|
|
20912
|
+
var import_dist3 = __toESM(require_dist4(), 1);
|
|
20913
|
+
var import_dist4 = __toESM(require_dist4(), 1);
|
|
20432
20914
|
var HIDDEN_BY_DEFAULT_LABELS = Object.freeze(
|
|
20433
20915
|
/* @__PURE__ */ new Set(["Chunk", "GraphPreference"])
|
|
20434
20916
|
);
|
|
@@ -20506,16 +20988,16 @@ var EXCLUDED_EDGE_TYPES = Object.freeze(
|
|
|
20506
20988
|
])
|
|
20507
20989
|
);
|
|
20508
20990
|
function isKnownLabel(label) {
|
|
20509
|
-
return Object.prototype.hasOwnProperty.call(
|
|
20991
|
+
return Object.prototype.hasOwnProperty.call(import_dist4.GRAPH_LABEL_COLOURS, label);
|
|
20510
20992
|
}
|
|
20511
20993
|
function isHiddenByDefault(label) {
|
|
20512
20994
|
return HIDDEN_BY_DEFAULT_LABELS.has(label);
|
|
20513
20995
|
}
|
|
20514
20996
|
|
|
20515
20997
|
// server/lib/top-level-labels.ts
|
|
20516
|
-
var
|
|
20998
|
+
var import_dist5 = __toESM(require_dist4(), 1);
|
|
20517
20999
|
import { readdirSync as readdirSync23, readFileSync as readFileSync35 } from "fs";
|
|
20518
|
-
import { join as join36, resolve as
|
|
21000
|
+
import { join as join36, resolve as resolve28 } from "path";
|
|
20519
21001
|
var STATIC_TOP_LEVEL_LABELS = Object.freeze(
|
|
20520
21002
|
/* @__PURE__ */ new Set([
|
|
20521
21003
|
// Base-schema infra (schema-base.md has no top-level section).
|
|
@@ -20621,7 +21103,7 @@ function resolveReferencesDir() {
|
|
|
20621
21103
|
if (process.env.MAXY_PLATFORM_ROOT) {
|
|
20622
21104
|
return join36(process.env.MAXY_PLATFORM_ROOT, "plugins", "memory", "references");
|
|
20623
21105
|
}
|
|
20624
|
-
return
|
|
21106
|
+
return resolve28(import.meta.dirname, "../../../plugins/memory/references");
|
|
20625
21107
|
}
|
|
20626
21108
|
function parseTableTopLevelLabels(referencesDir) {
|
|
20627
21109
|
const labels = /* @__PURE__ */ new Set();
|
|
@@ -20650,7 +21132,7 @@ function getTopLevelLabelAllowlist(opts = {}) {
|
|
|
20650
21132
|
const referencesDir = opts.referencesDir ?? resolveReferencesDir();
|
|
20651
21133
|
const { labels: derived, contributingFiles } = parseTableTopLevelLabels(referencesDir);
|
|
20652
21134
|
const excluded = [];
|
|
20653
|
-
for (const label of
|
|
21135
|
+
for (const label of import_dist5.ADDITIONAL_BASE_LABELS) {
|
|
20654
21136
|
if (derived.delete(label)) excluded.push(label);
|
|
20655
21137
|
}
|
|
20656
21138
|
const derivedCount = derived.size;
|
|
@@ -21786,7 +22268,7 @@ var graph_default_view_default = app29;
|
|
|
21786
22268
|
|
|
21787
22269
|
// server/routes/admin/sidebar-artefacts.ts
|
|
21788
22270
|
import { readdir as readdir5, stat as stat7 } from "fs/promises";
|
|
21789
|
-
import { resolve as
|
|
22271
|
+
import { resolve as resolve29, relative as relative6, isAbsolute as isAbsolute2, sep as sep9, basename as basename12 } from "path";
|
|
21790
22272
|
import { existsSync as existsSync35 } from "fs";
|
|
21791
22273
|
var LIMIT = 50;
|
|
21792
22274
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
@@ -21802,20 +22284,25 @@ app30.get("/", requireAdminSession, async (c) => {
|
|
|
21802
22284
|
return c.json({ error: "Account not found for session" }, 401);
|
|
21803
22285
|
}
|
|
21804
22286
|
const start = Date.now();
|
|
21805
|
-
|
|
21806
|
-
|
|
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) {
|
|
21807
22299
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
21808
22300
|
}
|
|
21809
|
-
const accountDir = resolve28(ACCOUNTS_DIR, accountId);
|
|
21810
|
-
const agents = await fetchAgentTemplateRows(accountDir);
|
|
21811
|
-
const artefacts = [...accountFiles, ...agents].sort(
|
|
21812
|
-
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
21813
|
-
).slice(0, LIMIT);
|
|
21814
22301
|
const ms = Date.now() - start;
|
|
21815
22302
|
console.log(
|
|
21816
|
-
`[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}`
|
|
21817
22304
|
);
|
|
21818
|
-
return c.json({ artefacts });
|
|
22305
|
+
return c.json({ artefacts: value.artefacts });
|
|
21819
22306
|
});
|
|
21820
22307
|
async function fetchAccountFileArtefacts(accountId) {
|
|
21821
22308
|
const session = getSession();
|
|
@@ -21858,8 +22345,8 @@ async function fetchAccountFileArtefacts(accountId) {
|
|
|
21858
22345
|
async function fetchAgentTemplateRows(accountDir) {
|
|
21859
22346
|
const rows = [];
|
|
21860
22347
|
for (const filename of ADMIN_AGENT_FILES) {
|
|
21861
|
-
const overridePath =
|
|
21862
|
-
const bundledPath =
|
|
22348
|
+
const overridePath = resolve29(accountDir, "agents", "admin", filename);
|
|
22349
|
+
const bundledPath = resolve29(PLATFORM_ROOT, "templates", "agents", "admin", filename);
|
|
21863
22350
|
const labelStem = filename.replace(/\.md$/, "");
|
|
21864
22351
|
const row = await readAgentTemplateRow({
|
|
21865
22352
|
id: `agent-template:admin:${filename}`,
|
|
@@ -21872,12 +22359,12 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
21872
22359
|
});
|
|
21873
22360
|
if (row) rows.push(row);
|
|
21874
22361
|
}
|
|
21875
|
-
const overrideDir =
|
|
21876
|
-
const bundledDir =
|
|
22362
|
+
const overrideDir = resolve29(accountDir, "specialists", "agents");
|
|
22363
|
+
const bundledDir = resolve29(PLATFORM_ROOT, "templates", "specialists", "agents");
|
|
21877
22364
|
const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
|
|
21878
22365
|
for (const filename of specialistNames) {
|
|
21879
|
-
const overridePath =
|
|
21880
|
-
const bundledPath =
|
|
22366
|
+
const overridePath = resolve29(overrideDir, filename);
|
|
22367
|
+
const bundledPath = resolve29(bundledDir, filename);
|
|
21881
22368
|
const row = await readAgentTemplateRow({
|
|
21882
22369
|
id: `agent-template:specialist:${filename}`,
|
|
21883
22370
|
displayName: filename.replace(/\.md$/, ""),
|
|
@@ -21896,8 +22383,8 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
21896
22383
|
for (const dir of [overrideDir, bundledDir]) {
|
|
21897
22384
|
if (!existsSync35(dir)) continue;
|
|
21898
22385
|
try {
|
|
21899
|
-
const
|
|
21900
|
-
for (const entry of
|
|
22386
|
+
const entries2 = await readdir5(dir);
|
|
22387
|
+
for (const entry of entries2) {
|
|
21901
22388
|
if (entry.endsWith(".md")) names.add(entry);
|
|
21902
22389
|
}
|
|
21903
22390
|
} catch (err) {
|
|
@@ -22363,24 +22850,24 @@ var session_rc_spawn_default = app36;
|
|
|
22363
22850
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
22364
22851
|
|
|
22365
22852
|
// server/wa-descriptor.ts
|
|
22366
|
-
import { resolve as
|
|
22853
|
+
import { resolve as resolve30, join as join38 } from "path";
|
|
22367
22854
|
function waGatewayUrl() {
|
|
22368
22855
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
22369
22856
|
}
|
|
22370
22857
|
function waServerPath() {
|
|
22371
|
-
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");
|
|
22372
22859
|
}
|
|
22373
22860
|
function waChannelDescriptor(senderId) {
|
|
22374
22861
|
return { senderId, gatewayUrl: waGatewayUrl(), serverPath: waServerPath() };
|
|
22375
22862
|
}
|
|
22376
22863
|
|
|
22377
22864
|
// server/telegram-descriptor.ts
|
|
22378
|
-
import { resolve as
|
|
22865
|
+
import { resolve as resolve31, join as join39 } from "path";
|
|
22379
22866
|
function telegramGatewayUrl() {
|
|
22380
22867
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
22381
22868
|
}
|
|
22382
22869
|
function telegramServerPath() {
|
|
22383
|
-
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");
|
|
22384
22871
|
}
|
|
22385
22872
|
function telegramChannelDescriptor(senderId) {
|
|
22386
22873
|
return { senderId, gatewayUrl: telegramGatewayUrl(), serverPath: telegramServerPath() };
|
|
@@ -22665,8 +23152,8 @@ var system_stats_default = app38;
|
|
|
22665
23152
|
|
|
22666
23153
|
// server/routes/admin/health.ts
|
|
22667
23154
|
import { existsSync as existsSync36, readFileSync as readFileSync37 } from "fs";
|
|
22668
|
-
import { resolve as
|
|
22669
|
-
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(), "..");
|
|
22670
23157
|
var brandHostname = "maxy";
|
|
22671
23158
|
var brandJsonPath = join40(PLATFORM_ROOT6, "config", "brand.json");
|
|
22672
23159
|
if (existsSync36(brandJsonPath)) {
|
|
@@ -22676,7 +23163,7 @@ if (existsSync36(brandJsonPath)) {
|
|
|
22676
23163
|
} catch {
|
|
22677
23164
|
}
|
|
22678
23165
|
}
|
|
22679
|
-
var VERSION_FILE =
|
|
23166
|
+
var VERSION_FILE = resolve32(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
|
|
22680
23167
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
22681
23168
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
22682
23169
|
function readVersion() {
|
|
@@ -24132,7 +24619,7 @@ var routines_default = app48;
|
|
|
24132
24619
|
|
|
24133
24620
|
// server/routes/admin/skills.ts
|
|
24134
24621
|
import { existsSync as existsSync38, readdirSync as readdirSync24, readFileSync as readFileSync39, rmSync as rmSync3 } from "fs";
|
|
24135
|
-
import { resolve as
|
|
24622
|
+
import { resolve as resolve33 } from "path";
|
|
24136
24623
|
var app49 = new Hono();
|
|
24137
24624
|
var NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
24138
24625
|
function splitSkillMd(text) {
|
|
@@ -24154,12 +24641,6 @@ function stripQuotes(s) {
|
|
|
24154
24641
|
if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) return s.slice(1, -1);
|
|
24155
24642
|
return s;
|
|
24156
24643
|
}
|
|
24157
|
-
function accountDirForSession(cacheKey) {
|
|
24158
|
-
const accountId = getAccountIdForSession(cacheKey);
|
|
24159
|
-
if (!accountId) return null;
|
|
24160
|
-
const match = listValidAccounts().find((a) => a.accountId === accountId);
|
|
24161
|
-
return match ? { accountId, accountDir: match.accountDir } : null;
|
|
24162
|
-
}
|
|
24163
24644
|
function subdirs2(dir) {
|
|
24164
24645
|
if (!existsSync38(dir)) return [];
|
|
24165
24646
|
try {
|
|
@@ -24174,28 +24655,32 @@ app49.get("/", requireAdminSession, async (c) => {
|
|
|
24174
24655
|
console.error('[admin:skills] op=list auth-rejected reason="no account for session"');
|
|
24175
24656
|
return c.json({ error: "Account not found for session" }, 401);
|
|
24176
24657
|
}
|
|
24177
|
-
|
|
24178
|
-
const
|
|
24179
|
-
|
|
24180
|
-
|
|
24181
|
-
pluginCount
|
|
24182
|
-
const
|
|
24183
|
-
|
|
24184
|
-
const
|
|
24185
|
-
|
|
24186
|
-
|
|
24187
|
-
|
|
24188
|
-
|
|
24189
|
-
|
|
24190
|
-
|
|
24191
|
-
|
|
24192
|
-
|
|
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
|
+
}
|
|
24193
24677
|
}
|
|
24194
24678
|
}
|
|
24195
|
-
|
|
24196
|
-
|
|
24197
|
-
|
|
24198
|
-
|
|
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 });
|
|
24199
24684
|
});
|
|
24200
24685
|
app49.get("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
24201
24686
|
const acct = accountDirForSession(c.var.cacheKey);
|
|
@@ -24206,14 +24691,14 @@ app49.get("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
24206
24691
|
console.error(`[admin:skills] op=get outcome=rejected reason=invalid-name plugin="${plugin}" skill="${skill}"`);
|
|
24207
24692
|
return c.json({ error: "Invalid plugin or skill name" }, 400);
|
|
24208
24693
|
}
|
|
24209
|
-
const skillDir =
|
|
24210
|
-
const skillMd =
|
|
24694
|
+
const skillDir = resolve33(acct.accountDir, "plugins", plugin, "skills", skill);
|
|
24695
|
+
const skillMd = resolve33(skillDir, "SKILL.md");
|
|
24211
24696
|
const found = existsSync38(skillMd);
|
|
24212
24697
|
console.log(`[admin:skills] op=get plugin=${plugin} skill=${skill} found=${found}`);
|
|
24213
24698
|
if (!found) return c.json({ error: "Skill not found" }, 404);
|
|
24214
24699
|
try {
|
|
24215
24700
|
const { frontmatter, body } = splitSkillMd(readFileSync39(skillMd, "utf-8"));
|
|
24216
|
-
const refsDir =
|
|
24701
|
+
const refsDir = resolve33(skillDir, "references");
|
|
24217
24702
|
const references = existsSync38(refsDir) ? readdirSync24(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).sort() : [];
|
|
24218
24703
|
return c.json({ skill: { plugin, skill, frontmatter, body, references } });
|
|
24219
24704
|
} catch (err) {
|
|
@@ -24231,10 +24716,10 @@ app49.delete("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
24231
24716
|
console.error(`[admin:skills] op=delete outcome=rejected reason=invalid-name plugin="${plugin}" skill="${skill}"`);
|
|
24232
24717
|
return c.json({ error: "Invalid plugin or skill name" }, 400);
|
|
24233
24718
|
}
|
|
24234
|
-
const pluginDir =
|
|
24235
|
-
const skillsDir =
|
|
24236
|
-
const skillDir =
|
|
24237
|
-
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"))) {
|
|
24238
24723
|
console.error(`[admin:skills] op=delete outcome=rejected reason=not-found plugin=${plugin} skill=${skill}`);
|
|
24239
24724
|
return c.json({ error: "Skill not found" }, 404);
|
|
24240
24725
|
}
|
|
@@ -24286,6 +24771,15 @@ app50.get("/", requireAdminSession, async (c) => {
|
|
|
24286
24771
|
console.error(`${TAG39} op=list auth-rejected reason="no account for session"`);
|
|
24287
24772
|
return c.json({ error: "Account not found for session" }, 401);
|
|
24288
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) {
|
|
24289
24783
|
const session = getSession();
|
|
24290
24784
|
try {
|
|
24291
24785
|
const result = await session.run(
|
|
@@ -24368,14 +24862,14 @@ app50.get("/", requireAdminSession, async (c) => {
|
|
|
24368
24862
|
console.log(
|
|
24369
24863
|
`${TAG39} op=list account=${accountId} returned=${returned} graphTotal=${graphTotal} open=${open.length} completed=${completed.length} missingTaskId=${missingTaskId} missingCreatedAt=${missingCreatedAt} assigned=${assigned}`
|
|
24370
24864
|
);
|
|
24371
|
-
return
|
|
24865
|
+
return { open, completed };
|
|
24372
24866
|
} catch (err) {
|
|
24373
24867
|
console.error(`${TAG39} op=list error="${err instanceof Error ? err.message : String(err)}"`);
|
|
24374
|
-
return
|
|
24868
|
+
return null;
|
|
24375
24869
|
} finally {
|
|
24376
24870
|
await session.close();
|
|
24377
24871
|
}
|
|
24378
|
-
}
|
|
24872
|
+
}
|
|
24379
24873
|
var tasks_list_default = app50;
|
|
24380
24874
|
|
|
24381
24875
|
// server/routes/admin/task-timer-start.ts
|
|
@@ -24419,6 +24913,7 @@ app51.post("/", requireAdminSession, async (c) => {
|
|
|
24419
24913
|
const priorStartedAt = open.records[0]?.get("startedAt");
|
|
24420
24914
|
if (priorEntryId) {
|
|
24421
24915
|
console.log(`${TAG40} op=start taskId=${taskId} accountId=${accountId} result=already-running`);
|
|
24916
|
+
invalidate("tasks-list", accountId);
|
|
24422
24917
|
return c.json({ ok: true, running: true, entryId: priorEntryId, startedAt: priorStartedAt });
|
|
24423
24918
|
}
|
|
24424
24919
|
const entryId = randomUUID14();
|
|
@@ -24429,6 +24924,7 @@ app51.post("/", requireAdminSession, async (c) => {
|
|
|
24429
24924
|
{ taskId, accountId, entryId, now }
|
|
24430
24925
|
);
|
|
24431
24926
|
console.log(`${TAG40} op=opened taskId=${taskId} entryId=${entryId} startedAt=${now}`);
|
|
24927
|
+
invalidate("tasks-list", accountId);
|
|
24432
24928
|
return c.json({ ok: true, running: true, entryId, startedAt: now });
|
|
24433
24929
|
} catch (err) {
|
|
24434
24930
|
console.error(`${TAG40} op=start error="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -24478,6 +24974,7 @@ app52.post("/", requireAdminSession, async (c) => {
|
|
|
24478
24974
|
);
|
|
24479
24975
|
const secondsLogged2 = toNum(cur.records[0]?.get("secondsLogged"));
|
|
24480
24976
|
console.log(`${TAG41} op=stop taskId=${taskId} entryId=none seconds=0 totalAfter=${secondsLogged2}`);
|
|
24977
|
+
invalidate("tasks-list", accountId);
|
|
24481
24978
|
return c.json({ ok: true, running: false, seconds: 0, secondsLogged: secondsLogged2 });
|
|
24482
24979
|
}
|
|
24483
24980
|
const seconds = secondsBetween(startedAt, now);
|
|
@@ -24502,6 +24999,7 @@ app52.post("/", requireAdminSession, async (c) => {
|
|
|
24502
24999
|
const secondsLogged = toNum(verify.records[0]?.get("secondsLogged"));
|
|
24503
25000
|
console.log(`${TAG41} op=stop taskId=${taskId} entryId=${entryId} seconds=${seconds} totalAfter=${secondsLogged}`);
|
|
24504
25001
|
console.log(`${TAG41} op=persisted taskId=${taskId} entryId=${entryId} sumEntries=${sumEntries} sumAdjustments=${sumAdjustments} secondsLogged=${secondsLogged}`);
|
|
25002
|
+
invalidate("tasks-list", accountId);
|
|
24505
25003
|
return c.json({ ok: true, running: false, seconds, secondsLogged });
|
|
24506
25004
|
} catch (err) {
|
|
24507
25005
|
console.error(`${TAG41} op=stop error="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -24545,6 +25043,7 @@ app53.post("/", requireAdminSession, async (c) => {
|
|
|
24545
25043
|
}
|
|
24546
25044
|
const secondsLogged = toNum(result.records[0].get("secondsLogged"));
|
|
24547
25045
|
console.log(`${TAG42} op=complete taskId=${taskId} status=completed secondsLogged=${secondsLogged}`);
|
|
25046
|
+
invalidate("tasks-list", accountId);
|
|
24548
25047
|
return c.json({ ok: true, status: "completed", secondsLogged });
|
|
24549
25048
|
} catch (err) {
|
|
24550
25049
|
console.error(`${TAG42} op=complete error="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -24626,6 +25125,7 @@ app54.post("/", requireAdminSession, async (c) => {
|
|
|
24626
25125
|
);
|
|
24627
25126
|
const secondsLoggedAfter = toNum(write.records[0]?.get("secondsLoggedAfter"));
|
|
24628
25127
|
console.log(`${TAG43} op=persisted adjustmentId=${adjustmentId} previousSeconds=${previousSeconds} newSeconds=${newSeconds} delta=${delta} secondsLoggedAfter=${secondsLoggedAfter}`);
|
|
25128
|
+
invalidate("tasks-list", accountId);
|
|
24629
25129
|
return c.json({ ok: true, secondsLogged: secondsLoggedAfter });
|
|
24630
25130
|
} catch (err) {
|
|
24631
25131
|
console.error(`${TAG43} op=error adjustmentId=${adjustmentId} taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -24638,16 +25138,16 @@ var task_time_adjust_default = app54;
|
|
|
24638
25138
|
|
|
24639
25139
|
// app/lib/admin-roster.ts
|
|
24640
25140
|
import { existsSync as existsSync39, readdirSync as readdirSync25, readFileSync as readFileSync40 } from "fs";
|
|
24641
|
-
import { resolve as
|
|
25141
|
+
import { resolve as resolve34 } from "path";
|
|
24642
25142
|
function readHouseAdminUserIds(accountsDir = ACCOUNTS_DIR) {
|
|
24643
|
-
let
|
|
25143
|
+
let entries2;
|
|
24644
25144
|
try {
|
|
24645
|
-
|
|
25145
|
+
entries2 = readdirSync25(accountsDir);
|
|
24646
25146
|
} catch {
|
|
24647
25147
|
return [];
|
|
24648
25148
|
}
|
|
24649
|
-
for (const entry of
|
|
24650
|
-
const cfgPath =
|
|
25149
|
+
for (const entry of entries2) {
|
|
25150
|
+
const cfgPath = resolve34(accountsDir, entry, "account.json");
|
|
24651
25151
|
if (!existsSync39(cfgPath)) continue;
|
|
24652
25152
|
try {
|
|
24653
25153
|
const cfg = JSON.parse(readFileSync40(cfgPath, "utf-8"));
|
|
@@ -24771,6 +25271,7 @@ app55.post("/", requireAdminSession, async (c) => {
|
|
|
24771
25271
|
{ taskId, accountId, now }
|
|
24772
25272
|
);
|
|
24773
25273
|
console.log(`${TAG44} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=none assigned=false source=route`);
|
|
25274
|
+
invalidate("tasks-list", accountId);
|
|
24774
25275
|
return c.json({ ok: true, assigneeUserId: null, assigneeName: null });
|
|
24775
25276
|
}
|
|
24776
25277
|
const set = await session.run(
|
|
@@ -24795,6 +25296,7 @@ app55.post("/", requireAdminSession, async (c) => {
|
|
|
24795
25296
|
console.log(
|
|
24796
25297
|
`${TAG44} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=true source=route`
|
|
24797
25298
|
);
|
|
25299
|
+
invalidate("tasks-list", accountId);
|
|
24798
25300
|
return c.json({ ok: true, assigneeUserId, assigneeName });
|
|
24799
25301
|
} catch (err) {
|
|
24800
25302
|
console.error(`${TAG44} op=assign error="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -25103,9 +25605,9 @@ var verify_token_default = app59;
|
|
|
25103
25605
|
|
|
25104
25606
|
// app/lib/access-email.ts
|
|
25105
25607
|
import { spawn as spawn2 } from "child_process";
|
|
25106
|
-
import { resolve as
|
|
25107
|
-
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ??
|
|
25108
|
-
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(
|
|
25109
25611
|
PLATFORM_ROOT7,
|
|
25110
25612
|
"plugins",
|
|
25111
25613
|
"email",
|
|
@@ -25248,7 +25750,7 @@ var access_default = app61;
|
|
|
25248
25750
|
|
|
25249
25751
|
// server/routes/sites.ts
|
|
25250
25752
|
import { existsSync as existsSync40, readFileSync as readFileSync41, realpathSync as realpathSync8, statSync as statSync17 } from "fs";
|
|
25251
|
-
import { resolve as
|
|
25753
|
+
import { resolve as resolve36 } from "path";
|
|
25252
25754
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
25253
25755
|
var MIME = {
|
|
25254
25756
|
".html": "text/html; charset=utf-8",
|
|
@@ -25305,8 +25807,8 @@ app62.get("/:rel{.*}", (c) => {
|
|
|
25305
25807
|
}
|
|
25306
25808
|
segments.push(seg);
|
|
25307
25809
|
}
|
|
25308
|
-
const rootDir =
|
|
25309
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
25810
|
+
const rootDir = resolve36(account.accountDir, "sites");
|
|
25811
|
+
let filePath = segments.length === 0 ? rootDir : resolve36(rootDir, ...segments);
|
|
25310
25812
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
25311
25813
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
25312
25814
|
return c.text("Forbidden", 403);
|
|
@@ -25326,7 +25828,7 @@ app62.get("/:rel{.*}", (c) => {
|
|
|
25326
25828
|
return c.redirect(target, 301);
|
|
25327
25829
|
}
|
|
25328
25830
|
if (stat11?.isDirectory()) {
|
|
25329
|
-
filePath =
|
|
25831
|
+
filePath = resolve36(filePath, "index.html");
|
|
25330
25832
|
}
|
|
25331
25833
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
25332
25834
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
@@ -25972,14 +26474,14 @@ async function writeEvent(opts) {
|
|
|
25972
26474
|
var visitor_event_default = app65;
|
|
25973
26475
|
|
|
25974
26476
|
// server/routes/session.ts
|
|
25975
|
-
import { resolve as
|
|
26477
|
+
import { resolve as resolve37 } from "path";
|
|
25976
26478
|
import { existsSync as existsSync42, writeFileSync as writeFileSync15, mkdirSync as mkdirSync9 } from "fs";
|
|
25977
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;
|
|
25978
26480
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
25979
26481
|
try {
|
|
25980
|
-
const cacheDir =
|
|
26482
|
+
const cacheDir = resolve37(MAXY_DIR, "branding-cache", accountId);
|
|
25981
26483
|
mkdirSync9(cacheDir, { recursive: true });
|
|
25982
|
-
writeFileSync15(
|
|
26484
|
+
writeFileSync15(resolve37(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
25983
26485
|
} catch (err) {
|
|
25984
26486
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25985
26487
|
}
|
|
@@ -26056,8 +26558,8 @@ app66.post("/", async (c) => {
|
|
|
26056
26558
|
}
|
|
26057
26559
|
let agentConfig = null;
|
|
26058
26560
|
if (account) {
|
|
26059
|
-
const agentDir =
|
|
26060
|
-
if (!existsSync42(agentDir) || !existsSync42(
|
|
26561
|
+
const agentDir = resolve37(account.accountDir, "agents", agentSlug);
|
|
26562
|
+
if (!existsSync42(agentDir) || !existsSync42(resolve37(agentDir, "config.json"))) {
|
|
26061
26563
|
return c.json({ error: "Agent not found" }, 404);
|
|
26062
26564
|
}
|
|
26063
26565
|
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
@@ -26317,7 +26819,7 @@ var calendar_public_default = app67;
|
|
|
26317
26819
|
// server/routes/portal-fetch.ts
|
|
26318
26820
|
import { createReadStream as createReadStream3 } from "fs";
|
|
26319
26821
|
import { stat as stat8, readFile as readFile9, realpath } from "fs/promises";
|
|
26320
|
-
import { join as join44, resolve as
|
|
26822
|
+
import { join as join44, resolve as resolve38, sep as sep10 } from "path";
|
|
26321
26823
|
import { Readable as Readable3 } from "stream";
|
|
26322
26824
|
var app68 = new Hono();
|
|
26323
26825
|
var TAG49 = "[portal-fetch]";
|
|
@@ -26370,7 +26872,7 @@ ${expiresAtMs}`)
|
|
|
26370
26872
|
return diff === 0;
|
|
26371
26873
|
}
|
|
26372
26874
|
async function readAccountSecret(accountId) {
|
|
26373
|
-
const path3 =
|
|
26875
|
+
const path3 = resolve38(DATA_ROOT, "accounts", accountId, "secrets", "data-portal.env");
|
|
26374
26876
|
let text;
|
|
26375
26877
|
try {
|
|
26376
26878
|
text = await readFile9(path3, "utf8");
|
|
@@ -26406,7 +26908,7 @@ async function resolveFetchTarget(accountDir, relPath, ownerId) {
|
|
|
26406
26908
|
const exposeFolders = await readExposeFolders2(accountDir);
|
|
26407
26909
|
const { exposed } = resolveExposedDirs(schemaText, exposeFolders);
|
|
26408
26910
|
if (exposed.length === 0 && !ownerId) return { ok: false, reason: "not-exposed" };
|
|
26409
|
-
const lexical =
|
|
26911
|
+
const lexical = resolve38(accountDir, relPath);
|
|
26410
26912
|
let root;
|
|
26411
26913
|
let absolute;
|
|
26412
26914
|
try {
|
|
@@ -26455,7 +26957,7 @@ async function handle(c, headOnly) {
|
|
|
26455
26957
|
return c.json({ ok: false, error: "denied" }, 401);
|
|
26456
26958
|
}
|
|
26457
26959
|
const target = await resolveFetchTarget(
|
|
26458
|
-
|
|
26960
|
+
resolve38(DATA_ROOT, "accounts", accountId),
|
|
26459
26961
|
relPath,
|
|
26460
26962
|
owner || void 0
|
|
26461
26963
|
);
|
|
@@ -26616,14 +27118,14 @@ function formatCensusLine(f) {
|
|
|
26616
27118
|
var TAG51 = "[ledger-census]";
|
|
26617
27119
|
var DAY_MS = 864e5;
|
|
26618
27120
|
function countWriteRejects24h(logDir, now) {
|
|
26619
|
-
let
|
|
27121
|
+
let entries2;
|
|
26620
27122
|
try {
|
|
26621
|
-
|
|
27123
|
+
entries2 = readdirSync27(logDir).filter((f) => f.startsWith("mcp-ledger-") && f.endsWith(".log"));
|
|
26622
27124
|
} catch {
|
|
26623
27125
|
return 0;
|
|
26624
27126
|
}
|
|
26625
27127
|
let total = 0;
|
|
26626
|
-
for (const name of
|
|
27128
|
+
for (const name of entries2) {
|
|
26627
27129
|
const path3 = join45(logDir, name);
|
|
26628
27130
|
try {
|
|
26629
27131
|
if (now - statSync18(path3).mtimeMs > DAY_MS) continue;
|
|
@@ -26778,7 +27280,7 @@ async function runConversationAudit() {
|
|
|
26778
27280
|
}
|
|
26779
27281
|
|
|
26780
27282
|
// app/lib/graph-health.ts
|
|
26781
|
-
var
|
|
27283
|
+
var import_dist7 = __toESM(require_dist5(), 1);
|
|
26782
27284
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
26783
27285
|
function renderLabelTop(rows) {
|
|
26784
27286
|
return rows.map((b) => {
|
|
@@ -26823,7 +27325,7 @@ async function runGraphHealthTick() {
|
|
|
26823
27325
|
console.error(
|
|
26824
27326
|
`[graph-health] userprofile-multi accounts=${upAccounts} top=${upTop.length > 0 ? upTop.join(",") : "none"}`
|
|
26825
27327
|
);
|
|
26826
|
-
const indexed = [...
|
|
27328
|
+
const indexed = [...import_dist7.VECTOR_INDEXED_LABELS];
|
|
26827
27329
|
const embCount = await session.run(
|
|
26828
27330
|
`MATCH (n) WHERE n.embedding IS NULL AND any(l IN labels(n) WHERE l IN $indexed)
|
|
26829
27331
|
RETURN count(n) AS total`,
|
|
@@ -26896,7 +27398,7 @@ function startGraphHealthTimer() {
|
|
|
26896
27398
|
|
|
26897
27399
|
// app/lib/file-watcher.ts
|
|
26898
27400
|
import * as fsp2 from "fs/promises";
|
|
26899
|
-
import { resolve as
|
|
27401
|
+
import { resolve as resolve39, sep as sep11 } from "path";
|
|
26900
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;
|
|
26901
27403
|
var DEFAULT_COALESCE_MS = 500;
|
|
26902
27404
|
var ROOTS = ["accounts"];
|
|
@@ -26915,7 +27417,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
26915
27417
|
const dropFn = opts.drop ?? dropFileIndex;
|
|
26916
27418
|
for (const r of ROOTS) {
|
|
26917
27419
|
try {
|
|
26918
|
-
await fsp2.mkdir(
|
|
27420
|
+
await fsp2.mkdir(resolve39(dataRoot, r), { recursive: true });
|
|
26919
27421
|
} catch (err) {
|
|
26920
27422
|
console.error(
|
|
26921
27423
|
`[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
|
|
@@ -26936,7 +27438,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
26936
27438
|
timers.set(relativePath, t);
|
|
26937
27439
|
}
|
|
26938
27440
|
async function runHook(relativePath, accountId) {
|
|
26939
|
-
const absolute =
|
|
27441
|
+
const absolute = resolve39(dataRoot, relativePath);
|
|
26940
27442
|
let exists = false;
|
|
26941
27443
|
try {
|
|
26942
27444
|
const st = await fsp2.stat(absolute);
|
|
@@ -26960,7 +27462,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
26960
27462
|
}
|
|
26961
27463
|
}
|
|
26962
27464
|
async function watchRoot(rootName) {
|
|
26963
|
-
const absRoot =
|
|
27465
|
+
const absRoot = resolve39(dataRoot, rootName);
|
|
26964
27466
|
try {
|
|
26965
27467
|
const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
|
|
26966
27468
|
for await (const event of iter) {
|
|
@@ -26999,18 +27501,18 @@ async function startFileWatcher(opts = {}) {
|
|
|
26999
27501
|
|
|
27000
27502
|
// app/lib/migrate-uploads.ts
|
|
27001
27503
|
import { mkdir as mkdir6, readdir as readdir6, rename as rename3, rm as rm3 } from "fs/promises";
|
|
27002
|
-
import { dirname as dirname15, relative as relative7, resolve as
|
|
27504
|
+
import { dirname as dirname15, relative as relative7, resolve as resolve40 } from "path";
|
|
27003
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;
|
|
27004
27506
|
async function walkFiles(dir) {
|
|
27005
27507
|
const out = [];
|
|
27006
|
-
let
|
|
27508
|
+
let entries2;
|
|
27007
27509
|
try {
|
|
27008
|
-
|
|
27510
|
+
entries2 = await readdir6(dir, { withFileTypes: true });
|
|
27009
27511
|
} catch {
|
|
27010
27512
|
return out;
|
|
27011
27513
|
}
|
|
27012
|
-
for (const e of
|
|
27013
|
-
const abs =
|
|
27514
|
+
for (const e of entries2) {
|
|
27515
|
+
const abs = resolve40(dir, e.name);
|
|
27014
27516
|
if (e.isDirectory()) {
|
|
27015
27517
|
out.push(...await walkFiles(abs));
|
|
27016
27518
|
} else if (e.isFile()) {
|
|
@@ -27030,7 +27532,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
27030
27532
|
let moved = 0;
|
|
27031
27533
|
for (const oldAbs of files) {
|
|
27032
27534
|
const suffix = relative7(srcDir, oldAbs);
|
|
27033
|
-
const newAbs =
|
|
27535
|
+
const newAbs = resolve40(destDir, suffix);
|
|
27034
27536
|
await mkdir6(dirname15(newAbs), { recursive: true });
|
|
27035
27537
|
await rename3(oldAbs, newAbs);
|
|
27036
27538
|
moved++;
|
|
@@ -27057,7 +27559,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
27057
27559
|
}
|
|
27058
27560
|
async function migrateUploads(opts = {}) {
|
|
27059
27561
|
const dataRoot = opts.dataRoot ?? DATA_ROOT;
|
|
27060
|
-
const oldRoot =
|
|
27562
|
+
const oldRoot = resolve40(dataRoot, "uploads");
|
|
27061
27563
|
let topEntries;
|
|
27062
27564
|
try {
|
|
27063
27565
|
topEntries = await readdir6(oldRoot, { withFileTypes: true });
|
|
@@ -27078,8 +27580,8 @@ async function migrateUploads(opts = {}) {
|
|
|
27078
27580
|
const name = entry.name;
|
|
27079
27581
|
if (ACCOUNT_UUID_RE5.test(name)) {
|
|
27080
27582
|
moved += await relocateTree(
|
|
27081
|
-
|
|
27082
|
-
|
|
27583
|
+
resolve40(oldRoot, name),
|
|
27584
|
+
resolve40(dataRoot, "accounts", name, "uploads"),
|
|
27083
27585
|
dataRoot,
|
|
27084
27586
|
name,
|
|
27085
27587
|
session
|
|
@@ -27091,8 +27593,8 @@ async function migrateUploads(opts = {}) {
|
|
|
27091
27593
|
continue;
|
|
27092
27594
|
}
|
|
27093
27595
|
moved += await relocateTree(
|
|
27094
|
-
|
|
27095
|
-
|
|
27596
|
+
resolve40(oldRoot, "public"),
|
|
27597
|
+
resolve40(dataRoot, "accounts", installAccountId, "uploads", "public"),
|
|
27096
27598
|
dataRoot,
|
|
27097
27599
|
null,
|
|
27098
27600
|
// public uploads carry no graph nodes
|
|
@@ -27169,13 +27671,13 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
27169
27671
|
for (const slug of slugs) {
|
|
27170
27672
|
if (!slug.isDirectory()) continue;
|
|
27171
27673
|
const slugDir = join46(projectsRoot, slug.name);
|
|
27172
|
-
let
|
|
27674
|
+
let entries2;
|
|
27173
27675
|
try {
|
|
27174
|
-
|
|
27676
|
+
entries2 = await readdir7(slugDir, { withFileTypes: true });
|
|
27175
27677
|
} catch {
|
|
27176
27678
|
continue;
|
|
27177
27679
|
}
|
|
27178
|
-
for (const entry of
|
|
27680
|
+
for (const entry of entries2) {
|
|
27179
27681
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
27180
27682
|
out.push(join46(slugDir, entry.name));
|
|
27181
27683
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
@@ -28234,16 +28736,16 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
28234
28736
|
* The public /api/chat route awaits this to return the reply on the same SSE
|
|
28235
28737
|
* response, preserving the pre-756 POST→single-blob browser contract. */
|
|
28236
28738
|
awaitReply(key, timeoutMs) {
|
|
28237
|
-
return new Promise((
|
|
28739
|
+
return new Promise((resolve46) => {
|
|
28238
28740
|
const timer = setTimeout(() => {
|
|
28239
28741
|
this.replyAwaiters.delete(key);
|
|
28240
|
-
|
|
28742
|
+
resolve46({ timeout: true });
|
|
28241
28743
|
}, timeoutMs);
|
|
28242
28744
|
if (timer.unref) timer.unref();
|
|
28243
28745
|
this.replyAwaiters.set(key, (r) => {
|
|
28244
28746
|
clearTimeout(timer);
|
|
28245
28747
|
this.replyAwaiters.delete(key);
|
|
28246
|
-
|
|
28748
|
+
resolve46(r);
|
|
28247
28749
|
});
|
|
28248
28750
|
});
|
|
28249
28751
|
}
|
|
@@ -28254,11 +28756,11 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
28254
28756
|
* terminal dialog stays answerable). */
|
|
28255
28757
|
awaitPermissionVerdict(p, timeoutMs) {
|
|
28256
28758
|
const k = _WebchatGateway.promptKey(p.key, p.requestId);
|
|
28257
|
-
return new Promise((
|
|
28759
|
+
return new Promise((resolve46) => {
|
|
28258
28760
|
this.pendingPrompts.get(k)?.resolve({ timeout: true });
|
|
28259
28761
|
const timer = setTimeout(() => {
|
|
28260
28762
|
this.pendingPrompts.delete(k);
|
|
28261
|
-
|
|
28763
|
+
resolve46({ timeout: true });
|
|
28262
28764
|
}, timeoutMs);
|
|
28263
28765
|
if (timer.unref) timer.unref();
|
|
28264
28766
|
this.pendingPrompts.set(k, {
|
|
@@ -28267,7 +28769,7 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
28267
28769
|
resolve: (r) => {
|
|
28268
28770
|
clearTimeout(timer);
|
|
28269
28771
|
this.pendingPrompts.delete(k);
|
|
28270
|
-
|
|
28772
|
+
resolve46(r);
|
|
28271
28773
|
}
|
|
28272
28774
|
});
|
|
28273
28775
|
console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
|
|
@@ -28832,7 +29334,7 @@ async function fanOut(subscribers, text, onError, tag) {
|
|
|
28832
29334
|
|
|
28833
29335
|
// app/lib/webchat/file-delivery.ts
|
|
28834
29336
|
import { realpathSync as realpathSync9 } from "fs";
|
|
28835
|
-
import { resolve as
|
|
29337
|
+
import { resolve as resolve41 } from "path";
|
|
28836
29338
|
|
|
28837
29339
|
// app/lib/channel-pty-bridge/file-delivery.ts
|
|
28838
29340
|
var SEND_USER_FILE = "SendUserFile";
|
|
@@ -28933,7 +29435,7 @@ function makeWebchatSendFile(entry) {
|
|
|
28933
29435
|
console.error(`${TAG53} file-delivery reject reason=no-account sender=${entry.senderId}`);
|
|
28934
29436
|
return { ok: false, error: "no-account" };
|
|
28935
29437
|
}
|
|
28936
|
-
const accountDir =
|
|
29438
|
+
const accountDir = resolve41(platformRoot2(), "..", "data/accounts", entry.accountId);
|
|
28937
29439
|
try {
|
|
28938
29440
|
const resolved = realpathSync9(filePath);
|
|
28939
29441
|
const accountResolved = realpathSync9(accountDir);
|
|
@@ -29497,7 +29999,7 @@ function buildTelegramSpawnRequest(input) {
|
|
|
29497
29999
|
// app/lib/telegram/outbound/send-document.ts
|
|
29498
30000
|
import { realpathSync as realpathSync10 } from "fs";
|
|
29499
30001
|
import { readFile as readFile11, stat as stat10 } from "fs/promises";
|
|
29500
|
-
import { resolve as
|
|
30002
|
+
import { resolve as resolve42, basename as basename13 } from "path";
|
|
29501
30003
|
var TAG55 = "[telegram:outbound]";
|
|
29502
30004
|
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
29503
30005
|
async function sendTelegramDocument(input) {
|
|
@@ -29508,7 +30010,7 @@ async function sendTelegramDocument(input) {
|
|
|
29508
30010
|
if (!maxyAccountId || !platformRoot5) {
|
|
29509
30011
|
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
29510
30012
|
}
|
|
29511
|
-
const accountDir =
|
|
30013
|
+
const accountDir = resolve42(platformRoot5, "..", "data/accounts", maxyAccountId);
|
|
29512
30014
|
let accountResolved;
|
|
29513
30015
|
try {
|
|
29514
30016
|
accountResolved = realpathSync10(accountDir);
|
|
@@ -29800,11 +30302,11 @@ async function firePublicSessionEndReview(input) {
|
|
|
29800
30302
|
}
|
|
29801
30303
|
|
|
29802
30304
|
// app/lib/whatsapp/inbound/resolve-client-graph-owner.ts
|
|
29803
|
-
var
|
|
30305
|
+
var import_dist8 = __toESM(require_dist(), 1);
|
|
29804
30306
|
async function resolveClientOwnerUserId(accountId, deps = {}) {
|
|
29805
30307
|
const listAccounts = deps.listAccounts ?? listValidAccounts;
|
|
29806
30308
|
const getSession4 = deps.getSession ?? getSession;
|
|
29807
|
-
const resolveOwner2 = deps.resolveOwner ??
|
|
30309
|
+
const resolveOwner2 = deps.resolveOwner ?? import_dist8.resolveOwnerUserId;
|
|
29808
30310
|
const role = listAccounts().find((a) => a.accountId === accountId)?.config.role;
|
|
29809
30311
|
if (role !== "client") return null;
|
|
29810
30312
|
const session = getSession4();
|
|
@@ -29901,10 +30403,10 @@ function warnOnChannelAdminBindingDrift() {
|
|
|
29901
30403
|
|
|
29902
30404
|
// server/lib/booking-site-accounts.ts
|
|
29903
30405
|
import { existsSync as existsSync44, readFileSync as readFileSync45 } from "fs";
|
|
29904
|
-
import { resolve as
|
|
30406
|
+
import { resolve as resolve43 } from "path";
|
|
29905
30407
|
function listBookingSiteAccounts(accounts) {
|
|
29906
30408
|
return accounts.filter((a) => {
|
|
29907
|
-
const availPath =
|
|
30409
|
+
const availPath = resolve43(a.accountDir, "calendar-availability.json");
|
|
29908
30410
|
if (!existsSync44(availPath)) return false;
|
|
29909
30411
|
try {
|
|
29910
30412
|
const cfg = JSON.parse(readFileSync45(availPath, "utf-8"));
|
|
@@ -29946,7 +30448,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
29946
30448
|
// ../lib/entitlement/src/index.ts
|
|
29947
30449
|
import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
|
|
29948
30450
|
import { existsSync as existsSync45, readFileSync as readFileSync46, statSync as statSync19 } from "fs";
|
|
29949
|
-
import { resolve as
|
|
30451
|
+
import { resolve as resolve44 } from "path";
|
|
29950
30452
|
|
|
29951
30453
|
// ../lib/entitlement/src/canonicalize.ts
|
|
29952
30454
|
function canonicalize(value) {
|
|
@@ -29981,7 +30483,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
29981
30483
|
var GRACE_DAYS = 7;
|
|
29982
30484
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
29983
30485
|
function pubkeyPath(brand) {
|
|
29984
|
-
return
|
|
30486
|
+
return resolve44(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
29985
30487
|
}
|
|
29986
30488
|
var memo = null;
|
|
29987
30489
|
function memoKey(mtimeMs, account) {
|
|
@@ -29993,7 +30495,7 @@ function resolveEntitlement(brand, account) {
|
|
|
29993
30495
|
if (brand.commercialMode !== true) {
|
|
29994
30496
|
return logResolved(implicitTrust(account), null);
|
|
29995
30497
|
}
|
|
29996
|
-
const entitlementPath =
|
|
30498
|
+
const entitlementPath = resolve44(brand.configDir, "entitlement.json");
|
|
29997
30499
|
if (!existsSync45(entitlementPath)) {
|
|
29998
30500
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
29999
30501
|
}
|
|
@@ -30249,6 +30751,7 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
|
|
|
30249
30751
|
aliasDomains = updated;
|
|
30250
30752
|
console.log(`[alias-domains] reloaded ${updated.size} alias domain(s): ${[...updated].join(", ")}`);
|
|
30251
30753
|
});
|
|
30754
|
+
attachRouteCacheWatchers();
|
|
30252
30755
|
function isPublicHost(host) {
|
|
30253
30756
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
30254
30757
|
}
|
|
@@ -30266,8 +30769,8 @@ var webchatFileFollowers = /* @__PURE__ */ new Map();
|
|
|
30266
30769
|
async function fetchAccountStandingRules(accountId) {
|
|
30267
30770
|
const session = getSession();
|
|
30268
30771
|
try {
|
|
30269
|
-
const res = await (0,
|
|
30270
|
-
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 };
|
|
30271
30774
|
} catch (err) {
|
|
30272
30775
|
console.error(
|
|
30273
30776
|
`[preference-inject] op=fetch-failed accountId=${accountId} error=${err instanceof Error ? err.message : String(err)}`
|
|
@@ -30301,7 +30804,7 @@ var waGateway = new WaGateway({
|
|
|
30301
30804
|
fetchStandingRules: fetchAccountStandingRules,
|
|
30302
30805
|
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
30303
30806
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
30304
|
-
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"),
|
|
30305
30808
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
30306
30809
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
30307
30810
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -30316,7 +30819,7 @@ var waGateway = new WaGateway({
|
|
|
30316
30819
|
caption,
|
|
30317
30820
|
accountId,
|
|
30318
30821
|
maxyAccountId,
|
|
30319
|
-
platformRoot:
|
|
30822
|
+
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, ".."))
|
|
30320
30823
|
});
|
|
30321
30824
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
30322
30825
|
},
|
|
@@ -30996,8 +31499,8 @@ app69.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
30996
31499
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
30997
31500
|
return c.text("Not found", 404);
|
|
30998
31501
|
}
|
|
30999
|
-
const filePath =
|
|
31000
|
-
const expectedDir =
|
|
31502
|
+
const filePath = resolve45(account.accountDir, "agents", slug, "assets", filename);
|
|
31503
|
+
const expectedDir = resolve45(account.accountDir, "agents", slug, "assets");
|
|
31001
31504
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
31002
31505
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
31003
31506
|
return c.text("Forbidden", 403);
|
|
@@ -31026,8 +31529,8 @@ app69.get("/generated/:filename", (c) => {
|
|
|
31026
31529
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
31027
31530
|
return c.text("Not found", 404);
|
|
31028
31531
|
}
|
|
31029
|
-
const filePath =
|
|
31030
|
-
const expectedDir =
|
|
31532
|
+
const filePath = resolve45(account.accountDir, "generated", filename);
|
|
31533
|
+
const expectedDir = resolve45(account.accountDir, "generated");
|
|
31031
31534
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
31032
31535
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
31033
31536
|
return c.text("Forbidden", 403);
|
|
@@ -31081,11 +31584,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
31081
31584
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
31082
31585
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
31083
31586
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
31084
|
-
(p) => existsSync46(
|
|
31587
|
+
(p) => existsSync46(resolve45(process.cwd(), "public", p.replace(/^\//, "")))
|
|
31085
31588
|
);
|
|
31086
31589
|
var SW_SOURCE = (() => {
|
|
31087
31590
|
try {
|
|
31088
|
-
return readFileSync47(
|
|
31591
|
+
return readFileSync47(resolve45(process.cwd(), "public", "sw.js"), "utf-8");
|
|
31089
31592
|
} catch {
|
|
31090
31593
|
return null;
|
|
31091
31594
|
}
|
|
@@ -31136,7 +31639,7 @@ var clientErrorReporterScript = `<script>
|
|
|
31136
31639
|
function cachedHtml(file) {
|
|
31137
31640
|
let html = htmlCache.get(file);
|
|
31138
31641
|
if (!html) {
|
|
31139
|
-
html = readFileSync47(
|
|
31642
|
+
html = readFileSync47(resolve45(process.cwd(), "public", file), "utf-8");
|
|
31140
31643
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
31141
31644
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
31142
31645
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -31254,7 +31757,7 @@ app69.use("/vnc-popout.html", logViewerFetch);
|
|
|
31254
31757
|
app69.get("/vnc-popout.html", (c) => {
|
|
31255
31758
|
let html = htmlCache.get("vnc-popout.html");
|
|
31256
31759
|
if (!html) {
|
|
31257
|
-
html = readFileSync47(
|
|
31760
|
+
html = readFileSync47(resolve45(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
31258
31761
|
const name = escapeHtml(BRAND.productName);
|
|
31259
31762
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
31260
31763
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -31407,7 +31910,7 @@ var httpServer = serve({ fetch: app69.fetch, port, hostname });
|
|
|
31407
31910
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
31408
31911
|
{
|
|
31409
31912
|
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31410
|
-
const reconcileScript =
|
|
31913
|
+
const reconcileScript = resolve45(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
31411
31914
|
const runReconcile = (ctx) => {
|
|
31412
31915
|
if (!existsSync46(reconcileScript)) return;
|
|
31413
31916
|
try {
|
|
@@ -31448,7 +31951,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31448
31951
|
{
|
|
31449
31952
|
const DREAM_CYCLE_SWEEP_INTERVAL_MS = 36e5;
|
|
31450
31953
|
const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31451
|
-
const dreamSweepScript =
|
|
31954
|
+
const dreamSweepScript = resolve45(dreamPlatformRoot, "plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js");
|
|
31452
31955
|
registerLoop({
|
|
31453
31956
|
name: "dream-cycle-sweep",
|
|
31454
31957
|
intervalMs: DREAM_CYCLE_SWEEP_INTERVAL_MS,
|
|
@@ -31472,7 +31975,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31472
31975
|
}
|
|
31473
31976
|
{
|
|
31474
31977
|
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31475
|
-
const outlookScript =
|
|
31978
|
+
const outlookScript = resolve45(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
|
|
31476
31979
|
const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
|
|
31477
31980
|
const runOutlookComplete = (ctx) => {
|
|
31478
31981
|
if (!existsSync46(outlookScript)) return;
|
|
@@ -31498,7 +32001,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31498
32001
|
}
|
|
31499
32002
|
{
|
|
31500
32003
|
const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31501
|
-
const strandedAccountsDir =
|
|
32004
|
+
const strandedAccountsDir = resolve45(auditRoot, "..", "data/accounts");
|
|
31502
32005
|
const STRANDED_AUDIT_INTERVAL_MS = 3e5;
|
|
31503
32006
|
const STRANDED_AGE_MS = 16 * 6e4;
|
|
31504
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;
|
|
@@ -31508,7 +32011,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31508
32011
|
const now = Date.now();
|
|
31509
32012
|
for (const name of readdirSync28(strandedAccountsDir)) {
|
|
31510
32013
|
if (!STRANDED_UUID_RE.test(name)) continue;
|
|
31511
|
-
const pendingPath2 =
|
|
32014
|
+
const pendingPath2 = resolve45(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
|
|
31512
32015
|
if (!existsSync46(pendingPath2)) continue;
|
|
31513
32016
|
const ageMs2 = now - statSync20(pendingPath2).mtimeMs;
|
|
31514
32017
|
if (ageMs2 > STRANDED_AGE_MS) {
|
|
@@ -31528,7 +32031,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31528
32031
|
}
|
|
31529
32032
|
{
|
|
31530
32033
|
const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31531
|
-
const googleAccountsDir =
|
|
32034
|
+
const googleAccountsDir = resolve45(googleRoot, "..", "data/accounts");
|
|
31532
32035
|
const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
|
|
31533
32036
|
const runGooglePendingAuditSafe = () => {
|
|
31534
32037
|
try {
|
|
@@ -31543,7 +32046,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31543
32046
|
firstRunDelayMs: 27e3,
|
|
31544
32047
|
run: runGooglePendingAuditSafe
|
|
31545
32048
|
});
|
|
31546
|
-
const googleAuditScript =
|
|
32049
|
+
const googleAuditScript = resolve45(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
|
|
31547
32050
|
const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
|
|
31548
32051
|
const runGoogleAccountAudit = (ctx) => {
|
|
31549
32052
|
if (!existsSync46(googleAuditScript)) return;
|
|
@@ -31618,7 +32121,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31618
32121
|
}
|
|
31619
32122
|
{
|
|
31620
32123
|
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31621
|
-
const publishScript =
|
|
32124
|
+
const publishScript = resolve45(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
31622
32125
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
31623
32126
|
const bookingAccounts = () => {
|
|
31624
32127
|
try {
|
|
@@ -31648,7 +32151,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31648
32151
|
}
|
|
31649
32152
|
};
|
|
31650
32153
|
const auditSnapshotAge = (account) => {
|
|
31651
|
-
const availPath =
|
|
32154
|
+
const availPath = resolve45(account.accountDir, "calendar-availability.json");
|
|
31652
32155
|
let hasBookingSite = false;
|
|
31653
32156
|
try {
|
|
31654
32157
|
if (existsSync46(availPath)) {
|
|
@@ -31658,7 +32161,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
31658
32161
|
} catch {
|
|
31659
32162
|
}
|
|
31660
32163
|
if (!hasBookingSite) return;
|
|
31661
|
-
const statePath =
|
|
32164
|
+
const statePath = resolve45(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
31662
32165
|
let lastSuccessAt = null;
|
|
31663
32166
|
if (existsSync46(statePath)) {
|
|
31664
32167
|
try {
|
|
@@ -31695,7 +32198,7 @@ startTimeEntryCensus(() => getSession());
|
|
|
31695
32198
|
startLedgerCensus(() => getSession());
|
|
31696
32199
|
{
|
|
31697
32200
|
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..");
|
|
31698
|
-
const auditScript =
|
|
32201
|
+
const auditScript = resolve45(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
31699
32202
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
31700
32203
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
31701
32204
|
const runConnectorAudit = (ctx) => {
|
|
@@ -32004,7 +32507,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
32004
32507
|
}
|
|
32005
32508
|
init({
|
|
32006
32509
|
configDir: configDirForWhatsApp,
|
|
32007
|
-
platformRoot:
|
|
32510
|
+
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ?? join47(__dirname, "..")),
|
|
32008
32511
|
accountConfig: bootAccountConfig,
|
|
32009
32512
|
onMessage: async (msg) => {
|
|
32010
32513
|
if (isObserveAccount(msg.accountId)) {
|