@rubytech/create-sitedesk-code 0.1.507 → 0.1.509
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +53 -0
- package/package.json +2 -2
- package/payload/platform/config/brand.json +12 -0
- package/payload/platform/docs/superpowers/plans/2026-07-26-task-1926-portal-push-concurrency-guard.md +482 -0
- package/payload/platform/docs/superpowers/specs/2026-07-26-task-1926-portal-push-concurrency-guard-design.md +200 -0
- package/payload/platform/plugins/admin/PLUGIN.md +10 -5
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +96 -13
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +13 -0
- package/payload/platform/plugins/cloudflare/bin/portal-index-push.mjs +68 -28
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-index-push.test.ts +271 -29
- package/payload/platform/plugins/cloudflare/skills/data-portal/schema.sql +5 -4
- package/payload/platform/plugins/docs/references/admin-ui.md +95 -12
- package/payload/platform/plugins/memory/PLUGIN.md +6 -3
- package/payload/platform/scripts/__tests__/check-brand-cache.test.sh +231 -0
- package/payload/platform/scripts/__tests__/provision-honours-disabled-agents.test.sh +84 -0
- package/payload/platform/scripts/check-brand-cache.sh +138 -0
- package/payload/platform/scripts/check-risk-class.mjs +7 -2
- package/payload/platform/scripts/check-specialist-tool-surface.mjs +5 -2
- package/payload/platform/scripts/lib/canonical-tool-names.mjs +7 -4
- package/payload/platform/scripts/lib/provision-account-dir.sh +43 -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 +56 -3
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/tool-surface.d.ts +19 -5
- package/payload/platform/services/claude-session-manager/dist/tool-surface.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/tool-surface.js +50 -11
- package/payload/platform/services/claude-session-manager/dist/tool-surface.js.map +1 -1
- package/payload/server/maxy-edge.js +15 -10
- package/payload/server/public/activity.html +5 -5
- package/payload/server/public/agents.html +4 -4
- package/payload/server/public/assets/{AdminLoginScreens-YT7jsh_H.js → AdminLoginScreens-8Jk7hnWH.js} +1 -1
- package/payload/server/public/assets/{AdminShell-Ckht9hmU.js → AdminShell-BMPEWNZk.js} +1 -1
- package/payload/server/public/assets/{activity-DH3Wgc3O.js → activity-ByWYF5dC.js} +1 -1
- package/payload/server/public/assets/admin-CnUWbKcx.js +1 -0
- package/payload/server/public/assets/agents-C0rTfrYd.js +1 -0
- package/payload/server/public/assets/{browser-Bym8QhfA.js → browser-4tTaem8N.js} +1 -1
- package/payload/server/public/assets/{calendar-CFnufJOy.js → calendar-BEjnkKG1.js} +1 -1
- package/payload/server/public/assets/chat-DBgzjDIE.js +1 -0
- package/payload/server/public/assets/chevron-left-DqYv3oFh.js +1 -0
- package/payload/server/public/assets/chevron-right-CQfPGsFb.js +1 -0
- package/payload/server/public/assets/clock-Dn6FHB51.js +1 -0
- package/payload/server/public/assets/data-D_e6Vdfd.js +1 -0
- package/payload/server/public/assets/{file-text-zch0E1GJ.js → file-text-DMKw3nIk.js} +1 -1
- package/payload/server/public/assets/{graph-BavHLzpV.js → graph-C3vW8lvw.js} +1 -1
- package/payload/server/public/assets/{graph-labels-CBwh4g-6.js → graph-labels-BZgd0L6n.js} +1 -1
- package/payload/server/public/assets/{maximize-2-bTn_Ozpi.js → maximize-2-la3IBA3M.js} +1 -1
- package/payload/server/public/assets/{operator-a4IOwf62.js → operator-RRRdqoRQ.js} +1 -1
- package/payload/server/public/assets/{page-3T2mE63b.js → page-BWFHRIAH.js} +1 -1
- package/payload/server/public/assets/{page-D6M1CmPe.js → page-DC11gesX.js} +1 -1
- package/payload/server/public/assets/{public-B3y_YU1G.js → public-DQbbLHQN.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-BGvzNB1b.js → rotate-ccw-OQD5si8N.js} +1 -1
- package/payload/server/public/assets/{routines-C7Dr16am.js → routines-asrBGZUR.js} +1 -1
- package/payload/server/public/assets/{skills-DFeb3XO3.js → skills-D4ECwoxa.js} +1 -1
- package/payload/server/public/assets/{tasks-CSfnCKJe.js → tasks-U3QdbUsO.js} +1 -1
- package/payload/server/public/assets/{time-entry-format-BgZqOOqG.js → time-entry-format-BgdTKgYr.js} +1 -1
- package/payload/server/public/assets/{triangle-alert-DBJ7xz_t.js → triangle-alert-Ck_3VhT9.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-BFOujMl-.js → useCopyFeedback-BdUwyNpa.js} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-CJKjEPNN.js +9 -0
- package/payload/server/public/assets/useSubAccountSwitcher-DOWQXDT8.css +1 -0
- package/payload/server/public/assets/{useVoiceRecorder-BP6CpYQD.js → useVoiceRecorder-ChnBrzbZ.js} +1 -1
- package/payload/server/public/assets/{wrench-DdaRhfzS.js → wrench-Cpee3C3J.js} +1 -1
- package/payload/server/public/brand/fonts/inter-600.woff2 +0 -0
- package/payload/server/public/brand/fonts/inter-700.woff2 +0 -0
- package/payload/server/public/brand/fonts/inter-800.woff2 +0 -0
- package/payload/server/public/brand-defaults.css +22 -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 +1139 -427
- package/payload/server/public/assets/admin-DetvulAu.js +0 -1
- package/payload/server/public/assets/agents-X_IvDHLZ.js +0 -1
- package/payload/server/public/assets/chat-wrckyIpw.js +0 -1
- package/payload/server/public/assets/chevron-left-Dtk3jEK5.js +0 -1
- package/payload/server/public/assets/chevron-right-Dih6IZ8B.js +0 -1
- package/payload/server/public/assets/clock-iJCrrmWM.js +0 -1
- package/payload/server/public/assets/data-D76--TYs.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-0PfRrN6i.js +0 -9
- package/payload/server/public/assets/useSubAccountSwitcher-BkQ5tA8A.css +0 -1
package/payload/server/server.js
CHANGED
|
@@ -1824,10 +1824,10 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1824
1824
|
};
|
|
1825
1825
|
|
|
1826
1826
|
// server/index.ts
|
|
1827
|
-
import { readFileSync as
|
|
1827
|
+
import { readFileSync as readFileSync48, existsSync as existsSync47, watchFile, readdirSync as readdirSync29, statSync as statSync22 } from "fs";
|
|
1828
1828
|
import { spawn as spawn3 } from "child_process";
|
|
1829
|
-
import { createHash as
|
|
1830
|
-
import { resolve as resolve45, join as
|
|
1829
|
+
import { createHash as createHash8 } from "crypto";
|
|
1830
|
+
import { resolve as resolve45, join as join49, basename as basename14 } from "path";
|
|
1831
1831
|
import { homedir as homedir4 } from "os";
|
|
1832
1832
|
|
|
1833
1833
|
// server/lib/route-cache-watchers.ts
|
|
@@ -2386,6 +2386,79 @@ async function connectLoop(signal) {
|
|
|
2386
2386
|
// server/index.ts
|
|
2387
2387
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
2388
2388
|
|
|
2389
|
+
// server/lib/event-loop-lag.ts
|
|
2390
|
+
function readAndResetWindow(hist, windowMs) {
|
|
2391
|
+
const toMs = (ns) => Math.round(ns / 1e6);
|
|
2392
|
+
const w = {
|
|
2393
|
+
p50Ms: toMs(hist.percentile(50)),
|
|
2394
|
+
p99Ms: toMs(hist.percentile(99)),
|
|
2395
|
+
maxMs: toMs(hist.max),
|
|
2396
|
+
// Read before the reset.
|
|
2397
|
+
count: hist.count,
|
|
2398
|
+
windowMs
|
|
2399
|
+
};
|
|
2400
|
+
hist.reset();
|
|
2401
|
+
return w;
|
|
2402
|
+
}
|
|
2403
|
+
function formatLagWindow(w) {
|
|
2404
|
+
return `[event-loop] op=lag-window p50=${w.p50Ms} p99=${w.p99Ms} max=${w.maxMs} n=${w.count} windowMs=${w.windowMs}`;
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
// server/lib/static-cache.ts
|
|
2408
|
+
import { statSync as statSync3 } from "fs";
|
|
2409
|
+
import { join as join3 } from "path";
|
|
2410
|
+
var IMMUTABLE = "public, max-age=31536000, immutable";
|
|
2411
|
+
var REVALIDATE = "public, no-cache";
|
|
2412
|
+
function statOrNull(path3) {
|
|
2413
|
+
try {
|
|
2414
|
+
return statSync3(path3);
|
|
2415
|
+
} catch {
|
|
2416
|
+
return null;
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
function tryDecodeURI2(str) {
|
|
2420
|
+
try {
|
|
2421
|
+
return decodeURI(str);
|
|
2422
|
+
} catch {
|
|
2423
|
+
return str.replace(/(?:%[0-9A-Fa-f]{2})+/g, (match) => {
|
|
2424
|
+
try {
|
|
2425
|
+
return decodeURI(match);
|
|
2426
|
+
} catch {
|
|
2427
|
+
return match;
|
|
2428
|
+
}
|
|
2429
|
+
});
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
function resolveStaticFile(root, reqPath) {
|
|
2433
|
+
const filename = tryDecodeURI2(reqPath);
|
|
2434
|
+
if (/(?:^|[/\\])\.\.(?:$|[/\\])/.test(filename)) return null;
|
|
2435
|
+
let path3 = join3(root, filename);
|
|
2436
|
+
let stats = statOrNull(path3);
|
|
2437
|
+
if (stats?.isDirectory()) {
|
|
2438
|
+
path3 = join3(path3, "index.html");
|
|
2439
|
+
stats = statOrNull(path3);
|
|
2440
|
+
}
|
|
2441
|
+
if (!stats?.isFile()) return null;
|
|
2442
|
+
return { path: path3, stats, filename };
|
|
2443
|
+
}
|
|
2444
|
+
function staticCacheHeaders(root) {
|
|
2445
|
+
return async (c, next) => {
|
|
2446
|
+
if (c.finalized) return next();
|
|
2447
|
+
const hit = resolveStaticFile(root, c.req.path);
|
|
2448
|
+
if (!hit) return next();
|
|
2449
|
+
if (hit.filename.startsWith("/assets/")) {
|
|
2450
|
+
c.header("Cache-Control", IMMUTABLE);
|
|
2451
|
+
return next();
|
|
2452
|
+
}
|
|
2453
|
+
const etag = `"${hit.stats.size}-${hit.stats.mtimeMs}"`;
|
|
2454
|
+
c.header("Cache-Control", REVALIDATE);
|
|
2455
|
+
c.header("Last-Modified", hit.stats.mtime.toUTCString());
|
|
2456
|
+
c.header("ETag", etag);
|
|
2457
|
+
if (c.req.header("If-None-Match") === etag) return c.body(null, 304);
|
|
2458
|
+
return next();
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2389
2462
|
// app/lib/whatsapp/inbound/media-download-guard.ts
|
|
2390
2463
|
var TAG2 = "[media-guard]";
|
|
2391
2464
|
var inFlight2 = 0;
|
|
@@ -2452,14 +2525,14 @@ function installMediaDownloadGuard() {
|
|
|
2452
2525
|
}
|
|
2453
2526
|
|
|
2454
2527
|
// app/lib/whatsapp/ingest-audit.ts
|
|
2455
|
-
import { readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as
|
|
2456
|
-
import { join as
|
|
2528
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
|
|
2529
|
+
import { join as join5 } from "path";
|
|
2457
2530
|
|
|
2458
2531
|
// app/lib/whatsapp/ingest-ledger.ts
|
|
2459
2532
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, appendFileSync as appendFileSync2, readFileSync as readFileSync3, writeFileSync, readdirSync as readdirSync3 } from "fs";
|
|
2460
|
-
import { join as
|
|
2533
|
+
import { join as join4 } from "path";
|
|
2461
2534
|
var TAG3 = "[wa-ingest-ledger]";
|
|
2462
|
-
var LEDGER_ROOT = () =>
|
|
2535
|
+
var LEDGER_ROOT = () => join4(process.env.MAXY_DIR_OVERRIDE ?? MAXY_DIR, "data", "whatsapp-ingest-ledger");
|
|
2463
2536
|
var ensuredRoots = /* @__PURE__ */ new Set();
|
|
2464
2537
|
function ledgerFilePath(account) {
|
|
2465
2538
|
const root = LEDGER_ROOT();
|
|
@@ -2467,7 +2540,7 @@ function ledgerFilePath(account) {
|
|
|
2467
2540
|
if (!existsSync3(root)) mkdirSync2(root, { recursive: true });
|
|
2468
2541
|
ensuredRoots.add(root);
|
|
2469
2542
|
}
|
|
2470
|
-
return
|
|
2543
|
+
return join4(root, `${account}.jsonl`);
|
|
2471
2544
|
}
|
|
2472
2545
|
function appendStanza(entry) {
|
|
2473
2546
|
try {
|
|
@@ -2573,16 +2646,16 @@ function readAllConversationsCached(accountId) {
|
|
|
2573
2646
|
const out = /* @__PURE__ */ new Map();
|
|
2574
2647
|
let dir;
|
|
2575
2648
|
try {
|
|
2576
|
-
dir = readdirSync4(
|
|
2649
|
+
dir = readdirSync4(join5(STORE_ROOT(), accountId));
|
|
2577
2650
|
} catch {
|
|
2578
2651
|
return out;
|
|
2579
2652
|
}
|
|
2580
2653
|
for (const file of dir) {
|
|
2581
2654
|
if (!file.endsWith(".jsonl")) continue;
|
|
2582
|
-
const path3 =
|
|
2655
|
+
const path3 = join5(STORE_ROOT(), accountId, file);
|
|
2583
2656
|
let stat11;
|
|
2584
2657
|
try {
|
|
2585
|
-
stat11 =
|
|
2658
|
+
stat11 = statSync4(path3);
|
|
2586
2659
|
} catch {
|
|
2587
2660
|
continue;
|
|
2588
2661
|
}
|
|
@@ -2756,12 +2829,12 @@ function pwaCensus(d) {
|
|
|
2756
2829
|
}
|
|
2757
2830
|
|
|
2758
2831
|
// server/webchat-descriptor.ts
|
|
2759
|
-
import { resolve as resolve3, join as
|
|
2832
|
+
import { resolve as resolve3, join as join6 } from "path";
|
|
2760
2833
|
function webchatGatewayUrl() {
|
|
2761
2834
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
2762
2835
|
}
|
|
2763
2836
|
function webchatServerPath() {
|
|
2764
|
-
return process.env.MAXY_WEBCHAT_CHANNEL_SERVER_PATH ?? resolve3(process.env.MAXY_PLATFORM_ROOT ??
|
|
2837
|
+
return process.env.MAXY_WEBCHAT_CHANNEL_SERVER_PATH ?? resolve3(process.env.MAXY_PLATFORM_ROOT ?? join6(__dirname, ".."), "services/webchat-channel/dist/server.js");
|
|
2765
2838
|
}
|
|
2766
2839
|
function webchatChannelDescriptor(key) {
|
|
2767
2840
|
return { key, gatewayUrl: webchatGatewayUrl(), serverPath: webchatServerPath() };
|
|
@@ -4983,7 +5056,7 @@ function resolvePlatformAccountId(accountsDir = ACCOUNTS_DIR) {
|
|
|
4983
5056
|
}
|
|
4984
5057
|
|
|
4985
5058
|
// app/lib/managed-service.ts
|
|
4986
|
-
import { join as
|
|
5059
|
+
import { join as join7 } from "path";
|
|
4987
5060
|
import { readFileSync as readFileSync9 } from "fs";
|
|
4988
5061
|
function isHouseManagedService(accountsDir = ACCOUNTS_DIR) {
|
|
4989
5062
|
let houseId;
|
|
@@ -4994,7 +5067,7 @@ function isHouseManagedService(accountsDir = ACCOUNTS_DIR) {
|
|
|
4994
5067
|
}
|
|
4995
5068
|
try {
|
|
4996
5069
|
const cfg = JSON.parse(
|
|
4997
|
-
readFileSync9(
|
|
5070
|
+
readFileSync9(join7(accountsDir, houseId, "account.json"), "utf-8")
|
|
4998
5071
|
);
|
|
4999
5072
|
return cfg.managedService === true;
|
|
5000
5073
|
} catch {
|
|
@@ -5008,7 +5081,7 @@ function isCustomerTurnSuppressed(role, managedService) {
|
|
|
5008
5081
|
// app/lib/whatsapp/inbound/media.ts
|
|
5009
5082
|
import { randomUUID as randomUUID4, createHash } from "crypto";
|
|
5010
5083
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
5011
|
-
import { join as
|
|
5084
|
+
import { join as join9 } from "path";
|
|
5012
5085
|
import {
|
|
5013
5086
|
downloadMediaMessage,
|
|
5014
5087
|
downloadContentFromMessage,
|
|
@@ -5023,7 +5096,7 @@ import { resolve as resolve7, extname } from "path";
|
|
|
5023
5096
|
|
|
5024
5097
|
// app/lib/data-path.ts
|
|
5025
5098
|
import { realpathSync } from "fs";
|
|
5026
|
-
import { resolve as resolve6, normalize, sep as sep2, relative, basename, join as
|
|
5099
|
+
import { resolve as resolve6, normalize, sep as sep2, relative, basename, join as join8 } from "path";
|
|
5027
5100
|
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "../platform");
|
|
5028
5101
|
var DATA_ROOT = resolve6(PLATFORM_ROOT3, "..", "data");
|
|
5029
5102
|
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve6(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
@@ -5091,7 +5164,7 @@ function scratchpadTmpBase() {
|
|
|
5091
5164
|
}
|
|
5092
5165
|
function sessionScratchpadRoot(projectDir, sessionId) {
|
|
5093
5166
|
const uid = typeof process.geteuid === "function" ? process.geteuid() : 0;
|
|
5094
|
-
return
|
|
5167
|
+
return join8(scratchpadTmpBase(), `claude-${uid}`, basename(projectDir), sessionId, "scratchpad");
|
|
5095
5168
|
}
|
|
5096
5169
|
function resolveScratchpadPath(raw, scratchpadRootAbs) {
|
|
5097
5170
|
return resolveUnderRoot(raw, scratchpadRootAbs, "scratchpad");
|
|
@@ -5634,7 +5707,7 @@ async function downloadInboundMedia(msg, sock, opts) {
|
|
|
5634
5707
|
if (!res) return void 0;
|
|
5635
5708
|
await mkdir2(MEDIA_DIR, { recursive: true });
|
|
5636
5709
|
const ext = mimeToExt(res.mimetype);
|
|
5637
|
-
const filePath =
|
|
5710
|
+
const filePath = join9(MEDIA_DIR, `${randomUUID4()}.${ext}`);
|
|
5638
5711
|
await writeFile2(filePath, res.buffer);
|
|
5639
5712
|
const sizeKB = (res.buffer.length / 1024).toFixed(0);
|
|
5640
5713
|
console.error(`${TAG14} media downloaded type=${res.mimetype} size=${sizeKB}KB path=${filePath}`);
|
|
@@ -7436,7 +7509,7 @@ var health_default = app;
|
|
|
7436
7509
|
// app/lib/stt/voice-note.ts
|
|
7437
7510
|
import { writeFile as writeFile3, mkdtemp, rm } from "fs/promises";
|
|
7438
7511
|
import { tmpdir } from "os";
|
|
7439
|
-
import { join as
|
|
7512
|
+
import { join as join10 } from "path";
|
|
7440
7513
|
|
|
7441
7514
|
// app/lib/stt/audio-mime.ts
|
|
7442
7515
|
var AUDIO_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -7490,9 +7563,9 @@ async function transcribeVoiceNote(file, source) {
|
|
|
7490
7563
|
let tempDir;
|
|
7491
7564
|
let tempPath;
|
|
7492
7565
|
try {
|
|
7493
|
-
tempDir = await mkdtemp(
|
|
7566
|
+
tempDir = await mkdtemp(join10(tmpdir(), "voice-"));
|
|
7494
7567
|
const ext = audioExtension(mimeType);
|
|
7495
|
-
tempPath =
|
|
7568
|
+
tempPath = join10(tempDir, `recording${ext}`);
|
|
7496
7569
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
7497
7570
|
await writeFile3(tempPath, buffer);
|
|
7498
7571
|
} catch (err) {
|
|
@@ -8068,7 +8141,7 @@ async function sendTelegramText(botToken, chatId, text) {
|
|
|
8068
8141
|
}
|
|
8069
8142
|
|
|
8070
8143
|
// server/routes/whatsapp.ts
|
|
8071
|
-
import { join as
|
|
8144
|
+
import { join as join13, resolve as resolve11 } from "path";
|
|
8072
8145
|
import { readdirSync as readdirSync7, readFileSync as readFileSync14, existsSync as existsSync8 } from "fs";
|
|
8073
8146
|
|
|
8074
8147
|
// app/lib/whatsapp/login.ts
|
|
@@ -8076,7 +8149,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
8076
8149
|
|
|
8077
8150
|
// app/lib/whatsapp/config-persist.ts
|
|
8078
8151
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "fs";
|
|
8079
|
-
import { resolve as resolve9, join as
|
|
8152
|
+
import { resolve as resolve9, join as join11, dirname as dirname2, basename as basename2 } from "path";
|
|
8080
8153
|
var TAG22 = "[whatsapp:config]";
|
|
8081
8154
|
function configPath(accountDir) {
|
|
8082
8155
|
return resolve9(accountDir, "account.json");
|
|
@@ -8423,7 +8496,7 @@ function setPublicAgent(accountDir, slug) {
|
|
|
8423
8496
|
if (!trimmed) {
|
|
8424
8497
|
return { ok: false, error: "Agent slug cannot be empty." };
|
|
8425
8498
|
}
|
|
8426
|
-
const agentConfigPath =
|
|
8499
|
+
const agentConfigPath = join11(accountDir, "agents", trimmed, "config.json");
|
|
8427
8500
|
if (!existsSync7(agentConfigPath)) {
|
|
8428
8501
|
return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
|
|
8429
8502
|
}
|
|
@@ -9060,12 +9133,12 @@ function serializeWhatsAppSchema() {
|
|
|
9060
9133
|
|
|
9061
9134
|
// app/lib/whatsapp/status-reconcile.ts
|
|
9062
9135
|
import { readdirSync as readdirSync6 } from "fs";
|
|
9063
|
-
import { join as
|
|
9136
|
+
import { join as join12 } from "path";
|
|
9064
9137
|
var TAG25 = "[whatsapp:reconcile]";
|
|
9065
9138
|
var HALF_REGISTERED_MIN_AGE_MS = 5 * 6e4;
|
|
9066
9139
|
function listCredsAccountIds(credsRoot) {
|
|
9067
9140
|
try {
|
|
9068
|
-
return readdirSync6(credsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && hasCredsSync(
|
|
9141
|
+
return readdirSync6(credsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && hasCredsSync(join12(credsRoot, e.name))).map((e) => e.name);
|
|
9069
9142
|
} catch {
|
|
9070
9143
|
return [];
|
|
9071
9144
|
}
|
|
@@ -9080,7 +9153,7 @@ function reconcileCredsOnDisk(opts) {
|
|
|
9080
9153
|
const entries2 = [];
|
|
9081
9154
|
for (const accountId of listCredsAccountIds(credsRoot)) {
|
|
9082
9155
|
if (liveAccountIds.has(accountId)) continue;
|
|
9083
|
-
const authDir =
|
|
9156
|
+
const authDir = join12(credsRoot, accountId);
|
|
9084
9157
|
const census = credsCensus(authDir);
|
|
9085
9158
|
if (census.registered && !census.hasAccountSignature) {
|
|
9086
9159
|
const inFlight3 = activeLoginAccountIds.has(accountId);
|
|
@@ -9317,7 +9390,7 @@ app2.get("/status", (c) => {
|
|
|
9317
9390
|
const live = getStatus();
|
|
9318
9391
|
const liveIds = new Set(live.map((a) => a.accountId));
|
|
9319
9392
|
const activeLoginIds = new Set(listActiveLoginAccountIds());
|
|
9320
|
-
const credsRoot =
|
|
9393
|
+
const credsRoot = join13(MAXY_DIR, "credentials", "whatsapp");
|
|
9321
9394
|
const accountDir = resolveAccount()?.accountDir ?? null;
|
|
9322
9395
|
const reconciled = reconcileCredsOnDisk({ credsRoot, accountDir, liveAccountIds: liveIds, activeLoginAccountIds: activeLoginIds });
|
|
9323
9396
|
const cfg = getWhatsAppConfig();
|
|
@@ -9354,7 +9427,7 @@ app2.post("/login/start", async (c) => {
|
|
|
9354
9427
|
if (!phone) {
|
|
9355
9428
|
return c.json({ error: 'Missing required field "phone" (E.164, e.g. +441234567890).' }, 400);
|
|
9356
9429
|
}
|
|
9357
|
-
const authDir =
|
|
9430
|
+
const authDir = join13(MAXY_DIR, "credentials", "whatsapp", accountId);
|
|
9358
9431
|
const accountDir = resolveAccount()?.accountDir ?? null;
|
|
9359
9432
|
const result = await startLogin({ accountId, authDir, phone, accountDir, force });
|
|
9360
9433
|
console.error(`${TAG26} login/start result account=${accountId} hasCode=${!!result.pairingCode}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
|
|
@@ -10001,12 +10074,12 @@ var bodyLimit = (options) => {
|
|
|
10001
10074
|
};
|
|
10002
10075
|
|
|
10003
10076
|
// server/routes/storage-broker.ts
|
|
10004
|
-
import { join as
|
|
10077
|
+
import { join as join14, resolve as resolve12 } from "path";
|
|
10005
10078
|
import { readdirSync as readdirSync8, readFileSync as readFileSync15, existsSync as existsSync9, realpathSync as realpathSync4 } from "fs";
|
|
10006
10079
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
10007
10080
|
var app3 = new Hono();
|
|
10008
10081
|
function platformRoot() {
|
|
10009
|
-
return process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ??
|
|
10082
|
+
return process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? join14(__dirname, "..", "..");
|
|
10010
10083
|
}
|
|
10011
10084
|
function caller(c) {
|
|
10012
10085
|
return c.req.header("x-maxy-caller-account")?.trim() || null;
|
|
@@ -10541,7 +10614,7 @@ async function runStorageAudit(root) {
|
|
|
10541
10614
|
if (!existsSync9(accountsDir)) return [];
|
|
10542
10615
|
const files = [];
|
|
10543
10616
|
for (const accountId of readdirSync8(accountsDir)) {
|
|
10544
|
-
const p =
|
|
10617
|
+
const p = join14(accountsDir, accountId, "secrets", "cloudflare.env");
|
|
10545
10618
|
if (existsSync9(p)) files.push({ accountId, path: p, contents: readFileSync15(p, "utf8") });
|
|
10546
10619
|
}
|
|
10547
10620
|
return files;
|
|
@@ -10640,7 +10713,7 @@ function discoverPortalAccounts(accountsDir) {
|
|
|
10640
10713
|
if (!existsSync9(accountsDir)) return [];
|
|
10641
10714
|
const found = [];
|
|
10642
10715
|
for (const accountId of readdirSync8(accountsDir)) {
|
|
10643
|
-
const configPath2 =
|
|
10716
|
+
const configPath2 = join14(accountsDir, accountId, "data-portal.json");
|
|
10644
10717
|
if (!existsSync9(configPath2)) continue;
|
|
10645
10718
|
try {
|
|
10646
10719
|
const cfg = JSON.parse(readFileSync15(configPath2, "utf-8"));
|
|
@@ -10723,7 +10796,7 @@ async function runDataPortalAudit(root) {
|
|
|
10723
10796
|
// server/portal-uploads-pull.ts
|
|
10724
10797
|
import { existsSync as existsSync10 } from "fs";
|
|
10725
10798
|
import { mkdir as mkdir3, readFile as readFile2, rename, stat as stat3, unlink as unlink2, writeFile as writeFile4 } from "fs/promises";
|
|
10726
|
-
import { basename as basename4, dirname as dirname4, join as
|
|
10799
|
+
import { basename as basename4, dirname as dirname4, join as join15, relative as relative2, resolve as resolve13, sep as sep3 } from "path";
|
|
10727
10800
|
|
|
10728
10801
|
// ../plugins/cloudflare/bin/schema-exposed-dirs.mjs
|
|
10729
10802
|
var ONT_START = "<!-- ontology-buckets:start -->";
|
|
@@ -10821,19 +10894,19 @@ function resolveDestination(accountDir, row, exposed) {
|
|
|
10821
10894
|
if (!segments.every(safeSegment)) return { ok: false, reason: "bad-target" };
|
|
10822
10895
|
if (!exposed.includes(segments[0])) return { ok: false, reason: "bad-target" };
|
|
10823
10896
|
root = resolve13(accountDir);
|
|
10824
|
-
dir =
|
|
10897
|
+
dir = join15(root, ...segments);
|
|
10825
10898
|
} else {
|
|
10826
10899
|
root = resolve13(accountDir, "uploads");
|
|
10827
|
-
dir =
|
|
10900
|
+
dir = join15(root, row.ownerId);
|
|
10828
10901
|
}
|
|
10829
|
-
const final =
|
|
10902
|
+
const final = join15(dir, row.filename);
|
|
10830
10903
|
if (!final.startsWith(root + sep3)) {
|
|
10831
10904
|
return { ok: false, reason: row.targetPath ? "bad-target" : "bad-filename" };
|
|
10832
10905
|
}
|
|
10833
10906
|
return {
|
|
10834
10907
|
ok: true,
|
|
10835
10908
|
dir,
|
|
10836
|
-
tmp:
|
|
10909
|
+
tmp: join15(dir, ".uploads-tmp", row.filename),
|
|
10837
10910
|
final,
|
|
10838
10911
|
relative: relative2(resolve13(accountDir), final).split(sep3).join("/")
|
|
10839
10912
|
};
|
|
@@ -10846,7 +10919,7 @@ function nextFreePath(final) {
|
|
|
10846
10919
|
const stem = dot > 0 ? base.slice(0, dot) : base;
|
|
10847
10920
|
const ext = dot > 0 ? base.slice(dot) : "";
|
|
10848
10921
|
for (let n = 2; ; n++) {
|
|
10849
|
-
const candidate =
|
|
10922
|
+
const candidate = join15(dir, `${stem} (${n})${ext}`);
|
|
10850
10923
|
if (!existsSync10(candidate)) return candidate;
|
|
10851
10924
|
}
|
|
10852
10925
|
}
|
|
@@ -10950,7 +11023,7 @@ async function withdrawRow(opts) {
|
|
|
10950
11023
|
log2(`${head} removed=false result=bad-path`);
|
|
10951
11024
|
return false;
|
|
10952
11025
|
}
|
|
10953
|
-
const absolute =
|
|
11026
|
+
const absolute = join15(resolve13(accountDir), ...segments);
|
|
10954
11027
|
if (!absolute.startsWith(resolve13(accountDir) + sep3)) {
|
|
10955
11028
|
log2(`${head} removed=false result=bad-path`);
|
|
10956
11029
|
return false;
|
|
@@ -11015,13 +11088,13 @@ function parsePendingRows(raw) {
|
|
|
11015
11088
|
async function readExposedDirs(accountDir) {
|
|
11016
11089
|
let schemaText = null;
|
|
11017
11090
|
try {
|
|
11018
|
-
schemaText = await readFile2(
|
|
11091
|
+
schemaText = await readFile2(join15(accountDir, "SCHEMA.md"), "utf8");
|
|
11019
11092
|
} catch {
|
|
11020
11093
|
schemaText = null;
|
|
11021
11094
|
}
|
|
11022
11095
|
let exposeFolders = [];
|
|
11023
11096
|
try {
|
|
11024
|
-
const cfg = JSON.parse(await readFile2(
|
|
11097
|
+
const cfg = JSON.parse(await readFile2(join15(accountDir, "data-portal.json"), "utf8"));
|
|
11025
11098
|
exposeFolders = Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === "string") : [];
|
|
11026
11099
|
} catch {
|
|
11027
11100
|
exposeFolders = [];
|
|
@@ -11046,7 +11119,7 @@ async function runPortalUploadsPull(root) {
|
|
|
11046
11119
|
console.error(`${TAG27} op=malformed account=${portal.accountId} rows=${malformed}`);
|
|
11047
11120
|
}
|
|
11048
11121
|
if (rows.length === 0) continue;
|
|
11049
|
-
const accountDir =
|
|
11122
|
+
const accountDir = join15(accountsRoot, portal.accountId);
|
|
11050
11123
|
const r = await pullAccount({
|
|
11051
11124
|
accountDir,
|
|
11052
11125
|
accountId: portal.accountId,
|
|
@@ -11079,7 +11152,7 @@ async function runPortalUploadsPull(root) {
|
|
|
11079
11152
|
import { readFile as readFile3, readdir, stat as stat4 } from "fs/promises";
|
|
11080
11153
|
import { existsSync as existsSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync6 } from "fs";
|
|
11081
11154
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
11082
|
-
import { join as
|
|
11155
|
+
import { join as join16, resolve as resolve14 } from "path";
|
|
11083
11156
|
import { fileURLToPath } from "url";
|
|
11084
11157
|
|
|
11085
11158
|
// ../plugins/cloudflare/bin/d1-http.mjs
|
|
@@ -11155,7 +11228,7 @@ function readEnvFile(path3) {
|
|
|
11155
11228
|
}
|
|
11156
11229
|
async function readExposeFolders(accountDir) {
|
|
11157
11230
|
try {
|
|
11158
|
-
const cfg = JSON.parse(await readFile3(
|
|
11231
|
+
const cfg = JSON.parse(await readFile3(join16(accountDir, "data-portal.json"), "utf8"));
|
|
11159
11232
|
return Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === "string") : [];
|
|
11160
11233
|
} catch {
|
|
11161
11234
|
return [];
|
|
@@ -11166,12 +11239,12 @@ async function walkExposed(accountDir, exposed) {
|
|
|
11166
11239
|
async function walk(rel) {
|
|
11167
11240
|
let entries2;
|
|
11168
11241
|
try {
|
|
11169
|
-
entries2 = await readdir(
|
|
11242
|
+
entries2 = await readdir(join16(accountDir, rel), { withFileTypes: true });
|
|
11170
11243
|
} catch {
|
|
11171
11244
|
return;
|
|
11172
11245
|
}
|
|
11173
11246
|
try {
|
|
11174
|
-
const st = await stat4(
|
|
11247
|
+
const st = await stat4(join16(accountDir, rel));
|
|
11175
11248
|
out.push({ relPath: rel, sizeBytes: 0, modifiedAt: st.mtime.toISOString(), isDir: true });
|
|
11176
11249
|
} catch {
|
|
11177
11250
|
}
|
|
@@ -11182,7 +11255,7 @@ async function walkExposed(accountDir, exposed) {
|
|
|
11182
11255
|
await walk(childRel);
|
|
11183
11256
|
} else if (e.isFile()) {
|
|
11184
11257
|
try {
|
|
11185
|
-
const st = await stat4(
|
|
11258
|
+
const st = await stat4(join16(accountDir, childRel));
|
|
11186
11259
|
out.push({
|
|
11187
11260
|
relPath: childRel,
|
|
11188
11261
|
sizeBytes: st.size,
|
|
@@ -11201,7 +11274,7 @@ async function pushAccount(opts) {
|
|
|
11201
11274
|
const { accountDir, accountId, client, log: log2, nowIso } = opts;
|
|
11202
11275
|
let schemaText = null;
|
|
11203
11276
|
try {
|
|
11204
|
-
schemaText = await readFile3(
|
|
11277
|
+
schemaText = await readFile3(join16(accountDir, "SCHEMA.md"), "utf8");
|
|
11205
11278
|
} catch {
|
|
11206
11279
|
schemaText = null;
|
|
11207
11280
|
}
|
|
@@ -11231,19 +11304,11 @@ async function pushAccount(opts) {
|
|
|
11231
11304
|
[accountId]
|
|
11232
11305
|
);
|
|
11233
11306
|
const current = Number(state[0]?.currentGeneration ?? 0);
|
|
11234
|
-
const
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
[accountId, next]
|
|
11307
|
+
const staged = await client.query(
|
|
11308
|
+
"SELECT MAX(generation) AS g FROM directory WHERE accountId = ?",
|
|
11309
|
+
[accountId]
|
|
11238
11310
|
);
|
|
11239
|
-
const
|
|
11240
|
-
if (orphanCount > 0) {
|
|
11241
|
-
await client.query("DELETE FROM directory WHERE accountId = ? AND generation = ?", [
|
|
11242
|
-
accountId,
|
|
11243
|
-
next
|
|
11244
|
-
]);
|
|
11245
|
-
log2(`${TAG28} op=stage-clear account=${accountId} generation=${next} cleared=${orphanCount}`);
|
|
11246
|
-
}
|
|
11311
|
+
const next = Math.max(current, Number(staged[0]?.g ?? 0)) + 1;
|
|
11247
11312
|
for (const row of rows) {
|
|
11248
11313
|
await client.query(
|
|
11249
11314
|
"INSERT INTO directory (accountId, relPath, sizeBytes, modifiedAt, indexedAt, isDir, generation) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
@@ -11252,12 +11317,29 @@ async function pushAccount(opts) {
|
|
|
11252
11317
|
}
|
|
11253
11318
|
log2(`${TAG28} op=stage account=${accountId} generation=${next} inserted=${rows.length}`);
|
|
11254
11319
|
await client.query(
|
|
11255
|
-
"INSERT INTO directory_state (accountId, currentGeneration) VALUES (?, ?) ON CONFLICT (accountId) DO UPDATE SET currentGeneration = excluded.currentGeneration",
|
|
11320
|
+
"INSERT INTO directory_state (accountId, currentGeneration) VALUES (?, ?) ON CONFLICT (accountId) DO UPDATE SET currentGeneration = excluded.currentGeneration WHERE excluded.currentGeneration > directory_state.currentGeneration",
|
|
11256
11321
|
[accountId, next]
|
|
11257
11322
|
);
|
|
11323
|
+
const flipped = await client.query(
|
|
11324
|
+
"SELECT currentGeneration FROM directory_state WHERE accountId = ?",
|
|
11325
|
+
[accountId]
|
|
11326
|
+
);
|
|
11327
|
+
const pointer = Number(flipped[0]?.currentGeneration ?? 0);
|
|
11328
|
+
if (pointer !== next) {
|
|
11329
|
+
log2(
|
|
11330
|
+
`${TAG28} op=stage-contended account=${accountId} generation=${next} pointer=${pointer} action=superseded`
|
|
11331
|
+
);
|
|
11332
|
+
return {
|
|
11333
|
+
exposed: r.exposed,
|
|
11334
|
+
rows: rows.length,
|
|
11335
|
+
schemaPresent: true,
|
|
11336
|
+
generation: next,
|
|
11337
|
+
superseded: true
|
|
11338
|
+
};
|
|
11339
|
+
}
|
|
11258
11340
|
log2(`${TAG28} op=flip account=${accountId} generation=${next}`);
|
|
11259
11341
|
try {
|
|
11260
|
-
await client.query("DELETE FROM directory WHERE accountId = ? AND generation
|
|
11342
|
+
await client.query("DELETE FROM directory WHERE accountId = ? AND generation < ?", [
|
|
11261
11343
|
accountId,
|
|
11262
11344
|
next
|
|
11263
11345
|
]);
|
|
@@ -11309,9 +11391,9 @@ async function portalAccounts(accountsRoot) {
|
|
|
11309
11391
|
return out;
|
|
11310
11392
|
}
|
|
11311
11393
|
for (const accountId of names) {
|
|
11312
|
-
const accountDir =
|
|
11394
|
+
const accountDir = join16(accountsRoot, accountId);
|
|
11313
11395
|
try {
|
|
11314
|
-
const cfg = JSON.parse(await readFile3(
|
|
11396
|
+
const cfg = JSON.parse(await readFile3(join16(accountDir, "data-portal.json"), "utf8"));
|
|
11315
11397
|
if (typeof cfg?.portalDbName === "string" && cfg.portalDbName) {
|
|
11316
11398
|
const exposeFolders = Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === "string") : [];
|
|
11317
11399
|
out.push({ accountDir, accountId, dbName: cfg.portalDbName, exposeFolders });
|
|
@@ -11324,11 +11406,11 @@ async function portalAccounts(accountsRoot) {
|
|
|
11324
11406
|
return out;
|
|
11325
11407
|
}
|
|
11326
11408
|
function resolveCloudflareCredentials(platformRoot5) {
|
|
11327
|
-
const houseEnvPath =
|
|
11409
|
+
const houseEnvPath = join16(platformRoot5, "config", "cloudflare-house.env");
|
|
11328
11410
|
const house = readEnvFile(houseEnvPath);
|
|
11329
11411
|
if (!house.CLOUDFLARE_API_TOKEN) return { error: "no-house-cloudflare-credential" };
|
|
11330
11412
|
if (!house.CLOUDFLARE_ACCOUNT_ID) return { error: "no-house-cloudflare-account-id" };
|
|
11331
|
-
const cfTokenSh =
|
|
11413
|
+
const cfTokenSh = join16(platformRoot5, "plugins", "cloudflare", "bin", "cf-token.sh");
|
|
11332
11414
|
if (!existsSync11(cfTokenSh)) return { error: "cf-token-sh-missing" };
|
|
11333
11415
|
let keyName;
|
|
11334
11416
|
try {
|
|
@@ -11434,17 +11516,17 @@ async function runPortalIndexPush(root) {
|
|
|
11434
11516
|
}
|
|
11435
11517
|
|
|
11436
11518
|
// server/google-pending-audit.ts
|
|
11437
|
-
import { existsSync as existsSync12, readdirSync as readdirSync9, statSync as
|
|
11438
|
-
import { join as
|
|
11519
|
+
import { existsSync as existsSync12, readdirSync as readdirSync9, statSync as statSync5 } from "fs";
|
|
11520
|
+
import { join as join17 } from "path";
|
|
11439
11521
|
var GOOGLE_PENDING_LIFETIME_SEC = 600;
|
|
11440
11522
|
var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
11441
|
-
function runGooglePendingAudit(accountsDir, nowMs, emit, readMtimeMs = (path3) =>
|
|
11523
|
+
function runGooglePendingAudit(accountsDir, nowMs, emit, readMtimeMs = (path3) => statSync5(path3).mtimeMs) {
|
|
11442
11524
|
let scanned = 0;
|
|
11443
11525
|
let stranded = 0;
|
|
11444
11526
|
if (existsSync12(accountsDir)) {
|
|
11445
11527
|
for (const accountId of readdirSync9(accountsDir)) {
|
|
11446
11528
|
if (!UUID_RE2.test(accountId)) continue;
|
|
11447
|
-
const pendingPath2 =
|
|
11529
|
+
const pendingPath2 = join17(accountsDir, accountId, "secrets", "google", "pending-oauth.enc");
|
|
11448
11530
|
try {
|
|
11449
11531
|
if (!existsSync12(pendingPath2)) continue;
|
|
11450
11532
|
const ageSec = Math.max(0, Math.floor((nowMs - readMtimeMs(pendingPath2)) / 1e3));
|
|
@@ -11501,19 +11583,19 @@ function runReconcileLivenessAudit(statePath, nowMs, intervalMs, emit) {
|
|
|
11501
11583
|
}
|
|
11502
11584
|
|
|
11503
11585
|
// server/routes/whatsapp-reader.ts
|
|
11504
|
-
import { readFileSync as readFileSync21, watch as watch2, statSync as
|
|
11505
|
-
import { basename as basename6, dirname as dirname7, isAbsolute, join as
|
|
11586
|
+
import { readFileSync as readFileSync21, watch as watch2, statSync as statSync9, openSync, readSync, closeSync, existsSync as existsSync17, readdirSync as readdirSync14, realpathSync as realpathSync6 } from "fs";
|
|
11587
|
+
import { basename as basename6, dirname as dirname7, isAbsolute, join as join22, relative as relative3, resolve as resolve17, sep as sep5 } from "path";
|
|
11506
11588
|
|
|
11507
11589
|
// server/routes/admin/sidebar-sessions.ts
|
|
11508
|
-
import { readdirSync as readdirSync12, readFileSync as readFileSync19, statSync as
|
|
11509
|
-
import { dirname as dirname5, join as
|
|
11590
|
+
import { readdirSync as readdirSync12, readFileSync as readFileSync19, statSync as statSync7 } from "fs";
|
|
11591
|
+
import { dirname as dirname5, join as join20, resolve as resolve15 } from "path";
|
|
11510
11592
|
|
|
11511
11593
|
// ../services/claude-session-manager/src/jsonl-path.ts
|
|
11512
11594
|
import { existsSync as existsSync14, readdirSync as readdirSync10 } from "fs";
|
|
11513
11595
|
import { homedir } from "os";
|
|
11514
|
-
import { join as
|
|
11596
|
+
import { join as join18 } from "path";
|
|
11515
11597
|
function findExistingJsonlForSessionId(claudeConfigDir2, sessionId) {
|
|
11516
|
-
const projectsRoot =
|
|
11598
|
+
const projectsRoot = join18(claudeConfigDir2, "projects");
|
|
11517
11599
|
if (!existsSync14(projectsRoot)) return null;
|
|
11518
11600
|
let slugs;
|
|
11519
11601
|
try {
|
|
@@ -11523,9 +11605,9 @@ function findExistingJsonlForSessionId(claudeConfigDir2, sessionId) {
|
|
|
11523
11605
|
}
|
|
11524
11606
|
const basename15 = `${sessionId}.jsonl`;
|
|
11525
11607
|
for (const slug of slugs) {
|
|
11526
|
-
const live =
|
|
11608
|
+
const live = join18(projectsRoot, slug, basename15);
|
|
11527
11609
|
if (existsSync14(live)) return live;
|
|
11528
|
-
const archived =
|
|
11610
|
+
const archived = join18(projectsRoot, slug, "archive", basename15);
|
|
11529
11611
|
if (existsSync14(archived)) return archived;
|
|
11530
11612
|
}
|
|
11531
11613
|
return null;
|
|
@@ -11533,8 +11615,8 @@ function findExistingJsonlForSessionId(claudeConfigDir2, sessionId) {
|
|
|
11533
11615
|
|
|
11534
11616
|
// app/lib/admin-identity/pin-validator.ts
|
|
11535
11617
|
import { createHash as createHash3 } from "crypto";
|
|
11536
|
-
import { existsSync as existsSync15, readFileSync as readFileSync18, readdirSync as readdirSync11, statSync as
|
|
11537
|
-
import { join as
|
|
11618
|
+
import { existsSync as existsSync15, readFileSync as readFileSync18, readdirSync as readdirSync11, statSync as statSync6 } from "fs";
|
|
11619
|
+
import { join as join19 } from "path";
|
|
11538
11620
|
function hashPin(pin) {
|
|
11539
11621
|
return createHash3("sha256").update(pin).digest("hex");
|
|
11540
11622
|
}
|
|
@@ -11665,7 +11747,7 @@ function enumerateJsonls(projectsRoot) {
|
|
|
11665
11747
|
throw err;
|
|
11666
11748
|
}
|
|
11667
11749
|
for (const slug of slugs) {
|
|
11668
|
-
const slugDir =
|
|
11750
|
+
const slugDir = join20(projectsRoot, slug);
|
|
11669
11751
|
let entries2;
|
|
11670
11752
|
try {
|
|
11671
11753
|
entries2 = readdirSync12(slugDir, { withFileTypes: true });
|
|
@@ -11676,9 +11758,9 @@ function enumerateJsonls(projectsRoot) {
|
|
|
11676
11758
|
}
|
|
11677
11759
|
for (const entry of entries2) {
|
|
11678
11760
|
if (entry.isFile() && SESSION_ID_RE.test(entry.name)) {
|
|
11679
|
-
out.push({ path:
|
|
11761
|
+
out.push({ path: join20(slugDir, entry.name), isSubagent: false, archived: false });
|
|
11680
11762
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
11681
|
-
const subDir =
|
|
11763
|
+
const subDir = join20(slugDir, entry.name);
|
|
11682
11764
|
let subEntries;
|
|
11683
11765
|
try {
|
|
11684
11766
|
subEntries = readdirSync12(subDir, { withFileTypes: true });
|
|
@@ -11689,11 +11771,11 @@ function enumerateJsonls(projectsRoot) {
|
|
|
11689
11771
|
}
|
|
11690
11772
|
for (const sub of subEntries) {
|
|
11691
11773
|
if (sub.isFile() && SESSION_ID_RE.test(sub.name)) {
|
|
11692
|
-
out.push({ path:
|
|
11774
|
+
out.push({ path: join20(subDir, sub.name), isSubagent: true, archived: false });
|
|
11693
11775
|
}
|
|
11694
11776
|
}
|
|
11695
11777
|
} else if (entry.isDirectory() && entry.name === "archive") {
|
|
11696
|
-
const archiveDir =
|
|
11778
|
+
const archiveDir = join20(slugDir, entry.name);
|
|
11697
11779
|
let archiveEntries;
|
|
11698
11780
|
try {
|
|
11699
11781
|
archiveEntries = readdirSync12(archiveDir, { withFileTypes: true });
|
|
@@ -11704,7 +11786,7 @@ function enumerateJsonls(projectsRoot) {
|
|
|
11704
11786
|
}
|
|
11705
11787
|
for (const arc of archiveEntries) {
|
|
11706
11788
|
if (arc.isFile() && SESSION_ID_RE.test(arc.name)) {
|
|
11707
|
-
out.push({ path:
|
|
11789
|
+
out.push({ path: join20(archiveDir, arc.name), isSubagent: false, archived: true });
|
|
11708
11790
|
}
|
|
11709
11791
|
}
|
|
11710
11792
|
}
|
|
@@ -11737,7 +11819,7 @@ async function fetchLiveSessions() {
|
|
|
11737
11819
|
function loadUserTitles(accountDir) {
|
|
11738
11820
|
const out = /* @__PURE__ */ new Map();
|
|
11739
11821
|
if (!accountDir) return out;
|
|
11740
|
-
const path3 =
|
|
11822
|
+
const path3 = join20(accountDir, "session-titles.json");
|
|
11741
11823
|
let raw;
|
|
11742
11824
|
try {
|
|
11743
11825
|
raw = readFileSync19(path3, "utf8");
|
|
@@ -11877,7 +11959,7 @@ async function computeSessionList(scopeAccountId) {
|
|
|
11877
11959
|
console.error("[admin-sessions-list] CLAUDE_CONFIG_DIR not set; returning empty list");
|
|
11878
11960
|
return { sessions: [], accountId: scopeAccountId };
|
|
11879
11961
|
}
|
|
11880
|
-
const projectsRoot =
|
|
11962
|
+
const projectsRoot = join20(configDir2, "projects");
|
|
11881
11963
|
const jsonls = enumerateJsonls(projectsRoot);
|
|
11882
11964
|
const liveSessions = await fetchLiveSessions();
|
|
11883
11965
|
const accountDir = scopeAccountId ? resolve15(ACCOUNTS_DIR, scopeAccountId) : null;
|
|
@@ -11922,7 +12004,7 @@ async function computeSessionList(scopeAccountId) {
|
|
|
11922
12004
|
let mtimeMs;
|
|
11923
12005
|
try {
|
|
11924
12006
|
body = readFileSync19(path3, "utf8");
|
|
11925
|
-
mtimeMs =
|
|
12007
|
+
mtimeMs = statSync7(path3).mtimeMs;
|
|
11926
12008
|
} catch {
|
|
11927
12009
|
continue;
|
|
11928
12010
|
}
|
|
@@ -11930,7 +12012,7 @@ async function computeSessionList(scopeAccountId) {
|
|
|
11930
12012
|
const pid = live ? liveSessions.get(sessionId) ?? null : null;
|
|
11931
12013
|
const resolved = resolveTitle(body, sessionId, userTitles);
|
|
11932
12014
|
titleSourceCounts[resolved.source] += 1;
|
|
11933
|
-
const metaPath =
|
|
12015
|
+
const metaPath = join20(projectDir, `${sessionId}.meta.json`);
|
|
11934
12016
|
const { bridgeIds, role, channel: sidecarChannel, senderId, personId, adminUserId, accountId: rowAccountId } = readSidecarMeta(metaPath);
|
|
11935
12017
|
if (multiAccount) {
|
|
11936
12018
|
if (rowAccountId === null) untaggedCount += 1;
|
|
@@ -12332,8 +12414,8 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
12332
12414
|
}
|
|
12333
12415
|
|
|
12334
12416
|
// app/lib/whatsapp-reader/enrich-attachments.ts
|
|
12335
|
-
import { readFileSync as readFileSync20, readdirSync as readdirSync13, existsSync as existsSync16, statSync as
|
|
12336
|
-
import { basename as basename5, dirname as dirname6, join as
|
|
12417
|
+
import { readFileSync as readFileSync20, readdirSync as readdirSync13, existsSync as existsSync16, statSync as statSync8, realpathSync as realpathSync5 } from "fs";
|
|
12418
|
+
import { basename as basename5, dirname as dirname6, join as join21, resolve as resolve16, sep as sep4 } from "path";
|
|
12337
12419
|
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;
|
|
12338
12420
|
function adminAttachmentIds(text) {
|
|
12339
12421
|
const ids = [];
|
|
@@ -12346,7 +12428,7 @@ function adminAttachmentIds(text) {
|
|
|
12346
12428
|
function adminAttachmentsFromText(text, uploadsBase) {
|
|
12347
12429
|
const out = [];
|
|
12348
12430
|
for (const id of adminAttachmentIds(text)) {
|
|
12349
|
-
const meta = readAttachmentMeta(
|
|
12431
|
+
const meta = readAttachmentMeta(join21(uploadsBase, id));
|
|
12350
12432
|
if (meta) out.push(meta);
|
|
12351
12433
|
}
|
|
12352
12434
|
return out;
|
|
@@ -12383,7 +12465,7 @@ function claudeUploadAttachmentsFromText(text, sessionId) {
|
|
|
12383
12465
|
attachmentId: filename,
|
|
12384
12466
|
filename,
|
|
12385
12467
|
mimeType: detectMimeType(filename),
|
|
12386
|
-
sizeBytes:
|
|
12468
|
+
sizeBytes: statSync8(real).size,
|
|
12387
12469
|
source: "claude-upload"
|
|
12388
12470
|
});
|
|
12389
12471
|
accepted.push(m[0]);
|
|
@@ -12396,7 +12478,7 @@ function claudeUploadAttachmentsFromText(text, sessionId) {
|
|
|
12396
12478
|
function readSidecar(dir, attachmentId) {
|
|
12397
12479
|
let parsed;
|
|
12398
12480
|
try {
|
|
12399
|
-
parsed = JSON.parse(readFileSync20(
|
|
12481
|
+
parsed = JSON.parse(readFileSync20(join21(dir, `${attachmentId}.meta.json`), "utf8"));
|
|
12400
12482
|
} catch {
|
|
12401
12483
|
return null;
|
|
12402
12484
|
}
|
|
@@ -12425,7 +12507,7 @@ function listSessionAttachmentsInDir(dir) {
|
|
|
12425
12507
|
}
|
|
12426
12508
|
const sidecars = [];
|
|
12427
12509
|
for (const name of entries2) {
|
|
12428
|
-
const sidecar = readSidecar(
|
|
12510
|
+
const sidecar = readSidecar(join21(dir, name), name);
|
|
12429
12511
|
if (sidecar) sidecars.push(sidecar);
|
|
12430
12512
|
}
|
|
12431
12513
|
sidecars.sort((a, b) => a.storedAt.localeCompare(b.storedAt));
|
|
@@ -12585,14 +12667,14 @@ app5.get("/conversations", requireAdminSession, async (c) => {
|
|
|
12585
12667
|
}
|
|
12586
12668
|
const cfg = claudeConfigDir();
|
|
12587
12669
|
if (!cfg) return c.json({ conversations: [] });
|
|
12588
|
-
const projectsRoot =
|
|
12670
|
+
const projectsRoot = join22(cfg, "projects");
|
|
12589
12671
|
const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
|
|
12590
12672
|
const rows = [];
|
|
12591
12673
|
let sessionRowsExcludedUntagged = 0;
|
|
12592
12674
|
for (const { path: path3 } of enumerateJsonls(projectsRoot)) {
|
|
12593
12675
|
const sessionId = basename6(path3).replace(/\.jsonl$/, "");
|
|
12594
12676
|
const projectDir = dirname7(path3);
|
|
12595
|
-
const meta = readSidecarMeta(
|
|
12677
|
+
const meta = readSidecarMeta(join22(projectDir, `${sessionId}.meta.json`));
|
|
12596
12678
|
if (!isReaderChannelSession(meta.role, meta.channel)) continue;
|
|
12597
12679
|
if (multiAccount) {
|
|
12598
12680
|
if (meta.accountId === null) sessionRowsExcludedUntagged += 1;
|
|
@@ -12723,7 +12805,7 @@ app5.get("/conversations-all", requireAdminSession, async (c) => {
|
|
|
12723
12805
|
const { value, state } = await getOrCompute("conversations-all", houseId, async () => {
|
|
12724
12806
|
const cfg = claudeConfigDir();
|
|
12725
12807
|
if (!cfg) return { conversations: [] };
|
|
12726
|
-
const projectsRoot =
|
|
12808
|
+
const projectsRoot = join22(cfg, "projects");
|
|
12727
12809
|
const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
|
|
12728
12810
|
const validAccounts = listValidAccounts();
|
|
12729
12811
|
const validIds = new Set(validAccounts.map((a) => a.accountId));
|
|
@@ -12732,7 +12814,7 @@ app5.get("/conversations-all", requireAdminSession, async (c) => {
|
|
|
12732
12814
|
for (const { path: path3 } of enumerateJsonls(projectsRoot)) {
|
|
12733
12815
|
const sessionId = basename6(path3).replace(/\.jsonl$/, "");
|
|
12734
12816
|
const projectDir = dirname7(path3);
|
|
12735
|
-
const meta = readSidecarMeta(
|
|
12817
|
+
const meta = readSidecarMeta(join22(projectDir, `${sessionId}.meta.json`));
|
|
12736
12818
|
if (!isReaderChannelSession(meta.role, meta.channel)) continue;
|
|
12737
12819
|
if (meta.accountId === null || !validIds.has(meta.accountId)) continue;
|
|
12738
12820
|
let body = "";
|
|
@@ -12803,7 +12885,7 @@ app5.get("/conversations-all", requireAdminSession, async (c) => {
|
|
|
12803
12885
|
return c.json(value);
|
|
12804
12886
|
});
|
|
12805
12887
|
function readFrom(path3, from) {
|
|
12806
|
-
const end =
|
|
12888
|
+
const end = statSync9(path3).size;
|
|
12807
12889
|
if (end <= from) return { buf: Buffer.alloc(0), end: from };
|
|
12808
12890
|
const fd = openSync(path3, "r");
|
|
12809
12891
|
try {
|
|
@@ -12897,7 +12979,7 @@ function openTaskKey(turns) {
|
|
|
12897
12979
|
}
|
|
12898
12980
|
function readSubagentMeta(dir, hex) {
|
|
12899
12981
|
try {
|
|
12900
|
-
const m = JSON.parse(readFileSync21(
|
|
12982
|
+
const m = JSON.parse(readFileSync21(join22(dir, `agent-${hex}.meta.json`), "utf8"));
|
|
12901
12983
|
if (typeof m.agentType === "string" && typeof m.description === "string" && typeof m.toolUseId === "string") {
|
|
12902
12984
|
return { agentType: m.agentType, description: m.description, toolUseId: m.toolUseId };
|
|
12903
12985
|
}
|
|
@@ -12929,7 +13011,7 @@ function runActivityTick(c) {
|
|
|
12929
13011
|
let size = 0;
|
|
12930
13012
|
let mtimeMs = 0;
|
|
12931
13013
|
try {
|
|
12932
|
-
const st =
|
|
13014
|
+
const st = statSync9(join22(c.subagentsDir, name));
|
|
12933
13015
|
size = st.size;
|
|
12934
13016
|
mtimeMs = st.mtimeMs;
|
|
12935
13017
|
} catch {
|
|
@@ -12974,11 +13056,11 @@ app5.get("/stream", requireAdminSession, (c) => {
|
|
|
12974
13056
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
12975
13057
|
const projectDir = c.req.query("projectDir") ?? "";
|
|
12976
13058
|
const cfg = claudeConfigDir();
|
|
12977
|
-
const projectsRoot = cfg ? resolve17(
|
|
13059
|
+
const projectsRoot = cfg ? resolve17(join22(cfg, "projects")) : null;
|
|
12978
13060
|
if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
|
|
12979
13061
|
return c.json({ error: "bad session reference" }, 400);
|
|
12980
13062
|
}
|
|
12981
|
-
const jsonlPath = resolve17(
|
|
13063
|
+
const jsonlPath = resolve17(join22(projectDir, `${sessionId}.jsonl`));
|
|
12982
13064
|
if (!jsonlPath.startsWith(projectsRoot + sep5)) {
|
|
12983
13065
|
return c.json({ error: "bad session reference" }, 400);
|
|
12984
13066
|
}
|
|
@@ -12997,7 +13079,7 @@ data: ${JSON.stringify(turn)}
|
|
|
12997
13079
|
start(controller) {
|
|
12998
13080
|
const fileEnd0 = (() => {
|
|
12999
13081
|
try {
|
|
13000
|
-
return
|
|
13082
|
+
return statSync9(jsonlPath).size;
|
|
13001
13083
|
} catch {
|
|
13002
13084
|
return 0;
|
|
13003
13085
|
}
|
|
@@ -13005,7 +13087,7 @@ data: ${JSON.stringify(turn)}
|
|
|
13005
13087
|
let offset = resumeOffset(lastEventId, fileEnd0);
|
|
13006
13088
|
console.log(`[wa-stream] op=open conn=${connId} sessionId=${sessionId} channel=reader resume=${offset}`);
|
|
13007
13089
|
const sessionKey = c.req.query("session_key") ?? "";
|
|
13008
|
-
const subagentsDir = resolve17(
|
|
13090
|
+
const subagentsDir = resolve17(join22(projectDir, sessionId, "subagents"));
|
|
13009
13091
|
console.log(
|
|
13010
13092
|
`[webchat-activity] op=watch-subagents key=${sessionKey} dir=${subagentsDir} exists=${existsSync17(subagentsDir)}`
|
|
13011
13093
|
);
|
|
@@ -13134,7 +13216,7 @@ app5.get("/directives", requireAdminSession, (c) => {
|
|
|
13134
13216
|
const pid = Number(pidPart);
|
|
13135
13217
|
let len = 0;
|
|
13136
13218
|
try {
|
|
13137
|
-
len =
|
|
13219
|
+
len = statSync9(join22(dir, name)).size;
|
|
13138
13220
|
} catch {
|
|
13139
13221
|
}
|
|
13140
13222
|
return { name, secs, pid, ts: Number.isFinite(secs) ? new Date(secs * 1e3).toISOString() : null, len };
|
|
@@ -13149,7 +13231,7 @@ app5.get("/directive", requireAdminSession, (c) => {
|
|
|
13149
13231
|
}
|
|
13150
13232
|
const dir = directiveStoreDir(sessionId);
|
|
13151
13233
|
if (!dir) return c.json({ error: "no account" }, 404);
|
|
13152
|
-
const path3 = resolve17(
|
|
13234
|
+
const path3 = resolve17(join22(dir, name));
|
|
13153
13235
|
if (!path3.startsWith(dir + sep5)) return c.json({ error: "bad reference" }, 400);
|
|
13154
13236
|
let body;
|
|
13155
13237
|
try {
|
|
@@ -13207,7 +13289,7 @@ app5.get("/store-stream", requireAdminSession, (c) => {
|
|
|
13207
13289
|
console.log(`[wa-reader] op=store-quote msgId=${r.msgKeyId} quoted=${!!q2}`);
|
|
13208
13290
|
}
|
|
13209
13291
|
if (r.attachmentId) {
|
|
13210
|
-
const meta = readAttachmentMeta(
|
|
13292
|
+
const meta = readAttachmentMeta(join22(uploadsBase, r.attachmentId));
|
|
13211
13293
|
if (meta) return { ...turn, attachments: [meta] };
|
|
13212
13294
|
}
|
|
13213
13295
|
return turn;
|
|
@@ -13240,7 +13322,7 @@ data: ${JSON.stringify(payload)}
|
|
|
13240
13322
|
};
|
|
13241
13323
|
const fileEnd0 = (() => {
|
|
13242
13324
|
try {
|
|
13243
|
-
return
|
|
13325
|
+
return statSync9(file).size;
|
|
13244
13326
|
} catch {
|
|
13245
13327
|
return 0;
|
|
13246
13328
|
}
|
|
@@ -13458,7 +13540,7 @@ app5.post("/reply", requireAdminSession, async (c) => {
|
|
|
13458
13540
|
const absolute = resolved.absolute;
|
|
13459
13541
|
let size = -1;
|
|
13460
13542
|
try {
|
|
13461
|
-
const st =
|
|
13543
|
+
const st = statSync9(absolute);
|
|
13462
13544
|
size = st.isFile() ? st.size : -1;
|
|
13463
13545
|
} catch {
|
|
13464
13546
|
size = -1;
|
|
@@ -13587,8 +13669,8 @@ app5.post("/reply", requireAdminSession, async (c) => {
|
|
|
13587
13669
|
var whatsapp_reader_default = app5;
|
|
13588
13670
|
|
|
13589
13671
|
// server/routes/public-reader.ts
|
|
13590
|
-
import { statSync as
|
|
13591
|
-
import { basename as basename7, dirname as dirname8, join as
|
|
13672
|
+
import { statSync as statSync10, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch3, readFileSync as readFileSync22, readdirSync as readdirSync15 } from "fs";
|
|
13673
|
+
import { basename as basename7, dirname as dirname8, join as join23, resolve as resolve18, sep as sep6 } from "path";
|
|
13592
13674
|
|
|
13593
13675
|
// app/lib/whatsapp-reader/delivered-kinds.ts
|
|
13594
13676
|
var PUBLIC_DELIVERED_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -13662,10 +13744,10 @@ function enumeratePublicRows() {
|
|
|
13662
13744
|
const cfg = claudeConfigDir();
|
|
13663
13745
|
if (!cfg) return [];
|
|
13664
13746
|
const rows = [];
|
|
13665
|
-
for (const { path: path3 } of enumerateJsonls(
|
|
13747
|
+
for (const { path: path3 } of enumerateJsonls(join23(cfg, "projects"))) {
|
|
13666
13748
|
const sessionId = basename7(path3).replace(/\.jsonl$/, "");
|
|
13667
13749
|
const projectDir = dirname8(path3);
|
|
13668
|
-
const meta = readSidecarMeta(
|
|
13750
|
+
const meta = readSidecarMeta(join23(projectDir, `${sessionId}.meta.json`));
|
|
13669
13751
|
rows.push({
|
|
13670
13752
|
sessionId,
|
|
13671
13753
|
projectDir,
|
|
@@ -13721,7 +13803,7 @@ function enrichPublicAttachments(turns, sessionAttachments, cursor) {
|
|
|
13721
13803
|
}
|
|
13722
13804
|
}
|
|
13723
13805
|
function readFrom2(path3, from) {
|
|
13724
|
-
const end =
|
|
13806
|
+
const end = statSync10(path3).size;
|
|
13725
13807
|
if (end <= from) return { buf: Buffer.alloc(0), end: from };
|
|
13726
13808
|
const fd = openSync2(path3, "r");
|
|
13727
13809
|
try {
|
|
@@ -13741,15 +13823,15 @@ app6.get("/stream", (c) => {
|
|
|
13741
13823
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
13742
13824
|
const projectDir = c.req.query("projectDir") ?? "";
|
|
13743
13825
|
const cfg = claudeConfigDir();
|
|
13744
|
-
const projectsRoot = cfg ? resolve18(
|
|
13826
|
+
const projectsRoot = cfg ? resolve18(join23(cfg, "projects")) : null;
|
|
13745
13827
|
if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
|
|
13746
13828
|
return c.json({ error: "bad session reference" }, 400);
|
|
13747
13829
|
}
|
|
13748
|
-
const jsonlPath = resolve18(
|
|
13830
|
+
const jsonlPath = resolve18(join23(projectDir, `${sessionId}.jsonl`));
|
|
13749
13831
|
if (!jsonlPath.startsWith(projectsRoot + sep6)) {
|
|
13750
13832
|
return c.json({ error: "bad session reference" }, 400);
|
|
13751
13833
|
}
|
|
13752
|
-
const meta = readSidecarMeta(
|
|
13834
|
+
const meta = readSidecarMeta(join23(projectDir, `${sessionId}.meta.json`));
|
|
13753
13835
|
const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
|
|
13754
13836
|
const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
|
|
13755
13837
|
if (!(isPublicWebchat && owns)) {
|
|
@@ -13886,16 +13968,16 @@ var public_reader_default = app6;
|
|
|
13886
13968
|
|
|
13887
13969
|
// server/routes/webchat.ts
|
|
13888
13970
|
import { basename as basename8, dirname as dirname9 } from "path";
|
|
13889
|
-
import { join as
|
|
13890
|
-
import { existsSync as existsSync19, readdirSync as readdirSync17, readFileSync as readFileSync25, renameSync as renameSync4, statSync as
|
|
13971
|
+
import { join as join26 } from "path";
|
|
13972
|
+
import { existsSync as existsSync19, readdirSync as readdirSync17, readFileSync as readFileSync25, renameSync as renameSync4, statSync as statSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
13891
13973
|
|
|
13892
13974
|
// server/canonical-webchat-override.ts
|
|
13893
13975
|
import { readFileSync as readFileSync23, writeFileSync as writeFileSync7, renameSync as renameSync2 } from "fs";
|
|
13894
|
-
import { join as
|
|
13976
|
+
import { join as join24 } from "path";
|
|
13895
13977
|
var FILE = "canonical-webchat-session.json";
|
|
13896
13978
|
var UUID2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
13897
13979
|
function canonicalOverridePath(accountDir) {
|
|
13898
|
-
return
|
|
13980
|
+
return join24(accountDir, FILE);
|
|
13899
13981
|
}
|
|
13900
13982
|
function readRaw(accountDir) {
|
|
13901
13983
|
try {
|
|
@@ -13975,7 +14057,7 @@ function isNewSessionEffortLevel(value) {
|
|
|
13975
14057
|
|
|
13976
14058
|
// ../services/claude-session-manager/src/sidecar-store.ts
|
|
13977
14059
|
import { existsSync as existsSync18, mkdirSync as mkdirSync4, readdirSync as readdirSync16, readFileSync as readFileSync24, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync8 } from "fs";
|
|
13978
|
-
import { join as
|
|
14060
|
+
import { join as join25 } from "path";
|
|
13979
14061
|
function escapeRegExp(s) {
|
|
13980
14062
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13981
14063
|
}
|
|
@@ -13983,7 +14065,7 @@ function createSidecarStore(config) {
|
|
|
13983
14065
|
const { suffix, validate: validate2 } = config;
|
|
13984
14066
|
const suffixRe = new RegExp(`${escapeRegExp(suffix)}$`);
|
|
13985
14067
|
function pathFor(sessionsDir, id) {
|
|
13986
|
-
return
|
|
14068
|
+
return join25(sessionsDir, `${id}${suffix}`);
|
|
13987
14069
|
}
|
|
13988
14070
|
function write(sessionsDir, id, record) {
|
|
13989
14071
|
mkdirSync4(sessionsDir, { recursive: true });
|
|
@@ -14027,7 +14109,7 @@ function createSidecarStore(config) {
|
|
|
14027
14109
|
const out = [];
|
|
14028
14110
|
for (const name of names) {
|
|
14029
14111
|
if (!suffixRe.test(name)) continue;
|
|
14030
|
-
const path3 =
|
|
14112
|
+
const path3 = join25(sessionsDir, name);
|
|
14031
14113
|
let raw;
|
|
14032
14114
|
try {
|
|
14033
14115
|
raw = readFileSync24(path3, "utf8");
|
|
@@ -14104,13 +14186,13 @@ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9
|
|
|
14104
14186
|
function locateSession(sessionId) {
|
|
14105
14187
|
const dir = findSessionProjectDir(sessionId);
|
|
14106
14188
|
if (!dir) return { projectDir: null, channel: null, accountId: null };
|
|
14107
|
-
const meta = readSidecarMeta(
|
|
14189
|
+
const meta = readSidecarMeta(join26(dir, `${sessionId}.meta.json`));
|
|
14108
14190
|
return { projectDir: dir, channel: meta.channel, accountId: meta.accountId };
|
|
14109
14191
|
}
|
|
14110
14192
|
function locateSidecar(sessionId) {
|
|
14111
14193
|
const cfg = claudeConfigDir();
|
|
14112
14194
|
if (!cfg) return null;
|
|
14113
|
-
const projectsRoot =
|
|
14195
|
+
const projectsRoot = join26(cfg, "projects");
|
|
14114
14196
|
let slugs;
|
|
14115
14197
|
try {
|
|
14116
14198
|
slugs = readdirSync17(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -14118,7 +14200,7 @@ function locateSidecar(sessionId) {
|
|
|
14118
14200
|
return null;
|
|
14119
14201
|
}
|
|
14120
14202
|
for (const slug of slugs) {
|
|
14121
|
-
const metaPath =
|
|
14203
|
+
const metaPath = join26(projectsRoot, slug, `${sessionId}.meta.json`);
|
|
14122
14204
|
if (existsSync19(metaPath)) return readSidecarMeta(metaPath);
|
|
14123
14205
|
}
|
|
14124
14206
|
return null;
|
|
@@ -14128,7 +14210,7 @@ function readTargetOwner(sessionId) {
|
|
|
14128
14210
|
if (sidecar !== null) return { found: true, owner: sidecar.adminUserId };
|
|
14129
14211
|
const { projectDir } = locateSession(sessionId);
|
|
14130
14212
|
if (projectDir !== null) {
|
|
14131
|
-
return { found: true, owner: readSidecarMeta(
|
|
14213
|
+
return { found: true, owner: readSidecarMeta(join26(projectDir, `${sessionId}.meta.json`)).adminUserId };
|
|
14132
14214
|
}
|
|
14133
14215
|
return { found: false, owner: null };
|
|
14134
14216
|
}
|
|
@@ -14136,7 +14218,7 @@ function readSourceRowMeta(sessionId) {
|
|
|
14136
14218
|
const sidecar = locateSidecar(sessionId);
|
|
14137
14219
|
if (sidecar !== null) return sidecar;
|
|
14138
14220
|
const { projectDir } = locateSession(sessionId);
|
|
14139
|
-
if (projectDir !== null) return readSidecarMeta(
|
|
14221
|
+
if (projectDir !== null) return readSidecarMeta(join26(projectDir, `${sessionId}.meta.json`));
|
|
14140
14222
|
return null;
|
|
14141
14223
|
}
|
|
14142
14224
|
function ownedByRequester(owner, requesterUserId, primaryUserId) {
|
|
@@ -14149,7 +14231,7 @@ function sessionSidecarExists(sessionId) {
|
|
|
14149
14231
|
function jsonlSizeBytes(projectDir, sessionId) {
|
|
14150
14232
|
if (projectDir === null) return null;
|
|
14151
14233
|
try {
|
|
14152
|
-
return
|
|
14234
|
+
return statSync11(join26(projectDir, `${sessionId}.jsonl`)).size;
|
|
14153
14235
|
} catch {
|
|
14154
14236
|
return null;
|
|
14155
14237
|
}
|
|
@@ -14285,15 +14367,15 @@ function latestAdminWebchatSessionId(requesterUserId, primaryUserId, scopeAccoun
|
|
|
14285
14367
|
const cfg = claudeConfigDir();
|
|
14286
14368
|
if (!cfg) return null;
|
|
14287
14369
|
let best = null;
|
|
14288
|
-
for (const { path: path3, isSubagent, archived } of enumerateJsonls(
|
|
14370
|
+
for (const { path: path3, isSubagent, archived } of enumerateJsonls(join26(cfg, "projects"))) {
|
|
14289
14371
|
if (isSubagent || archived) continue;
|
|
14290
14372
|
const id = basename8(path3).slice(0, -".jsonl".length);
|
|
14291
|
-
const meta = readSidecarMeta(
|
|
14373
|
+
const meta = readSidecarMeta(join26(dirname9(path3), `${id}.meta.json`));
|
|
14292
14374
|
if (meta.role !== "admin" || meta.channel !== "webchat") continue;
|
|
14293
14375
|
if (multiAccount && meta.accountId !== scopeAccountId) continue;
|
|
14294
14376
|
let mtimeMs;
|
|
14295
14377
|
try {
|
|
14296
|
-
mtimeMs =
|
|
14378
|
+
mtimeMs = statSync11(path3).mtimeMs;
|
|
14297
14379
|
} catch {
|
|
14298
14380
|
continue;
|
|
14299
14381
|
}
|
|
@@ -14601,7 +14683,7 @@ ${note}` : note;
|
|
|
14601
14683
|
console.error(`[webchat:settings] op=${op} outcome=refused value=${value} reason=account-unresolved`);
|
|
14602
14684
|
return c.json({ error: "account unresolved" }, 503);
|
|
14603
14685
|
}
|
|
14604
|
-
const accountJsonPath =
|
|
14686
|
+
const accountJsonPath = join26(account.accountDir, "account.json");
|
|
14605
14687
|
try {
|
|
14606
14688
|
const parsed = JSON.parse(readFileSync25(accountJsonPath, "utf8"));
|
|
14607
14689
|
if (op === "model") parsed.adminModel = value;
|
|
@@ -14649,7 +14731,7 @@ ${note}` : note;
|
|
|
14649
14731
|
const cfg2 = claudeConfigDir();
|
|
14650
14732
|
if (cfg2) {
|
|
14651
14733
|
const source2 = resolveBridgeSource(
|
|
14652
|
-
|
|
14734
|
+
join26(cfg2, "sessions"),
|
|
14653
14735
|
target,
|
|
14654
14736
|
(id) => locateSession(id).projectDir !== null
|
|
14655
14737
|
);
|
|
@@ -14681,7 +14763,7 @@ ${note}` : note;
|
|
|
14681
14763
|
const cfg = claudeConfigDir();
|
|
14682
14764
|
let projectDir = null;
|
|
14683
14765
|
if (cfg) {
|
|
14684
|
-
for (const { path: path3 } of enumerateJsonls(
|
|
14766
|
+
for (const { path: path3 } of enumerateJsonls(join26(cfg, "projects"))) {
|
|
14685
14767
|
if (basename8(path3) === `${sessionId}.jsonl`) {
|
|
14686
14768
|
projectDir = dirname9(path3);
|
|
14687
14769
|
break;
|
|
@@ -14689,7 +14771,7 @@ ${note}` : note;
|
|
|
14689
14771
|
}
|
|
14690
14772
|
}
|
|
14691
14773
|
if (source === "latest" && requesterUserId !== null) {
|
|
14692
|
-
const owner = readSidecarMeta(
|
|
14774
|
+
const owner = readSidecarMeta(join26(projectDir ?? "", `${sessionId}.meta.json`)).adminUserId;
|
|
14693
14775
|
if (owner !== null && owner !== requesterUserId && requesterUserId !== primaryUserId) {
|
|
14694
14776
|
console.error(`[webchat:inbound] op=session-resolve-foreign key=${sessionId.slice(0, 8)} owner=${owner.slice(0, 8)} requester=${requesterUserId.slice(0, 8)}`);
|
|
14695
14777
|
}
|
|
@@ -14722,7 +14804,7 @@ import { resolve as resolve19 } from "path";
|
|
|
14722
14804
|
|
|
14723
14805
|
// app/lib/claude-agent/specialist-roster.ts
|
|
14724
14806
|
import { existsSync as existsSync20, readFileSync as readFileSync26, readdirSync as readdirSync18 } from "fs";
|
|
14725
|
-
import { join as
|
|
14807
|
+
import { join as join27 } from "path";
|
|
14726
14808
|
function field(fm, name) {
|
|
14727
14809
|
const m = fm.match(new RegExp(`^${name}:\\s*(.*?)\\r?$`, "m"));
|
|
14728
14810
|
if (!m) return null;
|
|
@@ -14741,7 +14823,7 @@ function readSpecialistRoster(specialistsDir) {
|
|
|
14741
14823
|
if (!file.endsWith(".md")) continue;
|
|
14742
14824
|
let raw;
|
|
14743
14825
|
try {
|
|
14744
|
-
raw = readFileSync26(
|
|
14826
|
+
raw = readFileSync26(join27(specialistsDir, file), "utf-8");
|
|
14745
14827
|
} catch (err) {
|
|
14746
14828
|
skipped.push({ file, reason: err instanceof Error ? err.message : String(err) });
|
|
14747
14829
|
continue;
|
|
@@ -14858,7 +14940,7 @@ var webchat_greeting_default = app7;
|
|
|
14858
14940
|
|
|
14859
14941
|
// server/routes/telegram.ts
|
|
14860
14942
|
import { homedir as homedir2 } from "os";
|
|
14861
|
-
import { resolve as resolve20, join as
|
|
14943
|
+
import { resolve as resolve20, join as join28 } from "path";
|
|
14862
14944
|
import { existsSync as existsSync21, readFileSync as readFileSync27 } from "fs";
|
|
14863
14945
|
|
|
14864
14946
|
// app/lib/telegram/gateway/instance.ts
|
|
@@ -14918,7 +15000,7 @@ function routeTelegramUpdate(input) {
|
|
|
14918
15000
|
var TAG30 = "[telegram-inbound]";
|
|
14919
15001
|
function configDirName() {
|
|
14920
15002
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
|
|
14921
|
-
const brandPath =
|
|
15003
|
+
const brandPath = join28(platformRoot5, "config", "brand.json");
|
|
14922
15004
|
if (existsSync21(brandPath)) {
|
|
14923
15005
|
try {
|
|
14924
15006
|
return JSON.parse(readFileSync27(brandPath, "utf-8")).configDir ?? ".maxy";
|
|
@@ -15007,16 +15089,16 @@ app8.post("/", async (c) => {
|
|
|
15007
15089
|
var telegram_default = app8;
|
|
15008
15090
|
|
|
15009
15091
|
// server/routes/quickbooks.ts
|
|
15010
|
-
import { join as
|
|
15092
|
+
import { join as join29 } from "path";
|
|
15011
15093
|
import { existsSync as existsSync22, readFileSync as readFileSync28, writeFileSync as writeFileSync10, mkdirSync as mkdirSync5, rmSync, renameSync as renameSync5 } from "fs";
|
|
15012
15094
|
var TAG31 = "[quickbooks]";
|
|
15013
15095
|
var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
|
|
15014
15096
|
var STATE_RE = /^[A-Za-z0-9_-]+$/;
|
|
15015
15097
|
function pendingPath(accountDir, state) {
|
|
15016
|
-
return
|
|
15098
|
+
return join29(accountDir, "secrets", "quickbooks-pending", `${state}.json`);
|
|
15017
15099
|
}
|
|
15018
15100
|
function storePath(accountDir) {
|
|
15019
|
-
return
|
|
15101
|
+
return join29(accountDir, "secrets", "quickbooks.json");
|
|
15020
15102
|
}
|
|
15021
15103
|
async function completeConsent(args) {
|
|
15022
15104
|
const { accountDir, code, realmId } = args;
|
|
@@ -15083,7 +15165,7 @@ async function completeConsent(args) {
|
|
|
15083
15165
|
refreshTokenExpiry: now + (body.x_refresh_token_expires_in ?? 864e4) * 1e3,
|
|
15084
15166
|
lastRefresh: now
|
|
15085
15167
|
};
|
|
15086
|
-
mkdirSync5(
|
|
15168
|
+
mkdirSync5(join29(accountDir, "secrets"), { recursive: true });
|
|
15087
15169
|
writeFileSync10(storePath(accountDir), JSON.stringify(store2, null, 2), { mode: 384 });
|
|
15088
15170
|
const persisted = JSON.parse(readFileSync28(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
|
|
15089
15171
|
console.error(`${TAG31} op=token-exchange realmId=${realmId} persisted=${persisted}`);
|
|
@@ -15125,7 +15207,7 @@ function claudeBin() {
|
|
|
15125
15207
|
}
|
|
15126
15208
|
|
|
15127
15209
|
// server/routes/onboarding.ts
|
|
15128
|
-
import { dirname as dirname11, join as
|
|
15210
|
+
import { dirname as dirname11, join as join31 } from "path";
|
|
15129
15211
|
|
|
15130
15212
|
// app/lib/claude-keychain.ts
|
|
15131
15213
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
@@ -15254,8 +15336,8 @@ function readHostCredsBlob(service) {
|
|
|
15254
15336
|
}
|
|
15255
15337
|
|
|
15256
15338
|
// ../lib/admins-write/src/index.ts
|
|
15257
|
-
import { existsSync as existsSync23, readFileSync as readFileSync29, writeFileSync as writeFileSync11, renameSync as renameSync6, mkdirSync as mkdirSync6, readdirSync as readdirSync19, statSync as
|
|
15258
|
-
import { dirname as dirname10, join as
|
|
15339
|
+
import { existsSync as existsSync23, readFileSync as readFileSync29, writeFileSync as writeFileSync11, renameSync as renameSync6, mkdirSync as mkdirSync6, readdirSync as readdirSync19, statSync as statSync12, appendFileSync as appendFileSync3 } from "fs";
|
|
15340
|
+
import { dirname as dirname10, join as join30 } from "path";
|
|
15259
15341
|
function id8(value) {
|
|
15260
15342
|
return value.slice(0, 8);
|
|
15261
15343
|
}
|
|
@@ -15328,7 +15410,7 @@ function writeAdminEntry(input) {
|
|
|
15328
15410
|
return result;
|
|
15329
15411
|
}
|
|
15330
15412
|
try {
|
|
15331
|
-
const accountJsonPath =
|
|
15413
|
+
const accountJsonPath = join30(input.accountDir, "account.json");
|
|
15332
15414
|
if (!existsSync23(accountJsonPath)) {
|
|
15333
15415
|
throw new Error(`account.json not found at ${accountJsonPath}`);
|
|
15334
15416
|
}
|
|
@@ -15376,13 +15458,13 @@ function computeAdminStoreDivergence(input) {
|
|
|
15376
15458
|
}
|
|
15377
15459
|
for (const entry of entries2) {
|
|
15378
15460
|
if (entry.startsWith(".")) continue;
|
|
15379
|
-
const accountDir =
|
|
15461
|
+
const accountDir = join30(input.accountsDir, entry);
|
|
15380
15462
|
try {
|
|
15381
|
-
if (!
|
|
15463
|
+
if (!statSync12(accountDir).isDirectory()) continue;
|
|
15382
15464
|
} catch {
|
|
15383
15465
|
continue;
|
|
15384
15466
|
}
|
|
15385
|
-
const accountJsonPath =
|
|
15467
|
+
const accountJsonPath = join30(accountDir, "account.json");
|
|
15386
15468
|
if (!existsSync23(accountJsonPath)) continue;
|
|
15387
15469
|
let admins = [];
|
|
15388
15470
|
try {
|
|
@@ -15560,7 +15642,7 @@ app10.post("/claude-auth", async (c) => {
|
|
|
15560
15642
|
});
|
|
15561
15643
|
persistKeychainService(
|
|
15562
15644
|
writebackResult,
|
|
15563
|
-
(service) => writeFileSync12(
|
|
15645
|
+
(service) => writeFileSync12(join31(dirname11(CLAUDE_CREDENTIALS_FILE), ".keychain-service"), service, { mode: 384 }),
|
|
15564
15646
|
(line) => console.log(line)
|
|
15565
15647
|
);
|
|
15566
15648
|
if (darwinAuthChild === claudeProc2) darwinAuthChild = null;
|
|
@@ -15825,9 +15907,9 @@ ${body.newPin}
|
|
|
15825
15907
|
var onboarding_default = app10;
|
|
15826
15908
|
|
|
15827
15909
|
// server/routes/client-error.ts
|
|
15828
|
-
import { appendFileSync as appendFileSync4, existsSync as existsSync25, renameSync as renameSync8, statSync as
|
|
15829
|
-
import { join as
|
|
15830
|
-
var CLIENT_ERRORS_LOG =
|
|
15910
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync25, renameSync as renameSync8, statSync as statSync13 } from "fs";
|
|
15911
|
+
import { join as join32 } from "path";
|
|
15912
|
+
var CLIENT_ERRORS_LOG = join32(LOG_DIR, "client-errors.log");
|
|
15831
15913
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
15832
15914
|
var MAX_BODY_SIZE = 8 * 1024;
|
|
15833
15915
|
var MAX_STACK_LEN = 2e3;
|
|
@@ -15885,7 +15967,7 @@ function stackHead(stack) {
|
|
|
15885
15967
|
function rotateIfNeeded() {
|
|
15886
15968
|
try {
|
|
15887
15969
|
if (!existsSync25(CLIENT_ERRORS_LOG)) return;
|
|
15888
|
-
const stats =
|
|
15970
|
+
const stats = statSync13(CLIENT_ERRORS_LOG);
|
|
15889
15971
|
if (stats.size < MAX_LOG_SIZE) return;
|
|
15890
15972
|
renameSync8(CLIENT_ERRORS_LOG, CLIENT_ERRORS_LOG + ".1");
|
|
15891
15973
|
} catch (err) {
|
|
@@ -15971,7 +16053,7 @@ app11.post("/", async (c) => {
|
|
|
15971
16053
|
const safe = typeof v === "string" ? truncate(v, 200) : typeof v === "number" || typeof v === "boolean" ? String(v) : JSON.stringify(v).slice(0, 200);
|
|
15972
16054
|
return `${k}=${safe}`;
|
|
15973
16055
|
}).join(" ");
|
|
15974
|
-
const TAGGED_PREFIX_SOURCES = /* @__PURE__ */ new Set(["stream-log-click", "pwa", "graph-settle"]);
|
|
16056
|
+
const TAGGED_PREFIX_SOURCES = /* @__PURE__ */ new Set(["stream-log-click", "pwa", "graph-settle", "dashboard-load"]);
|
|
15975
16057
|
if (TAGGED_PREFIX_SOURCES.has(source)) {
|
|
15976
16058
|
console.log(
|
|
15977
16059
|
`[${source}] ts=${ts} ip=${ip} version=${version || "unknown"}${extra ? " " + extra : ""}`
|
|
@@ -16211,17 +16293,17 @@ app13.get("/", requireAdminSession, async (c) => {
|
|
|
16211
16293
|
var accounts_default = app13;
|
|
16212
16294
|
|
|
16213
16295
|
// server/routes/admin/logs.ts
|
|
16214
|
-
import { existsSync as existsSync27, readdirSync as readdirSync20, readFileSync as readFileSync31, statSync as
|
|
16296
|
+
import { existsSync as existsSync27, readdirSync as readdirSync20, readFileSync as readFileSync31, statSync as statSync14 } from "fs";
|
|
16215
16297
|
import { resolve as resolve21, basename as basename9 } from "path";
|
|
16216
16298
|
|
|
16217
16299
|
// app/lib/logs-read-resolve.ts
|
|
16218
16300
|
import { existsSync as existsSync26 } from "fs";
|
|
16219
|
-
import { join as
|
|
16301
|
+
import { join as join33 } from "path";
|
|
16220
16302
|
function resolveSessionLogPaths(filename, logDirs) {
|
|
16221
16303
|
const tried = [filename];
|
|
16222
16304
|
const hits = [];
|
|
16223
16305
|
for (const dir of logDirs) {
|
|
16224
|
-
const fullPath =
|
|
16306
|
+
const fullPath = join33(dir, filename);
|
|
16225
16307
|
if (existsSync26(fullPath)) {
|
|
16226
16308
|
hits.push({ path: fullPath, dir });
|
|
16227
16309
|
}
|
|
@@ -16251,7 +16333,7 @@ app14.get("/", async (c) => {
|
|
|
16251
16333
|
searched.push(filePath);
|
|
16252
16334
|
try {
|
|
16253
16335
|
const buffer = readFileSync31(filePath);
|
|
16254
|
-
const onDiskBytes =
|
|
16336
|
+
const onDiskBytes = statSync14(filePath).size;
|
|
16255
16337
|
const headers = {
|
|
16256
16338
|
"Content-Type": "text/plain; charset=utf-8",
|
|
16257
16339
|
"Content-Length": String(buffer.byteLength)
|
|
@@ -16316,7 +16398,7 @@ app14.get("/", async (c) => {
|
|
|
16316
16398
|
try {
|
|
16317
16399
|
const filename = basename9(hit.path);
|
|
16318
16400
|
const buffer = readFileSync31(hit.path);
|
|
16319
|
-
const onDiskBytes =
|
|
16401
|
+
const onDiskBytes = statSync14(hit.path).size;
|
|
16320
16402
|
const headers = {
|
|
16321
16403
|
"Content-Type": "text/plain; charset=utf-8",
|
|
16322
16404
|
"Content-Length": String(buffer.byteLength)
|
|
@@ -16359,7 +16441,7 @@ app14.get("/", async (c) => {
|
|
|
16359
16441
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
16360
16442
|
continue;
|
|
16361
16443
|
}
|
|
16362
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime:
|
|
16444
|
+
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync14(resolve21(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
16363
16445
|
seen.add(name);
|
|
16364
16446
|
try {
|
|
16365
16447
|
const content = readFileSync31(resolve21(dir, name));
|
|
@@ -16479,7 +16561,7 @@ var attachment_default = app16;
|
|
|
16479
16561
|
|
|
16480
16562
|
// server/routes/admin/session-upload.ts
|
|
16481
16563
|
import { readFile as readFile5 } from "fs/promises";
|
|
16482
|
-
import { existsSync as existsSync29, statSync as
|
|
16564
|
+
import { existsSync as existsSync29, statSync as statSync15 } from "fs";
|
|
16483
16565
|
var app17 = new Hono();
|
|
16484
16566
|
app17.get("/", requireAdminSession, async (c) => {
|
|
16485
16567
|
const cacheKey = c.var.cacheKey;
|
|
@@ -16500,7 +16582,7 @@ app17.get("/", requireAdminSession, async (c) => {
|
|
|
16500
16582
|
return c.json({ error: "Not found" }, 404);
|
|
16501
16583
|
}
|
|
16502
16584
|
const resolution = resolveClaudeUploadsPath(`${sessionId}/${file}`);
|
|
16503
|
-
if (!resolution.ok || !existsSync29(resolution.absolute) || !
|
|
16585
|
+
if (!resolution.ok || !existsSync29(resolution.absolute) || !statSync15(resolution.absolute).isFile()) {
|
|
16504
16586
|
console.log(`[session-upload] op=miss session=${short2} file=${file} status=404 reason=absent`);
|
|
16505
16587
|
return c.json({ error: "Not found" }, 404);
|
|
16506
16588
|
}
|
|
@@ -16522,7 +16604,7 @@ var session_upload_default = app17;
|
|
|
16522
16604
|
|
|
16523
16605
|
// server/routes/admin/agents.ts
|
|
16524
16606
|
import { resolve as resolve23 } from "path";
|
|
16525
|
-
import { readdirSync as
|
|
16607
|
+
import { readdirSync as readdirSync22, readFileSync as readFileSync33, existsSync as existsSync31, rmSync as rmSync2, renameSync as renameSync9, mkdirSync as mkdirSync7, writeFileSync as writeFileSync13 } from "fs";
|
|
16526
16608
|
|
|
16527
16609
|
// server/routes/admin/_account-scope.ts
|
|
16528
16610
|
function accountDirForSession(cacheKey) {
|
|
@@ -16549,12 +16631,390 @@ async function authorizeAccountWrite(c, accountId) {
|
|
|
16549
16631
|
return { ok: true, via: "session" };
|
|
16550
16632
|
}
|
|
16551
16633
|
|
|
16634
|
+
// ../services/claude-session-manager/src/tool-surface.ts
|
|
16635
|
+
import { readFileSync as readFileSync32, readdirSync as readdirSync21, statSync as statSync16, existsSync as existsSync30 } from "fs";
|
|
16636
|
+
import { createHash as createHash5 } from "crypto";
|
|
16637
|
+
import { join as join34 } from "path";
|
|
16638
|
+
|
|
16639
|
+
// ../services/claude-session-manager/src/canonical-tool-names.ts
|
|
16640
|
+
function canonicalToolName(plugin, tool) {
|
|
16641
|
+
return `mcp__plugin_${plugin}_${plugin}__${tool}`;
|
|
16642
|
+
}
|
|
16643
|
+
function qualifyShortToolName(plugin, tool) {
|
|
16644
|
+
return `mcp__${plugin}__${tool}`;
|
|
16645
|
+
}
|
|
16646
|
+
|
|
16647
|
+
// ../services/claude-session-manager/src/tool-surface.ts
|
|
16648
|
+
var RISK_CLASSES = ["read", "write_local", "exec", "external"];
|
|
16649
|
+
var ToolSurfaceError = class extends Error {
|
|
16650
|
+
constructor(reason, detail) {
|
|
16651
|
+
super(`${reason}: ${detail}`);
|
|
16652
|
+
this.reason = reason;
|
|
16653
|
+
this.detail = detail;
|
|
16654
|
+
this.name = "ToolSurfaceError";
|
|
16655
|
+
}
|
|
16656
|
+
};
|
|
16657
|
+
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
16658
|
+
function extractFrontmatter(raw, pluginDir) {
|
|
16659
|
+
const m = raw.match(FRONTMATTER_RE);
|
|
16660
|
+
if (!m) {
|
|
16661
|
+
throw new ToolSurfaceError("frontmatter-missing", `${pluginDir}/PLUGIN.md has no '---' delimited frontmatter`);
|
|
16662
|
+
}
|
|
16663
|
+
return m[1];
|
|
16664
|
+
}
|
|
16665
|
+
function parseToolsBlock(frontmatter, pluginDir) {
|
|
16666
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
16667
|
+
let i = 0;
|
|
16668
|
+
while (i < lines.length) {
|
|
16669
|
+
const line = lines[i];
|
|
16670
|
+
if (/^tools:\s*\[\s*\]\s*$/.test(line)) return [];
|
|
16671
|
+
if (/^tools:\s*$/.test(line)) {
|
|
16672
|
+
i++;
|
|
16673
|
+
break;
|
|
16674
|
+
}
|
|
16675
|
+
i++;
|
|
16676
|
+
}
|
|
16677
|
+
if (i >= lines.length) return [];
|
|
16678
|
+
const out = [];
|
|
16679
|
+
let current = null;
|
|
16680
|
+
const flush = () => {
|
|
16681
|
+
if (!current) return;
|
|
16682
|
+
if (typeof current.name !== "string" || current.name.length === 0) {
|
|
16683
|
+
throw new ToolSurfaceError("tool-name-missing", `${pluginDir}/PLUGIN.md has a tool entry with no name`);
|
|
16684
|
+
}
|
|
16685
|
+
if (typeof current.publicAllowlist !== "boolean") {
|
|
16686
|
+
if (current.publicAllowlist === void 0) {
|
|
16687
|
+
throw new ToolSurfaceError(
|
|
16688
|
+
"public-allowlist-missing",
|
|
16689
|
+
`${pluginDir}/${current.name} \u2014 every tool must declare publicAllowlist: <true|false>`
|
|
16690
|
+
);
|
|
16691
|
+
}
|
|
16692
|
+
throw new ToolSurfaceError(
|
|
16693
|
+
"public-allowlist-not-boolean",
|
|
16694
|
+
`${pluginDir}/${current.name} \u2014 publicAllowlist must be a YAML boolean (true|false), got ${JSON.stringify(current.publicAllowlist)}`
|
|
16695
|
+
);
|
|
16696
|
+
}
|
|
16697
|
+
if (typeof current.adminAllowlist !== "boolean") {
|
|
16698
|
+
if (current.adminAllowlist === void 0) {
|
|
16699
|
+
throw new ToolSurfaceError(
|
|
16700
|
+
"admin-allowlist-missing",
|
|
16701
|
+
`${pluginDir}/${current.name} \u2014 every tool must declare adminAllowlist: <true|false>`
|
|
16702
|
+
);
|
|
16703
|
+
}
|
|
16704
|
+
throw new ToolSurfaceError(
|
|
16705
|
+
"admin-allowlist-not-boolean",
|
|
16706
|
+
`${pluginDir}/${current.name} \u2014 adminAllowlist must be a YAML boolean (true|false), got ${JSON.stringify(current.adminAllowlist)}`
|
|
16707
|
+
);
|
|
16708
|
+
}
|
|
16709
|
+
if (current.riskClass === void 0) {
|
|
16710
|
+
throw new ToolSurfaceError(
|
|
16711
|
+
"risk-class-missing",
|
|
16712
|
+
`${pluginDir}/${current.name} \u2014 every tool must declare riskClass: <${RISK_CLASSES.join("|")}>`
|
|
16713
|
+
);
|
|
16714
|
+
}
|
|
16715
|
+
if (!RISK_CLASSES.includes(current.riskClass)) {
|
|
16716
|
+
throw new ToolSurfaceError(
|
|
16717
|
+
"risk-class-invalid",
|
|
16718
|
+
`${pluginDir}/${current.name} \u2014 riskClass must be one of ${RISK_CLASSES.join("|")}, got ${JSON.stringify(current.riskClass)}`
|
|
16719
|
+
);
|
|
16720
|
+
}
|
|
16721
|
+
out.push(current);
|
|
16722
|
+
current = null;
|
|
16723
|
+
};
|
|
16724
|
+
for (; i < lines.length; i++) {
|
|
16725
|
+
const line = lines[i];
|
|
16726
|
+
if (line.length === 0) continue;
|
|
16727
|
+
if (/^[A-Za-z_]/.test(line)) break;
|
|
16728
|
+
const newEntry = line.match(/^\s+-\s+name:\s*(.+?)\s*$/);
|
|
16729
|
+
if (newEntry) {
|
|
16730
|
+
flush();
|
|
16731
|
+
current = { name: newEntry[1] };
|
|
16732
|
+
continue;
|
|
16733
|
+
}
|
|
16734
|
+
const orphanListItem = line.match(/^\s+-\s+(?!name:)/);
|
|
16735
|
+
if (orphanListItem) {
|
|
16736
|
+
throw new ToolSurfaceError("tool-name-missing", `${pluginDir}/PLUGIN.md tool entry lacks a name field`);
|
|
16737
|
+
}
|
|
16738
|
+
const publicAllow = line.match(/^\s+publicAllowlist:\s*(.+?)\s*$/);
|
|
16739
|
+
if (publicAllow && current) {
|
|
16740
|
+
const v = publicAllow[1].trim();
|
|
16741
|
+
if (v === "true") current.publicAllowlist = true;
|
|
16742
|
+
else if (v === "false") current.publicAllowlist = false;
|
|
16743
|
+
else current.publicAllowlist = v;
|
|
16744
|
+
continue;
|
|
16745
|
+
}
|
|
16746
|
+
const adminAllow = line.match(/^\s+adminAllowlist:\s*(.+?)\s*$/);
|
|
16747
|
+
if (adminAllow && current) {
|
|
16748
|
+
const v = adminAllow[1].trim();
|
|
16749
|
+
if (v === "true") current.adminAllowlist = true;
|
|
16750
|
+
else if (v === "false") current.adminAllowlist = false;
|
|
16751
|
+
else current.adminAllowlist = v;
|
|
16752
|
+
continue;
|
|
16753
|
+
}
|
|
16754
|
+
const risk = line.match(/^\s+riskClass:\s*(.+?)\s*$/);
|
|
16755
|
+
if (risk && current) {
|
|
16756
|
+
current.riskClass = risk[1].trim();
|
|
16757
|
+
continue;
|
|
16758
|
+
}
|
|
16759
|
+
}
|
|
16760
|
+
flush();
|
|
16761
|
+
return out;
|
|
16762
|
+
}
|
|
16763
|
+
function parseHiddenBlock(frontmatter, pluginDir) {
|
|
16764
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
16765
|
+
let i = 0;
|
|
16766
|
+
while (i < lines.length) {
|
|
16767
|
+
if (/^hidden:\s*$/.test(lines[i])) {
|
|
16768
|
+
i++;
|
|
16769
|
+
break;
|
|
16770
|
+
}
|
|
16771
|
+
i++;
|
|
16772
|
+
}
|
|
16773
|
+
if (i >= lines.length) return [];
|
|
16774
|
+
const out = [];
|
|
16775
|
+
let current = null;
|
|
16776
|
+
const flush = () => {
|
|
16777
|
+
if (!current) return;
|
|
16778
|
+
if (current.riskClass === void 0) {
|
|
16779
|
+
throw new ToolSurfaceError(
|
|
16780
|
+
"risk-class-missing",
|
|
16781
|
+
`${pluginDir}/${current.name} \u2014 every hidden tool must declare riskClass: <${RISK_CLASSES.join("|")}>`
|
|
16782
|
+
);
|
|
16783
|
+
}
|
|
16784
|
+
if (!RISK_CLASSES.includes(current.riskClass)) {
|
|
16785
|
+
throw new ToolSurfaceError(
|
|
16786
|
+
"risk-class-invalid",
|
|
16787
|
+
`${pluginDir}/${current.name} \u2014 hidden riskClass must be one of ${RISK_CLASSES.join("|")}, got ${JSON.stringify(current.riskClass)}`
|
|
16788
|
+
);
|
|
16789
|
+
}
|
|
16790
|
+
out.push(current);
|
|
16791
|
+
current = null;
|
|
16792
|
+
};
|
|
16793
|
+
for (; i < lines.length; i++) {
|
|
16794
|
+
const line = lines[i];
|
|
16795
|
+
if (line.length === 0) continue;
|
|
16796
|
+
if (/^[A-Za-z_]/.test(line)) break;
|
|
16797
|
+
const newEntry = line.match(/^\s+-\s+name:\s*(.+?)\s*$/);
|
|
16798
|
+
if (newEntry) {
|
|
16799
|
+
flush();
|
|
16800
|
+
current = { name: newEntry[1] };
|
|
16801
|
+
continue;
|
|
16802
|
+
}
|
|
16803
|
+
const bareItem = line.match(/^\s+-\s+(.+?)\s*$/);
|
|
16804
|
+
if (bareItem) {
|
|
16805
|
+
throw new ToolSurfaceError(
|
|
16806
|
+
"hidden-tool-name-missing",
|
|
16807
|
+
`${pluginDir}/PLUGIN.md hidden entry "${bareItem[1]}" is a bare name \u2014 declare it as "- name: ${bareItem[1]}" with a riskClass`
|
|
16808
|
+
);
|
|
16809
|
+
}
|
|
16810
|
+
const risk = line.match(/^\s+riskClass:\s*(.+?)\s*$/);
|
|
16811
|
+
if (risk && current) {
|
|
16812
|
+
current.riskClass = risk[1].trim();
|
|
16813
|
+
continue;
|
|
16814
|
+
}
|
|
16815
|
+
}
|
|
16816
|
+
flush();
|
|
16817
|
+
return out;
|
|
16818
|
+
}
|
|
16819
|
+
function parseMcpBlock(frontmatter, pluginDir) {
|
|
16820
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
16821
|
+
let i = 0;
|
|
16822
|
+
while (i < lines.length) {
|
|
16823
|
+
if (/^mcp:\s*$/.test(lines[i])) {
|
|
16824
|
+
i++;
|
|
16825
|
+
break;
|
|
16826
|
+
}
|
|
16827
|
+
i++;
|
|
16828
|
+
}
|
|
16829
|
+
if (i >= lines.length) return null;
|
|
16830
|
+
let command = null;
|
|
16831
|
+
const args = [];
|
|
16832
|
+
const env = {};
|
|
16833
|
+
let envHasEntry = false;
|
|
16834
|
+
let mode = "top";
|
|
16835
|
+
for (; i < lines.length; i++) {
|
|
16836
|
+
const line = lines[i];
|
|
16837
|
+
if (line.length === 0) continue;
|
|
16838
|
+
if (/^[A-Za-z_]/.test(line)) break;
|
|
16839
|
+
const topCommand = line.match(/^\s\s(command):\s*(.+?)\s*$/);
|
|
16840
|
+
const topArgsHeader = line.match(/^\s\s(args):\s*$/);
|
|
16841
|
+
const topEnvHeader = line.match(/^\s\s(env):\s*$/);
|
|
16842
|
+
if (topCommand) {
|
|
16843
|
+
command = topCommand[2];
|
|
16844
|
+
mode = "top";
|
|
16845
|
+
continue;
|
|
16846
|
+
}
|
|
16847
|
+
if (topArgsHeader) {
|
|
16848
|
+
mode = "args";
|
|
16849
|
+
continue;
|
|
16850
|
+
}
|
|
16851
|
+
if (topEnvHeader) {
|
|
16852
|
+
mode = "env";
|
|
16853
|
+
continue;
|
|
16854
|
+
}
|
|
16855
|
+
if (mode === "args") {
|
|
16856
|
+
const item = line.match(/^\s+-\s+(.+?)\s*$/);
|
|
16857
|
+
if (item) {
|
|
16858
|
+
args.push(item[1]);
|
|
16859
|
+
continue;
|
|
16860
|
+
}
|
|
16861
|
+
}
|
|
16862
|
+
if (mode === "env") {
|
|
16863
|
+
const kv = line.match(/^\s+([A-Za-z_][A-Za-z0-9_]*):\s*(.+?)\s*$/);
|
|
16864
|
+
if (kv) {
|
|
16865
|
+
env[kv[1]] = kv[2];
|
|
16866
|
+
envHasEntry = true;
|
|
16867
|
+
continue;
|
|
16868
|
+
}
|
|
16869
|
+
}
|
|
16870
|
+
}
|
|
16871
|
+
if (command === null) {
|
|
16872
|
+
throw new ToolSurfaceError("mcp-command-missing", `${pluginDir}/PLUGIN.md mcp: block has no command`);
|
|
16873
|
+
}
|
|
16874
|
+
const desc = { command, args };
|
|
16875
|
+
if (envHasEntry) desc.env = env;
|
|
16876
|
+
return desc;
|
|
16877
|
+
}
|
|
16878
|
+
function loadToolSurface(pluginsRoots) {
|
|
16879
|
+
const roots = Array.isArray(pluginsRoots) ? pluginsRoots : [pluginsRoots];
|
|
16880
|
+
for (const r of roots) {
|
|
16881
|
+
if (!existsSync30(r)) throw new ToolSurfaceError("plugins-root-missing", r);
|
|
16882
|
+
}
|
|
16883
|
+
const admin = /* @__PURE__ */ new Set();
|
|
16884
|
+
const pub = /* @__PURE__ */ new Set();
|
|
16885
|
+
const all = /* @__PURE__ */ new Set();
|
|
16886
|
+
const mcpServers = /* @__PURE__ */ new Map();
|
|
16887
|
+
const riskByTool = /* @__PURE__ */ new Map();
|
|
16888
|
+
const manifestPaths = /* @__PURE__ */ new Map();
|
|
16889
|
+
const loadOne = (pluginsRoot, plugin) => {
|
|
16890
|
+
const pluginDir = join34(pluginsRoot, plugin);
|
|
16891
|
+
try {
|
|
16892
|
+
if (!statSync16(pluginDir).isDirectory()) return;
|
|
16893
|
+
} catch {
|
|
16894
|
+
return;
|
|
16895
|
+
}
|
|
16896
|
+
const manifestPath = join34(pluginDir, "PLUGIN.md");
|
|
16897
|
+
if (!existsSync30(manifestPath)) return;
|
|
16898
|
+
const raw = readFileSync32(manifestPath, "utf-8");
|
|
16899
|
+
const frontmatter = extractFrontmatter(raw, plugin);
|
|
16900
|
+
const tools = parseToolsBlock(frontmatter, plugin);
|
|
16901
|
+
if (tools.length === 0) return;
|
|
16902
|
+
const mcp = parseMcpBlock(frontmatter, plugin);
|
|
16903
|
+
if (!mcp) {
|
|
16904
|
+
throw new ToolSurfaceError(
|
|
16905
|
+
"mcp-block-missing",
|
|
16906
|
+
`${plugin}/PLUGIN.md declares tools but no mcp: block; spawns would --allowed-tools mcp__${plugin}__* with no registered server`
|
|
16907
|
+
);
|
|
16908
|
+
}
|
|
16909
|
+
if (mcpServers.has(plugin)) {
|
|
16910
|
+
const priorPath = manifestPaths.get(plugin);
|
|
16911
|
+
if (!priorPath) {
|
|
16912
|
+
throw new ToolSurfaceError(
|
|
16913
|
+
"mcp-plugin-duplicate-no-prior-path",
|
|
16914
|
+
`${plugin}: mcpServers has entry but manifestPaths is missing \u2014 invariant violated, both maps must be written together`
|
|
16915
|
+
);
|
|
16916
|
+
}
|
|
16917
|
+
const priorSha = createHash5("sha256").update(readFileSync32(priorPath)).digest("hex").slice(0, 8);
|
|
16918
|
+
const newSha = createHash5("sha256").update(raw).digest("hex").slice(0, 8);
|
|
16919
|
+
throw new ToolSurfaceError(
|
|
16920
|
+
"mcp-plugin-duplicate",
|
|
16921
|
+
`${plugin} declared by more than one plugins root: ${priorPath} (sha=${priorSha}) vs ${manifestPath} (sha=${newSha})`
|
|
16922
|
+
);
|
|
16923
|
+
}
|
|
16924
|
+
mcpServers.set(plugin, mcp);
|
|
16925
|
+
manifestPaths.set(plugin, manifestPath);
|
|
16926
|
+
for (const t of tools) {
|
|
16927
|
+
const canonical = canonicalToolName(plugin, t.name);
|
|
16928
|
+
all.add(canonical);
|
|
16929
|
+
riskByTool.set(canonical, t.riskClass);
|
|
16930
|
+
if (t.adminAllowlist) admin.add(canonical);
|
|
16931
|
+
if (t.publicAllowlist) pub.add(qualifyShortToolName(plugin, t.name));
|
|
16932
|
+
}
|
|
16933
|
+
for (const h of parseHiddenBlock(frontmatter, plugin)) {
|
|
16934
|
+
const canonical = canonicalToolName(plugin, h.name);
|
|
16935
|
+
all.add(canonical);
|
|
16936
|
+
riskByTool.set(canonical, h.riskClass);
|
|
16937
|
+
}
|
|
16938
|
+
};
|
|
16939
|
+
for (const root of roots) {
|
|
16940
|
+
const entries2 = readdirSync21(root).sort();
|
|
16941
|
+
for (const plugin of entries2) {
|
|
16942
|
+
const pluginDir = join34(root, plugin);
|
|
16943
|
+
loadOne(root, plugin);
|
|
16944
|
+
const subRoot = join34(pluginDir, "plugins");
|
|
16945
|
+
let subEntries;
|
|
16946
|
+
try {
|
|
16947
|
+
if (!statSync16(subRoot).isDirectory()) continue;
|
|
16948
|
+
subEntries = readdirSync21(subRoot).sort();
|
|
16949
|
+
} catch {
|
|
16950
|
+
continue;
|
|
16951
|
+
}
|
|
16952
|
+
for (const sub of subEntries) {
|
|
16953
|
+
loadOne(subRoot, sub);
|
|
16954
|
+
}
|
|
16955
|
+
}
|
|
16956
|
+
}
|
|
16957
|
+
return { admin, public: pub, all, mcpServers, riskByTool };
|
|
16958
|
+
}
|
|
16959
|
+
|
|
16960
|
+
// server/lib/agent-risk.ts
|
|
16961
|
+
var BUILTIN_TOOL_RISK = {
|
|
16962
|
+
Bash: "exec",
|
|
16963
|
+
Task: "exec",
|
|
16964
|
+
Skill: "exec",
|
|
16965
|
+
Edit: "write_local",
|
|
16966
|
+
MultiEdit: "write_local",
|
|
16967
|
+
Write: "write_local",
|
|
16968
|
+
Read: "read",
|
|
16969
|
+
Glob: "read",
|
|
16970
|
+
Grep: "read",
|
|
16971
|
+
WebFetch: "external",
|
|
16972
|
+
WebSearch: "external"
|
|
16973
|
+
};
|
|
16974
|
+
var SEVERITY = {
|
|
16975
|
+
read: 0,
|
|
16976
|
+
write_local: 1,
|
|
16977
|
+
exec: 2,
|
|
16978
|
+
external: 2
|
|
16979
|
+
};
|
|
16980
|
+
var AGENT_CLASS = {
|
|
16981
|
+
read: "green",
|
|
16982
|
+
write_local: "amber",
|
|
16983
|
+
exec: "red",
|
|
16984
|
+
external: "red"
|
|
16985
|
+
};
|
|
16986
|
+
function agentRiskClass(tools, riskByTool) {
|
|
16987
|
+
const byTool = [];
|
|
16988
|
+
const unresolved = [];
|
|
16989
|
+
let worst = null;
|
|
16990
|
+
let riskiestTool = null;
|
|
16991
|
+
for (const tool of tools) {
|
|
16992
|
+
const cls = BUILTIN_TOOL_RISK[tool] ?? riskByTool.get(tool) ?? null;
|
|
16993
|
+
byTool.push({ tool, risk: cls });
|
|
16994
|
+
if (cls === null) {
|
|
16995
|
+
unresolved.push(tool);
|
|
16996
|
+
continue;
|
|
16997
|
+
}
|
|
16998
|
+
if (worst === null || SEVERITY[cls] > SEVERITY[worst]) {
|
|
16999
|
+
worst = cls;
|
|
17000
|
+
riskiestTool = tool;
|
|
17001
|
+
}
|
|
17002
|
+
}
|
|
17003
|
+
if (unresolved.length > 0) {
|
|
17004
|
+
return { risk: "red", riskiestTool: unresolved[0], unresolved, byTool };
|
|
17005
|
+
}
|
|
17006
|
+
if (worst === null) {
|
|
17007
|
+
return { risk: "red", riskiestTool: null, unresolved, byTool };
|
|
17008
|
+
}
|
|
17009
|
+
return { risk: AGENT_CLASS[worst], riskiestTool, unresolved, byTool };
|
|
17010
|
+
}
|
|
17011
|
+
|
|
16552
17012
|
// server/routes/admin/agents.ts
|
|
16553
17013
|
var SLUG_UNSAFE = (s) => s.includes("/") || s.includes("..") || s.includes("\\");
|
|
16554
17014
|
function subdirs(dir) {
|
|
16555
|
-
if (!
|
|
17015
|
+
if (!existsSync31(dir)) return [];
|
|
16556
17016
|
try {
|
|
16557
|
-
return
|
|
17017
|
+
return readdirSync22(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
16558
17018
|
} catch {
|
|
16559
17019
|
return [];
|
|
16560
17020
|
}
|
|
@@ -16569,34 +17029,148 @@ function splitFrontmatter(text) {
|
|
|
16569
17029
|
}
|
|
16570
17030
|
return { fm, body: text.slice(m[0].length) };
|
|
16571
17031
|
}
|
|
16572
|
-
|
|
17032
|
+
var SPECIALISTS_DIR = ["specialists", "agents"];
|
|
17033
|
+
var QUARANTINE_DIR = ["specialists", "agents-disabled"];
|
|
17034
|
+
var DISABLED_STORE = "agents-disabled.json";
|
|
17035
|
+
function loadRiskSurface() {
|
|
17036
|
+
const roots = [resolve23(PLATFORM_ROOT, "plugins"), resolve23(PLATFORM_ROOT, "..", "premium-plugins")].filter((r) => existsSync31(r));
|
|
17037
|
+
try {
|
|
17038
|
+
return { riskByTool: loadToolSurface(roots).riskByTool, failed: null };
|
|
17039
|
+
} catch (err) {
|
|
17040
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
17041
|
+
console.error(`[admin/agents] op=risk-surface status=load-failed reason="${msg}"`);
|
|
17042
|
+
return { riskByTool: /* @__PURE__ */ new Map(), failed: msg };
|
|
17043
|
+
}
|
|
17044
|
+
}
|
|
17045
|
+
function readDisabledStoreStrict(accountDir) {
|
|
17046
|
+
const p = resolve23(accountDir, DISABLED_STORE);
|
|
17047
|
+
if (!existsSync31(p)) return /* @__PURE__ */ new Set();
|
|
17048
|
+
const parsed = JSON.parse(readFileSync33(p, "utf-8"));
|
|
17049
|
+
if (!Array.isArray(parsed.disabled)) throw new Error("disabled is not an array");
|
|
17050
|
+
return new Set(parsed.disabled.filter((x) => typeof x === "string"));
|
|
17051
|
+
}
|
|
17052
|
+
function readDisabledStore(accountDir) {
|
|
17053
|
+
try {
|
|
17054
|
+
return readDisabledStoreStrict(accountDir);
|
|
17055
|
+
} catch (err) {
|
|
17056
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
17057
|
+
console.error(`[admin/agents] op=disabled-store status=parse-failed reason="${msg}"`);
|
|
17058
|
+
return /* @__PURE__ */ new Set();
|
|
17059
|
+
}
|
|
17060
|
+
}
|
|
17061
|
+
function shippedSourceDirs(accountDir) {
|
|
17062
|
+
return [
|
|
17063
|
+
resolve23(accountDir, ...SPECIALISTS_DIR),
|
|
17064
|
+
resolve23(accountDir, ...QUARANTINE_DIR),
|
|
17065
|
+
resolve23(PLATFORM_ROOT, "templates", "specialists", "agents")
|
|
17066
|
+
];
|
|
17067
|
+
}
|
|
17068
|
+
function writeDisabledStore(accountDir, disabled) {
|
|
17069
|
+
writeFileSync13(
|
|
17070
|
+
resolve23(accountDir, DISABLED_STORE),
|
|
17071
|
+
`${JSON.stringify({ disabled: [...disabled].sort() }, null, 2)}
|
|
17072
|
+
`,
|
|
17073
|
+
{ mode: 384 }
|
|
17074
|
+
);
|
|
17075
|
+
}
|
|
17076
|
+
function listShipped(accountDir, riskByTool, disabled, riskSurfaceFailed) {
|
|
17077
|
+
const dirs = shippedSourceDirs(accountDir);
|
|
17078
|
+
const names = /* @__PURE__ */ new Set();
|
|
17079
|
+
for (const dir of dirs) {
|
|
17080
|
+
if (!existsSync31(dir)) continue;
|
|
17081
|
+
try {
|
|
17082
|
+
for (const entry of readdirSync22(dir)) if (entry.endsWith(".md")) names.add(entry);
|
|
17083
|
+
} catch (err) {
|
|
17084
|
+
console.error(`[admin/agents] op=list-shipped read-failed dir=${dir} error="${err}"`);
|
|
17085
|
+
}
|
|
17086
|
+
}
|
|
17087
|
+
const shipped = [];
|
|
17088
|
+
let shippedSkipped = 0;
|
|
17089
|
+
for (const filename of [...names].sort()) {
|
|
17090
|
+
const source = dirs.find((d) => existsSync31(resolve23(d, filename)));
|
|
17091
|
+
if (!source) continue;
|
|
17092
|
+
try {
|
|
17093
|
+
const { fm } = splitFrontmatter(readFileSync33(resolve23(source, filename), "utf-8"));
|
|
17094
|
+
if (!fm.name) {
|
|
17095
|
+
shippedSkipped++;
|
|
17096
|
+
console.error(`[admin/agents] op=list-shipped-skip file=${filename} reason=no-name`);
|
|
17097
|
+
continue;
|
|
17098
|
+
}
|
|
17099
|
+
const tools = parseToolsLine(fm.tools);
|
|
17100
|
+
const r = classify(tools, riskByTool, riskSurfaceFailed);
|
|
17101
|
+
shipped.push({
|
|
17102
|
+
slug: filename.replace(/\.md$/, ""),
|
|
17103
|
+
displayName: fm.name,
|
|
17104
|
+
kind: "specialist",
|
|
17105
|
+
origin: "shipped",
|
|
17106
|
+
plugin: "",
|
|
17107
|
+
description: fm.description ?? "",
|
|
17108
|
+
risk: r.risk,
|
|
17109
|
+
riskiestTool: r.riskiestTool,
|
|
17110
|
+
unresolved: r.unresolved,
|
|
17111
|
+
toolCount: tools.length,
|
|
17112
|
+
// The DIRECTORY is the ground truth, not the store. `spawn-context.ts`
|
|
17113
|
+
// reads the live dir, so a file found in quarantine is not dispatchable
|
|
17114
|
+
// whatever the JSON says, and a file found live IS dispatchable even if
|
|
17115
|
+
// the store claims otherwise. The store only answers for a bundled-only
|
|
17116
|
+
// agent, which has no account copy in either directory to look at.
|
|
17117
|
+
disabled: source === dirs[1] || source === dirs[2] && disabled.has(filename)
|
|
17118
|
+
});
|
|
17119
|
+
} catch (err) {
|
|
17120
|
+
shippedSkipped++;
|
|
17121
|
+
console.error(`[admin/agents] op=list-shipped-skip file=${filename} error="${err}"`);
|
|
17122
|
+
}
|
|
17123
|
+
}
|
|
17124
|
+
return { shipped, shippedSkipped };
|
|
17125
|
+
}
|
|
17126
|
+
function parseToolsLine(raw) {
|
|
17127
|
+
return raw ? raw.split(",").map((t) => t.trim()).filter(Boolean) : [];
|
|
17128
|
+
}
|
|
17129
|
+
function classify(tools, riskByTool, riskSurfaceFailed) {
|
|
17130
|
+
if (riskSurfaceFailed) {
|
|
17131
|
+
return { risk: "red", riskiestTool: null, unresolved: [], byTool: tools.map((t) => ({ tool: t, risk: null })) };
|
|
17132
|
+
}
|
|
17133
|
+
return agentRiskClass(tools, riskByTool);
|
|
17134
|
+
}
|
|
17135
|
+
function listSpecialists(accountDir, riskByTool, riskSurfaceFailed) {
|
|
16573
17136
|
const pluginsDir = resolve23(accountDir, "plugins");
|
|
16574
17137
|
const specialists = [];
|
|
16575
17138
|
let specialistsSkipped = 0;
|
|
16576
17139
|
for (const plugin of subdirs(pluginsDir).sort()) {
|
|
16577
17140
|
const agentsDirP = resolve23(pluginsDir, plugin, "agents");
|
|
16578
|
-
if (!
|
|
17141
|
+
if (!existsSync31(agentsDirP)) continue;
|
|
16579
17142
|
let entries2;
|
|
16580
17143
|
try {
|
|
16581
|
-
entries2 =
|
|
17144
|
+
entries2 = readdirSync22(agentsDirP, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
16582
17145
|
} catch {
|
|
16583
17146
|
continue;
|
|
16584
17147
|
}
|
|
16585
17148
|
for (const entry of entries2) {
|
|
16586
17149
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
16587
17150
|
try {
|
|
16588
|
-
const { fm } = splitFrontmatter(
|
|
17151
|
+
const { fm } = splitFrontmatter(readFileSync33(resolve23(agentsDirP, entry.name), "utf-8"));
|
|
16589
17152
|
if (!fm.name) {
|
|
16590
17153
|
specialistsSkipped++;
|
|
16591
17154
|
console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${entry.name}`);
|
|
16592
17155
|
continue;
|
|
16593
17156
|
}
|
|
17157
|
+
const specialistTools = parseToolsLine(fm.tools);
|
|
17158
|
+
const r = classify(specialistTools, riskByTool, riskSurfaceFailed);
|
|
16594
17159
|
specialists.push({
|
|
16595
17160
|
slug: entry.name.replace(/\.md$/, ""),
|
|
16596
17161
|
displayName: fm.name,
|
|
16597
17162
|
kind: "specialist",
|
|
17163
|
+
origin: "specialist",
|
|
16598
17164
|
plugin,
|
|
16599
|
-
description: fm.description ?? ""
|
|
17165
|
+
description: fm.description ?? "",
|
|
17166
|
+
risk: r.risk,
|
|
17167
|
+
riskiestTool: r.riskiestTool,
|
|
17168
|
+
unresolved: r.unresolved,
|
|
17169
|
+
toolCount: specialistTools.length,
|
|
17170
|
+
// A user-created specialist lives under the account plugins tree,
|
|
17171
|
+
// which provisioning never rewrites, so the quarantine mechanism
|
|
17172
|
+
// does not apply to it.
|
|
17173
|
+
disabled: false
|
|
16600
17174
|
});
|
|
16601
17175
|
} catch {
|
|
16602
17176
|
specialistsSkipped++;
|
|
@@ -16619,24 +17193,31 @@ app18.get("/", requireAdminSession, (c) => {
|
|
|
16619
17193
|
console.error("[admin/agents] op=list auth-rejected reason=no-account-for-session");
|
|
16620
17194
|
return c.json({ error: "Account not found for session" }, 401);
|
|
16621
17195
|
}
|
|
17196
|
+
const { riskByTool, failed: riskSurfaceFailed } = loadRiskSurface();
|
|
17197
|
+
const disabled = readDisabledStore(account.accountDir);
|
|
16622
17198
|
const agents = [];
|
|
16623
17199
|
let skipped = 0;
|
|
16624
17200
|
const agentsDir = resolve23(account.accountDir, "agents");
|
|
16625
|
-
if (
|
|
17201
|
+
if (existsSync31(agentsDir)) {
|
|
16626
17202
|
try {
|
|
16627
|
-
const entries2 =
|
|
17203
|
+
const entries2 = readdirSync22(agentsDir, { withFileTypes: true });
|
|
16628
17204
|
for (const entry of entries2.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
16629
17205
|
if (!entry.isDirectory()) continue;
|
|
16630
17206
|
if (entry.name === "admin") continue;
|
|
16631
17207
|
const configPath2 = resolve23(agentsDir, entry.name, "config.json");
|
|
16632
|
-
if (!
|
|
17208
|
+
if (!existsSync31(configPath2)) continue;
|
|
16633
17209
|
try {
|
|
16634
|
-
const config = JSON.parse(
|
|
17210
|
+
const config = JSON.parse(readFileSync33(configPath2, "utf-8"));
|
|
16635
17211
|
agents.push({
|
|
16636
17212
|
slug: entry.name,
|
|
16637
17213
|
displayName: config.displayName ?? entry.name,
|
|
16638
17214
|
status: config.status ?? "unknown",
|
|
16639
|
-
kind: "public"
|
|
17215
|
+
kind: "public",
|
|
17216
|
+
origin: "public",
|
|
17217
|
+
risk: null,
|
|
17218
|
+
riskiestTool: null,
|
|
17219
|
+
unresolved: [],
|
|
17220
|
+
disabled: false
|
|
16640
17221
|
});
|
|
16641
17222
|
} catch {
|
|
16642
17223
|
skipped++;
|
|
@@ -16647,9 +17228,25 @@ app18.get("/", requireAdminSession, (c) => {
|
|
|
16647
17228
|
console.error(`[admin/agents] failed to scan agents directory: ${err}`);
|
|
16648
17229
|
}
|
|
16649
17230
|
}
|
|
16650
|
-
const { specialists, specialistsSkipped } = listSpecialists(account.accountDir);
|
|
16651
|
-
|
|
16652
|
-
|
|
17231
|
+
const { specialists, specialistsSkipped } = listSpecialists(account.accountDir, riskByTool, riskSurfaceFailed !== null);
|
|
17232
|
+
const { shipped, shippedSkipped } = listShipped(account.accountDir, riskByTool, disabled, riskSurfaceFailed !== null);
|
|
17233
|
+
const rows = [...agents, ...specialists, ...shipped];
|
|
17234
|
+
const acct = account.accountId.slice(0, 8);
|
|
17235
|
+
for (const row of rows) {
|
|
17236
|
+
if (row.risk === null) continue;
|
|
17237
|
+
console.log(
|
|
17238
|
+
`[admin/agents] op=classify account=${acct} agent=${row.slug} origin=${row.origin} risk=${row.risk} riskiestTool=${row.riskiestTool ?? "none"} tools=${row.toolCount} unresolved=${row.unresolved.length > 0 ? row.unresolved.join(",") : "none"} disabled=${row.disabled}`
|
|
17239
|
+
);
|
|
17240
|
+
}
|
|
17241
|
+
console.log(`[admin/agents] op=list accountId=${acct} agents=${agents.length} specialists=${specialists.length} shipped=${shipped.length} skipped=${skipped} specialistsSkipped=${specialistsSkipped} shippedSkipped=${shippedSkipped}`);
|
|
17242
|
+
return c.json({
|
|
17243
|
+
agents: rows,
|
|
17244
|
+
accountId: account.accountId,
|
|
17245
|
+
skipped,
|
|
17246
|
+
specialistsSkipped,
|
|
17247
|
+
shippedSkipped,
|
|
17248
|
+
riskSurfaceFailed: riskSurfaceFailed !== null
|
|
17249
|
+
});
|
|
16653
17250
|
});
|
|
16654
17251
|
app18.get("/:slug", requireAdminSession, (c) => {
|
|
16655
17252
|
const slug = c.req.param("slug");
|
|
@@ -16659,34 +17256,72 @@ app18.get("/:slug", requireAdminSession, (c) => {
|
|
|
16659
17256
|
return c.json({ error: "Account not found for session" }, 401);
|
|
16660
17257
|
}
|
|
16661
17258
|
if (SLUG_UNSAFE(slug)) return c.json({ error: "Invalid agent slug" }, 400);
|
|
17259
|
+
if (c.req.query("origin") === "shipped") {
|
|
17260
|
+
const dirs = shippedSourceDirs(account.accountDir);
|
|
17261
|
+
const source = dirs.find((d) => existsSync31(resolve23(d, `${slug}.md`)));
|
|
17262
|
+
if (!source) return c.json({ error: "Agent not found" }, 404);
|
|
17263
|
+
const { fm, body } = splitFrontmatter(readFileSync33(resolve23(source, `${slug}.md`), "utf-8"));
|
|
17264
|
+
const { riskByTool, failed } = loadRiskSurface();
|
|
17265
|
+
const tools = parseToolsLine(fm.tools);
|
|
17266
|
+
const r = classify(tools, riskByTool, failed !== null);
|
|
17267
|
+
console.log(`[admin/agents] op=detail accountId=${account.accountId.slice(0, 8)} slug=${slug} kind=specialist origin=shipped risk=${r.risk}`);
|
|
17268
|
+
return c.json({
|
|
17269
|
+
agent: {
|
|
17270
|
+
kind: "specialist",
|
|
17271
|
+
origin: "shipped",
|
|
17272
|
+
slug,
|
|
17273
|
+
displayName: fm.name ?? slug,
|
|
17274
|
+
description: fm.description ?? "",
|
|
17275
|
+
summary: fm.summary ?? "",
|
|
17276
|
+
model: fm.model ?? null,
|
|
17277
|
+
tools,
|
|
17278
|
+
systemPrompt: body.trim(),
|
|
17279
|
+
skills: [],
|
|
17280
|
+
risk: r.risk,
|
|
17281
|
+
riskiestTool: r.riskiestTool,
|
|
17282
|
+
unresolved: r.unresolved,
|
|
17283
|
+
byTool: r.byTool,
|
|
17284
|
+
disabled: readDisabledStore(account.accountDir).has(`${slug}.md`)
|
|
17285
|
+
}
|
|
17286
|
+
});
|
|
17287
|
+
}
|
|
16662
17288
|
const pluginParam = c.req.query("plugin");
|
|
16663
17289
|
if (pluginParam) {
|
|
16664
17290
|
if (SLUG_UNSAFE(pluginParam)) return c.json({ error: "Invalid plugin" }, 400);
|
|
16665
17291
|
const file = resolve23(account.accountDir, "plugins", pluginParam, "agents", `${slug}.md`);
|
|
16666
|
-
if (!
|
|
16667
|
-
const { fm, body } = splitFrontmatter(
|
|
17292
|
+
if (!existsSync31(file)) return c.json({ error: "Agent not found" }, 404);
|
|
17293
|
+
const { fm, body } = splitFrontmatter(readFileSync33(file, "utf-8"));
|
|
16668
17294
|
const skills = subdirs(resolve23(account.accountDir, "plugins", pluginParam, "skills"));
|
|
16669
|
-
|
|
17295
|
+
const { riskByTool, failed } = loadRiskSurface();
|
|
17296
|
+
const tools = parseToolsLine(fm.tools);
|
|
17297
|
+
const r = classify(tools, riskByTool, failed !== null);
|
|
17298
|
+
console.log(`[admin/agents] op=detail accountId=${account.accountId.slice(0, 8)} slug=${slug} kind=specialist plugin=${pluginParam} risk=${r.risk}`);
|
|
16670
17299
|
return c.json({
|
|
16671
17300
|
agent: {
|
|
16672
17301
|
kind: "specialist",
|
|
17302
|
+
origin: "specialist",
|
|
16673
17303
|
slug,
|
|
16674
17304
|
displayName: fm.name ?? slug,
|
|
16675
17305
|
description: fm.description ?? "",
|
|
16676
17306
|
summary: fm.summary ?? "",
|
|
16677
17307
|
model: fm.model ?? null,
|
|
16678
|
-
tools
|
|
17308
|
+
tools,
|
|
16679
17309
|
systemPrompt: body.trim(),
|
|
16680
|
-
skills
|
|
17310
|
+
skills,
|
|
17311
|
+
risk: r.risk,
|
|
17312
|
+
riskiestTool: r.riskiestTool,
|
|
17313
|
+
unresolved: r.unresolved,
|
|
17314
|
+
byTool: r.byTool,
|
|
17315
|
+
disabled: false
|
|
16681
17316
|
}
|
|
16682
17317
|
});
|
|
16683
17318
|
}
|
|
16684
17319
|
const agentDir = resolve23(account.accountDir, "agents", slug);
|
|
16685
17320
|
const configPath2 = resolve23(agentDir, "config.json");
|
|
16686
|
-
if (!
|
|
17321
|
+
if (!existsSync31(configPath2)) return c.json({ error: "Agent not found" }, 404);
|
|
16687
17322
|
let config;
|
|
16688
17323
|
try {
|
|
16689
|
-
config = JSON.parse(
|
|
17324
|
+
config = JSON.parse(readFileSync33(configPath2, "utf-8"));
|
|
16690
17325
|
} catch {
|
|
16691
17326
|
console.error(`[admin/agents] op=detail slug=${slug} error="config.json parse failed"`);
|
|
16692
17327
|
return c.json({ error: "Agent config unreadable" }, 500);
|
|
@@ -16696,8 +17331,8 @@ app18.get("/:slug", requireAdminSession, (c) => {
|
|
|
16696
17331
|
for (const { file, key } of OWNED_DOCS) {
|
|
16697
17332
|
const p = resolve23(agentDir, file);
|
|
16698
17333
|
try {
|
|
16699
|
-
if (
|
|
16700
|
-
docs[key] =
|
|
17334
|
+
if (existsSync31(p)) {
|
|
17335
|
+
docs[key] = readFileSync33(p, "utf-8");
|
|
16701
17336
|
present[key] = true;
|
|
16702
17337
|
} else {
|
|
16703
17338
|
docs[key] = "";
|
|
@@ -16726,6 +17361,84 @@ app18.get("/:slug", requireAdminSession, (c) => {
|
|
|
16726
17361
|
}
|
|
16727
17362
|
});
|
|
16728
17363
|
});
|
|
17364
|
+
async function resolveWritableAccount(c, op, slug) {
|
|
17365
|
+
const accountIdParam = c.req.query("accountId") ?? "";
|
|
17366
|
+
if (!accountIdParam) {
|
|
17367
|
+
console.error(`[admin/agents] op=${op} rejected reason=missing-accountId slug=${slug}`);
|
|
17368
|
+
return { status: 400, error: "accountId is required" };
|
|
17369
|
+
}
|
|
17370
|
+
const account = validateAccountId2(accountIdParam);
|
|
17371
|
+
if (!account) {
|
|
17372
|
+
console.error(`[admin/agents] op=${op} rejected reason=unknown-accountId accountId=${accountIdParam.slice(0, 8)} slug=${slug}`);
|
|
17373
|
+
return { status: 400, error: "Unknown accountId" };
|
|
17374
|
+
}
|
|
17375
|
+
const authz = await authorizeAccountWrite(c, account.accountId);
|
|
17376
|
+
if (!authz.ok) {
|
|
17377
|
+
console.error(`[admin/agents] op=${op} rejected reason=${authz.reason} accountId=${account.accountId.slice(0, 8)} slug=${slug}`);
|
|
17378
|
+
return { status: 403, error: "Not authorized for this account" };
|
|
17379
|
+
}
|
|
17380
|
+
return account;
|
|
17381
|
+
}
|
|
17382
|
+
app18.post("/:slug/disable", async (c) => {
|
|
17383
|
+
const slug = c.req.param("slug");
|
|
17384
|
+
if (SLUG_UNSAFE(slug)) return c.json({ error: "Invalid agent slug" }, 400);
|
|
17385
|
+
const resolved = await resolveWritableAccount(c, "disable", slug);
|
|
17386
|
+
if ("status" in resolved) return c.json({ error: resolved.error }, resolved.status);
|
|
17387
|
+
const filename = `${slug}.md`;
|
|
17388
|
+
const live = resolve23(resolved.accountDir, ...SPECIALISTS_DIR, filename);
|
|
17389
|
+
const quarantined = resolve23(resolved.accountDir, ...QUARANTINE_DIR, filename);
|
|
17390
|
+
if (!shippedSourceDirs(resolved.accountDir).some((d) => existsSync31(resolve23(d, filename)))) {
|
|
17391
|
+
console.error(`[admin/agents] op=disable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=failed reason=not-a-shipped-agent`);
|
|
17392
|
+
return c.json({ error: "Only a shipped agent can be disabled" }, 404);
|
|
17393
|
+
}
|
|
17394
|
+
let moved = false;
|
|
17395
|
+
try {
|
|
17396
|
+
const disabled = readDisabledStoreStrict(resolved.accountDir);
|
|
17397
|
+
if (existsSync31(live)) {
|
|
17398
|
+
mkdirSync7(resolve23(resolved.accountDir, ...QUARANTINE_DIR), { recursive: true });
|
|
17399
|
+
renameSync9(live, quarantined);
|
|
17400
|
+
moved = true;
|
|
17401
|
+
}
|
|
17402
|
+
disabled.add(filename);
|
|
17403
|
+
writeDisabledStore(resolved.accountDir, disabled);
|
|
17404
|
+
} catch (err) {
|
|
17405
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
17406
|
+
console.error(`[admin/agents] op=disable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=failed reason="${msg}"`);
|
|
17407
|
+
return c.json({ error: `Disable failed, disabled store unreadable or unwritable: ${msg}` }, 500);
|
|
17408
|
+
}
|
|
17409
|
+
console.log(`[admin/agents] op=disable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=ok moved=${moved}`);
|
|
17410
|
+
return c.json({ ok: true, moved });
|
|
17411
|
+
});
|
|
17412
|
+
app18.post("/:slug/enable", async (c) => {
|
|
17413
|
+
const slug = c.req.param("slug");
|
|
17414
|
+
if (SLUG_UNSAFE(slug)) return c.json({ error: "Invalid agent slug" }, 400);
|
|
17415
|
+
const resolved = await resolveWritableAccount(c, "enable", slug);
|
|
17416
|
+
if ("status" in resolved) return c.json({ error: resolved.error }, resolved.status);
|
|
17417
|
+
const filename = `${slug}.md`;
|
|
17418
|
+
const live = resolve23(resolved.accountDir, ...SPECIALISTS_DIR, filename);
|
|
17419
|
+
const quarantined = resolve23(resolved.accountDir, ...QUARANTINE_DIR, filename);
|
|
17420
|
+
let restored = false;
|
|
17421
|
+
try {
|
|
17422
|
+
const disabled = readDisabledStoreStrict(resolved.accountDir);
|
|
17423
|
+
if (!existsSync31(quarantined) && !disabled.has(filename)) {
|
|
17424
|
+
console.error(`[admin/agents] op=enable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=failed reason=not-disabled`);
|
|
17425
|
+
return c.json({ error: "This agent is not disabled" }, 404);
|
|
17426
|
+
}
|
|
17427
|
+
if (existsSync31(quarantined)) {
|
|
17428
|
+
mkdirSync7(resolve23(resolved.accountDir, ...SPECIALISTS_DIR), { recursive: true });
|
|
17429
|
+
renameSync9(quarantined, live);
|
|
17430
|
+
restored = true;
|
|
17431
|
+
}
|
|
17432
|
+
disabled.delete(filename);
|
|
17433
|
+
writeDisabledStore(resolved.accountDir, disabled);
|
|
17434
|
+
} catch (err) {
|
|
17435
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
17436
|
+
console.error(`[admin/agents] op=enable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=failed reason="${msg}"`);
|
|
17437
|
+
return c.json({ error: `Enable failed: ${msg}` }, 500);
|
|
17438
|
+
}
|
|
17439
|
+
console.log(`[admin/agents] op=enable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=ok restored=${restored}`);
|
|
17440
|
+
return c.json({ ok: true, restored });
|
|
17441
|
+
});
|
|
16729
17442
|
app18.delete("/:slug", async (c) => {
|
|
16730
17443
|
const slug = c.req.param("slug");
|
|
16731
17444
|
if (slug === "admin") {
|
|
@@ -16750,7 +17463,7 @@ app18.delete("/:slug", async (c) => {
|
|
|
16750
17463
|
return c.json({ error: "Not authorized for this account" }, 403);
|
|
16751
17464
|
}
|
|
16752
17465
|
const agentDir = resolve23(account.accountDir, "agents", slug);
|
|
16753
|
-
if (!
|
|
17466
|
+
if (!existsSync31(agentDir)) {
|
|
16754
17467
|
return c.json({ error: "Agent not found" }, 404);
|
|
16755
17468
|
}
|
|
16756
17469
|
try {
|
|
@@ -16793,7 +17506,7 @@ app18.post("/:slug/project", async (c) => {
|
|
|
16793
17506
|
return c.json({ error: "Not authorized for this account" }, 403);
|
|
16794
17507
|
}
|
|
16795
17508
|
const agentDir = resolve23(account.accountDir, "agents", slug);
|
|
16796
|
-
if (!
|
|
17509
|
+
if (!existsSync31(agentDir)) {
|
|
16797
17510
|
return c.json({ error: "Agent not found on disk" }, 404);
|
|
16798
17511
|
}
|
|
16799
17512
|
try {
|
|
@@ -16809,7 +17522,7 @@ var agents_default = app18;
|
|
|
16809
17522
|
// server/routes/admin/sessions.ts
|
|
16810
17523
|
import crypto2 from "crypto";
|
|
16811
17524
|
import { resolve as resolvePath } from "path";
|
|
16812
|
-
import { existsSync as
|
|
17525
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync8, createWriteStream } from "fs";
|
|
16813
17526
|
|
|
16814
17527
|
// ../lib/admin-conversation-purge/src/index.ts
|
|
16815
17528
|
async function purgeAdminConversationJsonls(args) {
|
|
@@ -16956,7 +17669,7 @@ var pickComponentBytes = (..._args) => null;
|
|
|
16956
17669
|
function openResumeStreamLog(accountId, sessionId) {
|
|
16957
17670
|
const dir = resolvePath(ACCOUNTS_DIR, accountId, "resume-logs");
|
|
16958
17671
|
try {
|
|
16959
|
-
|
|
17672
|
+
mkdirSync8(dir, { recursive: true });
|
|
16960
17673
|
} catch {
|
|
16961
17674
|
}
|
|
16962
17675
|
return createWriteStream(resolvePath(dir, `${sessionId}.log`), { flags: "a" });
|
|
@@ -16969,7 +17682,7 @@ function validateAndShapeAttachments(raws, conversationAccountId, sessionId, mes
|
|
|
16969
17682
|
let reason = null;
|
|
16970
17683
|
if (!a.attachmentId || !a.filename || !a.mimeType || !a.storagePath) reason = "schema-fail";
|
|
16971
17684
|
else if (a.accountId !== conversationAccountId) reason = "account-mismatch";
|
|
16972
|
-
else if (!
|
|
17685
|
+
else if (!existsSync32(a.storagePath)) reason = "missing-file";
|
|
16973
17686
|
if (reason) {
|
|
16974
17687
|
invalid++;
|
|
16975
17688
|
try {
|
|
@@ -17495,8 +18208,8 @@ app19.put("/:id/label", requireAdminSession, async (c) => {
|
|
|
17495
18208
|
var sessions_default = app19;
|
|
17496
18209
|
|
|
17497
18210
|
// app/lib/claude-agent/spawn-context.ts
|
|
17498
|
-
import { existsSync as
|
|
17499
|
-
import { dirname as dirname12, resolve as resolve24, join as
|
|
18211
|
+
import { existsSync as existsSync33, readFileSync as readFileSync34, readdirSync as readdirSync23, statSync as statSync17 } from "fs";
|
|
18212
|
+
import { dirname as dirname12, resolve as resolve24, join as join35 } from "path";
|
|
17500
18213
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
17501
18214
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
17502
18215
|
try {
|
|
@@ -17537,14 +18250,14 @@ function frontmatterField(manifest, field2) {
|
|
|
17537
18250
|
function extractPluginToolDescriptions(pluginDir) {
|
|
17538
18251
|
const out = /* @__PURE__ */ new Map();
|
|
17539
18252
|
const candidates = [
|
|
17540
|
-
|
|
17541
|
-
|
|
18253
|
+
join35(pluginDir, "mcp/dist/index.js"),
|
|
18254
|
+
join35(pluginDir, "mcp/src/index.ts")
|
|
17542
18255
|
];
|
|
17543
18256
|
let raw = null;
|
|
17544
18257
|
for (const path3 of candidates) {
|
|
17545
|
-
if (!
|
|
18258
|
+
if (!existsSync33(path3)) continue;
|
|
17546
18259
|
try {
|
|
17547
|
-
raw =
|
|
18260
|
+
raw = readFileSync34(path3, "utf-8");
|
|
17548
18261
|
break;
|
|
17549
18262
|
} catch {
|
|
17550
18263
|
}
|
|
@@ -17594,25 +18307,25 @@ function parseSkillPathsFromFrontmatter(fm) {
|
|
|
17594
18307
|
function listPluginDirs(accountPluginsDir) {
|
|
17595
18308
|
const out = /* @__PURE__ */ new Map();
|
|
17596
18309
|
const platformPlugins = resolve24(PLATFORM_ROOT, "plugins");
|
|
17597
|
-
if (
|
|
17598
|
-
for (const name of
|
|
17599
|
-
const dir =
|
|
18310
|
+
if (existsSync33(platformPlugins)) {
|
|
18311
|
+
for (const name of readdirSync23(platformPlugins)) {
|
|
18312
|
+
const dir = join35(platformPlugins, name);
|
|
17600
18313
|
try {
|
|
17601
|
-
if (
|
|
18314
|
+
if (statSync17(dir).isDirectory()) out.set(name, dir);
|
|
17602
18315
|
} catch {
|
|
17603
18316
|
}
|
|
17604
18317
|
}
|
|
17605
18318
|
}
|
|
17606
18319
|
const premiumRoot = resolve24(dirname12(PLATFORM_ROOT), "premium-plugins");
|
|
17607
|
-
if (
|
|
17608
|
-
for (const bundle of
|
|
17609
|
-
const bundlePlugins =
|
|
17610
|
-
if (!
|
|
18320
|
+
if (existsSync33(premiumRoot)) {
|
|
18321
|
+
for (const bundle of readdirSync23(premiumRoot)) {
|
|
18322
|
+
const bundlePlugins = join35(premiumRoot, bundle, "plugins");
|
|
18323
|
+
if (!existsSync33(bundlePlugins)) continue;
|
|
17611
18324
|
try {
|
|
17612
|
-
for (const name of
|
|
17613
|
-
const dir =
|
|
18325
|
+
for (const name of readdirSync23(bundlePlugins)) {
|
|
18326
|
+
const dir = join35(bundlePlugins, name);
|
|
17614
18327
|
try {
|
|
17615
|
-
if (
|
|
18328
|
+
if (statSync17(dir).isDirectory()) out.set(name, dir);
|
|
17616
18329
|
} catch {
|
|
17617
18330
|
}
|
|
17618
18331
|
}
|
|
@@ -17620,13 +18333,13 @@ function listPluginDirs(accountPluginsDir) {
|
|
|
17620
18333
|
}
|
|
17621
18334
|
}
|
|
17622
18335
|
}
|
|
17623
|
-
if (accountPluginsDir &&
|
|
18336
|
+
if (accountPluginsDir && existsSync33(accountPluginsDir)) {
|
|
17624
18337
|
try {
|
|
17625
|
-
for (const name of
|
|
18338
|
+
for (const name of readdirSync23(accountPluginsDir)) {
|
|
17626
18339
|
if (out.has(name)) continue;
|
|
17627
|
-
const dir =
|
|
18340
|
+
const dir = join35(accountPluginsDir, name);
|
|
17628
18341
|
try {
|
|
17629
|
-
if (
|
|
18342
|
+
if (statSync17(dir).isDirectory()) out.set(name, dir);
|
|
17630
18343
|
} catch {
|
|
17631
18344
|
}
|
|
17632
18345
|
}
|
|
@@ -17639,11 +18352,11 @@ function accountPluginsDirFor(accountId) {
|
|
|
17639
18352
|
return resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "plugins");
|
|
17640
18353
|
}
|
|
17641
18354
|
function listAccountPluginNames(accountPluginsDir) {
|
|
17642
|
-
if (!
|
|
18355
|
+
if (!existsSync33(accountPluginsDir)) return [];
|
|
17643
18356
|
try {
|
|
17644
|
-
return
|
|
18357
|
+
return readdirSync23(accountPluginsDir).filter((name) => {
|
|
17645
18358
|
try {
|
|
17646
|
-
return
|
|
18359
|
+
return statSync17(join35(accountPluginsDir, name)).isDirectory();
|
|
17647
18360
|
} catch {
|
|
17648
18361
|
return false;
|
|
17649
18362
|
}
|
|
@@ -17654,9 +18367,9 @@ function listAccountPluginNames(accountPluginsDir) {
|
|
|
17654
18367
|
}
|
|
17655
18368
|
function readEnabledPlugins(accountId) {
|
|
17656
18369
|
const accountFile = resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
17657
|
-
if (!
|
|
18370
|
+
if (!existsSync33(accountFile)) return /* @__PURE__ */ new Set();
|
|
17658
18371
|
try {
|
|
17659
|
-
const parsed = JSON.parse(
|
|
18372
|
+
const parsed = JSON.parse(readFileSync34(accountFile, "utf-8"));
|
|
17660
18373
|
return new Set(
|
|
17661
18374
|
Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
|
|
17662
18375
|
);
|
|
@@ -17665,10 +18378,10 @@ function readEnabledPlugins(accountId) {
|
|
|
17665
18378
|
}
|
|
17666
18379
|
}
|
|
17667
18380
|
function readFrontmatter(path3) {
|
|
17668
|
-
if (!
|
|
18381
|
+
if (!existsSync33(path3)) return null;
|
|
17669
18382
|
let raw;
|
|
17670
18383
|
try {
|
|
17671
|
-
raw =
|
|
18384
|
+
raw = readFileSync34(path3, "utf-8");
|
|
17672
18385
|
} catch {
|
|
17673
18386
|
return null;
|
|
17674
18387
|
}
|
|
@@ -17686,7 +18399,7 @@ function computeActivePlugins(accountId) {
|
|
|
17686
18399
|
for (const name of emitNames) {
|
|
17687
18400
|
const dir = pluginDirs.get(name);
|
|
17688
18401
|
if (!dir) continue;
|
|
17689
|
-
const fm = readFrontmatter(
|
|
18402
|
+
const fm = readFrontmatter(join35(dir, "PLUGIN.md"));
|
|
17690
18403
|
if (!fm) continue;
|
|
17691
18404
|
const description = frontmatterField(`---
|
|
17692
18405
|
${fm}
|
|
@@ -17703,7 +18416,7 @@ ${fm}
|
|
|
17703
18416
|
`plugin-manifest-tool-coverage plugin=${name} tools=${tools.length} with-desc=${withDesc}`
|
|
17704
18417
|
);
|
|
17705
18418
|
if (toolNames.length > 0 && descMap.size === 0) {
|
|
17706
|
-
const hasSource =
|
|
18419
|
+
const hasSource = existsSync33(join35(dir, "mcp/dist/index.js")) || existsSync33(join35(dir, "mcp/src/index.ts"));
|
|
17707
18420
|
if (hasSource) {
|
|
17708
18421
|
console.warn(
|
|
17709
18422
|
`[spawn-context] WARN plugin=${name} mcp source present but tool-description regex matched zero entries \u2014 SDK upgrade may have changed the registration shape`
|
|
@@ -17713,7 +18426,7 @@ ${fm}
|
|
|
17713
18426
|
const skillPaths = parseSkillPathsFromFrontmatter(fm);
|
|
17714
18427
|
const skills = [];
|
|
17715
18428
|
for (const relPath of skillPaths) {
|
|
17716
|
-
const skillPath =
|
|
18429
|
+
const skillPath = join35(dir, relPath);
|
|
17717
18430
|
const skillFm = readFrontmatter(skillPath);
|
|
17718
18431
|
if (!skillFm) continue;
|
|
17719
18432
|
const skillName = frontmatterField(`---
|
|
@@ -17730,10 +18443,10 @@ ${skillFm}
|
|
|
17730
18443
|
}
|
|
17731
18444
|
function computeSpecialistDomains(accountId) {
|
|
17732
18445
|
const specialistsDir = resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
|
|
17733
|
-
if (!
|
|
18446
|
+
if (!existsSync33(specialistsDir)) return [];
|
|
17734
18447
|
let entries2;
|
|
17735
18448
|
try {
|
|
17736
|
-
entries2 =
|
|
18449
|
+
entries2 = readdirSync23(specialistsDir);
|
|
17737
18450
|
} catch {
|
|
17738
18451
|
return [];
|
|
17739
18452
|
}
|
|
@@ -17757,7 +18470,7 @@ function computeSpecialistDomains(accountId) {
|
|
|
17757
18470
|
const out = [];
|
|
17758
18471
|
for (const file of entries2.sort()) {
|
|
17759
18472
|
if (!file.endsWith(".md")) continue;
|
|
17760
|
-
const fm = readFrontmatter(
|
|
18473
|
+
const fm = readFrontmatter(join35(specialistsDir, file));
|
|
17761
18474
|
if (!fm) continue;
|
|
17762
18475
|
const wrapped = `---
|
|
17763
18476
|
${fm}
|
|
@@ -17776,10 +18489,10 @@ ${fm}
|
|
|
17776
18489
|
}
|
|
17777
18490
|
function computeDormantPlugins(accountId) {
|
|
17778
18491
|
const accountFile = resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
17779
|
-
if (!
|
|
18492
|
+
if (!existsSync33(accountFile)) return [];
|
|
17780
18493
|
let enabled;
|
|
17781
18494
|
try {
|
|
17782
|
-
const raw =
|
|
18495
|
+
const raw = readFileSync34(accountFile, "utf-8");
|
|
17783
18496
|
const parsed = JSON.parse(raw);
|
|
17784
18497
|
enabled = new Set(
|
|
17785
18498
|
Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
|
|
@@ -17791,10 +18504,10 @@ function computeDormantPlugins(accountId) {
|
|
|
17791
18504
|
return [];
|
|
17792
18505
|
}
|
|
17793
18506
|
const pluginsDir = resolve24(PLATFORM_ROOT, "plugins");
|
|
17794
|
-
if (!
|
|
18507
|
+
if (!existsSync33(pluginsDir)) return [];
|
|
17795
18508
|
let entries2;
|
|
17796
18509
|
try {
|
|
17797
|
-
entries2 =
|
|
18510
|
+
entries2 = readdirSync23(pluginsDir);
|
|
17798
18511
|
} catch {
|
|
17799
18512
|
return [];
|
|
17800
18513
|
}
|
|
@@ -17802,10 +18515,10 @@ function computeDormantPlugins(accountId) {
|
|
|
17802
18515
|
for (const name of entries2) {
|
|
17803
18516
|
if (enabled.has(name)) continue;
|
|
17804
18517
|
const manifestPath = resolve24(pluginsDir, name, "PLUGIN.md");
|
|
17805
|
-
if (!
|
|
18518
|
+
if (!existsSync33(manifestPath)) continue;
|
|
17806
18519
|
let manifest;
|
|
17807
18520
|
try {
|
|
17808
|
-
manifest =
|
|
18521
|
+
manifest = readFileSync34(manifestPath, "utf-8");
|
|
17809
18522
|
} catch {
|
|
17810
18523
|
continue;
|
|
17811
18524
|
}
|
|
@@ -17819,13 +18532,13 @@ function computeDormantPlugins(accountId) {
|
|
|
17819
18532
|
}
|
|
17820
18533
|
|
|
17821
18534
|
// server/routes/admin/claude-sessions.ts
|
|
17822
|
-
import { existsSync as
|
|
18535
|
+
import { existsSync as existsSync34, readFileSync as readFileSync35 } from "fs";
|
|
17823
18536
|
import { resolve as resolve25 } from "path";
|
|
17824
18537
|
function readTunnelState(brandConfigDir) {
|
|
17825
18538
|
const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
|
|
17826
|
-
if (!
|
|
18539
|
+
if (!existsSync34(statePath)) return null;
|
|
17827
18540
|
try {
|
|
17828
|
-
const parsed = JSON.parse(
|
|
18541
|
+
const parsed = JSON.parse(readFileSync35(statePath, "utf-8"));
|
|
17829
18542
|
const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
|
|
17830
18543
|
const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
|
|
17831
18544
|
const domain = typeof parsed.domain === "string" ? parsed.domain : null;
|
|
@@ -17839,7 +18552,7 @@ function resolveTunnelUrl() {
|
|
|
17839
18552
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
|
|
17840
18553
|
let brand;
|
|
17841
18554
|
try {
|
|
17842
|
-
brand = JSON.parse(
|
|
18555
|
+
brand = JSON.parse(readFileSync35(resolve25(platformRoot5, "config", "brand.json"), "utf-8"));
|
|
17843
18556
|
} catch {
|
|
17844
18557
|
return null;
|
|
17845
18558
|
}
|
|
@@ -18152,11 +18865,11 @@ app22.get("/", requireAdminSession, async (c) => {
|
|
|
18152
18865
|
var events_default = app22;
|
|
18153
18866
|
|
|
18154
18867
|
// server/routes/admin/files.ts
|
|
18155
|
-
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as
|
|
18868
|
+
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync35 } from "fs";
|
|
18156
18869
|
import { readdir as readdir4, readFile as readFile7, stat as stat6, mkdir as mkdir4, unlink as unlink3, rename as rename2, rmdir } from "fs/promises";
|
|
18157
18870
|
import { realpathSync as realpathSync7 } from "fs";
|
|
18158
18871
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
18159
|
-
import { basename as basename11, dirname as dirname13, join as
|
|
18872
|
+
import { basename as basename11, dirname as dirname13, join as join37, relative as relative5, resolve as resolve27, sep as sep8 } from "path";
|
|
18160
18873
|
import { Readable as Readable2 } from "stream";
|
|
18161
18874
|
|
|
18162
18875
|
// ../lib/graph-trash/src/index.ts
|
|
@@ -18474,7 +19187,7 @@ async function cascadeDeleteDocument(params) {
|
|
|
18474
19187
|
|
|
18475
19188
|
// app/lib/file-index.ts
|
|
18476
19189
|
import * as fsp from "fs/promises";
|
|
18477
|
-
import { resolve as resolve26, relative as relative4, join as
|
|
19190
|
+
import { resolve as resolve26, relative as relative4, join as join36, basename as basename10, extname as extname2, sep as sep7 } from "path";
|
|
18478
19191
|
import { tmpdir as tmpdir2 } from "os";
|
|
18479
19192
|
import { execFile as execFile2 } from "child_process";
|
|
18480
19193
|
import { promisify as promisify2 } from "util";
|
|
@@ -18556,7 +19269,7 @@ async function extractPdf(absolute) {
|
|
|
18556
19269
|
return stdout.trim();
|
|
18557
19270
|
}
|
|
18558
19271
|
async function ocrPdf(absolute) {
|
|
18559
|
-
const outPdf =
|
|
19272
|
+
const outPdf = join36(tmpdir2(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
|
|
18560
19273
|
try {
|
|
18561
19274
|
await execFileAsync2(
|
|
18562
19275
|
"ocrmypdf",
|
|
@@ -18618,7 +19331,7 @@ async function readDisplayName(absolute) {
|
|
|
18618
19331
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
18619
19332
|
if (base === `${stem}.meta.json`) return null;
|
|
18620
19333
|
try {
|
|
18621
|
-
const raw = await fsp.readFile(
|
|
19334
|
+
const raw = await fsp.readFile(join36(dir, `${stem}.meta.json`), "utf-8");
|
|
18622
19335
|
const meta = JSON.parse(raw);
|
|
18623
19336
|
const dn = meta.displayName ?? meta.filename;
|
|
18624
19337
|
return typeof dn === "string" && dn.length > 0 ? dn : null;
|
|
@@ -18639,7 +19352,7 @@ async function walkSubtree(root, dataRoot, out) {
|
|
|
18639
19352
|
for (const entry of entries2) {
|
|
18640
19353
|
if (entry.isSymbolicLink()) continue;
|
|
18641
19354
|
if (entry.isDirectory() && entry.name === ".uploads-tmp") continue;
|
|
18642
|
-
const abs =
|
|
19355
|
+
const abs = join36(root, entry.name);
|
|
18643
19356
|
if (entry.isDirectory()) {
|
|
18644
19357
|
const sub = await walkSubtree(abs, dataRoot, out);
|
|
18645
19358
|
if (!sub.clean) clean = false;
|
|
@@ -19012,7 +19725,7 @@ var UPLOAD_TMP_DIR = ".uploads-tmp";
|
|
|
19012
19725
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19013
19726
|
async function readMeta(absDir, baseName) {
|
|
19014
19727
|
try {
|
|
19015
|
-
const raw = await readFile7(
|
|
19728
|
+
const raw = await readFile7(join37(absDir, `${baseName}.meta.json`), "utf8");
|
|
19016
19729
|
const parsed = JSON.parse(raw);
|
|
19017
19730
|
if (typeof parsed?.filename === "string") {
|
|
19018
19731
|
return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
|
|
@@ -19067,7 +19780,7 @@ async function servableFilesIn(dirAbs, uuid) {
|
|
|
19067
19780
|
}
|
|
19068
19781
|
async function enrich(absolute, entry, accountNames) {
|
|
19069
19782
|
if (entry.kind === "directory" && UUID_RE4.test(entry.name)) {
|
|
19070
|
-
const dirAbs =
|
|
19783
|
+
const dirAbs = join37(absolute, entry.name);
|
|
19071
19784
|
const meta = await readMeta(dirAbs, entry.name);
|
|
19072
19785
|
if (meta?.filename) {
|
|
19073
19786
|
const servable = await servableFilesIn(dirAbs, entry.name);
|
|
@@ -19076,7 +19789,7 @@ async function enrich(absolute, entry, accountNames) {
|
|
|
19076
19789
|
let size = null;
|
|
19077
19790
|
let modifiedAt = entry.modifiedAt;
|
|
19078
19791
|
try {
|
|
19079
|
-
const st = await stat6(
|
|
19792
|
+
const st = await stat6(join37(dirAbs, innerName));
|
|
19080
19793
|
size = st.size;
|
|
19081
19794
|
modifiedAt = st.mtime.toISOString();
|
|
19082
19795
|
} catch {
|
|
@@ -19269,7 +19982,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
19269
19982
|
const childRel = relPath === "" || relPath === "." ? name : `${relPath}/${name}`;
|
|
19270
19983
|
const protectedEntry = isProtectedFromDeletion(childRel).protected;
|
|
19271
19984
|
try {
|
|
19272
|
-
const entryPath =
|
|
19985
|
+
const entryPath = join37(absolute, name);
|
|
19273
19986
|
const s = await stat6(entryPath);
|
|
19274
19987
|
entries2.push({
|
|
19275
19988
|
name,
|
|
@@ -19298,7 +20011,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
19298
20011
|
const displayPath = buildDisplayPath(relPath, accountNames);
|
|
19299
20012
|
if (groupReq || isAccountRoot(relPath)) {
|
|
19300
20013
|
const acctId = relPath.split("/")[1] ?? accountId;
|
|
19301
|
-
const schemaMd = await readFile7(
|
|
20014
|
+
const schemaMd = await readFile7(join37(absolute, "SCHEMA.md"), "utf8").catch(() => null);
|
|
19302
20015
|
const groups = classifyAccountRoot(schemaMd, entries2.map((e) => e.name));
|
|
19303
20016
|
console.error(
|
|
19304
20017
|
`[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"}`
|
|
@@ -19336,7 +20049,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
19336
20049
|
for (const name of homeDirs) {
|
|
19337
20050
|
const base = byName.get(name);
|
|
19338
20051
|
try {
|
|
19339
|
-
const dirents = await readdir4(
|
|
20052
|
+
const dirents = await readdir4(join37(absolute, name), { withFileTypes: true });
|
|
19340
20053
|
const visible = dirents.filter((d) => !d.name.startsWith("."));
|
|
19341
20054
|
visible.sort((a, b) => {
|
|
19342
20055
|
const ad = a.isDirectory() ? 0 : 1;
|
|
@@ -19401,7 +20114,7 @@ app23.get("/download", requireAdminSession, async (c) => {
|
|
|
19401
20114
|
console.error(`[data] op=scratchpad-no-session sessionId="${sessionId.slice(0, 8)}\u2026"`);
|
|
19402
20115
|
return c.json({ error: "Not found" }, 404);
|
|
19403
20116
|
}
|
|
19404
|
-
const meta = readSidecarMeta(
|
|
20117
|
+
const meta = readSidecarMeta(join37(projectDir, `${sessionId}.meta.json`));
|
|
19405
20118
|
if (!meta.accountId || meta.accountId !== accountId) {
|
|
19406
20119
|
console.error(`[data] account-scope-blocked root="scratchpad" sessionId="${sessionId.slice(0, 8)}\u2026" account=${accountId.slice(0, 8)}\u2026`);
|
|
19407
20120
|
return c.json({ error: "Not found" }, 404);
|
|
@@ -19496,7 +20209,7 @@ async function* walkRegularFiles(dirAbsolute) {
|
|
|
19496
20209
|
const dirents = await readdir4(dirAbsolute, { withFileTypes: true });
|
|
19497
20210
|
for (const d of dirents) {
|
|
19498
20211
|
if (d.isSymbolicLink()) continue;
|
|
19499
|
-
const child =
|
|
20212
|
+
const child = join37(dirAbsolute, d.name);
|
|
19500
20213
|
if (d.isDirectory()) {
|
|
19501
20214
|
yield* walkRegularFiles(child);
|
|
19502
20215
|
continue;
|
|
@@ -19606,7 +20319,7 @@ async function resolveUploadTarget(c, accountId, uid) {
|
|
|
19606
20319
|
const base = resolveOwnAccountWrite(rawDir, accountId, "POST /api/admin/files/upload");
|
|
19607
20320
|
if (!base.ok) return { ok: false, status: base.status, error: base.error };
|
|
19608
20321
|
const relDir = relpath ? dirname13(relpath) : ".";
|
|
19609
|
-
const destRel = relDir === "." ? base.relative :
|
|
20322
|
+
const destRel = relDir === "." ? base.relative : join37(base.relative, relDir);
|
|
19610
20323
|
if (crossesForeignAccountPartition(destRel, accountId)) {
|
|
19611
20324
|
console.error(`[data] account-scope-blocked endpoint="POST /api/admin/files/upload" path="${destRel}" account=${accountId.slice(0, 8)}\u2026`);
|
|
19612
20325
|
return { ok: false, status: 404, error: "Not found" };
|
|
@@ -19680,7 +20393,7 @@ app23.post("/upload", requireAdminSession, async (c) => {
|
|
|
19680
20393
|
});
|
|
19681
20394
|
await unlink3(tmpPath).catch(() => {
|
|
19682
20395
|
});
|
|
19683
|
-
const tempRemoved = !
|
|
20396
|
+
const tempRemoved = !existsSync35(tmpPath);
|
|
19684
20397
|
console.error(`[data-upload] op=cleanup uid=${uid} tempRemoved=${tempRemoved}`);
|
|
19685
20398
|
};
|
|
19686
20399
|
const reader = body.getReader();
|
|
@@ -19754,7 +20467,7 @@ async function discardChunkUpload(uploadId, state) {
|
|
|
19754
20467
|
});
|
|
19755
20468
|
await unlink3(state.tmpPath).catch(() => {
|
|
19756
20469
|
});
|
|
19757
|
-
const tempRemoved = !
|
|
20470
|
+
const tempRemoved = !existsSync35(state.tmpPath);
|
|
19758
20471
|
console.error(`[data-upload] op=cleanup uid=${uploadId} tempRemoved=${tempRemoved}`);
|
|
19759
20472
|
}
|
|
19760
20473
|
async function writeChunkBody(body, out, alreadyReceived, ceiling) {
|
|
@@ -19983,7 +20696,7 @@ app23.delete("/", requireAdminSession, async (c) => {
|
|
|
19983
20696
|
const dot = base.lastIndexOf(".");
|
|
19984
20697
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
19985
20698
|
const containerDir = dirname13(absolute);
|
|
19986
|
-
const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ?
|
|
20699
|
+
const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ? join37(containerDir, `${stem}.meta.json`) : null;
|
|
19987
20700
|
const isAttachmentContainer = UUID_RE4.test(stem) && basename11(containerDir) === stem;
|
|
19988
20701
|
await unlink3(absolute);
|
|
19989
20702
|
if (sidecarPath) {
|
|
@@ -19993,7 +20706,7 @@ app23.delete("/", requireAdminSession, async (c) => {
|
|
|
19993
20706
|
}
|
|
19994
20707
|
}
|
|
19995
20708
|
if (isAttachmentContainer) {
|
|
19996
|
-
await unlink3(
|
|
20709
|
+
await unlink3(join37(containerDir, `${stem}${EXTRACTED_TEXT_SUFFIX}`)).catch(() => {
|
|
19997
20710
|
});
|
|
19998
20711
|
await rmdir(containerDir).catch(() => {
|
|
19999
20712
|
});
|
|
@@ -20172,7 +20885,7 @@ function applyBacklinkBoost(hits) {
|
|
|
20172
20885
|
}
|
|
20173
20886
|
|
|
20174
20887
|
// ../lib/graph-search/src/dedup.ts
|
|
20175
|
-
import { createHash as
|
|
20888
|
+
import { createHash as createHash6 } from "crypto";
|
|
20176
20889
|
var DEFAULT_LAYERS = [
|
|
20177
20890
|
"nodeId",
|
|
20178
20891
|
"slug",
|
|
@@ -20199,7 +20912,7 @@ function readContentHash(props) {
|
|
|
20199
20912
|
const content = props["content"];
|
|
20200
20913
|
const source = typeof compiled === "string" && compiled.length > 0 ? compiled : typeof content === "string" && content.length > 0 ? content : null;
|
|
20201
20914
|
if (source === null) return null;
|
|
20202
|
-
return
|
|
20915
|
+
return createHash6("sha256").update(source).digest("hex").slice(0, 16);
|
|
20203
20916
|
}
|
|
20204
20917
|
function dedup(hits, layers = DEFAULT_LAYERS) {
|
|
20205
20918
|
const sorted = [...hits].sort((a, b) => b.score - a.score);
|
|
@@ -21102,8 +21815,8 @@ function isHiddenByDefault(label) {
|
|
|
21102
21815
|
|
|
21103
21816
|
// server/lib/top-level-labels.ts
|
|
21104
21817
|
var import_dist5 = __toESM(require_dist4(), 1);
|
|
21105
|
-
import { readdirSync as
|
|
21106
|
-
import { join as
|
|
21818
|
+
import { readdirSync as readdirSync24, readFileSync as readFileSync36 } from "fs";
|
|
21819
|
+
import { join as join38, resolve as resolve28 } from "path";
|
|
21107
21820
|
var STATIC_TOP_LEVEL_LABELS = Object.freeze(
|
|
21108
21821
|
/* @__PURE__ */ new Set([
|
|
21109
21822
|
// Base-schema infra (schema-base.md has no top-level section).
|
|
@@ -21207,7 +21920,7 @@ function parseTopLevelLabels(content) {
|
|
|
21207
21920
|
}
|
|
21208
21921
|
function resolveReferencesDir() {
|
|
21209
21922
|
if (process.env.MAXY_PLATFORM_ROOT) {
|
|
21210
|
-
return
|
|
21923
|
+
return join38(process.env.MAXY_PLATFORM_ROOT, "plugins", "memory", "references");
|
|
21211
21924
|
}
|
|
21212
21925
|
return resolve28(import.meta.dirname, "../../../plugins/memory/references");
|
|
21213
21926
|
}
|
|
@@ -21215,9 +21928,9 @@ function parseTableTopLevelLabels(referencesDir) {
|
|
|
21215
21928
|
const labels = /* @__PURE__ */ new Set();
|
|
21216
21929
|
const contributingFiles = [];
|
|
21217
21930
|
try {
|
|
21218
|
-
const fileNames =
|
|
21931
|
+
const fileNames = readdirSync24(referencesDir).filter((f) => f.startsWith("schema-") && f.endsWith(".md")).sort();
|
|
21219
21932
|
for (const fileName of fileNames) {
|
|
21220
|
-
const content =
|
|
21933
|
+
const content = readFileSync36(join38(referencesDir, fileName), "utf-8");
|
|
21221
21934
|
const parsed = parseTopLevelLabels(content);
|
|
21222
21935
|
if (parsed.length > 0) contributingFiles.push(fileName);
|
|
21223
21936
|
for (const label of parsed) labels.add(label);
|
|
@@ -22375,7 +23088,7 @@ var graph_default_view_default = app29;
|
|
|
22375
23088
|
// server/routes/admin/sidebar-artefacts.ts
|
|
22376
23089
|
import { readdir as readdir5, stat as stat7 } from "fs/promises";
|
|
22377
23090
|
import { resolve as resolve29, relative as relative6, isAbsolute as isAbsolute2, sep as sep9, basename as basename12 } from "path";
|
|
22378
|
-
import { existsSync as
|
|
23091
|
+
import { existsSync as existsSync36 } from "fs";
|
|
22379
23092
|
var LIMIT = 50;
|
|
22380
23093
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
22381
23094
|
function toDataRootRelPath(absPath) {
|
|
@@ -22487,7 +23200,7 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
22487
23200
|
async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
22488
23201
|
const names = /* @__PURE__ */ new Set();
|
|
22489
23202
|
for (const dir of [overrideDir, bundledDir]) {
|
|
22490
|
-
if (!
|
|
23203
|
+
if (!existsSync36(dir)) continue;
|
|
22491
23204
|
try {
|
|
22492
23205
|
const entries2 = await readdir5(dir);
|
|
22493
23206
|
for (const entry of entries2) {
|
|
@@ -22502,7 +23215,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
22502
23215
|
}
|
|
22503
23216
|
async function readAgentTemplateRow(inp) {
|
|
22504
23217
|
let chosenPath = null;
|
|
22505
|
-
if (
|
|
23218
|
+
if (existsSync36(inp.overridePath)) {
|
|
22506
23219
|
try {
|
|
22507
23220
|
validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
|
|
22508
23221
|
chosenPath = inp.overridePath;
|
|
@@ -22513,7 +23226,7 @@ async function readAgentTemplateRow(inp) {
|
|
|
22513
23226
|
);
|
|
22514
23227
|
return null;
|
|
22515
23228
|
}
|
|
22516
|
-
} else if (
|
|
23229
|
+
} else if (existsSync36(inp.bundledPath)) {
|
|
22517
23230
|
if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
|
|
22518
23231
|
console.error(
|
|
22519
23232
|
`[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
|
|
@@ -22555,19 +23268,19 @@ function isWithin(target, root) {
|
|
|
22555
23268
|
var sidebar_artefacts_default = app30;
|
|
22556
23269
|
|
|
22557
23270
|
// server/channel-session-override.ts
|
|
22558
|
-
import { readFileSync as
|
|
22559
|
-
import { join as
|
|
23271
|
+
import { readFileSync as readFileSync37, writeFileSync as writeFileSync14, renameSync as renameSync10 } from "fs";
|
|
23272
|
+
import { join as join39 } from "path";
|
|
22560
23273
|
var FILE2 = "channel-session-override.json";
|
|
22561
23274
|
var UUID3 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
22562
23275
|
function channelOverridePath(accountDir) {
|
|
22563
|
-
return
|
|
23276
|
+
return join39(accountDir, FILE2);
|
|
22564
23277
|
}
|
|
22565
23278
|
function senderKey(channel, senderId) {
|
|
22566
23279
|
return `${channel}:${senderId}`;
|
|
22567
23280
|
}
|
|
22568
23281
|
function readRaw2(accountDir) {
|
|
22569
23282
|
try {
|
|
22570
|
-
return JSON.parse(
|
|
23283
|
+
return JSON.parse(readFileSync37(channelOverridePath(accountDir), "utf8"));
|
|
22571
23284
|
} catch {
|
|
22572
23285
|
return null;
|
|
22573
23286
|
}
|
|
@@ -22589,8 +23302,8 @@ function writeChannelOverrideId(accountDir, channel, senderId, sessionId) {
|
|
|
22589
23302
|
bySender[senderKey(channel, senderId)] = sessionId;
|
|
22590
23303
|
const path3 = channelOverridePath(accountDir);
|
|
22591
23304
|
const tmp = `${path3}.${process.pid}.tmp`;
|
|
22592
|
-
|
|
22593
|
-
|
|
23305
|
+
writeFileSync14(tmp, JSON.stringify({ bySender }, null, 2), "utf8");
|
|
23306
|
+
renameSync10(tmp, path3);
|
|
22594
23307
|
}
|
|
22595
23308
|
function clearChannelOverrideForSession(accountDir, sessionId) {
|
|
22596
23309
|
const raw = readRaw2(accountDir);
|
|
@@ -22605,8 +23318,8 @@ function clearChannelOverrideForSession(accountDir, sessionId) {
|
|
|
22605
23318
|
if (cleared.length === 0) return [];
|
|
22606
23319
|
const path3 = channelOverridePath(accountDir);
|
|
22607
23320
|
const tmp = `${path3}.${process.pid}.tmp`;
|
|
22608
|
-
|
|
22609
|
-
|
|
23321
|
+
writeFileSync14(tmp, JSON.stringify({ bySender: kept }, null, 2), "utf8");
|
|
23322
|
+
renameSync10(tmp, path3);
|
|
22610
23323
|
return cleared;
|
|
22611
23324
|
}
|
|
22612
23325
|
|
|
@@ -22956,24 +23669,24 @@ var session_rc_spawn_default = app36;
|
|
|
22956
23669
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
22957
23670
|
|
|
22958
23671
|
// server/wa-descriptor.ts
|
|
22959
|
-
import { resolve as resolve30, join as
|
|
23672
|
+
import { resolve as resolve30, join as join40 } from "path";
|
|
22960
23673
|
function waGatewayUrl() {
|
|
22961
23674
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
22962
23675
|
}
|
|
22963
23676
|
function waServerPath() {
|
|
22964
|
-
return process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve30(process.env.MAXY_PLATFORM_ROOT ??
|
|
23677
|
+
return process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve30(process.env.MAXY_PLATFORM_ROOT ?? join40(__dirname, ".."), "services/whatsapp-channel/dist/server.js");
|
|
22965
23678
|
}
|
|
22966
23679
|
function waChannelDescriptor(senderId) {
|
|
22967
23680
|
return { senderId, gatewayUrl: waGatewayUrl(), serverPath: waServerPath() };
|
|
22968
23681
|
}
|
|
22969
23682
|
|
|
22970
23683
|
// server/telegram-descriptor.ts
|
|
22971
|
-
import { resolve as resolve31, join as
|
|
23684
|
+
import { resolve as resolve31, join as join41 } from "path";
|
|
22972
23685
|
function telegramGatewayUrl() {
|
|
22973
23686
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
22974
23687
|
}
|
|
22975
23688
|
function telegramServerPath() {
|
|
22976
|
-
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve31(process.env.MAXY_PLATFORM_ROOT ??
|
|
23689
|
+
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve31(process.env.MAXY_PLATFORM_ROOT ?? join41(__dirname, ".."), "services/telegram-channel/dist/server.js");
|
|
22977
23690
|
}
|
|
22978
23691
|
function telegramChannelDescriptor(senderId) {
|
|
22979
23692
|
return { senderId, gatewayUrl: telegramGatewayUrl(), serverPath: telegramServerPath() };
|
|
@@ -23257,14 +23970,14 @@ app38.get("/", async (c) => {
|
|
|
23257
23970
|
var system_stats_default = app38;
|
|
23258
23971
|
|
|
23259
23972
|
// server/routes/admin/health.ts
|
|
23260
|
-
import { existsSync as
|
|
23261
|
-
import { resolve as resolve32, join as
|
|
23973
|
+
import { existsSync as existsSync37, readFileSync as readFileSync38 } from "fs";
|
|
23974
|
+
import { resolve as resolve32, join as join42 } from "path";
|
|
23262
23975
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve32(process.cwd(), "..");
|
|
23263
23976
|
var brandHostname = "maxy";
|
|
23264
|
-
var brandJsonPath =
|
|
23265
|
-
if (
|
|
23977
|
+
var brandJsonPath = join42(PLATFORM_ROOT6, "config", "brand.json");
|
|
23978
|
+
if (existsSync37(brandJsonPath)) {
|
|
23266
23979
|
try {
|
|
23267
|
-
const brand = JSON.parse(
|
|
23980
|
+
const brand = JSON.parse(readFileSync38(brandJsonPath, "utf-8"));
|
|
23268
23981
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
23269
23982
|
} catch {
|
|
23270
23983
|
}
|
|
@@ -23273,8 +23986,8 @@ var VERSION_FILE = resolve32(PLATFORM_ROOT6, `config/.${brandHostname}-version`)
|
|
|
23273
23986
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
23274
23987
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
23275
23988
|
function readVersion() {
|
|
23276
|
-
if (!
|
|
23277
|
-
return
|
|
23989
|
+
if (!existsSync37(VERSION_FILE)) return "unknown";
|
|
23990
|
+
return readFileSync38(VERSION_FILE, "utf-8").trim() || "unknown";
|
|
23278
23991
|
}
|
|
23279
23992
|
async function probeConversationDb() {
|
|
23280
23993
|
let session;
|
|
@@ -24050,8 +24763,8 @@ app46.post("/launch", requireAdminSession, async (c) => {
|
|
|
24050
24763
|
var browser_default = app46;
|
|
24051
24764
|
|
|
24052
24765
|
// server/routes/admin/calendar.ts
|
|
24053
|
-
import { existsSync as
|
|
24054
|
-
import { join as
|
|
24766
|
+
import { existsSync as existsSync38 } from "fs";
|
|
24767
|
+
import { join as join44 } from "path";
|
|
24055
24768
|
|
|
24056
24769
|
// server/lib/calendar-ics.ts
|
|
24057
24770
|
var CRLF = "\r\n";
|
|
@@ -24150,8 +24863,8 @@ function countVEvents(ics) {
|
|
|
24150
24863
|
}
|
|
24151
24864
|
|
|
24152
24865
|
// server/lib/calendar-availability.ts
|
|
24153
|
-
import { readFileSync as
|
|
24154
|
-
import { join as
|
|
24866
|
+
import { readFileSync as readFileSync39 } from "fs";
|
|
24867
|
+
import { join as join43 } from "path";
|
|
24155
24868
|
var AVAILABILITY_FILENAME = "calendar-availability.json";
|
|
24156
24869
|
var REQUIRED_KEYS = [
|
|
24157
24870
|
"timezone",
|
|
@@ -24160,10 +24873,10 @@ var REQUIRED_KEYS = [
|
|
|
24160
24873
|
"weekly"
|
|
24161
24874
|
];
|
|
24162
24875
|
function readAvailabilityConfig(accountDir) {
|
|
24163
|
-
const path3 =
|
|
24876
|
+
const path3 = join43(accountDir, AVAILABILITY_FILENAME);
|
|
24164
24877
|
let raw;
|
|
24165
24878
|
try {
|
|
24166
|
-
raw =
|
|
24879
|
+
raw = readFileSync39(path3, "utf-8");
|
|
24167
24880
|
} catch {
|
|
24168
24881
|
throw new Error(`availability not configured: ${path3} not found`);
|
|
24169
24882
|
}
|
|
@@ -24522,7 +25235,7 @@ app47.get("/booking-link", requireAdminSession, (c) => {
|
|
|
24522
25235
|
console.error('[calendar] op=booking-link-fail err="no account configured"');
|
|
24523
25236
|
return c.json({ bookingDomain: null });
|
|
24524
25237
|
}
|
|
24525
|
-
if (!
|
|
25238
|
+
if (!existsSync38(join44(account.accountDir, AVAILABILITY_FILENAME))) {
|
|
24526
25239
|
console.log("[calendar] op=booking-link bookingDomain=none source=availability-config");
|
|
24527
25240
|
return c.json({ bookingDomain: null });
|
|
24528
25241
|
}
|
|
@@ -24724,7 +25437,7 @@ app48.delete("/:eventId", requireAdminSession, async (c) => {
|
|
|
24724
25437
|
var routines_default = app48;
|
|
24725
25438
|
|
|
24726
25439
|
// server/routes/admin/skills.ts
|
|
24727
|
-
import { existsSync as
|
|
25440
|
+
import { existsSync as existsSync39, readdirSync as readdirSync25, readFileSync as readFileSync40, rmSync as rmSync3 } from "fs";
|
|
24728
25441
|
import { resolve as resolve33 } from "path";
|
|
24729
25442
|
var app49 = new Hono();
|
|
24730
25443
|
var NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
@@ -24748,9 +25461,9 @@ function stripQuotes(s) {
|
|
|
24748
25461
|
return s;
|
|
24749
25462
|
}
|
|
24750
25463
|
function subdirs2(dir) {
|
|
24751
|
-
if (!
|
|
25464
|
+
if (!existsSync39(dir)) return [];
|
|
24752
25465
|
try {
|
|
24753
|
-
return
|
|
25466
|
+
return readdirSync25(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
24754
25467
|
} catch {
|
|
24755
25468
|
return [];
|
|
24756
25469
|
}
|
|
@@ -24771,11 +25484,11 @@ app49.get("/", requireAdminSession, async (c) => {
|
|
|
24771
25484
|
const skillsDir = resolve33(pluginsDir, plugin, "skills");
|
|
24772
25485
|
for (const skill of subdirs2(skillsDir)) {
|
|
24773
25486
|
const skillMd = resolve33(skillsDir, skill, "SKILL.md");
|
|
24774
|
-
if (!
|
|
25487
|
+
if (!existsSync39(skillMd)) continue;
|
|
24775
25488
|
try {
|
|
24776
|
-
const { frontmatter } = splitSkillMd(
|
|
25489
|
+
const { frontmatter } = splitSkillMd(readFileSync40(skillMd, "utf-8"));
|
|
24777
25490
|
const refsDir = resolve33(skillsDir, skill, "references");
|
|
24778
|
-
const refCount =
|
|
25491
|
+
const refCount = existsSync39(refsDir) ? readdirSync25(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).length : 0;
|
|
24779
25492
|
skills.push({ plugin, skill, description: frontmatter.description, refCount });
|
|
24780
25493
|
} catch (err) {
|
|
24781
25494
|
console.error(`[admin:skills] op=list-skip plugin=${plugin} skill=${skill} reason="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -24799,13 +25512,13 @@ app49.get("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
24799
25512
|
}
|
|
24800
25513
|
const skillDir = resolve33(acct.accountDir, "plugins", plugin, "skills", skill);
|
|
24801
25514
|
const skillMd = resolve33(skillDir, "SKILL.md");
|
|
24802
|
-
const found =
|
|
25515
|
+
const found = existsSync39(skillMd);
|
|
24803
25516
|
console.log(`[admin:skills] op=get plugin=${plugin} skill=${skill} found=${found}`);
|
|
24804
25517
|
if (!found) return c.json({ error: "Skill not found" }, 404);
|
|
24805
25518
|
try {
|
|
24806
|
-
const { frontmatter, body } = splitSkillMd(
|
|
25519
|
+
const { frontmatter, body } = splitSkillMd(readFileSync40(skillMd, "utf-8"));
|
|
24807
25520
|
const refsDir = resolve33(skillDir, "references");
|
|
24808
|
-
const references =
|
|
25521
|
+
const references = existsSync39(refsDir) ? readdirSync25(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).sort() : [];
|
|
24809
25522
|
return c.json({ skill: { plugin, skill, frontmatter, body, references } });
|
|
24810
25523
|
} catch (err) {
|
|
24811
25524
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -24825,7 +25538,7 @@ app49.delete("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
24825
25538
|
const pluginDir = resolve33(acct.accountDir, "plugins", plugin);
|
|
24826
25539
|
const skillsDir = resolve33(pluginDir, "skills");
|
|
24827
25540
|
const skillDir = resolve33(skillsDir, skill);
|
|
24828
|
-
if (!
|
|
25541
|
+
if (!existsSync39(resolve33(skillDir, "SKILL.md"))) {
|
|
24829
25542
|
console.error(`[admin:skills] op=delete outcome=rejected reason=not-found plugin=${plugin} skill=${skill}`);
|
|
24830
25543
|
return c.json({ error: "Skill not found" }, 404);
|
|
24831
25544
|
}
|
|
@@ -24833,7 +25546,7 @@ app49.delete("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
24833
25546
|
rmSync3(skillDir, { recursive: true, force: true });
|
|
24834
25547
|
const pluginDirRemoved = subdirs2(skillsDir).length === 0;
|
|
24835
25548
|
if (pluginDirRemoved) rmSync3(pluginDir, { recursive: true, force: true });
|
|
24836
|
-
const skillDirGone = !
|
|
25549
|
+
const skillDirGone = !existsSync39(skillDir);
|
|
24837
25550
|
if (!skillDirGone) {
|
|
24838
25551
|
console.error(`[admin:skills] op=delete plugin=${plugin} skill=${skill} outcome=rejected reason=rm-incomplete skillDirGone=false`);
|
|
24839
25552
|
return c.json({ error: "Delete did not remove the skill directory" }, 503);
|
|
@@ -25243,20 +25956,20 @@ app54.post("/", requireAdminSession, async (c) => {
|
|
|
25243
25956
|
var task_time_adjust_default = app54;
|
|
25244
25957
|
|
|
25245
25958
|
// app/lib/admin-roster.ts
|
|
25246
|
-
import { existsSync as
|
|
25959
|
+
import { existsSync as existsSync40, readdirSync as readdirSync26, readFileSync as readFileSync41 } from "fs";
|
|
25247
25960
|
import { resolve as resolve34 } from "path";
|
|
25248
25961
|
function readHouseAdminUserIds(accountsDir = ACCOUNTS_DIR) {
|
|
25249
25962
|
let entries2;
|
|
25250
25963
|
try {
|
|
25251
|
-
entries2 =
|
|
25964
|
+
entries2 = readdirSync26(accountsDir);
|
|
25252
25965
|
} catch {
|
|
25253
25966
|
return [];
|
|
25254
25967
|
}
|
|
25255
25968
|
for (const entry of entries2) {
|
|
25256
25969
|
const cfgPath = resolve34(accountsDir, entry, "account.json");
|
|
25257
|
-
if (!
|
|
25970
|
+
if (!existsSync40(cfgPath)) continue;
|
|
25258
25971
|
try {
|
|
25259
|
-
const cfg = JSON.parse(
|
|
25972
|
+
const cfg = JSON.parse(readFileSync41(cfgPath, "utf-8"));
|
|
25260
25973
|
if (cfg.role !== "house") continue;
|
|
25261
25974
|
if (!Array.isArray(cfg.admins)) return [];
|
|
25262
25975
|
return cfg.admins.map(
|
|
@@ -25435,7 +26148,7 @@ app56.get("/", requireAdminSession, async (c) => {
|
|
|
25435
26148
|
var admin_users_list_default = app56;
|
|
25436
26149
|
|
|
25437
26150
|
// server/routes/admin/activity.ts
|
|
25438
|
-
import { readdirSync as
|
|
26151
|
+
import { readdirSync as readdirSync27 } from "fs";
|
|
25439
26152
|
var app57 = new Hono();
|
|
25440
26153
|
var TAG47 = "[activity]";
|
|
25441
26154
|
function resolveHouseAccountId(accounts) {
|
|
@@ -25451,7 +26164,7 @@ function requestToken() {
|
|
|
25451
26164
|
var short = (id) => id ? id.slice(0, 8) : "unset";
|
|
25452
26165
|
function countAccountDirsOnDisk() {
|
|
25453
26166
|
try {
|
|
25454
|
-
return
|
|
26167
|
+
return readdirSync27(ACCOUNTS_DIR, { withFileTypes: true }).filter(
|
|
25455
26168
|
(e) => e.isDirectory() && !e.name.startsWith(".")
|
|
25456
26169
|
).length;
|
|
25457
26170
|
} catch {
|
|
@@ -25855,7 +26568,7 @@ app61.route("/request-magic-link", request_magic_link_default);
|
|
|
25855
26568
|
var access_default = app61;
|
|
25856
26569
|
|
|
25857
26570
|
// server/routes/sites.ts
|
|
25858
|
-
import { existsSync as
|
|
26571
|
+
import { existsSync as existsSync41, readFileSync as readFileSync42, realpathSync as realpathSync8, statSync as statSync19 } from "fs";
|
|
25859
26572
|
import { resolve as resolve36 } from "path";
|
|
25860
26573
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
25861
26574
|
var MIME = {
|
|
@@ -25921,7 +26634,7 @@ app62.get("/:rel{.*}", (c) => {
|
|
|
25921
26634
|
}
|
|
25922
26635
|
let stat11;
|
|
25923
26636
|
try {
|
|
25924
|
-
stat11 =
|
|
26637
|
+
stat11 = existsSync41(filePath) ? statSync19(filePath) : null;
|
|
25925
26638
|
} catch {
|
|
25926
26639
|
stat11 = null;
|
|
25927
26640
|
}
|
|
@@ -25940,7 +26653,7 @@ app62.get("/:rel{.*}", (c) => {
|
|
|
25940
26653
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
25941
26654
|
return c.text("Forbidden", 403);
|
|
25942
26655
|
}
|
|
25943
|
-
if (!
|
|
26656
|
+
if (!existsSync41(filePath)) {
|
|
25944
26657
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
25945
26658
|
return c.text("Not found", 404);
|
|
25946
26659
|
}
|
|
@@ -25959,7 +26672,7 @@ app62.get("/:rel{.*}", (c) => {
|
|
|
25959
26672
|
}
|
|
25960
26673
|
let body;
|
|
25961
26674
|
try {
|
|
25962
|
-
body =
|
|
26675
|
+
body = readFileSync42(realPath);
|
|
25963
26676
|
} catch (err) {
|
|
25964
26677
|
const code = err?.code;
|
|
25965
26678
|
if (code === "EISDIR") {
|
|
@@ -25995,7 +26708,7 @@ var sites_default = app62;
|
|
|
25995
26708
|
|
|
25996
26709
|
// app/lib/visitor-token.ts
|
|
25997
26710
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
25998
|
-
import { mkdirSync as
|
|
26711
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync43, writeFileSync as writeFileSync15 } from "fs";
|
|
25999
26712
|
import { dirname as dirname14 } from "path";
|
|
26000
26713
|
var TOKEN_PREFIX = "v1.";
|
|
26001
26714
|
var SECRET_BYTES = 32;
|
|
@@ -26004,7 +26717,7 @@ var cachedSecret = null;
|
|
|
26004
26717
|
function getSecret() {
|
|
26005
26718
|
if (cachedSecret) return cachedSecret;
|
|
26006
26719
|
try {
|
|
26007
|
-
const hex2 =
|
|
26720
|
+
const hex2 = readFileSync43(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
26008
26721
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
26009
26722
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
26010
26723
|
return cachedSecret;
|
|
@@ -26013,12 +26726,12 @@ function getSecret() {
|
|
|
26013
26726
|
}
|
|
26014
26727
|
const fresh = randomBytes(SECRET_BYTES).toString("hex");
|
|
26015
26728
|
try {
|
|
26016
|
-
|
|
26017
|
-
|
|
26729
|
+
mkdirSync9(dirname14(VISITOR_TOKEN_SECRET_FILE), { recursive: true, mode: 448 });
|
|
26730
|
+
writeFileSync15(VISITOR_TOKEN_SECRET_FILE, fresh, { mode: 384, flag: "wx" });
|
|
26018
26731
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
26019
26732
|
} catch {
|
|
26020
26733
|
}
|
|
26021
|
-
const hex =
|
|
26734
|
+
const hex = readFileSync43(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
26022
26735
|
cachedSecret = Buffer.from(hex, "hex");
|
|
26023
26736
|
return cachedSecret;
|
|
26024
26737
|
}
|
|
@@ -26071,8 +26784,8 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
26071
26784
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
26072
26785
|
|
|
26073
26786
|
// app/lib/brand-config.ts
|
|
26074
|
-
import { existsSync as
|
|
26075
|
-
import { join as
|
|
26787
|
+
import { existsSync as existsSync42, readFileSync as readFileSync44 } from "fs";
|
|
26788
|
+
import { join as join45 } from "path";
|
|
26076
26789
|
var cached2 = null;
|
|
26077
26790
|
var cachedAttempted = false;
|
|
26078
26791
|
function readBrandConfig() {
|
|
@@ -26080,10 +26793,10 @@ function readBrandConfig() {
|
|
|
26080
26793
|
cachedAttempted = true;
|
|
26081
26794
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
|
|
26082
26795
|
if (!platformRoot5) return null;
|
|
26083
|
-
const brandPath =
|
|
26084
|
-
if (!
|
|
26796
|
+
const brandPath = join45(platformRoot5, "config", "brand.json");
|
|
26797
|
+
if (!existsSync42(brandPath)) return null;
|
|
26085
26798
|
try {
|
|
26086
|
-
cached2 = JSON.parse(
|
|
26799
|
+
cached2 = JSON.parse(readFileSync44(brandPath, "utf-8"));
|
|
26087
26800
|
return cached2;
|
|
26088
26801
|
} catch {
|
|
26089
26802
|
return null;
|
|
@@ -26581,13 +27294,13 @@ var visitor_event_default = app65;
|
|
|
26581
27294
|
|
|
26582
27295
|
// server/routes/session.ts
|
|
26583
27296
|
import { resolve as resolve37 } from "path";
|
|
26584
|
-
import { existsSync as
|
|
27297
|
+
import { existsSync as existsSync43, writeFileSync as writeFileSync16, mkdirSync as mkdirSync10 } from "fs";
|
|
26585
27298
|
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
26586
27299
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
26587
27300
|
try {
|
|
26588
27301
|
const cacheDir = resolve37(MAXY_DIR, "branding-cache", accountId);
|
|
26589
|
-
|
|
26590
|
-
|
|
27302
|
+
mkdirSync10(cacheDir, { recursive: true });
|
|
27303
|
+
writeFileSync16(resolve37(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
26591
27304
|
} catch (err) {
|
|
26592
27305
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
26593
27306
|
}
|
|
@@ -26665,7 +27378,7 @@ app66.post("/", async (c) => {
|
|
|
26665
27378
|
let agentConfig = null;
|
|
26666
27379
|
if (account) {
|
|
26667
27380
|
const agentDir = resolve37(account.accountDir, "agents", agentSlug);
|
|
26668
|
-
if (!
|
|
27381
|
+
if (!existsSync43(agentDir) || !existsSync43(resolve37(agentDir, "config.json"))) {
|
|
26669
27382
|
return c.json({ error: "Agent not found" }, 404);
|
|
26670
27383
|
}
|
|
26671
27384
|
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
@@ -26925,7 +27638,7 @@ var calendar_public_default = app67;
|
|
|
26925
27638
|
// server/routes/portal-fetch.ts
|
|
26926
27639
|
import { createReadStream as createReadStream3 } from "fs";
|
|
26927
27640
|
import { stat as stat8, readFile as readFile9, realpath } from "fs/promises";
|
|
26928
|
-
import { join as
|
|
27641
|
+
import { join as join46, resolve as resolve38, sep as sep10 } from "path";
|
|
26929
27642
|
import { Readable as Readable3 } from "stream";
|
|
26930
27643
|
var app68 = new Hono();
|
|
26931
27644
|
var TAG50 = "[portal-fetch]";
|
|
@@ -26998,7 +27711,7 @@ function isValidAccountId2(value) {
|
|
|
26998
27711
|
}
|
|
26999
27712
|
async function readExposeFolders2(accountDir) {
|
|
27000
27713
|
try {
|
|
27001
|
-
const cfg = JSON.parse(await readFile9(
|
|
27714
|
+
const cfg = JSON.parse(await readFile9(join46(accountDir, "data-portal.json"), "utf8"));
|
|
27002
27715
|
return Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === "string") : [];
|
|
27003
27716
|
} catch {
|
|
27004
27717
|
return [];
|
|
@@ -27007,7 +27720,7 @@ async function readExposeFolders2(accountDir) {
|
|
|
27007
27720
|
async function resolveFetchTarget(accountDir, relPath, ownerId) {
|
|
27008
27721
|
let schemaText = null;
|
|
27009
27722
|
try {
|
|
27010
|
-
schemaText = await readFile9(
|
|
27723
|
+
schemaText = await readFile9(join46(accountDir, "SCHEMA.md"), "utf8");
|
|
27011
27724
|
} catch {
|
|
27012
27725
|
schemaText = null;
|
|
27013
27726
|
}
|
|
@@ -27177,8 +27890,8 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
27177
27890
|
}
|
|
27178
27891
|
|
|
27179
27892
|
// app/lib/ledger-census.ts
|
|
27180
|
-
import { readdirSync as
|
|
27181
|
-
import { join as
|
|
27893
|
+
import { readdirSync as readdirSync28, readFileSync as readFileSync45, statSync as statSync20 } from "fs";
|
|
27894
|
+
import { join as join47 } from "path";
|
|
27182
27895
|
|
|
27183
27896
|
// ../lib/ledger-core/dist/reconcile.js
|
|
27184
27897
|
var LEDGER_INDEX_NAMES = [
|
|
@@ -27226,16 +27939,16 @@ var DAY_MS = 864e5;
|
|
|
27226
27939
|
function countWriteRejects24h(logDir, now) {
|
|
27227
27940
|
let entries2;
|
|
27228
27941
|
try {
|
|
27229
|
-
entries2 =
|
|
27942
|
+
entries2 = readdirSync28(logDir).filter((f) => f.startsWith("mcp-ledger-") && f.endsWith(".log"));
|
|
27230
27943
|
} catch {
|
|
27231
27944
|
return 0;
|
|
27232
27945
|
}
|
|
27233
27946
|
let total = 0;
|
|
27234
27947
|
for (const name of entries2) {
|
|
27235
|
-
const path3 =
|
|
27948
|
+
const path3 = join47(logDir, name);
|
|
27236
27949
|
try {
|
|
27237
|
-
if (now -
|
|
27238
|
-
for (const line of
|
|
27950
|
+
if (now - statSync20(path3).mtimeMs > DAY_MS) continue;
|
|
27951
|
+
for (const line of readFileSync45(path3, "utf8").split("\n")) {
|
|
27239
27952
|
if (line.includes("[graph-write] reject")) total += 1;
|
|
27240
27953
|
}
|
|
27241
27954
|
} catch {
|
|
@@ -27730,8 +28443,8 @@ async function migrateUploads(opts = {}) {
|
|
|
27730
28443
|
|
|
27731
28444
|
// app/lib/migrate-admin-webchat-sidecars.ts
|
|
27732
28445
|
import { readdir as readdir7, readFile as readFile10, rename as rename4, writeFile as writeFile5, unlink as unlink4 } from "fs/promises";
|
|
27733
|
-
import { existsSync as
|
|
27734
|
-
import { join as
|
|
28446
|
+
import { existsSync as existsSync44 } from "fs";
|
|
28447
|
+
import { join as join48 } from "path";
|
|
27735
28448
|
var ADMIN_ROLE2 = "admin";
|
|
27736
28449
|
var WEBCHAT_CHANNEL2 = "webchat";
|
|
27737
28450
|
var SESSION_META_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
|
|
@@ -27760,7 +28473,7 @@ async function writeRaw(path3, obj) {
|
|
|
27760
28473
|
await rename4(tmp, path3);
|
|
27761
28474
|
} catch (err) {
|
|
27762
28475
|
try {
|
|
27763
|
-
if (
|
|
28476
|
+
if (existsSync44(tmp)) await unlink4(tmp);
|
|
27764
28477
|
} catch {
|
|
27765
28478
|
}
|
|
27766
28479
|
throw err;
|
|
@@ -27776,7 +28489,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
27776
28489
|
}
|
|
27777
28490
|
for (const slug of slugs) {
|
|
27778
28491
|
if (!slug.isDirectory()) continue;
|
|
27779
|
-
const slugDir =
|
|
28492
|
+
const slugDir = join48(projectsRoot, slug.name);
|
|
27780
28493
|
let entries2;
|
|
27781
28494
|
try {
|
|
27782
28495
|
entries2 = await readdir7(slugDir, { withFileTypes: true });
|
|
@@ -27785,9 +28498,9 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
27785
28498
|
}
|
|
27786
28499
|
for (const entry of entries2) {
|
|
27787
28500
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
27788
|
-
out.push(
|
|
28501
|
+
out.push(join48(slugDir, entry.name));
|
|
27789
28502
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
27790
|
-
const subDir =
|
|
28503
|
+
const subDir = join48(slugDir, entry.name);
|
|
27791
28504
|
let subs;
|
|
27792
28505
|
try {
|
|
27793
28506
|
subs = await readdir7(subDir, { withFileTypes: true });
|
|
@@ -27795,7 +28508,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
27795
28508
|
continue;
|
|
27796
28509
|
}
|
|
27797
28510
|
for (const s of subs) {
|
|
27798
|
-
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(
|
|
28511
|
+
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join48(subDir, s.name));
|
|
27799
28512
|
}
|
|
27800
28513
|
}
|
|
27801
28514
|
}
|
|
@@ -27807,7 +28520,7 @@ function shortId(path3) {
|
|
|
27807
28520
|
return base.slice(0, 8);
|
|
27808
28521
|
}
|
|
27809
28522
|
async function migrateAdminWebchatSidecars(opts = {}) {
|
|
27810
|
-
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ?
|
|
28523
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join48(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
27811
28524
|
const usersFile = opts.usersFile ?? USERS_FILE;
|
|
27812
28525
|
const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
|
|
27813
28526
|
const resolveName = opts.resolveName ?? defaultResolveName;
|
|
@@ -30510,14 +31223,14 @@ function warnOnChannelAdminBindingDrift() {
|
|
|
30510
31223
|
}
|
|
30511
31224
|
|
|
30512
31225
|
// server/lib/booking-site-accounts.ts
|
|
30513
|
-
import { existsSync as
|
|
31226
|
+
import { existsSync as existsSync45, readFileSync as readFileSync46 } from "fs";
|
|
30514
31227
|
import { resolve as resolve43 } from "path";
|
|
30515
31228
|
function listBookingSiteAccounts(accounts) {
|
|
30516
31229
|
return accounts.filter((a) => {
|
|
30517
31230
|
const availPath = resolve43(a.accountDir, "calendar-availability.json");
|
|
30518
|
-
if (!
|
|
31231
|
+
if (!existsSync45(availPath)) return false;
|
|
30519
31232
|
try {
|
|
30520
|
-
const cfg = JSON.parse(
|
|
31233
|
+
const cfg = JSON.parse(readFileSync46(availPath, "utf-8"));
|
|
30521
31234
|
return typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
30522
31235
|
} catch {
|
|
30523
31236
|
return false;
|
|
@@ -30554,8 +31267,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
30554
31267
|
}
|
|
30555
31268
|
|
|
30556
31269
|
// ../lib/entitlement/src/index.ts
|
|
30557
|
-
import { createPublicKey, createHash as
|
|
30558
|
-
import { existsSync as
|
|
31270
|
+
import { createPublicKey, createHash as createHash7, verify as cryptoVerify } from "crypto";
|
|
31271
|
+
import { existsSync as existsSync46, readFileSync as readFileSync47, statSync as statSync21 } from "fs";
|
|
30559
31272
|
import { resolve as resolve44 } from "path";
|
|
30560
31273
|
|
|
30561
31274
|
// ../lib/entitlement/src/canonicalize.ts
|
|
@@ -30604,10 +31317,10 @@ function resolveEntitlement(brand, account) {
|
|
|
30604
31317
|
return logResolved(implicitTrust(account), null);
|
|
30605
31318
|
}
|
|
30606
31319
|
const entitlementPath = resolve44(brand.configDir, "entitlement.json");
|
|
30607
|
-
if (!
|
|
31320
|
+
if (!existsSync46(entitlementPath)) {
|
|
30608
31321
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
30609
31322
|
}
|
|
30610
|
-
const stat11 =
|
|
31323
|
+
const stat11 = statSync21(entitlementPath);
|
|
30611
31324
|
const key = memoKey(stat11.mtimeMs, account);
|
|
30612
31325
|
if (memo && memo.key === key) {
|
|
30613
31326
|
return memo.result;
|
|
@@ -30619,13 +31332,13 @@ function resolveEntitlement(brand, account) {
|
|
|
30619
31332
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
30620
31333
|
let pubkeyPem;
|
|
30621
31334
|
try {
|
|
30622
|
-
pubkeyPem =
|
|
31335
|
+
pubkeyPem = readFileSync47(pubkeyPath(brand), "utf-8");
|
|
30623
31336
|
} catch (err) {
|
|
30624
31337
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
30625
31338
|
reason: "pubkey-missing"
|
|
30626
31339
|
});
|
|
30627
31340
|
}
|
|
30628
|
-
const pubkeyHash =
|
|
31341
|
+
const pubkeyHash = createHash7("sha256").update(pubkeyPem).digest("hex");
|
|
30629
31342
|
if (pubkeyHash !== PUBKEY_SHA256) {
|
|
30630
31343
|
return logResolved(anonymousFallback("pubkey-tamper"), {
|
|
30631
31344
|
reason: "pubkey-tamper"
|
|
@@ -30633,7 +31346,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
30633
31346
|
}
|
|
30634
31347
|
let envelope;
|
|
30635
31348
|
try {
|
|
30636
|
-
envelope = JSON.parse(
|
|
31349
|
+
envelope = JSON.parse(readFileSync47(entitlementPath, "utf-8"));
|
|
30637
31350
|
} catch {
|
|
30638
31351
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
30639
31352
|
}
|
|
@@ -30795,14 +31508,14 @@ function clientFrom(c) {
|
|
|
30795
31508
|
}
|
|
30796
31509
|
installMediaDownloadGuard();
|
|
30797
31510
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
30798
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT8 ?
|
|
31511
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join49(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
30799
31512
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
30800
|
-
if (BRAND_JSON_PATH && !
|
|
31513
|
+
if (BRAND_JSON_PATH && !existsSync47(BRAND_JSON_PATH)) {
|
|
30801
31514
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
30802
31515
|
}
|
|
30803
|
-
if (BRAND_JSON_PATH &&
|
|
31516
|
+
if (BRAND_JSON_PATH && existsSync47(BRAND_JSON_PATH)) {
|
|
30804
31517
|
try {
|
|
30805
|
-
const parsed = JSON.parse(
|
|
31518
|
+
const parsed = JSON.parse(readFileSync48(BRAND_JSON_PATH, "utf-8"));
|
|
30806
31519
|
BRAND = { ...BRAND, ...parsed };
|
|
30807
31520
|
} catch (err) {
|
|
30808
31521
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -30834,11 +31547,11 @@ var brandLoginOpts = {
|
|
|
30834
31547
|
appleTouchIconPath: brandAppIcon512Path,
|
|
30835
31548
|
themeColor: BRAND.defaultColors?.primary
|
|
30836
31549
|
};
|
|
30837
|
-
var ALIAS_DOMAINS_PATH =
|
|
31550
|
+
var ALIAS_DOMAINS_PATH = join49(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
30838
31551
|
function loadAliasDomains() {
|
|
30839
31552
|
try {
|
|
30840
|
-
if (!
|
|
30841
|
-
const parsed = JSON.parse(
|
|
31553
|
+
if (!existsSync47(ALIAS_DOMAINS_PATH)) return null;
|
|
31554
|
+
const parsed = JSON.parse(readFileSync48(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
30842
31555
|
if (!Array.isArray(parsed)) {
|
|
30843
31556
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
30844
31557
|
return null;
|
|
@@ -30913,7 +31626,7 @@ var waGateway = new WaGateway({
|
|
|
30913
31626
|
fetchStandingRules: fetchAccountStandingRules,
|
|
30914
31627
|
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
30915
31628
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
30916
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve45(process.env.MAXY_PLATFORM_ROOT ??
|
|
31629
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve45(process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
30917
31630
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
30918
31631
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
30919
31632
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -30928,13 +31641,13 @@ var waGateway = new WaGateway({
|
|
|
30928
31641
|
caption,
|
|
30929
31642
|
accountId,
|
|
30930
31643
|
maxyAccountId,
|
|
30931
|
-
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ??
|
|
31644
|
+
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, ".."))
|
|
30932
31645
|
});
|
|
30933
31646
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
30934
31647
|
},
|
|
30935
31648
|
ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId, ownerSelfPhone, replyContext }) => {
|
|
30936
31649
|
if (passive) {
|
|
30937
|
-
const phoneHash =
|
|
31650
|
+
const phoneHash = createHash8("sha256").update(senderId).digest("hex").slice(0, 12);
|
|
30938
31651
|
const prompt = (text ?? "").trim();
|
|
30939
31652
|
if (prompt.length === 0) {
|
|
30940
31653
|
console.error(`[wa-passive] op=dropped account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"} reason=empty-text`);
|
|
@@ -31046,7 +31759,7 @@ function runDuplicateSenderAudit() {
|
|
|
31046
31759
|
try {
|
|
31047
31760
|
const configDir2 = process.env.CLAUDE_CONFIG_DIR;
|
|
31048
31761
|
if (!configDir2) return;
|
|
31049
|
-
const projectsRoot =
|
|
31762
|
+
const projectsRoot = join49(configDir2, "projects");
|
|
31050
31763
|
const records = [];
|
|
31051
31764
|
for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
|
|
31052
31765
|
if (isSubagent || archived) continue;
|
|
@@ -31621,14 +32334,14 @@ app69.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
31621
32334
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
31622
32335
|
return c.text("Forbidden", 403);
|
|
31623
32336
|
}
|
|
31624
|
-
if (!
|
|
32337
|
+
if (!existsSync47(filePath)) {
|
|
31625
32338
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
31626
32339
|
return c.text("Not found", 404);
|
|
31627
32340
|
}
|
|
31628
32341
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
31629
32342
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
31630
32343
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
31631
|
-
const body =
|
|
32344
|
+
const body = readFileSync48(filePath);
|
|
31632
32345
|
return c.body(body, 200, {
|
|
31633
32346
|
"Content-Type": contentType,
|
|
31634
32347
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -31651,14 +32364,14 @@ app69.get("/generated/:filename", (c) => {
|
|
|
31651
32364
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
31652
32365
|
return c.text("Forbidden", 403);
|
|
31653
32366
|
}
|
|
31654
|
-
if (!
|
|
32367
|
+
if (!existsSync47(filePath)) {
|
|
31655
32368
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
31656
32369
|
return c.text("Not found", 404);
|
|
31657
32370
|
}
|
|
31658
32371
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
31659
32372
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
31660
32373
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
31661
|
-
const body =
|
|
32374
|
+
const body = readFileSync48(filePath);
|
|
31662
32375
|
return c.body(body, 200, {
|
|
31663
32376
|
"Content-Type": contentType,
|
|
31664
32377
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -31671,9 +32384,9 @@ app69.route("/v", visitor_consent_default);
|
|
|
31671
32384
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
31672
32385
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
31673
32386
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
31674
|
-
if (BRAND_JSON_PATH &&
|
|
32387
|
+
if (BRAND_JSON_PATH && existsSync47(BRAND_JSON_PATH)) {
|
|
31675
32388
|
try {
|
|
31676
|
-
const fullBrand = JSON.parse(
|
|
32389
|
+
const fullBrand = JSON.parse(readFileSync48(BRAND_JSON_PATH, "utf-8"));
|
|
31677
32390
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
31678
32391
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
31679
32392
|
} catch {
|
|
@@ -31700,11 +32413,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
31700
32413
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
31701
32414
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
31702
32415
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
31703
|
-
(p) =>
|
|
32416
|
+
(p) => existsSync47(resolve45(process.cwd(), "public", p.replace(/^\//, "")))
|
|
31704
32417
|
);
|
|
31705
32418
|
var SW_SOURCE = (() => {
|
|
31706
32419
|
try {
|
|
31707
|
-
return
|
|
32420
|
+
return readFileSync48(resolve45(process.cwd(), "public", "sw.js"), "utf-8");
|
|
31708
32421
|
} catch {
|
|
31709
32422
|
return null;
|
|
31710
32423
|
}
|
|
@@ -31712,9 +32425,9 @@ var SW_SOURCE = (() => {
|
|
|
31712
32425
|
function readInstalledVersion() {
|
|
31713
32426
|
try {
|
|
31714
32427
|
if (!PLATFORM_ROOT8) return "unknown";
|
|
31715
|
-
const versionFile =
|
|
31716
|
-
if (!
|
|
31717
|
-
const content =
|
|
32428
|
+
const versionFile = join49(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
32429
|
+
if (!existsSync47(versionFile)) return "unknown";
|
|
32430
|
+
const content = readFileSync48(versionFile, "utf-8").trim();
|
|
31718
32431
|
return content || "unknown";
|
|
31719
32432
|
} catch {
|
|
31720
32433
|
return "unknown";
|
|
@@ -31755,7 +32468,7 @@ var clientErrorReporterScript = `<script>
|
|
|
31755
32468
|
function cachedHtml(file) {
|
|
31756
32469
|
let html = htmlCache.get(file);
|
|
31757
32470
|
if (!html) {
|
|
31758
|
-
html =
|
|
32471
|
+
html = readFileSync48(resolve45(process.cwd(), "public", file), "utf-8");
|
|
31759
32472
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
31760
32473
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
31761
32474
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -31772,13 +32485,13 @@ ${clientErrorReporterScript}
|
|
|
31772
32485
|
return html;
|
|
31773
32486
|
}
|
|
31774
32487
|
function loadBrandingCache(agentSlug) {
|
|
31775
|
-
const configDir2 =
|
|
32488
|
+
const configDir2 = join49(homedir4(), BRAND.configDir);
|
|
31776
32489
|
try {
|
|
31777
32490
|
const accountId = getDefaultAccountId();
|
|
31778
32491
|
if (!accountId) return null;
|
|
31779
|
-
const cachePath =
|
|
31780
|
-
if (!
|
|
31781
|
-
return JSON.parse(
|
|
32492
|
+
const cachePath = join49(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
32493
|
+
if (!existsSync47(cachePath)) return null;
|
|
32494
|
+
return JSON.parse(readFileSync48(cachePath, "utf-8"));
|
|
31782
32495
|
} catch {
|
|
31783
32496
|
return null;
|
|
31784
32497
|
}
|
|
@@ -31873,7 +32586,7 @@ app69.use("/vnc-popout.html", logViewerFetch);
|
|
|
31873
32586
|
app69.get("/vnc-popout.html", (c) => {
|
|
31874
32587
|
let html = htmlCache.get("vnc-popout.html");
|
|
31875
32588
|
if (!html) {
|
|
31876
|
-
html =
|
|
32589
|
+
html = readFileSync48(resolve45(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
31877
32590
|
const name = escapeHtml(BRAND.productName);
|
|
31878
32591
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
31879
32592
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -32007,6 +32720,7 @@ if (brandFaviconPath !== "/favicon.ico") {
|
|
|
32007
32720
|
return c.redirect(brandFaviconPath, 302);
|
|
32008
32721
|
});
|
|
32009
32722
|
}
|
|
32723
|
+
app69.use("/*", staticCacheHeaders("./public"));
|
|
32010
32724
|
app69.use("/*", serveStatic({ root: "./public" }));
|
|
32011
32725
|
app69.all("*", (c) => {
|
|
32012
32726
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
@@ -32025,10 +32739,10 @@ var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
|
32025
32739
|
var httpServer = serve({ fetch: app69.fetch, port, hostname });
|
|
32026
32740
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
32027
32741
|
{
|
|
32028
|
-
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32742
|
+
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
|
|
32029
32743
|
const reconcileScript = resolve45(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
32030
32744
|
const runReconcile = (ctx) => {
|
|
32031
|
-
if (!
|
|
32745
|
+
if (!existsSync47(reconcileScript)) return;
|
|
32032
32746
|
try {
|
|
32033
32747
|
const child = spawn3(process.execPath, [reconcileScript], {
|
|
32034
32748
|
env: { ...process.env, PLATFORM_ROOT: reconcilePlatformRoot },
|
|
@@ -32048,7 +32762,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32048
32762
|
detached: true,
|
|
32049
32763
|
run: runReconcile
|
|
32050
32764
|
});
|
|
32051
|
-
const reconcileStatePath =
|
|
32765
|
+
const reconcileStatePath = join49(MAXY_DIR, "booking-reconcile-state.json");
|
|
32052
32766
|
registerLoop({
|
|
32053
32767
|
name: "calendar-reconcile-liveness",
|
|
32054
32768
|
intervalMs: 6e5,
|
|
@@ -32066,7 +32780,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32066
32780
|
}
|
|
32067
32781
|
{
|
|
32068
32782
|
const DREAM_CYCLE_SWEEP_INTERVAL_MS = 36e5;
|
|
32069
|
-
const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32783
|
+
const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
|
|
32070
32784
|
const dreamSweepScript = resolve45(dreamPlatformRoot, "plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js");
|
|
32071
32785
|
registerLoop({
|
|
32072
32786
|
name: "dream-cycle-sweep",
|
|
@@ -32074,7 +32788,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32074
32788
|
firstRunDelayMs: 3e4,
|
|
32075
32789
|
detached: true,
|
|
32076
32790
|
run: (ctx) => {
|
|
32077
|
-
if (!
|
|
32791
|
+
if (!existsSync47(dreamSweepScript)) return;
|
|
32078
32792
|
try {
|
|
32079
32793
|
const child = spawn3(process.execPath, [dreamSweepScript], {
|
|
32080
32794
|
env: { ...process.env, PLATFORM_ROOT: dreamPlatformRoot },
|
|
@@ -32090,11 +32804,11 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32090
32804
|
});
|
|
32091
32805
|
}
|
|
32092
32806
|
{
|
|
32093
|
-
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32807
|
+
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
|
|
32094
32808
|
const outlookScript = resolve45(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
|
|
32095
32809
|
const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
|
|
32096
32810
|
const runOutlookComplete = (ctx) => {
|
|
32097
|
-
if (!
|
|
32811
|
+
if (!existsSync47(outlookScript)) return;
|
|
32098
32812
|
try {
|
|
32099
32813
|
const child = spawn3(process.execPath, [outlookScript], {
|
|
32100
32814
|
env: { ...process.env, PLATFORM_ROOT: outlookPlatformRoot },
|
|
@@ -32116,20 +32830,20 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32116
32830
|
});
|
|
32117
32831
|
}
|
|
32118
32832
|
{
|
|
32119
|
-
const auditRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32833
|
+
const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
|
|
32120
32834
|
const strandedAccountsDir = resolve45(auditRoot, "..", "data/accounts");
|
|
32121
32835
|
const STRANDED_AUDIT_INTERVAL_MS = 3e5;
|
|
32122
32836
|
const STRANDED_AGE_MS = 16 * 6e4;
|
|
32123
32837
|
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;
|
|
32124
32838
|
const runStrandedAudit = () => {
|
|
32125
32839
|
try {
|
|
32126
|
-
if (!
|
|
32840
|
+
if (!existsSync47(strandedAccountsDir)) return;
|
|
32127
32841
|
const now = Date.now();
|
|
32128
|
-
for (const name of
|
|
32842
|
+
for (const name of readdirSync29(strandedAccountsDir)) {
|
|
32129
32843
|
if (!STRANDED_UUID_RE.test(name)) continue;
|
|
32130
32844
|
const pendingPath2 = resolve45(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
|
|
32131
|
-
if (!
|
|
32132
|
-
const ageMs2 = now -
|
|
32845
|
+
if (!existsSync47(pendingPath2)) continue;
|
|
32846
|
+
const ageMs2 = now - statSync22(pendingPath2).mtimeMs;
|
|
32133
32847
|
if (ageMs2 > STRANDED_AGE_MS) {
|
|
32134
32848
|
console.error(`[outlook-mcp] devicecode-stranded account=${name} ageSec=${Math.floor(ageMs2 / 1e3)}`);
|
|
32135
32849
|
}
|
|
@@ -32146,7 +32860,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32146
32860
|
});
|
|
32147
32861
|
}
|
|
32148
32862
|
{
|
|
32149
|
-
const googleRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32863
|
+
const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
|
|
32150
32864
|
const googleAccountsDir = resolve45(googleRoot, "..", "data/accounts");
|
|
32151
32865
|
const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
|
|
32152
32866
|
const runGooglePendingAuditSafe = () => {
|
|
@@ -32165,7 +32879,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32165
32879
|
const googleAuditScript = resolve45(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
|
|
32166
32880
|
const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
|
|
32167
32881
|
const runGoogleAccountAudit = (ctx) => {
|
|
32168
|
-
if (!
|
|
32882
|
+
if (!existsSync47(googleAuditScript)) return;
|
|
32169
32883
|
try {
|
|
32170
32884
|
const child = spawn3(process.execPath, [googleAuditScript], {
|
|
32171
32885
|
env: { ...process.env, PLATFORM_ROOT: googleRoot },
|
|
@@ -32190,7 +32904,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32190
32904
|
});
|
|
32191
32905
|
}
|
|
32192
32906
|
{
|
|
32193
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32907
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
|
|
32194
32908
|
const STORAGE_AUDIT_INTERVAL_MS = 3e5;
|
|
32195
32909
|
const runStorageAuditSafe = () => runStorageAudit(auditPlatformRoot).catch((err) => {
|
|
32196
32910
|
console.error(`[storage-audit] error="${err.message}"`);
|
|
@@ -32236,7 +32950,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32236
32950
|
});
|
|
32237
32951
|
}
|
|
32238
32952
|
{
|
|
32239
|
-
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32953
|
+
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
|
|
32240
32954
|
const publishScript = resolve45(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
32241
32955
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
32242
32956
|
const bookingAccounts = () => {
|
|
@@ -32248,7 +32962,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32248
32962
|
}
|
|
32249
32963
|
};
|
|
32250
32964
|
const spawnPublish = (account, ctx) => {
|
|
32251
|
-
if (!
|
|
32965
|
+
if (!existsSync47(publishScript)) return;
|
|
32252
32966
|
try {
|
|
32253
32967
|
const child = spawn3(process.execPath, [publishScript], {
|
|
32254
32968
|
env: {
|
|
@@ -32270,8 +32984,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32270
32984
|
const availPath = resolve45(account.accountDir, "calendar-availability.json");
|
|
32271
32985
|
let hasBookingSite = false;
|
|
32272
32986
|
try {
|
|
32273
|
-
if (
|
|
32274
|
-
const cfg = JSON.parse(
|
|
32987
|
+
if (existsSync47(availPath)) {
|
|
32988
|
+
const cfg = JSON.parse(readFileSync48(availPath, "utf-8"));
|
|
32275
32989
|
hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
32276
32990
|
}
|
|
32277
32991
|
} catch {
|
|
@@ -32279,9 +32993,9 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32279
32993
|
if (!hasBookingSite) return;
|
|
32280
32994
|
const statePath = resolve45(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
32281
32995
|
let lastSuccessAt = null;
|
|
32282
|
-
if (
|
|
32996
|
+
if (existsSync47(statePath)) {
|
|
32283
32997
|
try {
|
|
32284
|
-
const rec = JSON.parse(
|
|
32998
|
+
const rec = JSON.parse(readFileSync48(statePath, "utf-8"));
|
|
32285
32999
|
lastSuccessAt = rec.lastSuccessAt ?? null;
|
|
32286
33000
|
} catch {
|
|
32287
33001
|
console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
|
|
@@ -32313,12 +33027,12 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32313
33027
|
startTimeEntryCensus(() => getSession());
|
|
32314
33028
|
startLedgerCensus(() => getSession());
|
|
32315
33029
|
{
|
|
32316
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33030
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
|
|
32317
33031
|
const auditScript = resolve45(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
32318
33032
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
32319
33033
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
32320
33034
|
const runConnectorAudit = (ctx) => {
|
|
32321
|
-
if (!
|
|
33035
|
+
if (!existsSync47(auditScript)) return;
|
|
32322
33036
|
try {
|
|
32323
33037
|
const child = spawn3(process.execPath, [auditScript], {
|
|
32324
33038
|
env: { ...process.env, PLATFORM_ROOT: auditPlatformRoot, LOG_DIR: auditLogDir },
|
|
@@ -32358,19 +33072,17 @@ startAuthHealthHeartbeat();
|
|
|
32358
33072
|
{
|
|
32359
33073
|
const loopHist = monitorEventLoopDelay({ resolution: 50 });
|
|
32360
33074
|
loopHist.enable();
|
|
32361
|
-
const
|
|
33075
|
+
const LOUD_THRESHOLD_MS = 1e3;
|
|
32362
33076
|
const WINDOW_MS2 = 3e4;
|
|
32363
33077
|
registerLoop({
|
|
32364
33078
|
name: "event-loop-lag",
|
|
32365
33079
|
intervalMs: WINDOW_MS2,
|
|
32366
33080
|
run: () => {
|
|
32367
|
-
const
|
|
32368
|
-
const
|
|
32369
|
-
|
|
32370
|
-
|
|
32371
|
-
|
|
32372
|
-
}
|
|
32373
|
-
return `p99 ${Math.round(p99Ms)}ms, max ${Math.round(maxMs)}ms`;
|
|
33081
|
+
const w = readAndResetWindow(loopHist, WINDOW_MS2);
|
|
33082
|
+
const line = formatLagWindow(w);
|
|
33083
|
+
if (w.maxMs >= LOUD_THRESHOLD_MS) console.error(line);
|
|
33084
|
+
else console.log(line);
|
|
33085
|
+
return `p99 ${w.p99Ms}ms, max ${w.maxMs}ms`;
|
|
32374
33086
|
}
|
|
32375
33087
|
});
|
|
32376
33088
|
}
|
|
@@ -32438,11 +33150,11 @@ try {
|
|
|
32438
33150
|
var ADMINUSER_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
32439
33151
|
async function runAdminUserReconcileTick() {
|
|
32440
33152
|
try {
|
|
32441
|
-
if (!
|
|
33153
|
+
if (!existsSync47(USERS_FILE)) {
|
|
32442
33154
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
32443
33155
|
return;
|
|
32444
33156
|
}
|
|
32445
|
-
const usersRaw =
|
|
33157
|
+
const usersRaw = readFileSync48(USERS_FILE, "utf-8").trim();
|
|
32446
33158
|
if (!usersRaw) {
|
|
32447
33159
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
32448
33160
|
return;
|
|
@@ -32489,8 +33201,8 @@ registerLoop({
|
|
|
32489
33201
|
});
|
|
32490
33202
|
var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
32491
33203
|
function countUsersRows() {
|
|
32492
|
-
if (!
|
|
32493
|
-
const raw =
|
|
33204
|
+
if (!existsSync47(USERS_FILE)) return 0;
|
|
33205
|
+
const raw = readFileSync48(USERS_FILE, "utf-8").trim();
|
|
32494
33206
|
if (!raw) return 0;
|
|
32495
33207
|
const users = JSON.parse(raw);
|
|
32496
33208
|
return users.filter((u) => typeof u.userId === "string").length;
|
|
@@ -32623,7 +33335,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
32623
33335
|
}
|
|
32624
33336
|
init({
|
|
32625
33337
|
configDir: configDirForWhatsApp,
|
|
32626
|
-
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ??
|
|
33338
|
+
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..")),
|
|
32627
33339
|
accountConfig: bootAccountConfig,
|
|
32628
33340
|
onMessage: async (msg) => {
|
|
32629
33341
|
if (isObserveAccount(msg.accountId)) {
|