@rubytech/create-sitedesk-code 0.1.507 → 0.1.508
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/payload/platform/config/brand.json +12 -0
- package/payload/platform/plugins/admin/PLUGIN.md +10 -5
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +91 -13
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +7 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +90 -12
- package/payload/platform/plugins/memory/PLUGIN.md +6 -3
- package/payload/platform/scripts/__tests__/provision-honours-disabled-agents.test.sh +84 -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/public/activity.html +5 -5
- package/payload/server/public/agents.html +4 -4
- package/payload/server/public/assets/{AdminLoginScreens-YT7jsh_H.js → AdminLoginScreens-DKeCt1uf.js} +1 -1
- package/payload/server/public/assets/{AdminShell-Ckht9hmU.js → AdminShell-HeELCCjJ.js} +1 -1
- package/payload/server/public/assets/{activity-DH3Wgc3O.js → activity-BEIDAxDA.js} +1 -1
- package/payload/server/public/assets/admin-C0-WU1sf.js +1 -0
- package/payload/server/public/assets/agents-DjUvMFTj.js +1 -0
- package/payload/server/public/assets/{browser-Bym8QhfA.js → browser-GkVTGqlb.js} +1 -1
- package/payload/server/public/assets/{calendar-CFnufJOy.js → calendar-CSqJeFEg.js} +1 -1
- package/payload/server/public/assets/chat-C0Ps84U5.js +1 -0
- package/payload/server/public/assets/chevron-left-DQ2uNlu9.js +1 -0
- package/payload/server/public/assets/chevron-right-C4QCiRGD.js +1 -0
- package/payload/server/public/assets/clock-BEeQ7JxX.js +1 -0
- package/payload/server/public/assets/data-CGuNfym7.js +1 -0
- package/payload/server/public/assets/{file-text-zch0E1GJ.js → file-text-DiscM2pP.js} +1 -1
- package/payload/server/public/assets/{graph-BavHLzpV.js → graph-CRP5DM8C.js} +1 -1
- package/payload/server/public/assets/{graph-labels-CBwh4g-6.js → graph-labels-Du-6KoBE.js} +1 -1
- package/payload/server/public/assets/{maximize-2-bTn_Ozpi.js → maximize-2-D-H_uIV-.js} +1 -1
- package/payload/server/public/assets/{operator-a4IOwf62.js → operator-D5rA76YY.js} +1 -1
- package/payload/server/public/assets/{page-3T2mE63b.js → page-CHK1415C.js} +1 -1
- package/payload/server/public/assets/{page-D6M1CmPe.js → page-DZE3H_n4.js} +1 -1
- package/payload/server/public/assets/{public-B3y_YU1G.js → public-DFHXVkdD.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-BGvzNB1b.js → rotate-ccw-CesEJsuO.js} +1 -1
- package/payload/server/public/assets/{routines-C7Dr16am.js → routines-DYMYY_r8.js} +1 -1
- package/payload/server/public/assets/{skills-DFeb3XO3.js → skills-67jtJ16D.js} +1 -1
- package/payload/server/public/assets/{tasks-CSfnCKJe.js → tasks-D2C2a-uk.js} +1 -1
- package/payload/server/public/assets/{time-entry-format-BgZqOOqG.js → time-entry-format-BixTGQAd.js} +1 -1
- package/payload/server/public/assets/{triangle-alert-DBJ7xz_t.js → triangle-alert-Co9RWb9q.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-BFOujMl-.js → useCopyFeedback-7zwf9_w4.js} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-Bg72hdQ8.js +9 -0
- package/payload/server/public/assets/useSubAccountSwitcher-DE4v5_Fz.css +1 -0
- package/payload/server/public/assets/{useVoiceRecorder-BP6CpYQD.js → useVoiceRecorder-CZDDiwMD.js} +1 -1
- package/payload/server/public/assets/{wrench-DdaRhfzS.js → wrench-CjQqdj4P.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 +882 -251
- 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 statSync21 } 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 join48, basename as basename14 } from "path";
|
|
1831
1831
|
import { homedir as homedir4 } from "os";
|
|
1832
1832
|
|
|
1833
1833
|
// server/lib/route-cache-watchers.ts
|
|
@@ -16522,7 +16522,7 @@ var session_upload_default = app17;
|
|
|
16522
16522
|
|
|
16523
16523
|
// server/routes/admin/agents.ts
|
|
16524
16524
|
import { resolve as resolve23 } from "path";
|
|
16525
|
-
import { readdirSync as
|
|
16525
|
+
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
16526
|
|
|
16527
16527
|
// server/routes/admin/_account-scope.ts
|
|
16528
16528
|
function accountDirForSession(cacheKey) {
|
|
@@ -16549,12 +16549,390 @@ async function authorizeAccountWrite(c, accountId) {
|
|
|
16549
16549
|
return { ok: true, via: "session" };
|
|
16550
16550
|
}
|
|
16551
16551
|
|
|
16552
|
+
// ../services/claude-session-manager/src/tool-surface.ts
|
|
16553
|
+
import { readFileSync as readFileSync32, readdirSync as readdirSync21, statSync as statSync15, existsSync as existsSync30 } from "fs";
|
|
16554
|
+
import { createHash as createHash5 } from "crypto";
|
|
16555
|
+
import { join as join33 } from "path";
|
|
16556
|
+
|
|
16557
|
+
// ../services/claude-session-manager/src/canonical-tool-names.ts
|
|
16558
|
+
function canonicalToolName(plugin, tool) {
|
|
16559
|
+
return `mcp__plugin_${plugin}_${plugin}__${tool}`;
|
|
16560
|
+
}
|
|
16561
|
+
function qualifyShortToolName(plugin, tool) {
|
|
16562
|
+
return `mcp__${plugin}__${tool}`;
|
|
16563
|
+
}
|
|
16564
|
+
|
|
16565
|
+
// ../services/claude-session-manager/src/tool-surface.ts
|
|
16566
|
+
var RISK_CLASSES = ["read", "write_local", "exec", "external"];
|
|
16567
|
+
var ToolSurfaceError = class extends Error {
|
|
16568
|
+
constructor(reason, detail) {
|
|
16569
|
+
super(`${reason}: ${detail}`);
|
|
16570
|
+
this.reason = reason;
|
|
16571
|
+
this.detail = detail;
|
|
16572
|
+
this.name = "ToolSurfaceError";
|
|
16573
|
+
}
|
|
16574
|
+
};
|
|
16575
|
+
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
16576
|
+
function extractFrontmatter(raw, pluginDir) {
|
|
16577
|
+
const m = raw.match(FRONTMATTER_RE);
|
|
16578
|
+
if (!m) {
|
|
16579
|
+
throw new ToolSurfaceError("frontmatter-missing", `${pluginDir}/PLUGIN.md has no '---' delimited frontmatter`);
|
|
16580
|
+
}
|
|
16581
|
+
return m[1];
|
|
16582
|
+
}
|
|
16583
|
+
function parseToolsBlock(frontmatter, pluginDir) {
|
|
16584
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
16585
|
+
let i = 0;
|
|
16586
|
+
while (i < lines.length) {
|
|
16587
|
+
const line = lines[i];
|
|
16588
|
+
if (/^tools:\s*\[\s*\]\s*$/.test(line)) return [];
|
|
16589
|
+
if (/^tools:\s*$/.test(line)) {
|
|
16590
|
+
i++;
|
|
16591
|
+
break;
|
|
16592
|
+
}
|
|
16593
|
+
i++;
|
|
16594
|
+
}
|
|
16595
|
+
if (i >= lines.length) return [];
|
|
16596
|
+
const out = [];
|
|
16597
|
+
let current = null;
|
|
16598
|
+
const flush = () => {
|
|
16599
|
+
if (!current) return;
|
|
16600
|
+
if (typeof current.name !== "string" || current.name.length === 0) {
|
|
16601
|
+
throw new ToolSurfaceError("tool-name-missing", `${pluginDir}/PLUGIN.md has a tool entry with no name`);
|
|
16602
|
+
}
|
|
16603
|
+
if (typeof current.publicAllowlist !== "boolean") {
|
|
16604
|
+
if (current.publicAllowlist === void 0) {
|
|
16605
|
+
throw new ToolSurfaceError(
|
|
16606
|
+
"public-allowlist-missing",
|
|
16607
|
+
`${pluginDir}/${current.name} \u2014 every tool must declare publicAllowlist: <true|false>`
|
|
16608
|
+
);
|
|
16609
|
+
}
|
|
16610
|
+
throw new ToolSurfaceError(
|
|
16611
|
+
"public-allowlist-not-boolean",
|
|
16612
|
+
`${pluginDir}/${current.name} \u2014 publicAllowlist must be a YAML boolean (true|false), got ${JSON.stringify(current.publicAllowlist)}`
|
|
16613
|
+
);
|
|
16614
|
+
}
|
|
16615
|
+
if (typeof current.adminAllowlist !== "boolean") {
|
|
16616
|
+
if (current.adminAllowlist === void 0) {
|
|
16617
|
+
throw new ToolSurfaceError(
|
|
16618
|
+
"admin-allowlist-missing",
|
|
16619
|
+
`${pluginDir}/${current.name} \u2014 every tool must declare adminAllowlist: <true|false>`
|
|
16620
|
+
);
|
|
16621
|
+
}
|
|
16622
|
+
throw new ToolSurfaceError(
|
|
16623
|
+
"admin-allowlist-not-boolean",
|
|
16624
|
+
`${pluginDir}/${current.name} \u2014 adminAllowlist must be a YAML boolean (true|false), got ${JSON.stringify(current.adminAllowlist)}`
|
|
16625
|
+
);
|
|
16626
|
+
}
|
|
16627
|
+
if (current.riskClass === void 0) {
|
|
16628
|
+
throw new ToolSurfaceError(
|
|
16629
|
+
"risk-class-missing",
|
|
16630
|
+
`${pluginDir}/${current.name} \u2014 every tool must declare riskClass: <${RISK_CLASSES.join("|")}>`
|
|
16631
|
+
);
|
|
16632
|
+
}
|
|
16633
|
+
if (!RISK_CLASSES.includes(current.riskClass)) {
|
|
16634
|
+
throw new ToolSurfaceError(
|
|
16635
|
+
"risk-class-invalid",
|
|
16636
|
+
`${pluginDir}/${current.name} \u2014 riskClass must be one of ${RISK_CLASSES.join("|")}, got ${JSON.stringify(current.riskClass)}`
|
|
16637
|
+
);
|
|
16638
|
+
}
|
|
16639
|
+
out.push(current);
|
|
16640
|
+
current = null;
|
|
16641
|
+
};
|
|
16642
|
+
for (; i < lines.length; i++) {
|
|
16643
|
+
const line = lines[i];
|
|
16644
|
+
if (line.length === 0) continue;
|
|
16645
|
+
if (/^[A-Za-z_]/.test(line)) break;
|
|
16646
|
+
const newEntry = line.match(/^\s+-\s+name:\s*(.+?)\s*$/);
|
|
16647
|
+
if (newEntry) {
|
|
16648
|
+
flush();
|
|
16649
|
+
current = { name: newEntry[1] };
|
|
16650
|
+
continue;
|
|
16651
|
+
}
|
|
16652
|
+
const orphanListItem = line.match(/^\s+-\s+(?!name:)/);
|
|
16653
|
+
if (orphanListItem) {
|
|
16654
|
+
throw new ToolSurfaceError("tool-name-missing", `${pluginDir}/PLUGIN.md tool entry lacks a name field`);
|
|
16655
|
+
}
|
|
16656
|
+
const publicAllow = line.match(/^\s+publicAllowlist:\s*(.+?)\s*$/);
|
|
16657
|
+
if (publicAllow && current) {
|
|
16658
|
+
const v = publicAllow[1].trim();
|
|
16659
|
+
if (v === "true") current.publicAllowlist = true;
|
|
16660
|
+
else if (v === "false") current.publicAllowlist = false;
|
|
16661
|
+
else current.publicAllowlist = v;
|
|
16662
|
+
continue;
|
|
16663
|
+
}
|
|
16664
|
+
const adminAllow = line.match(/^\s+adminAllowlist:\s*(.+?)\s*$/);
|
|
16665
|
+
if (adminAllow && current) {
|
|
16666
|
+
const v = adminAllow[1].trim();
|
|
16667
|
+
if (v === "true") current.adminAllowlist = true;
|
|
16668
|
+
else if (v === "false") current.adminAllowlist = false;
|
|
16669
|
+
else current.adminAllowlist = v;
|
|
16670
|
+
continue;
|
|
16671
|
+
}
|
|
16672
|
+
const risk = line.match(/^\s+riskClass:\s*(.+?)\s*$/);
|
|
16673
|
+
if (risk && current) {
|
|
16674
|
+
current.riskClass = risk[1].trim();
|
|
16675
|
+
continue;
|
|
16676
|
+
}
|
|
16677
|
+
}
|
|
16678
|
+
flush();
|
|
16679
|
+
return out;
|
|
16680
|
+
}
|
|
16681
|
+
function parseHiddenBlock(frontmatter, pluginDir) {
|
|
16682
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
16683
|
+
let i = 0;
|
|
16684
|
+
while (i < lines.length) {
|
|
16685
|
+
if (/^hidden:\s*$/.test(lines[i])) {
|
|
16686
|
+
i++;
|
|
16687
|
+
break;
|
|
16688
|
+
}
|
|
16689
|
+
i++;
|
|
16690
|
+
}
|
|
16691
|
+
if (i >= lines.length) return [];
|
|
16692
|
+
const out = [];
|
|
16693
|
+
let current = null;
|
|
16694
|
+
const flush = () => {
|
|
16695
|
+
if (!current) return;
|
|
16696
|
+
if (current.riskClass === void 0) {
|
|
16697
|
+
throw new ToolSurfaceError(
|
|
16698
|
+
"risk-class-missing",
|
|
16699
|
+
`${pluginDir}/${current.name} \u2014 every hidden tool must declare riskClass: <${RISK_CLASSES.join("|")}>`
|
|
16700
|
+
);
|
|
16701
|
+
}
|
|
16702
|
+
if (!RISK_CLASSES.includes(current.riskClass)) {
|
|
16703
|
+
throw new ToolSurfaceError(
|
|
16704
|
+
"risk-class-invalid",
|
|
16705
|
+
`${pluginDir}/${current.name} \u2014 hidden riskClass must be one of ${RISK_CLASSES.join("|")}, got ${JSON.stringify(current.riskClass)}`
|
|
16706
|
+
);
|
|
16707
|
+
}
|
|
16708
|
+
out.push(current);
|
|
16709
|
+
current = null;
|
|
16710
|
+
};
|
|
16711
|
+
for (; i < lines.length; i++) {
|
|
16712
|
+
const line = lines[i];
|
|
16713
|
+
if (line.length === 0) continue;
|
|
16714
|
+
if (/^[A-Za-z_]/.test(line)) break;
|
|
16715
|
+
const newEntry = line.match(/^\s+-\s+name:\s*(.+?)\s*$/);
|
|
16716
|
+
if (newEntry) {
|
|
16717
|
+
flush();
|
|
16718
|
+
current = { name: newEntry[1] };
|
|
16719
|
+
continue;
|
|
16720
|
+
}
|
|
16721
|
+
const bareItem = line.match(/^\s+-\s+(.+?)\s*$/);
|
|
16722
|
+
if (bareItem) {
|
|
16723
|
+
throw new ToolSurfaceError(
|
|
16724
|
+
"hidden-tool-name-missing",
|
|
16725
|
+
`${pluginDir}/PLUGIN.md hidden entry "${bareItem[1]}" is a bare name \u2014 declare it as "- name: ${bareItem[1]}" with a riskClass`
|
|
16726
|
+
);
|
|
16727
|
+
}
|
|
16728
|
+
const risk = line.match(/^\s+riskClass:\s*(.+?)\s*$/);
|
|
16729
|
+
if (risk && current) {
|
|
16730
|
+
current.riskClass = risk[1].trim();
|
|
16731
|
+
continue;
|
|
16732
|
+
}
|
|
16733
|
+
}
|
|
16734
|
+
flush();
|
|
16735
|
+
return out;
|
|
16736
|
+
}
|
|
16737
|
+
function parseMcpBlock(frontmatter, pluginDir) {
|
|
16738
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
16739
|
+
let i = 0;
|
|
16740
|
+
while (i < lines.length) {
|
|
16741
|
+
if (/^mcp:\s*$/.test(lines[i])) {
|
|
16742
|
+
i++;
|
|
16743
|
+
break;
|
|
16744
|
+
}
|
|
16745
|
+
i++;
|
|
16746
|
+
}
|
|
16747
|
+
if (i >= lines.length) return null;
|
|
16748
|
+
let command = null;
|
|
16749
|
+
const args = [];
|
|
16750
|
+
const env = {};
|
|
16751
|
+
let envHasEntry = false;
|
|
16752
|
+
let mode = "top";
|
|
16753
|
+
for (; i < lines.length; i++) {
|
|
16754
|
+
const line = lines[i];
|
|
16755
|
+
if (line.length === 0) continue;
|
|
16756
|
+
if (/^[A-Za-z_]/.test(line)) break;
|
|
16757
|
+
const topCommand = line.match(/^\s\s(command):\s*(.+?)\s*$/);
|
|
16758
|
+
const topArgsHeader = line.match(/^\s\s(args):\s*$/);
|
|
16759
|
+
const topEnvHeader = line.match(/^\s\s(env):\s*$/);
|
|
16760
|
+
if (topCommand) {
|
|
16761
|
+
command = topCommand[2];
|
|
16762
|
+
mode = "top";
|
|
16763
|
+
continue;
|
|
16764
|
+
}
|
|
16765
|
+
if (topArgsHeader) {
|
|
16766
|
+
mode = "args";
|
|
16767
|
+
continue;
|
|
16768
|
+
}
|
|
16769
|
+
if (topEnvHeader) {
|
|
16770
|
+
mode = "env";
|
|
16771
|
+
continue;
|
|
16772
|
+
}
|
|
16773
|
+
if (mode === "args") {
|
|
16774
|
+
const item = line.match(/^\s+-\s+(.+?)\s*$/);
|
|
16775
|
+
if (item) {
|
|
16776
|
+
args.push(item[1]);
|
|
16777
|
+
continue;
|
|
16778
|
+
}
|
|
16779
|
+
}
|
|
16780
|
+
if (mode === "env") {
|
|
16781
|
+
const kv = line.match(/^\s+([A-Za-z_][A-Za-z0-9_]*):\s*(.+?)\s*$/);
|
|
16782
|
+
if (kv) {
|
|
16783
|
+
env[kv[1]] = kv[2];
|
|
16784
|
+
envHasEntry = true;
|
|
16785
|
+
continue;
|
|
16786
|
+
}
|
|
16787
|
+
}
|
|
16788
|
+
}
|
|
16789
|
+
if (command === null) {
|
|
16790
|
+
throw new ToolSurfaceError("mcp-command-missing", `${pluginDir}/PLUGIN.md mcp: block has no command`);
|
|
16791
|
+
}
|
|
16792
|
+
const desc = { command, args };
|
|
16793
|
+
if (envHasEntry) desc.env = env;
|
|
16794
|
+
return desc;
|
|
16795
|
+
}
|
|
16796
|
+
function loadToolSurface(pluginsRoots) {
|
|
16797
|
+
const roots = Array.isArray(pluginsRoots) ? pluginsRoots : [pluginsRoots];
|
|
16798
|
+
for (const r of roots) {
|
|
16799
|
+
if (!existsSync30(r)) throw new ToolSurfaceError("plugins-root-missing", r);
|
|
16800
|
+
}
|
|
16801
|
+
const admin = /* @__PURE__ */ new Set();
|
|
16802
|
+
const pub = /* @__PURE__ */ new Set();
|
|
16803
|
+
const all = /* @__PURE__ */ new Set();
|
|
16804
|
+
const mcpServers = /* @__PURE__ */ new Map();
|
|
16805
|
+
const riskByTool = /* @__PURE__ */ new Map();
|
|
16806
|
+
const manifestPaths = /* @__PURE__ */ new Map();
|
|
16807
|
+
const loadOne = (pluginsRoot, plugin) => {
|
|
16808
|
+
const pluginDir = join33(pluginsRoot, plugin);
|
|
16809
|
+
try {
|
|
16810
|
+
if (!statSync15(pluginDir).isDirectory()) return;
|
|
16811
|
+
} catch {
|
|
16812
|
+
return;
|
|
16813
|
+
}
|
|
16814
|
+
const manifestPath = join33(pluginDir, "PLUGIN.md");
|
|
16815
|
+
if (!existsSync30(manifestPath)) return;
|
|
16816
|
+
const raw = readFileSync32(manifestPath, "utf-8");
|
|
16817
|
+
const frontmatter = extractFrontmatter(raw, plugin);
|
|
16818
|
+
const tools = parseToolsBlock(frontmatter, plugin);
|
|
16819
|
+
if (tools.length === 0) return;
|
|
16820
|
+
const mcp = parseMcpBlock(frontmatter, plugin);
|
|
16821
|
+
if (!mcp) {
|
|
16822
|
+
throw new ToolSurfaceError(
|
|
16823
|
+
"mcp-block-missing",
|
|
16824
|
+
`${plugin}/PLUGIN.md declares tools but no mcp: block; spawns would --allowed-tools mcp__${plugin}__* with no registered server`
|
|
16825
|
+
);
|
|
16826
|
+
}
|
|
16827
|
+
if (mcpServers.has(plugin)) {
|
|
16828
|
+
const priorPath = manifestPaths.get(plugin);
|
|
16829
|
+
if (!priorPath) {
|
|
16830
|
+
throw new ToolSurfaceError(
|
|
16831
|
+
"mcp-plugin-duplicate-no-prior-path",
|
|
16832
|
+
`${plugin}: mcpServers has entry but manifestPaths is missing \u2014 invariant violated, both maps must be written together`
|
|
16833
|
+
);
|
|
16834
|
+
}
|
|
16835
|
+
const priorSha = createHash5("sha256").update(readFileSync32(priorPath)).digest("hex").slice(0, 8);
|
|
16836
|
+
const newSha = createHash5("sha256").update(raw).digest("hex").slice(0, 8);
|
|
16837
|
+
throw new ToolSurfaceError(
|
|
16838
|
+
"mcp-plugin-duplicate",
|
|
16839
|
+
`${plugin} declared by more than one plugins root: ${priorPath} (sha=${priorSha}) vs ${manifestPath} (sha=${newSha})`
|
|
16840
|
+
);
|
|
16841
|
+
}
|
|
16842
|
+
mcpServers.set(plugin, mcp);
|
|
16843
|
+
manifestPaths.set(plugin, manifestPath);
|
|
16844
|
+
for (const t of tools) {
|
|
16845
|
+
const canonical = canonicalToolName(plugin, t.name);
|
|
16846
|
+
all.add(canonical);
|
|
16847
|
+
riskByTool.set(canonical, t.riskClass);
|
|
16848
|
+
if (t.adminAllowlist) admin.add(canonical);
|
|
16849
|
+
if (t.publicAllowlist) pub.add(qualifyShortToolName(plugin, t.name));
|
|
16850
|
+
}
|
|
16851
|
+
for (const h of parseHiddenBlock(frontmatter, plugin)) {
|
|
16852
|
+
const canonical = canonicalToolName(plugin, h.name);
|
|
16853
|
+
all.add(canonical);
|
|
16854
|
+
riskByTool.set(canonical, h.riskClass);
|
|
16855
|
+
}
|
|
16856
|
+
};
|
|
16857
|
+
for (const root of roots) {
|
|
16858
|
+
const entries2 = readdirSync21(root).sort();
|
|
16859
|
+
for (const plugin of entries2) {
|
|
16860
|
+
const pluginDir = join33(root, plugin);
|
|
16861
|
+
loadOne(root, plugin);
|
|
16862
|
+
const subRoot = join33(pluginDir, "plugins");
|
|
16863
|
+
let subEntries;
|
|
16864
|
+
try {
|
|
16865
|
+
if (!statSync15(subRoot).isDirectory()) continue;
|
|
16866
|
+
subEntries = readdirSync21(subRoot).sort();
|
|
16867
|
+
} catch {
|
|
16868
|
+
continue;
|
|
16869
|
+
}
|
|
16870
|
+
for (const sub of subEntries) {
|
|
16871
|
+
loadOne(subRoot, sub);
|
|
16872
|
+
}
|
|
16873
|
+
}
|
|
16874
|
+
}
|
|
16875
|
+
return { admin, public: pub, all, mcpServers, riskByTool };
|
|
16876
|
+
}
|
|
16877
|
+
|
|
16878
|
+
// server/lib/agent-risk.ts
|
|
16879
|
+
var BUILTIN_TOOL_RISK = {
|
|
16880
|
+
Bash: "exec",
|
|
16881
|
+
Task: "exec",
|
|
16882
|
+
Skill: "exec",
|
|
16883
|
+
Edit: "write_local",
|
|
16884
|
+
MultiEdit: "write_local",
|
|
16885
|
+
Write: "write_local",
|
|
16886
|
+
Read: "read",
|
|
16887
|
+
Glob: "read",
|
|
16888
|
+
Grep: "read",
|
|
16889
|
+
WebFetch: "external",
|
|
16890
|
+
WebSearch: "external"
|
|
16891
|
+
};
|
|
16892
|
+
var SEVERITY = {
|
|
16893
|
+
read: 0,
|
|
16894
|
+
write_local: 1,
|
|
16895
|
+
exec: 2,
|
|
16896
|
+
external: 2
|
|
16897
|
+
};
|
|
16898
|
+
var AGENT_CLASS = {
|
|
16899
|
+
read: "green",
|
|
16900
|
+
write_local: "amber",
|
|
16901
|
+
exec: "red",
|
|
16902
|
+
external: "red"
|
|
16903
|
+
};
|
|
16904
|
+
function agentRiskClass(tools, riskByTool) {
|
|
16905
|
+
const byTool = [];
|
|
16906
|
+
const unresolved = [];
|
|
16907
|
+
let worst = null;
|
|
16908
|
+
let worstTool = null;
|
|
16909
|
+
for (const tool of tools) {
|
|
16910
|
+
const cls = BUILTIN_TOOL_RISK[tool] ?? riskByTool.get(tool) ?? null;
|
|
16911
|
+
byTool.push({ tool, risk: cls });
|
|
16912
|
+
if (cls === null) {
|
|
16913
|
+
unresolved.push(tool);
|
|
16914
|
+
continue;
|
|
16915
|
+
}
|
|
16916
|
+
if (worst === null || SEVERITY[cls] > SEVERITY[worst]) {
|
|
16917
|
+
worst = cls;
|
|
16918
|
+
worstTool = tool;
|
|
16919
|
+
}
|
|
16920
|
+
}
|
|
16921
|
+
if (unresolved.length > 0) {
|
|
16922
|
+
return { risk: "red", worstTool: unresolved[0], unresolved, byTool };
|
|
16923
|
+
}
|
|
16924
|
+
if (worst === null) {
|
|
16925
|
+
return { risk: "red", worstTool: null, unresolved, byTool };
|
|
16926
|
+
}
|
|
16927
|
+
return { risk: AGENT_CLASS[worst], worstTool, unresolved, byTool };
|
|
16928
|
+
}
|
|
16929
|
+
|
|
16552
16930
|
// server/routes/admin/agents.ts
|
|
16553
16931
|
var SLUG_UNSAFE = (s) => s.includes("/") || s.includes("..") || s.includes("\\");
|
|
16554
16932
|
function subdirs(dir) {
|
|
16555
|
-
if (!
|
|
16933
|
+
if (!existsSync31(dir)) return [];
|
|
16556
16934
|
try {
|
|
16557
|
-
return
|
|
16935
|
+
return readdirSync22(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
16558
16936
|
} catch {
|
|
16559
16937
|
return [];
|
|
16560
16938
|
}
|
|
@@ -16569,34 +16947,148 @@ function splitFrontmatter(text) {
|
|
|
16569
16947
|
}
|
|
16570
16948
|
return { fm, body: text.slice(m[0].length) };
|
|
16571
16949
|
}
|
|
16572
|
-
|
|
16950
|
+
var SPECIALISTS_DIR = ["specialists", "agents"];
|
|
16951
|
+
var QUARANTINE_DIR = ["specialists", "agents-disabled"];
|
|
16952
|
+
var DISABLED_STORE = "agents-disabled.json";
|
|
16953
|
+
function loadRiskSurface() {
|
|
16954
|
+
const roots = [resolve23(PLATFORM_ROOT, "plugins"), resolve23(PLATFORM_ROOT, "..", "premium-plugins")].filter((r) => existsSync31(r));
|
|
16955
|
+
try {
|
|
16956
|
+
return { riskByTool: loadToolSurface(roots).riskByTool, failed: null };
|
|
16957
|
+
} catch (err) {
|
|
16958
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
16959
|
+
console.error(`[admin/agents] op=risk-surface status=load-failed reason="${msg}"`);
|
|
16960
|
+
return { riskByTool: /* @__PURE__ */ new Map(), failed: msg };
|
|
16961
|
+
}
|
|
16962
|
+
}
|
|
16963
|
+
function readDisabledStoreStrict(accountDir) {
|
|
16964
|
+
const p = resolve23(accountDir, DISABLED_STORE);
|
|
16965
|
+
if (!existsSync31(p)) return /* @__PURE__ */ new Set();
|
|
16966
|
+
const parsed = JSON.parse(readFileSync33(p, "utf-8"));
|
|
16967
|
+
if (!Array.isArray(parsed.disabled)) throw new Error("disabled is not an array");
|
|
16968
|
+
return new Set(parsed.disabled.filter((x) => typeof x === "string"));
|
|
16969
|
+
}
|
|
16970
|
+
function readDisabledStore(accountDir) {
|
|
16971
|
+
try {
|
|
16972
|
+
return readDisabledStoreStrict(accountDir);
|
|
16973
|
+
} catch (err) {
|
|
16974
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
16975
|
+
console.error(`[admin/agents] op=disabled-store status=parse-failed reason="${msg}"`);
|
|
16976
|
+
return /* @__PURE__ */ new Set();
|
|
16977
|
+
}
|
|
16978
|
+
}
|
|
16979
|
+
function shippedSourceDirs(accountDir) {
|
|
16980
|
+
return [
|
|
16981
|
+
resolve23(accountDir, ...SPECIALISTS_DIR),
|
|
16982
|
+
resolve23(accountDir, ...QUARANTINE_DIR),
|
|
16983
|
+
resolve23(PLATFORM_ROOT, "templates", "specialists", "agents")
|
|
16984
|
+
];
|
|
16985
|
+
}
|
|
16986
|
+
function writeDisabledStore(accountDir, disabled) {
|
|
16987
|
+
writeFileSync13(
|
|
16988
|
+
resolve23(accountDir, DISABLED_STORE),
|
|
16989
|
+
`${JSON.stringify({ disabled: [...disabled].sort() }, null, 2)}
|
|
16990
|
+
`,
|
|
16991
|
+
{ mode: 384 }
|
|
16992
|
+
);
|
|
16993
|
+
}
|
|
16994
|
+
function listShipped(accountDir, riskByTool, disabled, riskSurfaceFailed) {
|
|
16995
|
+
const dirs = shippedSourceDirs(accountDir);
|
|
16996
|
+
const names = /* @__PURE__ */ new Set();
|
|
16997
|
+
for (const dir of dirs) {
|
|
16998
|
+
if (!existsSync31(dir)) continue;
|
|
16999
|
+
try {
|
|
17000
|
+
for (const entry of readdirSync22(dir)) if (entry.endsWith(".md")) names.add(entry);
|
|
17001
|
+
} catch (err) {
|
|
17002
|
+
console.error(`[admin/agents] op=list-shipped read-failed dir=${dir} error="${err}"`);
|
|
17003
|
+
}
|
|
17004
|
+
}
|
|
17005
|
+
const shipped = [];
|
|
17006
|
+
let shippedSkipped = 0;
|
|
17007
|
+
for (const filename of [...names].sort()) {
|
|
17008
|
+
const source = dirs.find((d) => existsSync31(resolve23(d, filename)));
|
|
17009
|
+
if (!source) continue;
|
|
17010
|
+
try {
|
|
17011
|
+
const { fm } = splitFrontmatter(readFileSync33(resolve23(source, filename), "utf-8"));
|
|
17012
|
+
if (!fm.name) {
|
|
17013
|
+
shippedSkipped++;
|
|
17014
|
+
console.error(`[admin/agents] op=list-shipped-skip file=${filename} reason=no-name`);
|
|
17015
|
+
continue;
|
|
17016
|
+
}
|
|
17017
|
+
const tools = parseToolsLine(fm.tools);
|
|
17018
|
+
const r = classify(tools, riskByTool, riskSurfaceFailed);
|
|
17019
|
+
shipped.push({
|
|
17020
|
+
slug: filename.replace(/\.md$/, ""),
|
|
17021
|
+
displayName: fm.name,
|
|
17022
|
+
kind: "specialist",
|
|
17023
|
+
origin: "shipped",
|
|
17024
|
+
plugin: "",
|
|
17025
|
+
description: fm.description ?? "",
|
|
17026
|
+
risk: r.risk,
|
|
17027
|
+
worstTool: r.worstTool,
|
|
17028
|
+
unresolved: r.unresolved,
|
|
17029
|
+
toolCount: tools.length,
|
|
17030
|
+
// The DIRECTORY is the ground truth, not the store. `spawn-context.ts`
|
|
17031
|
+
// reads the live dir, so a file found in quarantine is not dispatchable
|
|
17032
|
+
// whatever the JSON says, and a file found live IS dispatchable even if
|
|
17033
|
+
// the store claims otherwise. The store only answers for a bundled-only
|
|
17034
|
+
// agent, which has no account copy in either directory to look at.
|
|
17035
|
+
disabled: source === dirs[1] || source === dirs[2] && disabled.has(filename)
|
|
17036
|
+
});
|
|
17037
|
+
} catch (err) {
|
|
17038
|
+
shippedSkipped++;
|
|
17039
|
+
console.error(`[admin/agents] op=list-shipped-skip file=${filename} error="${err}"`);
|
|
17040
|
+
}
|
|
17041
|
+
}
|
|
17042
|
+
return { shipped, shippedSkipped };
|
|
17043
|
+
}
|
|
17044
|
+
function parseToolsLine(raw) {
|
|
17045
|
+
return raw ? raw.split(",").map((t) => t.trim()).filter(Boolean) : [];
|
|
17046
|
+
}
|
|
17047
|
+
function classify(tools, riskByTool, riskSurfaceFailed) {
|
|
17048
|
+
if (riskSurfaceFailed) {
|
|
17049
|
+
return { risk: "red", worstTool: null, unresolved: [], byTool: tools.map((t) => ({ tool: t, risk: null })) };
|
|
17050
|
+
}
|
|
17051
|
+
return agentRiskClass(tools, riskByTool);
|
|
17052
|
+
}
|
|
17053
|
+
function listSpecialists(accountDir, riskByTool, riskSurfaceFailed) {
|
|
16573
17054
|
const pluginsDir = resolve23(accountDir, "plugins");
|
|
16574
17055
|
const specialists = [];
|
|
16575
17056
|
let specialistsSkipped = 0;
|
|
16576
17057
|
for (const plugin of subdirs(pluginsDir).sort()) {
|
|
16577
17058
|
const agentsDirP = resolve23(pluginsDir, plugin, "agents");
|
|
16578
|
-
if (!
|
|
17059
|
+
if (!existsSync31(agentsDirP)) continue;
|
|
16579
17060
|
let entries2;
|
|
16580
17061
|
try {
|
|
16581
|
-
entries2 =
|
|
17062
|
+
entries2 = readdirSync22(agentsDirP, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
16582
17063
|
} catch {
|
|
16583
17064
|
continue;
|
|
16584
17065
|
}
|
|
16585
17066
|
for (const entry of entries2) {
|
|
16586
17067
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
16587
17068
|
try {
|
|
16588
|
-
const { fm } = splitFrontmatter(
|
|
17069
|
+
const { fm } = splitFrontmatter(readFileSync33(resolve23(agentsDirP, entry.name), "utf-8"));
|
|
16589
17070
|
if (!fm.name) {
|
|
16590
17071
|
specialistsSkipped++;
|
|
16591
17072
|
console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${entry.name}`);
|
|
16592
17073
|
continue;
|
|
16593
17074
|
}
|
|
17075
|
+
const specialistTools = parseToolsLine(fm.tools);
|
|
17076
|
+
const r = classify(specialistTools, riskByTool, riskSurfaceFailed);
|
|
16594
17077
|
specialists.push({
|
|
16595
17078
|
slug: entry.name.replace(/\.md$/, ""),
|
|
16596
17079
|
displayName: fm.name,
|
|
16597
17080
|
kind: "specialist",
|
|
17081
|
+
origin: "specialist",
|
|
16598
17082
|
plugin,
|
|
16599
|
-
description: fm.description ?? ""
|
|
17083
|
+
description: fm.description ?? "",
|
|
17084
|
+
risk: r.risk,
|
|
17085
|
+
worstTool: r.worstTool,
|
|
17086
|
+
unresolved: r.unresolved,
|
|
17087
|
+
toolCount: specialistTools.length,
|
|
17088
|
+
// A user-created specialist lives under the account plugins tree,
|
|
17089
|
+
// which provisioning never rewrites, so the quarantine mechanism
|
|
17090
|
+
// does not apply to it.
|
|
17091
|
+
disabled: false
|
|
16600
17092
|
});
|
|
16601
17093
|
} catch {
|
|
16602
17094
|
specialistsSkipped++;
|
|
@@ -16619,24 +17111,31 @@ app18.get("/", requireAdminSession, (c) => {
|
|
|
16619
17111
|
console.error("[admin/agents] op=list auth-rejected reason=no-account-for-session");
|
|
16620
17112
|
return c.json({ error: "Account not found for session" }, 401);
|
|
16621
17113
|
}
|
|
17114
|
+
const { riskByTool, failed: riskSurfaceFailed } = loadRiskSurface();
|
|
17115
|
+
const disabled = readDisabledStore(account.accountDir);
|
|
16622
17116
|
const agents = [];
|
|
16623
17117
|
let skipped = 0;
|
|
16624
17118
|
const agentsDir = resolve23(account.accountDir, "agents");
|
|
16625
|
-
if (
|
|
17119
|
+
if (existsSync31(agentsDir)) {
|
|
16626
17120
|
try {
|
|
16627
|
-
const entries2 =
|
|
17121
|
+
const entries2 = readdirSync22(agentsDir, { withFileTypes: true });
|
|
16628
17122
|
for (const entry of entries2.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
16629
17123
|
if (!entry.isDirectory()) continue;
|
|
16630
17124
|
if (entry.name === "admin") continue;
|
|
16631
17125
|
const configPath2 = resolve23(agentsDir, entry.name, "config.json");
|
|
16632
|
-
if (!
|
|
17126
|
+
if (!existsSync31(configPath2)) continue;
|
|
16633
17127
|
try {
|
|
16634
|
-
const config = JSON.parse(
|
|
17128
|
+
const config = JSON.parse(readFileSync33(configPath2, "utf-8"));
|
|
16635
17129
|
agents.push({
|
|
16636
17130
|
slug: entry.name,
|
|
16637
17131
|
displayName: config.displayName ?? entry.name,
|
|
16638
17132
|
status: config.status ?? "unknown",
|
|
16639
|
-
kind: "public"
|
|
17133
|
+
kind: "public",
|
|
17134
|
+
origin: "public",
|
|
17135
|
+
risk: null,
|
|
17136
|
+
worstTool: null,
|
|
17137
|
+
unresolved: [],
|
|
17138
|
+
disabled: false
|
|
16640
17139
|
});
|
|
16641
17140
|
} catch {
|
|
16642
17141
|
skipped++;
|
|
@@ -16647,9 +17146,25 @@ app18.get("/", requireAdminSession, (c) => {
|
|
|
16647
17146
|
console.error(`[admin/agents] failed to scan agents directory: ${err}`);
|
|
16648
17147
|
}
|
|
16649
17148
|
}
|
|
16650
|
-
const { specialists, specialistsSkipped } = listSpecialists(account.accountDir);
|
|
16651
|
-
|
|
16652
|
-
|
|
17149
|
+
const { specialists, specialistsSkipped } = listSpecialists(account.accountDir, riskByTool, riskSurfaceFailed !== null);
|
|
17150
|
+
const { shipped, shippedSkipped } = listShipped(account.accountDir, riskByTool, disabled, riskSurfaceFailed !== null);
|
|
17151
|
+
const rows = [...agents, ...specialists, ...shipped];
|
|
17152
|
+
const acct = account.accountId.slice(0, 8);
|
|
17153
|
+
for (const row of rows) {
|
|
17154
|
+
if (row.risk === null) continue;
|
|
17155
|
+
console.log(
|
|
17156
|
+
`[admin/agents] op=classify account=${acct} agent=${row.slug} origin=${row.origin} risk=${row.risk} worstTool=${row.worstTool ?? "none"} tools=${row.toolCount} unresolved=${row.unresolved.length > 0 ? row.unresolved.join(",") : "none"} disabled=${row.disabled}`
|
|
17157
|
+
);
|
|
17158
|
+
}
|
|
17159
|
+
console.log(`[admin/agents] op=list accountId=${acct} agents=${agents.length} specialists=${specialists.length} shipped=${shipped.length} skipped=${skipped} specialistsSkipped=${specialistsSkipped} shippedSkipped=${shippedSkipped}`);
|
|
17160
|
+
return c.json({
|
|
17161
|
+
agents: rows,
|
|
17162
|
+
accountId: account.accountId,
|
|
17163
|
+
skipped,
|
|
17164
|
+
specialistsSkipped,
|
|
17165
|
+
shippedSkipped,
|
|
17166
|
+
riskSurfaceFailed: riskSurfaceFailed !== null
|
|
17167
|
+
});
|
|
16653
17168
|
});
|
|
16654
17169
|
app18.get("/:slug", requireAdminSession, (c) => {
|
|
16655
17170
|
const slug = c.req.param("slug");
|
|
@@ -16659,34 +17174,72 @@ app18.get("/:slug", requireAdminSession, (c) => {
|
|
|
16659
17174
|
return c.json({ error: "Account not found for session" }, 401);
|
|
16660
17175
|
}
|
|
16661
17176
|
if (SLUG_UNSAFE(slug)) return c.json({ error: "Invalid agent slug" }, 400);
|
|
17177
|
+
if (c.req.query("origin") === "shipped") {
|
|
17178
|
+
const dirs = shippedSourceDirs(account.accountDir);
|
|
17179
|
+
const source = dirs.find((d) => existsSync31(resolve23(d, `${slug}.md`)));
|
|
17180
|
+
if (!source) return c.json({ error: "Agent not found" }, 404);
|
|
17181
|
+
const { fm, body } = splitFrontmatter(readFileSync33(resolve23(source, `${slug}.md`), "utf-8"));
|
|
17182
|
+
const { riskByTool, failed } = loadRiskSurface();
|
|
17183
|
+
const tools = parseToolsLine(fm.tools);
|
|
17184
|
+
const r = classify(tools, riskByTool, failed !== null);
|
|
17185
|
+
console.log(`[admin/agents] op=detail accountId=${account.accountId.slice(0, 8)} slug=${slug} kind=specialist origin=shipped risk=${r.risk}`);
|
|
17186
|
+
return c.json({
|
|
17187
|
+
agent: {
|
|
17188
|
+
kind: "specialist",
|
|
17189
|
+
origin: "shipped",
|
|
17190
|
+
slug,
|
|
17191
|
+
displayName: fm.name ?? slug,
|
|
17192
|
+
description: fm.description ?? "",
|
|
17193
|
+
summary: fm.summary ?? "",
|
|
17194
|
+
model: fm.model ?? null,
|
|
17195
|
+
tools,
|
|
17196
|
+
systemPrompt: body.trim(),
|
|
17197
|
+
skills: [],
|
|
17198
|
+
risk: r.risk,
|
|
17199
|
+
worstTool: r.worstTool,
|
|
17200
|
+
unresolved: r.unresolved,
|
|
17201
|
+
byTool: r.byTool,
|
|
17202
|
+
disabled: readDisabledStore(account.accountDir).has(`${slug}.md`)
|
|
17203
|
+
}
|
|
17204
|
+
});
|
|
17205
|
+
}
|
|
16662
17206
|
const pluginParam = c.req.query("plugin");
|
|
16663
17207
|
if (pluginParam) {
|
|
16664
17208
|
if (SLUG_UNSAFE(pluginParam)) return c.json({ error: "Invalid plugin" }, 400);
|
|
16665
17209
|
const file = resolve23(account.accountDir, "plugins", pluginParam, "agents", `${slug}.md`);
|
|
16666
|
-
if (!
|
|
16667
|
-
const { fm, body } = splitFrontmatter(
|
|
17210
|
+
if (!existsSync31(file)) return c.json({ error: "Agent not found" }, 404);
|
|
17211
|
+
const { fm, body } = splitFrontmatter(readFileSync33(file, "utf-8"));
|
|
16668
17212
|
const skills = subdirs(resolve23(account.accountDir, "plugins", pluginParam, "skills"));
|
|
16669
|
-
|
|
17213
|
+
const { riskByTool, failed } = loadRiskSurface();
|
|
17214
|
+
const tools = parseToolsLine(fm.tools);
|
|
17215
|
+
const r = classify(tools, riskByTool, failed !== null);
|
|
17216
|
+
console.log(`[admin/agents] op=detail accountId=${account.accountId.slice(0, 8)} slug=${slug} kind=specialist plugin=${pluginParam} risk=${r.risk}`);
|
|
16670
17217
|
return c.json({
|
|
16671
17218
|
agent: {
|
|
16672
17219
|
kind: "specialist",
|
|
17220
|
+
origin: "specialist",
|
|
16673
17221
|
slug,
|
|
16674
17222
|
displayName: fm.name ?? slug,
|
|
16675
17223
|
description: fm.description ?? "",
|
|
16676
17224
|
summary: fm.summary ?? "",
|
|
16677
17225
|
model: fm.model ?? null,
|
|
16678
|
-
tools
|
|
17226
|
+
tools,
|
|
16679
17227
|
systemPrompt: body.trim(),
|
|
16680
|
-
skills
|
|
17228
|
+
skills,
|
|
17229
|
+
risk: r.risk,
|
|
17230
|
+
worstTool: r.worstTool,
|
|
17231
|
+
unresolved: r.unresolved,
|
|
17232
|
+
byTool: r.byTool,
|
|
17233
|
+
disabled: false
|
|
16681
17234
|
}
|
|
16682
17235
|
});
|
|
16683
17236
|
}
|
|
16684
17237
|
const agentDir = resolve23(account.accountDir, "agents", slug);
|
|
16685
17238
|
const configPath2 = resolve23(agentDir, "config.json");
|
|
16686
|
-
if (!
|
|
17239
|
+
if (!existsSync31(configPath2)) return c.json({ error: "Agent not found" }, 404);
|
|
16687
17240
|
let config;
|
|
16688
17241
|
try {
|
|
16689
|
-
config = JSON.parse(
|
|
17242
|
+
config = JSON.parse(readFileSync33(configPath2, "utf-8"));
|
|
16690
17243
|
} catch {
|
|
16691
17244
|
console.error(`[admin/agents] op=detail slug=${slug} error="config.json parse failed"`);
|
|
16692
17245
|
return c.json({ error: "Agent config unreadable" }, 500);
|
|
@@ -16696,8 +17249,8 @@ app18.get("/:slug", requireAdminSession, (c) => {
|
|
|
16696
17249
|
for (const { file, key } of OWNED_DOCS) {
|
|
16697
17250
|
const p = resolve23(agentDir, file);
|
|
16698
17251
|
try {
|
|
16699
|
-
if (
|
|
16700
|
-
docs[key] =
|
|
17252
|
+
if (existsSync31(p)) {
|
|
17253
|
+
docs[key] = readFileSync33(p, "utf-8");
|
|
16701
17254
|
present[key] = true;
|
|
16702
17255
|
} else {
|
|
16703
17256
|
docs[key] = "";
|
|
@@ -16726,6 +17279,84 @@ app18.get("/:slug", requireAdminSession, (c) => {
|
|
|
16726
17279
|
}
|
|
16727
17280
|
});
|
|
16728
17281
|
});
|
|
17282
|
+
async function resolveWritableAccount(c, op, slug) {
|
|
17283
|
+
const accountIdParam = c.req.query("accountId") ?? "";
|
|
17284
|
+
if (!accountIdParam) {
|
|
17285
|
+
console.error(`[admin/agents] op=${op} rejected reason=missing-accountId slug=${slug}`);
|
|
17286
|
+
return { status: 400, error: "accountId is required" };
|
|
17287
|
+
}
|
|
17288
|
+
const account = validateAccountId2(accountIdParam);
|
|
17289
|
+
if (!account) {
|
|
17290
|
+
console.error(`[admin/agents] op=${op} rejected reason=unknown-accountId accountId=${accountIdParam.slice(0, 8)} slug=${slug}`);
|
|
17291
|
+
return { status: 400, error: "Unknown accountId" };
|
|
17292
|
+
}
|
|
17293
|
+
const authz = await authorizeAccountWrite(c, account.accountId);
|
|
17294
|
+
if (!authz.ok) {
|
|
17295
|
+
console.error(`[admin/agents] op=${op} rejected reason=${authz.reason} accountId=${account.accountId.slice(0, 8)} slug=${slug}`);
|
|
17296
|
+
return { status: 403, error: "Not authorized for this account" };
|
|
17297
|
+
}
|
|
17298
|
+
return account;
|
|
17299
|
+
}
|
|
17300
|
+
app18.post("/:slug/disable", async (c) => {
|
|
17301
|
+
const slug = c.req.param("slug");
|
|
17302
|
+
if (SLUG_UNSAFE(slug)) return c.json({ error: "Invalid agent slug" }, 400);
|
|
17303
|
+
const resolved = await resolveWritableAccount(c, "disable", slug);
|
|
17304
|
+
if ("status" in resolved) return c.json({ error: resolved.error }, resolved.status);
|
|
17305
|
+
const filename = `${slug}.md`;
|
|
17306
|
+
const live = resolve23(resolved.accountDir, ...SPECIALISTS_DIR, filename);
|
|
17307
|
+
const quarantined = resolve23(resolved.accountDir, ...QUARANTINE_DIR, filename);
|
|
17308
|
+
if (!shippedSourceDirs(resolved.accountDir).some((d) => existsSync31(resolve23(d, filename)))) {
|
|
17309
|
+
console.error(`[admin/agents] op=disable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=failed reason=not-a-shipped-agent`);
|
|
17310
|
+
return c.json({ error: "Only a shipped agent can be disabled" }, 404);
|
|
17311
|
+
}
|
|
17312
|
+
let moved = false;
|
|
17313
|
+
try {
|
|
17314
|
+
const disabled = readDisabledStoreStrict(resolved.accountDir);
|
|
17315
|
+
if (existsSync31(live)) {
|
|
17316
|
+
mkdirSync7(resolve23(resolved.accountDir, ...QUARANTINE_DIR), { recursive: true });
|
|
17317
|
+
renameSync9(live, quarantined);
|
|
17318
|
+
moved = true;
|
|
17319
|
+
}
|
|
17320
|
+
disabled.add(filename);
|
|
17321
|
+
writeDisabledStore(resolved.accountDir, disabled);
|
|
17322
|
+
} catch (err) {
|
|
17323
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
17324
|
+
console.error(`[admin/agents] op=disable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=failed reason="${msg}"`);
|
|
17325
|
+
return c.json({ error: `Disable failed, disabled store unreadable or unwritable: ${msg}` }, 500);
|
|
17326
|
+
}
|
|
17327
|
+
console.log(`[admin/agents] op=disable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=ok moved=${moved}`);
|
|
17328
|
+
return c.json({ ok: true, moved });
|
|
17329
|
+
});
|
|
17330
|
+
app18.post("/:slug/enable", async (c) => {
|
|
17331
|
+
const slug = c.req.param("slug");
|
|
17332
|
+
if (SLUG_UNSAFE(slug)) return c.json({ error: "Invalid agent slug" }, 400);
|
|
17333
|
+
const resolved = await resolveWritableAccount(c, "enable", slug);
|
|
17334
|
+
if ("status" in resolved) return c.json({ error: resolved.error }, resolved.status);
|
|
17335
|
+
const filename = `${slug}.md`;
|
|
17336
|
+
const live = resolve23(resolved.accountDir, ...SPECIALISTS_DIR, filename);
|
|
17337
|
+
const quarantined = resolve23(resolved.accountDir, ...QUARANTINE_DIR, filename);
|
|
17338
|
+
let restored = false;
|
|
17339
|
+
try {
|
|
17340
|
+
const disabled = readDisabledStoreStrict(resolved.accountDir);
|
|
17341
|
+
if (!existsSync31(quarantined) && !disabled.has(filename)) {
|
|
17342
|
+
console.error(`[admin/agents] op=enable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=failed reason=not-disabled`);
|
|
17343
|
+
return c.json({ error: "This agent is not disabled" }, 404);
|
|
17344
|
+
}
|
|
17345
|
+
if (existsSync31(quarantined)) {
|
|
17346
|
+
mkdirSync7(resolve23(resolved.accountDir, ...SPECIALISTS_DIR), { recursive: true });
|
|
17347
|
+
renameSync9(quarantined, live);
|
|
17348
|
+
restored = true;
|
|
17349
|
+
}
|
|
17350
|
+
disabled.delete(filename);
|
|
17351
|
+
writeDisabledStore(resolved.accountDir, disabled);
|
|
17352
|
+
} catch (err) {
|
|
17353
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
17354
|
+
console.error(`[admin/agents] op=enable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=failed reason="${msg}"`);
|
|
17355
|
+
return c.json({ error: `Enable failed: ${msg}` }, 500);
|
|
17356
|
+
}
|
|
17357
|
+
console.log(`[admin/agents] op=enable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=ok restored=${restored}`);
|
|
17358
|
+
return c.json({ ok: true, restored });
|
|
17359
|
+
});
|
|
16729
17360
|
app18.delete("/:slug", async (c) => {
|
|
16730
17361
|
const slug = c.req.param("slug");
|
|
16731
17362
|
if (slug === "admin") {
|
|
@@ -16750,7 +17381,7 @@ app18.delete("/:slug", async (c) => {
|
|
|
16750
17381
|
return c.json({ error: "Not authorized for this account" }, 403);
|
|
16751
17382
|
}
|
|
16752
17383
|
const agentDir = resolve23(account.accountDir, "agents", slug);
|
|
16753
|
-
if (!
|
|
17384
|
+
if (!existsSync31(agentDir)) {
|
|
16754
17385
|
return c.json({ error: "Agent not found" }, 404);
|
|
16755
17386
|
}
|
|
16756
17387
|
try {
|
|
@@ -16793,7 +17424,7 @@ app18.post("/:slug/project", async (c) => {
|
|
|
16793
17424
|
return c.json({ error: "Not authorized for this account" }, 403);
|
|
16794
17425
|
}
|
|
16795
17426
|
const agentDir = resolve23(account.accountDir, "agents", slug);
|
|
16796
|
-
if (!
|
|
17427
|
+
if (!existsSync31(agentDir)) {
|
|
16797
17428
|
return c.json({ error: "Agent not found on disk" }, 404);
|
|
16798
17429
|
}
|
|
16799
17430
|
try {
|
|
@@ -16809,7 +17440,7 @@ var agents_default = app18;
|
|
|
16809
17440
|
// server/routes/admin/sessions.ts
|
|
16810
17441
|
import crypto2 from "crypto";
|
|
16811
17442
|
import { resolve as resolvePath } from "path";
|
|
16812
|
-
import { existsSync as
|
|
17443
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync8, createWriteStream } from "fs";
|
|
16813
17444
|
|
|
16814
17445
|
// ../lib/admin-conversation-purge/src/index.ts
|
|
16815
17446
|
async function purgeAdminConversationJsonls(args) {
|
|
@@ -16956,7 +17587,7 @@ var pickComponentBytes = (..._args) => null;
|
|
|
16956
17587
|
function openResumeStreamLog(accountId, sessionId) {
|
|
16957
17588
|
const dir = resolvePath(ACCOUNTS_DIR, accountId, "resume-logs");
|
|
16958
17589
|
try {
|
|
16959
|
-
|
|
17590
|
+
mkdirSync8(dir, { recursive: true });
|
|
16960
17591
|
} catch {
|
|
16961
17592
|
}
|
|
16962
17593
|
return createWriteStream(resolvePath(dir, `${sessionId}.log`), { flags: "a" });
|
|
@@ -16969,7 +17600,7 @@ function validateAndShapeAttachments(raws, conversationAccountId, sessionId, mes
|
|
|
16969
17600
|
let reason = null;
|
|
16970
17601
|
if (!a.attachmentId || !a.filename || !a.mimeType || !a.storagePath) reason = "schema-fail";
|
|
16971
17602
|
else if (a.accountId !== conversationAccountId) reason = "account-mismatch";
|
|
16972
|
-
else if (!
|
|
17603
|
+
else if (!existsSync32(a.storagePath)) reason = "missing-file";
|
|
16973
17604
|
if (reason) {
|
|
16974
17605
|
invalid++;
|
|
16975
17606
|
try {
|
|
@@ -17495,8 +18126,8 @@ app19.put("/:id/label", requireAdminSession, async (c) => {
|
|
|
17495
18126
|
var sessions_default = app19;
|
|
17496
18127
|
|
|
17497
18128
|
// app/lib/claude-agent/spawn-context.ts
|
|
17498
|
-
import { existsSync as
|
|
17499
|
-
import { dirname as dirname12, resolve as resolve24, join as
|
|
18129
|
+
import { existsSync as existsSync33, readFileSync as readFileSync34, readdirSync as readdirSync23, statSync as statSync16 } from "fs";
|
|
18130
|
+
import { dirname as dirname12, resolve as resolve24, join as join34 } from "path";
|
|
17500
18131
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
17501
18132
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
17502
18133
|
try {
|
|
@@ -17537,14 +18168,14 @@ function frontmatterField(manifest, field2) {
|
|
|
17537
18168
|
function extractPluginToolDescriptions(pluginDir) {
|
|
17538
18169
|
const out = /* @__PURE__ */ new Map();
|
|
17539
18170
|
const candidates = [
|
|
17540
|
-
|
|
17541
|
-
|
|
18171
|
+
join34(pluginDir, "mcp/dist/index.js"),
|
|
18172
|
+
join34(pluginDir, "mcp/src/index.ts")
|
|
17542
18173
|
];
|
|
17543
18174
|
let raw = null;
|
|
17544
18175
|
for (const path3 of candidates) {
|
|
17545
|
-
if (!
|
|
18176
|
+
if (!existsSync33(path3)) continue;
|
|
17546
18177
|
try {
|
|
17547
|
-
raw =
|
|
18178
|
+
raw = readFileSync34(path3, "utf-8");
|
|
17548
18179
|
break;
|
|
17549
18180
|
} catch {
|
|
17550
18181
|
}
|
|
@@ -17594,25 +18225,25 @@ function parseSkillPathsFromFrontmatter(fm) {
|
|
|
17594
18225
|
function listPluginDirs(accountPluginsDir) {
|
|
17595
18226
|
const out = /* @__PURE__ */ new Map();
|
|
17596
18227
|
const platformPlugins = resolve24(PLATFORM_ROOT, "plugins");
|
|
17597
|
-
if (
|
|
17598
|
-
for (const name of
|
|
17599
|
-
const dir =
|
|
18228
|
+
if (existsSync33(platformPlugins)) {
|
|
18229
|
+
for (const name of readdirSync23(platformPlugins)) {
|
|
18230
|
+
const dir = join34(platformPlugins, name);
|
|
17600
18231
|
try {
|
|
17601
|
-
if (
|
|
18232
|
+
if (statSync16(dir).isDirectory()) out.set(name, dir);
|
|
17602
18233
|
} catch {
|
|
17603
18234
|
}
|
|
17604
18235
|
}
|
|
17605
18236
|
}
|
|
17606
18237
|
const premiumRoot = resolve24(dirname12(PLATFORM_ROOT), "premium-plugins");
|
|
17607
|
-
if (
|
|
17608
|
-
for (const bundle of
|
|
17609
|
-
const bundlePlugins =
|
|
17610
|
-
if (!
|
|
18238
|
+
if (existsSync33(premiumRoot)) {
|
|
18239
|
+
for (const bundle of readdirSync23(premiumRoot)) {
|
|
18240
|
+
const bundlePlugins = join34(premiumRoot, bundle, "plugins");
|
|
18241
|
+
if (!existsSync33(bundlePlugins)) continue;
|
|
17611
18242
|
try {
|
|
17612
|
-
for (const name of
|
|
17613
|
-
const dir =
|
|
18243
|
+
for (const name of readdirSync23(bundlePlugins)) {
|
|
18244
|
+
const dir = join34(bundlePlugins, name);
|
|
17614
18245
|
try {
|
|
17615
|
-
if (
|
|
18246
|
+
if (statSync16(dir).isDirectory()) out.set(name, dir);
|
|
17616
18247
|
} catch {
|
|
17617
18248
|
}
|
|
17618
18249
|
}
|
|
@@ -17620,13 +18251,13 @@ function listPluginDirs(accountPluginsDir) {
|
|
|
17620
18251
|
}
|
|
17621
18252
|
}
|
|
17622
18253
|
}
|
|
17623
|
-
if (accountPluginsDir &&
|
|
18254
|
+
if (accountPluginsDir && existsSync33(accountPluginsDir)) {
|
|
17624
18255
|
try {
|
|
17625
|
-
for (const name of
|
|
18256
|
+
for (const name of readdirSync23(accountPluginsDir)) {
|
|
17626
18257
|
if (out.has(name)) continue;
|
|
17627
|
-
const dir =
|
|
18258
|
+
const dir = join34(accountPluginsDir, name);
|
|
17628
18259
|
try {
|
|
17629
|
-
if (
|
|
18260
|
+
if (statSync16(dir).isDirectory()) out.set(name, dir);
|
|
17630
18261
|
} catch {
|
|
17631
18262
|
}
|
|
17632
18263
|
}
|
|
@@ -17639,11 +18270,11 @@ function accountPluginsDirFor(accountId) {
|
|
|
17639
18270
|
return resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "plugins");
|
|
17640
18271
|
}
|
|
17641
18272
|
function listAccountPluginNames(accountPluginsDir) {
|
|
17642
|
-
if (!
|
|
18273
|
+
if (!existsSync33(accountPluginsDir)) return [];
|
|
17643
18274
|
try {
|
|
17644
|
-
return
|
|
18275
|
+
return readdirSync23(accountPluginsDir).filter((name) => {
|
|
17645
18276
|
try {
|
|
17646
|
-
return
|
|
18277
|
+
return statSync16(join34(accountPluginsDir, name)).isDirectory();
|
|
17647
18278
|
} catch {
|
|
17648
18279
|
return false;
|
|
17649
18280
|
}
|
|
@@ -17654,9 +18285,9 @@ function listAccountPluginNames(accountPluginsDir) {
|
|
|
17654
18285
|
}
|
|
17655
18286
|
function readEnabledPlugins(accountId) {
|
|
17656
18287
|
const accountFile = resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
17657
|
-
if (!
|
|
18288
|
+
if (!existsSync33(accountFile)) return /* @__PURE__ */ new Set();
|
|
17658
18289
|
try {
|
|
17659
|
-
const parsed = JSON.parse(
|
|
18290
|
+
const parsed = JSON.parse(readFileSync34(accountFile, "utf-8"));
|
|
17660
18291
|
return new Set(
|
|
17661
18292
|
Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
|
|
17662
18293
|
);
|
|
@@ -17665,10 +18296,10 @@ function readEnabledPlugins(accountId) {
|
|
|
17665
18296
|
}
|
|
17666
18297
|
}
|
|
17667
18298
|
function readFrontmatter(path3) {
|
|
17668
|
-
if (!
|
|
18299
|
+
if (!existsSync33(path3)) return null;
|
|
17669
18300
|
let raw;
|
|
17670
18301
|
try {
|
|
17671
|
-
raw =
|
|
18302
|
+
raw = readFileSync34(path3, "utf-8");
|
|
17672
18303
|
} catch {
|
|
17673
18304
|
return null;
|
|
17674
18305
|
}
|
|
@@ -17686,7 +18317,7 @@ function computeActivePlugins(accountId) {
|
|
|
17686
18317
|
for (const name of emitNames) {
|
|
17687
18318
|
const dir = pluginDirs.get(name);
|
|
17688
18319
|
if (!dir) continue;
|
|
17689
|
-
const fm = readFrontmatter(
|
|
18320
|
+
const fm = readFrontmatter(join34(dir, "PLUGIN.md"));
|
|
17690
18321
|
if (!fm) continue;
|
|
17691
18322
|
const description = frontmatterField(`---
|
|
17692
18323
|
${fm}
|
|
@@ -17703,7 +18334,7 @@ ${fm}
|
|
|
17703
18334
|
`plugin-manifest-tool-coverage plugin=${name} tools=${tools.length} with-desc=${withDesc}`
|
|
17704
18335
|
);
|
|
17705
18336
|
if (toolNames.length > 0 && descMap.size === 0) {
|
|
17706
|
-
const hasSource =
|
|
18337
|
+
const hasSource = existsSync33(join34(dir, "mcp/dist/index.js")) || existsSync33(join34(dir, "mcp/src/index.ts"));
|
|
17707
18338
|
if (hasSource) {
|
|
17708
18339
|
console.warn(
|
|
17709
18340
|
`[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 +18344,7 @@ ${fm}
|
|
|
17713
18344
|
const skillPaths = parseSkillPathsFromFrontmatter(fm);
|
|
17714
18345
|
const skills = [];
|
|
17715
18346
|
for (const relPath of skillPaths) {
|
|
17716
|
-
const skillPath =
|
|
18347
|
+
const skillPath = join34(dir, relPath);
|
|
17717
18348
|
const skillFm = readFrontmatter(skillPath);
|
|
17718
18349
|
if (!skillFm) continue;
|
|
17719
18350
|
const skillName = frontmatterField(`---
|
|
@@ -17730,10 +18361,10 @@ ${skillFm}
|
|
|
17730
18361
|
}
|
|
17731
18362
|
function computeSpecialistDomains(accountId) {
|
|
17732
18363
|
const specialistsDir = resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
|
|
17733
|
-
if (!
|
|
18364
|
+
if (!existsSync33(specialistsDir)) return [];
|
|
17734
18365
|
let entries2;
|
|
17735
18366
|
try {
|
|
17736
|
-
entries2 =
|
|
18367
|
+
entries2 = readdirSync23(specialistsDir);
|
|
17737
18368
|
} catch {
|
|
17738
18369
|
return [];
|
|
17739
18370
|
}
|
|
@@ -17757,7 +18388,7 @@ function computeSpecialistDomains(accountId) {
|
|
|
17757
18388
|
const out = [];
|
|
17758
18389
|
for (const file of entries2.sort()) {
|
|
17759
18390
|
if (!file.endsWith(".md")) continue;
|
|
17760
|
-
const fm = readFrontmatter(
|
|
18391
|
+
const fm = readFrontmatter(join34(specialistsDir, file));
|
|
17761
18392
|
if (!fm) continue;
|
|
17762
18393
|
const wrapped = `---
|
|
17763
18394
|
${fm}
|
|
@@ -17776,10 +18407,10 @@ ${fm}
|
|
|
17776
18407
|
}
|
|
17777
18408
|
function computeDormantPlugins(accountId) {
|
|
17778
18409
|
const accountFile = resolve24(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
17779
|
-
if (!
|
|
18410
|
+
if (!existsSync33(accountFile)) return [];
|
|
17780
18411
|
let enabled;
|
|
17781
18412
|
try {
|
|
17782
|
-
const raw =
|
|
18413
|
+
const raw = readFileSync34(accountFile, "utf-8");
|
|
17783
18414
|
const parsed = JSON.parse(raw);
|
|
17784
18415
|
enabled = new Set(
|
|
17785
18416
|
Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
|
|
@@ -17791,10 +18422,10 @@ function computeDormantPlugins(accountId) {
|
|
|
17791
18422
|
return [];
|
|
17792
18423
|
}
|
|
17793
18424
|
const pluginsDir = resolve24(PLATFORM_ROOT, "plugins");
|
|
17794
|
-
if (!
|
|
18425
|
+
if (!existsSync33(pluginsDir)) return [];
|
|
17795
18426
|
let entries2;
|
|
17796
18427
|
try {
|
|
17797
|
-
entries2 =
|
|
18428
|
+
entries2 = readdirSync23(pluginsDir);
|
|
17798
18429
|
} catch {
|
|
17799
18430
|
return [];
|
|
17800
18431
|
}
|
|
@@ -17802,10 +18433,10 @@ function computeDormantPlugins(accountId) {
|
|
|
17802
18433
|
for (const name of entries2) {
|
|
17803
18434
|
if (enabled.has(name)) continue;
|
|
17804
18435
|
const manifestPath = resolve24(pluginsDir, name, "PLUGIN.md");
|
|
17805
|
-
if (!
|
|
18436
|
+
if (!existsSync33(manifestPath)) continue;
|
|
17806
18437
|
let manifest;
|
|
17807
18438
|
try {
|
|
17808
|
-
manifest =
|
|
18439
|
+
manifest = readFileSync34(manifestPath, "utf-8");
|
|
17809
18440
|
} catch {
|
|
17810
18441
|
continue;
|
|
17811
18442
|
}
|
|
@@ -17819,13 +18450,13 @@ function computeDormantPlugins(accountId) {
|
|
|
17819
18450
|
}
|
|
17820
18451
|
|
|
17821
18452
|
// server/routes/admin/claude-sessions.ts
|
|
17822
|
-
import { existsSync as
|
|
18453
|
+
import { existsSync as existsSync34, readFileSync as readFileSync35 } from "fs";
|
|
17823
18454
|
import { resolve as resolve25 } from "path";
|
|
17824
18455
|
function readTunnelState(brandConfigDir) {
|
|
17825
18456
|
const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
|
|
17826
|
-
if (!
|
|
18457
|
+
if (!existsSync34(statePath)) return null;
|
|
17827
18458
|
try {
|
|
17828
|
-
const parsed = JSON.parse(
|
|
18459
|
+
const parsed = JSON.parse(readFileSync35(statePath, "utf-8"));
|
|
17829
18460
|
const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
|
|
17830
18461
|
const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
|
|
17831
18462
|
const domain = typeof parsed.domain === "string" ? parsed.domain : null;
|
|
@@ -17839,7 +18470,7 @@ function resolveTunnelUrl() {
|
|
|
17839
18470
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
|
|
17840
18471
|
let brand;
|
|
17841
18472
|
try {
|
|
17842
|
-
brand = JSON.parse(
|
|
18473
|
+
brand = JSON.parse(readFileSync35(resolve25(platformRoot5, "config", "brand.json"), "utf-8"));
|
|
17843
18474
|
} catch {
|
|
17844
18475
|
return null;
|
|
17845
18476
|
}
|
|
@@ -18152,11 +18783,11 @@ app22.get("/", requireAdminSession, async (c) => {
|
|
|
18152
18783
|
var events_default = app22;
|
|
18153
18784
|
|
|
18154
18785
|
// server/routes/admin/files.ts
|
|
18155
|
-
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as
|
|
18786
|
+
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync35 } from "fs";
|
|
18156
18787
|
import { readdir as readdir4, readFile as readFile7, stat as stat6, mkdir as mkdir4, unlink as unlink3, rename as rename2, rmdir } from "fs/promises";
|
|
18157
18788
|
import { realpathSync as realpathSync7 } from "fs";
|
|
18158
18789
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
18159
|
-
import { basename as basename11, dirname as dirname13, join as
|
|
18790
|
+
import { basename as basename11, dirname as dirname13, join as join36, relative as relative5, resolve as resolve27, sep as sep8 } from "path";
|
|
18160
18791
|
import { Readable as Readable2 } from "stream";
|
|
18161
18792
|
|
|
18162
18793
|
// ../lib/graph-trash/src/index.ts
|
|
@@ -18474,7 +19105,7 @@ async function cascadeDeleteDocument(params) {
|
|
|
18474
19105
|
|
|
18475
19106
|
// app/lib/file-index.ts
|
|
18476
19107
|
import * as fsp from "fs/promises";
|
|
18477
|
-
import { resolve as resolve26, relative as relative4, join as
|
|
19108
|
+
import { resolve as resolve26, relative as relative4, join as join35, basename as basename10, extname as extname2, sep as sep7 } from "path";
|
|
18478
19109
|
import { tmpdir as tmpdir2 } from "os";
|
|
18479
19110
|
import { execFile as execFile2 } from "child_process";
|
|
18480
19111
|
import { promisify as promisify2 } from "util";
|
|
@@ -18556,7 +19187,7 @@ async function extractPdf(absolute) {
|
|
|
18556
19187
|
return stdout.trim();
|
|
18557
19188
|
}
|
|
18558
19189
|
async function ocrPdf(absolute) {
|
|
18559
|
-
const outPdf =
|
|
19190
|
+
const outPdf = join35(tmpdir2(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
|
|
18560
19191
|
try {
|
|
18561
19192
|
await execFileAsync2(
|
|
18562
19193
|
"ocrmypdf",
|
|
@@ -18618,7 +19249,7 @@ async function readDisplayName(absolute) {
|
|
|
18618
19249
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
18619
19250
|
if (base === `${stem}.meta.json`) return null;
|
|
18620
19251
|
try {
|
|
18621
|
-
const raw = await fsp.readFile(
|
|
19252
|
+
const raw = await fsp.readFile(join35(dir, `${stem}.meta.json`), "utf-8");
|
|
18622
19253
|
const meta = JSON.parse(raw);
|
|
18623
19254
|
const dn = meta.displayName ?? meta.filename;
|
|
18624
19255
|
return typeof dn === "string" && dn.length > 0 ? dn : null;
|
|
@@ -18639,7 +19270,7 @@ async function walkSubtree(root, dataRoot, out) {
|
|
|
18639
19270
|
for (const entry of entries2) {
|
|
18640
19271
|
if (entry.isSymbolicLink()) continue;
|
|
18641
19272
|
if (entry.isDirectory() && entry.name === ".uploads-tmp") continue;
|
|
18642
|
-
const abs =
|
|
19273
|
+
const abs = join35(root, entry.name);
|
|
18643
19274
|
if (entry.isDirectory()) {
|
|
18644
19275
|
const sub = await walkSubtree(abs, dataRoot, out);
|
|
18645
19276
|
if (!sub.clean) clean = false;
|
|
@@ -19012,7 +19643,7 @@ var UPLOAD_TMP_DIR = ".uploads-tmp";
|
|
|
19012
19643
|
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
19644
|
async function readMeta(absDir, baseName) {
|
|
19014
19645
|
try {
|
|
19015
|
-
const raw = await readFile7(
|
|
19646
|
+
const raw = await readFile7(join36(absDir, `${baseName}.meta.json`), "utf8");
|
|
19016
19647
|
const parsed = JSON.parse(raw);
|
|
19017
19648
|
if (typeof parsed?.filename === "string") {
|
|
19018
19649
|
return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
|
|
@@ -19067,7 +19698,7 @@ async function servableFilesIn(dirAbs, uuid) {
|
|
|
19067
19698
|
}
|
|
19068
19699
|
async function enrich(absolute, entry, accountNames) {
|
|
19069
19700
|
if (entry.kind === "directory" && UUID_RE4.test(entry.name)) {
|
|
19070
|
-
const dirAbs =
|
|
19701
|
+
const dirAbs = join36(absolute, entry.name);
|
|
19071
19702
|
const meta = await readMeta(dirAbs, entry.name);
|
|
19072
19703
|
if (meta?.filename) {
|
|
19073
19704
|
const servable = await servableFilesIn(dirAbs, entry.name);
|
|
@@ -19076,7 +19707,7 @@ async function enrich(absolute, entry, accountNames) {
|
|
|
19076
19707
|
let size = null;
|
|
19077
19708
|
let modifiedAt = entry.modifiedAt;
|
|
19078
19709
|
try {
|
|
19079
|
-
const st = await stat6(
|
|
19710
|
+
const st = await stat6(join36(dirAbs, innerName));
|
|
19080
19711
|
size = st.size;
|
|
19081
19712
|
modifiedAt = st.mtime.toISOString();
|
|
19082
19713
|
} catch {
|
|
@@ -19269,7 +19900,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
19269
19900
|
const childRel = relPath === "" || relPath === "." ? name : `${relPath}/${name}`;
|
|
19270
19901
|
const protectedEntry = isProtectedFromDeletion(childRel).protected;
|
|
19271
19902
|
try {
|
|
19272
|
-
const entryPath =
|
|
19903
|
+
const entryPath = join36(absolute, name);
|
|
19273
19904
|
const s = await stat6(entryPath);
|
|
19274
19905
|
entries2.push({
|
|
19275
19906
|
name,
|
|
@@ -19298,7 +19929,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
19298
19929
|
const displayPath = buildDisplayPath(relPath, accountNames);
|
|
19299
19930
|
if (groupReq || isAccountRoot(relPath)) {
|
|
19300
19931
|
const acctId = relPath.split("/")[1] ?? accountId;
|
|
19301
|
-
const schemaMd = await readFile7(
|
|
19932
|
+
const schemaMd = await readFile7(join36(absolute, "SCHEMA.md"), "utf8").catch(() => null);
|
|
19302
19933
|
const groups = classifyAccountRoot(schemaMd, entries2.map((e) => e.name));
|
|
19303
19934
|
console.error(
|
|
19304
19935
|
`[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 +19967,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
19336
19967
|
for (const name of homeDirs) {
|
|
19337
19968
|
const base = byName.get(name);
|
|
19338
19969
|
try {
|
|
19339
|
-
const dirents = await readdir4(
|
|
19970
|
+
const dirents = await readdir4(join36(absolute, name), { withFileTypes: true });
|
|
19340
19971
|
const visible = dirents.filter((d) => !d.name.startsWith("."));
|
|
19341
19972
|
visible.sort((a, b) => {
|
|
19342
19973
|
const ad = a.isDirectory() ? 0 : 1;
|
|
@@ -19401,7 +20032,7 @@ app23.get("/download", requireAdminSession, async (c) => {
|
|
|
19401
20032
|
console.error(`[data] op=scratchpad-no-session sessionId="${sessionId.slice(0, 8)}\u2026"`);
|
|
19402
20033
|
return c.json({ error: "Not found" }, 404);
|
|
19403
20034
|
}
|
|
19404
|
-
const meta = readSidecarMeta(
|
|
20035
|
+
const meta = readSidecarMeta(join36(projectDir, `${sessionId}.meta.json`));
|
|
19405
20036
|
if (!meta.accountId || meta.accountId !== accountId) {
|
|
19406
20037
|
console.error(`[data] account-scope-blocked root="scratchpad" sessionId="${sessionId.slice(0, 8)}\u2026" account=${accountId.slice(0, 8)}\u2026`);
|
|
19407
20038
|
return c.json({ error: "Not found" }, 404);
|
|
@@ -19496,7 +20127,7 @@ async function* walkRegularFiles(dirAbsolute) {
|
|
|
19496
20127
|
const dirents = await readdir4(dirAbsolute, { withFileTypes: true });
|
|
19497
20128
|
for (const d of dirents) {
|
|
19498
20129
|
if (d.isSymbolicLink()) continue;
|
|
19499
|
-
const child =
|
|
20130
|
+
const child = join36(dirAbsolute, d.name);
|
|
19500
20131
|
if (d.isDirectory()) {
|
|
19501
20132
|
yield* walkRegularFiles(child);
|
|
19502
20133
|
continue;
|
|
@@ -19606,7 +20237,7 @@ async function resolveUploadTarget(c, accountId, uid) {
|
|
|
19606
20237
|
const base = resolveOwnAccountWrite(rawDir, accountId, "POST /api/admin/files/upload");
|
|
19607
20238
|
if (!base.ok) return { ok: false, status: base.status, error: base.error };
|
|
19608
20239
|
const relDir = relpath ? dirname13(relpath) : ".";
|
|
19609
|
-
const destRel = relDir === "." ? base.relative :
|
|
20240
|
+
const destRel = relDir === "." ? base.relative : join36(base.relative, relDir);
|
|
19610
20241
|
if (crossesForeignAccountPartition(destRel, accountId)) {
|
|
19611
20242
|
console.error(`[data] account-scope-blocked endpoint="POST /api/admin/files/upload" path="${destRel}" account=${accountId.slice(0, 8)}\u2026`);
|
|
19612
20243
|
return { ok: false, status: 404, error: "Not found" };
|
|
@@ -19680,7 +20311,7 @@ app23.post("/upload", requireAdminSession, async (c) => {
|
|
|
19680
20311
|
});
|
|
19681
20312
|
await unlink3(tmpPath).catch(() => {
|
|
19682
20313
|
});
|
|
19683
|
-
const tempRemoved = !
|
|
20314
|
+
const tempRemoved = !existsSync35(tmpPath);
|
|
19684
20315
|
console.error(`[data-upload] op=cleanup uid=${uid} tempRemoved=${tempRemoved}`);
|
|
19685
20316
|
};
|
|
19686
20317
|
const reader = body.getReader();
|
|
@@ -19754,7 +20385,7 @@ async function discardChunkUpload(uploadId, state) {
|
|
|
19754
20385
|
});
|
|
19755
20386
|
await unlink3(state.tmpPath).catch(() => {
|
|
19756
20387
|
});
|
|
19757
|
-
const tempRemoved = !
|
|
20388
|
+
const tempRemoved = !existsSync35(state.tmpPath);
|
|
19758
20389
|
console.error(`[data-upload] op=cleanup uid=${uploadId} tempRemoved=${tempRemoved}`);
|
|
19759
20390
|
}
|
|
19760
20391
|
async function writeChunkBody(body, out, alreadyReceived, ceiling) {
|
|
@@ -19983,7 +20614,7 @@ app23.delete("/", requireAdminSession, async (c) => {
|
|
|
19983
20614
|
const dot = base.lastIndexOf(".");
|
|
19984
20615
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
19985
20616
|
const containerDir = dirname13(absolute);
|
|
19986
|
-
const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ?
|
|
20617
|
+
const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ? join36(containerDir, `${stem}.meta.json`) : null;
|
|
19987
20618
|
const isAttachmentContainer = UUID_RE4.test(stem) && basename11(containerDir) === stem;
|
|
19988
20619
|
await unlink3(absolute);
|
|
19989
20620
|
if (sidecarPath) {
|
|
@@ -19993,7 +20624,7 @@ app23.delete("/", requireAdminSession, async (c) => {
|
|
|
19993
20624
|
}
|
|
19994
20625
|
}
|
|
19995
20626
|
if (isAttachmentContainer) {
|
|
19996
|
-
await unlink3(
|
|
20627
|
+
await unlink3(join36(containerDir, `${stem}${EXTRACTED_TEXT_SUFFIX}`)).catch(() => {
|
|
19997
20628
|
});
|
|
19998
20629
|
await rmdir(containerDir).catch(() => {
|
|
19999
20630
|
});
|
|
@@ -20172,7 +20803,7 @@ function applyBacklinkBoost(hits) {
|
|
|
20172
20803
|
}
|
|
20173
20804
|
|
|
20174
20805
|
// ../lib/graph-search/src/dedup.ts
|
|
20175
|
-
import { createHash as
|
|
20806
|
+
import { createHash as createHash6 } from "crypto";
|
|
20176
20807
|
var DEFAULT_LAYERS = [
|
|
20177
20808
|
"nodeId",
|
|
20178
20809
|
"slug",
|
|
@@ -20199,7 +20830,7 @@ function readContentHash(props) {
|
|
|
20199
20830
|
const content = props["content"];
|
|
20200
20831
|
const source = typeof compiled === "string" && compiled.length > 0 ? compiled : typeof content === "string" && content.length > 0 ? content : null;
|
|
20201
20832
|
if (source === null) return null;
|
|
20202
|
-
return
|
|
20833
|
+
return createHash6("sha256").update(source).digest("hex").slice(0, 16);
|
|
20203
20834
|
}
|
|
20204
20835
|
function dedup(hits, layers = DEFAULT_LAYERS) {
|
|
20205
20836
|
const sorted = [...hits].sort((a, b) => b.score - a.score);
|
|
@@ -21102,8 +21733,8 @@ function isHiddenByDefault(label) {
|
|
|
21102
21733
|
|
|
21103
21734
|
// server/lib/top-level-labels.ts
|
|
21104
21735
|
var import_dist5 = __toESM(require_dist4(), 1);
|
|
21105
|
-
import { readdirSync as
|
|
21106
|
-
import { join as
|
|
21736
|
+
import { readdirSync as readdirSync24, readFileSync as readFileSync36 } from "fs";
|
|
21737
|
+
import { join as join37, resolve as resolve28 } from "path";
|
|
21107
21738
|
var STATIC_TOP_LEVEL_LABELS = Object.freeze(
|
|
21108
21739
|
/* @__PURE__ */ new Set([
|
|
21109
21740
|
// Base-schema infra (schema-base.md has no top-level section).
|
|
@@ -21207,7 +21838,7 @@ function parseTopLevelLabels(content) {
|
|
|
21207
21838
|
}
|
|
21208
21839
|
function resolveReferencesDir() {
|
|
21209
21840
|
if (process.env.MAXY_PLATFORM_ROOT) {
|
|
21210
|
-
return
|
|
21841
|
+
return join37(process.env.MAXY_PLATFORM_ROOT, "plugins", "memory", "references");
|
|
21211
21842
|
}
|
|
21212
21843
|
return resolve28(import.meta.dirname, "../../../plugins/memory/references");
|
|
21213
21844
|
}
|
|
@@ -21215,9 +21846,9 @@ function parseTableTopLevelLabels(referencesDir) {
|
|
|
21215
21846
|
const labels = /* @__PURE__ */ new Set();
|
|
21216
21847
|
const contributingFiles = [];
|
|
21217
21848
|
try {
|
|
21218
|
-
const fileNames =
|
|
21849
|
+
const fileNames = readdirSync24(referencesDir).filter((f) => f.startsWith("schema-") && f.endsWith(".md")).sort();
|
|
21219
21850
|
for (const fileName of fileNames) {
|
|
21220
|
-
const content =
|
|
21851
|
+
const content = readFileSync36(join37(referencesDir, fileName), "utf-8");
|
|
21221
21852
|
const parsed = parseTopLevelLabels(content);
|
|
21222
21853
|
if (parsed.length > 0) contributingFiles.push(fileName);
|
|
21223
21854
|
for (const label of parsed) labels.add(label);
|
|
@@ -22375,7 +23006,7 @@ var graph_default_view_default = app29;
|
|
|
22375
23006
|
// server/routes/admin/sidebar-artefacts.ts
|
|
22376
23007
|
import { readdir as readdir5, stat as stat7 } from "fs/promises";
|
|
22377
23008
|
import { resolve as resolve29, relative as relative6, isAbsolute as isAbsolute2, sep as sep9, basename as basename12 } from "path";
|
|
22378
|
-
import { existsSync as
|
|
23009
|
+
import { existsSync as existsSync36 } from "fs";
|
|
22379
23010
|
var LIMIT = 50;
|
|
22380
23011
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
22381
23012
|
function toDataRootRelPath(absPath) {
|
|
@@ -22487,7 +23118,7 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
22487
23118
|
async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
22488
23119
|
const names = /* @__PURE__ */ new Set();
|
|
22489
23120
|
for (const dir of [overrideDir, bundledDir]) {
|
|
22490
|
-
if (!
|
|
23121
|
+
if (!existsSync36(dir)) continue;
|
|
22491
23122
|
try {
|
|
22492
23123
|
const entries2 = await readdir5(dir);
|
|
22493
23124
|
for (const entry of entries2) {
|
|
@@ -22502,7 +23133,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
22502
23133
|
}
|
|
22503
23134
|
async function readAgentTemplateRow(inp) {
|
|
22504
23135
|
let chosenPath = null;
|
|
22505
|
-
if (
|
|
23136
|
+
if (existsSync36(inp.overridePath)) {
|
|
22506
23137
|
try {
|
|
22507
23138
|
validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
|
|
22508
23139
|
chosenPath = inp.overridePath;
|
|
@@ -22513,7 +23144,7 @@ async function readAgentTemplateRow(inp) {
|
|
|
22513
23144
|
);
|
|
22514
23145
|
return null;
|
|
22515
23146
|
}
|
|
22516
|
-
} else if (
|
|
23147
|
+
} else if (existsSync36(inp.bundledPath)) {
|
|
22517
23148
|
if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
|
|
22518
23149
|
console.error(
|
|
22519
23150
|
`[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
|
|
@@ -22555,19 +23186,19 @@ function isWithin(target, root) {
|
|
|
22555
23186
|
var sidebar_artefacts_default = app30;
|
|
22556
23187
|
|
|
22557
23188
|
// server/channel-session-override.ts
|
|
22558
|
-
import { readFileSync as
|
|
22559
|
-
import { join as
|
|
23189
|
+
import { readFileSync as readFileSync37, writeFileSync as writeFileSync14, renameSync as renameSync10 } from "fs";
|
|
23190
|
+
import { join as join38 } from "path";
|
|
22560
23191
|
var FILE2 = "channel-session-override.json";
|
|
22561
23192
|
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
23193
|
function channelOverridePath(accountDir) {
|
|
22563
|
-
return
|
|
23194
|
+
return join38(accountDir, FILE2);
|
|
22564
23195
|
}
|
|
22565
23196
|
function senderKey(channel, senderId) {
|
|
22566
23197
|
return `${channel}:${senderId}`;
|
|
22567
23198
|
}
|
|
22568
23199
|
function readRaw2(accountDir) {
|
|
22569
23200
|
try {
|
|
22570
|
-
return JSON.parse(
|
|
23201
|
+
return JSON.parse(readFileSync37(channelOverridePath(accountDir), "utf8"));
|
|
22571
23202
|
} catch {
|
|
22572
23203
|
return null;
|
|
22573
23204
|
}
|
|
@@ -22589,8 +23220,8 @@ function writeChannelOverrideId(accountDir, channel, senderId, sessionId) {
|
|
|
22589
23220
|
bySender[senderKey(channel, senderId)] = sessionId;
|
|
22590
23221
|
const path3 = channelOverridePath(accountDir);
|
|
22591
23222
|
const tmp = `${path3}.${process.pid}.tmp`;
|
|
22592
|
-
|
|
22593
|
-
|
|
23223
|
+
writeFileSync14(tmp, JSON.stringify({ bySender }, null, 2), "utf8");
|
|
23224
|
+
renameSync10(tmp, path3);
|
|
22594
23225
|
}
|
|
22595
23226
|
function clearChannelOverrideForSession(accountDir, sessionId) {
|
|
22596
23227
|
const raw = readRaw2(accountDir);
|
|
@@ -22605,8 +23236,8 @@ function clearChannelOverrideForSession(accountDir, sessionId) {
|
|
|
22605
23236
|
if (cleared.length === 0) return [];
|
|
22606
23237
|
const path3 = channelOverridePath(accountDir);
|
|
22607
23238
|
const tmp = `${path3}.${process.pid}.tmp`;
|
|
22608
|
-
|
|
22609
|
-
|
|
23239
|
+
writeFileSync14(tmp, JSON.stringify({ bySender: kept }, null, 2), "utf8");
|
|
23240
|
+
renameSync10(tmp, path3);
|
|
22610
23241
|
return cleared;
|
|
22611
23242
|
}
|
|
22612
23243
|
|
|
@@ -22956,24 +23587,24 @@ var session_rc_spawn_default = app36;
|
|
|
22956
23587
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
22957
23588
|
|
|
22958
23589
|
// server/wa-descriptor.ts
|
|
22959
|
-
import { resolve as resolve30, join as
|
|
23590
|
+
import { resolve as resolve30, join as join39 } from "path";
|
|
22960
23591
|
function waGatewayUrl() {
|
|
22961
23592
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
22962
23593
|
}
|
|
22963
23594
|
function waServerPath() {
|
|
22964
|
-
return process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve30(process.env.MAXY_PLATFORM_ROOT ??
|
|
23595
|
+
return process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve30(process.env.MAXY_PLATFORM_ROOT ?? join39(__dirname, ".."), "services/whatsapp-channel/dist/server.js");
|
|
22965
23596
|
}
|
|
22966
23597
|
function waChannelDescriptor(senderId) {
|
|
22967
23598
|
return { senderId, gatewayUrl: waGatewayUrl(), serverPath: waServerPath() };
|
|
22968
23599
|
}
|
|
22969
23600
|
|
|
22970
23601
|
// server/telegram-descriptor.ts
|
|
22971
|
-
import { resolve as resolve31, join as
|
|
23602
|
+
import { resolve as resolve31, join as join40 } from "path";
|
|
22972
23603
|
function telegramGatewayUrl() {
|
|
22973
23604
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
22974
23605
|
}
|
|
22975
23606
|
function telegramServerPath() {
|
|
22976
|
-
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve31(process.env.MAXY_PLATFORM_ROOT ??
|
|
23607
|
+
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve31(process.env.MAXY_PLATFORM_ROOT ?? join40(__dirname, ".."), "services/telegram-channel/dist/server.js");
|
|
22977
23608
|
}
|
|
22978
23609
|
function telegramChannelDescriptor(senderId) {
|
|
22979
23610
|
return { senderId, gatewayUrl: telegramGatewayUrl(), serverPath: telegramServerPath() };
|
|
@@ -23257,14 +23888,14 @@ app38.get("/", async (c) => {
|
|
|
23257
23888
|
var system_stats_default = app38;
|
|
23258
23889
|
|
|
23259
23890
|
// server/routes/admin/health.ts
|
|
23260
|
-
import { existsSync as
|
|
23261
|
-
import { resolve as resolve32, join as
|
|
23891
|
+
import { existsSync as existsSync37, readFileSync as readFileSync38 } from "fs";
|
|
23892
|
+
import { resolve as resolve32, join as join41 } from "path";
|
|
23262
23893
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve32(process.cwd(), "..");
|
|
23263
23894
|
var brandHostname = "maxy";
|
|
23264
|
-
var brandJsonPath =
|
|
23265
|
-
if (
|
|
23895
|
+
var brandJsonPath = join41(PLATFORM_ROOT6, "config", "brand.json");
|
|
23896
|
+
if (existsSync37(brandJsonPath)) {
|
|
23266
23897
|
try {
|
|
23267
|
-
const brand = JSON.parse(
|
|
23898
|
+
const brand = JSON.parse(readFileSync38(brandJsonPath, "utf-8"));
|
|
23268
23899
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
23269
23900
|
} catch {
|
|
23270
23901
|
}
|
|
@@ -23273,8 +23904,8 @@ var VERSION_FILE = resolve32(PLATFORM_ROOT6, `config/.${brandHostname}-version`)
|
|
|
23273
23904
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
23274
23905
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
23275
23906
|
function readVersion() {
|
|
23276
|
-
if (!
|
|
23277
|
-
return
|
|
23907
|
+
if (!existsSync37(VERSION_FILE)) return "unknown";
|
|
23908
|
+
return readFileSync38(VERSION_FILE, "utf-8").trim() || "unknown";
|
|
23278
23909
|
}
|
|
23279
23910
|
async function probeConversationDb() {
|
|
23280
23911
|
let session;
|
|
@@ -24050,8 +24681,8 @@ app46.post("/launch", requireAdminSession, async (c) => {
|
|
|
24050
24681
|
var browser_default = app46;
|
|
24051
24682
|
|
|
24052
24683
|
// server/routes/admin/calendar.ts
|
|
24053
|
-
import { existsSync as
|
|
24054
|
-
import { join as
|
|
24684
|
+
import { existsSync as existsSync38 } from "fs";
|
|
24685
|
+
import { join as join43 } from "path";
|
|
24055
24686
|
|
|
24056
24687
|
// server/lib/calendar-ics.ts
|
|
24057
24688
|
var CRLF = "\r\n";
|
|
@@ -24150,8 +24781,8 @@ function countVEvents(ics) {
|
|
|
24150
24781
|
}
|
|
24151
24782
|
|
|
24152
24783
|
// server/lib/calendar-availability.ts
|
|
24153
|
-
import { readFileSync as
|
|
24154
|
-
import { join as
|
|
24784
|
+
import { readFileSync as readFileSync39 } from "fs";
|
|
24785
|
+
import { join as join42 } from "path";
|
|
24155
24786
|
var AVAILABILITY_FILENAME = "calendar-availability.json";
|
|
24156
24787
|
var REQUIRED_KEYS = [
|
|
24157
24788
|
"timezone",
|
|
@@ -24160,10 +24791,10 @@ var REQUIRED_KEYS = [
|
|
|
24160
24791
|
"weekly"
|
|
24161
24792
|
];
|
|
24162
24793
|
function readAvailabilityConfig(accountDir) {
|
|
24163
|
-
const path3 =
|
|
24794
|
+
const path3 = join42(accountDir, AVAILABILITY_FILENAME);
|
|
24164
24795
|
let raw;
|
|
24165
24796
|
try {
|
|
24166
|
-
raw =
|
|
24797
|
+
raw = readFileSync39(path3, "utf-8");
|
|
24167
24798
|
} catch {
|
|
24168
24799
|
throw new Error(`availability not configured: ${path3} not found`);
|
|
24169
24800
|
}
|
|
@@ -24522,7 +25153,7 @@ app47.get("/booking-link", requireAdminSession, (c) => {
|
|
|
24522
25153
|
console.error('[calendar] op=booking-link-fail err="no account configured"');
|
|
24523
25154
|
return c.json({ bookingDomain: null });
|
|
24524
25155
|
}
|
|
24525
|
-
if (!
|
|
25156
|
+
if (!existsSync38(join43(account.accountDir, AVAILABILITY_FILENAME))) {
|
|
24526
25157
|
console.log("[calendar] op=booking-link bookingDomain=none source=availability-config");
|
|
24527
25158
|
return c.json({ bookingDomain: null });
|
|
24528
25159
|
}
|
|
@@ -24724,7 +25355,7 @@ app48.delete("/:eventId", requireAdminSession, async (c) => {
|
|
|
24724
25355
|
var routines_default = app48;
|
|
24725
25356
|
|
|
24726
25357
|
// server/routes/admin/skills.ts
|
|
24727
|
-
import { existsSync as
|
|
25358
|
+
import { existsSync as existsSync39, readdirSync as readdirSync25, readFileSync as readFileSync40, rmSync as rmSync3 } from "fs";
|
|
24728
25359
|
import { resolve as resolve33 } from "path";
|
|
24729
25360
|
var app49 = new Hono();
|
|
24730
25361
|
var NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
@@ -24748,9 +25379,9 @@ function stripQuotes(s) {
|
|
|
24748
25379
|
return s;
|
|
24749
25380
|
}
|
|
24750
25381
|
function subdirs2(dir) {
|
|
24751
|
-
if (!
|
|
25382
|
+
if (!existsSync39(dir)) return [];
|
|
24752
25383
|
try {
|
|
24753
|
-
return
|
|
25384
|
+
return readdirSync25(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
24754
25385
|
} catch {
|
|
24755
25386
|
return [];
|
|
24756
25387
|
}
|
|
@@ -24771,11 +25402,11 @@ app49.get("/", requireAdminSession, async (c) => {
|
|
|
24771
25402
|
const skillsDir = resolve33(pluginsDir, plugin, "skills");
|
|
24772
25403
|
for (const skill of subdirs2(skillsDir)) {
|
|
24773
25404
|
const skillMd = resolve33(skillsDir, skill, "SKILL.md");
|
|
24774
|
-
if (!
|
|
25405
|
+
if (!existsSync39(skillMd)) continue;
|
|
24775
25406
|
try {
|
|
24776
|
-
const { frontmatter } = splitSkillMd(
|
|
25407
|
+
const { frontmatter } = splitSkillMd(readFileSync40(skillMd, "utf-8"));
|
|
24777
25408
|
const refsDir = resolve33(skillsDir, skill, "references");
|
|
24778
|
-
const refCount =
|
|
25409
|
+
const refCount = existsSync39(refsDir) ? readdirSync25(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).length : 0;
|
|
24779
25410
|
skills.push({ plugin, skill, description: frontmatter.description, refCount });
|
|
24780
25411
|
} catch (err) {
|
|
24781
25412
|
console.error(`[admin:skills] op=list-skip plugin=${plugin} skill=${skill} reason="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -24799,13 +25430,13 @@ app49.get("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
24799
25430
|
}
|
|
24800
25431
|
const skillDir = resolve33(acct.accountDir, "plugins", plugin, "skills", skill);
|
|
24801
25432
|
const skillMd = resolve33(skillDir, "SKILL.md");
|
|
24802
|
-
const found =
|
|
25433
|
+
const found = existsSync39(skillMd);
|
|
24803
25434
|
console.log(`[admin:skills] op=get plugin=${plugin} skill=${skill} found=${found}`);
|
|
24804
25435
|
if (!found) return c.json({ error: "Skill not found" }, 404);
|
|
24805
25436
|
try {
|
|
24806
|
-
const { frontmatter, body } = splitSkillMd(
|
|
25437
|
+
const { frontmatter, body } = splitSkillMd(readFileSync40(skillMd, "utf-8"));
|
|
24807
25438
|
const refsDir = resolve33(skillDir, "references");
|
|
24808
|
-
const references =
|
|
25439
|
+
const references = existsSync39(refsDir) ? readdirSync25(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).sort() : [];
|
|
24809
25440
|
return c.json({ skill: { plugin, skill, frontmatter, body, references } });
|
|
24810
25441
|
} catch (err) {
|
|
24811
25442
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -24825,7 +25456,7 @@ app49.delete("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
24825
25456
|
const pluginDir = resolve33(acct.accountDir, "plugins", plugin);
|
|
24826
25457
|
const skillsDir = resolve33(pluginDir, "skills");
|
|
24827
25458
|
const skillDir = resolve33(skillsDir, skill);
|
|
24828
|
-
if (!
|
|
25459
|
+
if (!existsSync39(resolve33(skillDir, "SKILL.md"))) {
|
|
24829
25460
|
console.error(`[admin:skills] op=delete outcome=rejected reason=not-found plugin=${plugin} skill=${skill}`);
|
|
24830
25461
|
return c.json({ error: "Skill not found" }, 404);
|
|
24831
25462
|
}
|
|
@@ -24833,7 +25464,7 @@ app49.delete("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
24833
25464
|
rmSync3(skillDir, { recursive: true, force: true });
|
|
24834
25465
|
const pluginDirRemoved = subdirs2(skillsDir).length === 0;
|
|
24835
25466
|
if (pluginDirRemoved) rmSync3(pluginDir, { recursive: true, force: true });
|
|
24836
|
-
const skillDirGone = !
|
|
25467
|
+
const skillDirGone = !existsSync39(skillDir);
|
|
24837
25468
|
if (!skillDirGone) {
|
|
24838
25469
|
console.error(`[admin:skills] op=delete plugin=${plugin} skill=${skill} outcome=rejected reason=rm-incomplete skillDirGone=false`);
|
|
24839
25470
|
return c.json({ error: "Delete did not remove the skill directory" }, 503);
|
|
@@ -25243,20 +25874,20 @@ app54.post("/", requireAdminSession, async (c) => {
|
|
|
25243
25874
|
var task_time_adjust_default = app54;
|
|
25244
25875
|
|
|
25245
25876
|
// app/lib/admin-roster.ts
|
|
25246
|
-
import { existsSync as
|
|
25877
|
+
import { existsSync as existsSync40, readdirSync as readdirSync26, readFileSync as readFileSync41 } from "fs";
|
|
25247
25878
|
import { resolve as resolve34 } from "path";
|
|
25248
25879
|
function readHouseAdminUserIds(accountsDir = ACCOUNTS_DIR) {
|
|
25249
25880
|
let entries2;
|
|
25250
25881
|
try {
|
|
25251
|
-
entries2 =
|
|
25882
|
+
entries2 = readdirSync26(accountsDir);
|
|
25252
25883
|
} catch {
|
|
25253
25884
|
return [];
|
|
25254
25885
|
}
|
|
25255
25886
|
for (const entry of entries2) {
|
|
25256
25887
|
const cfgPath = resolve34(accountsDir, entry, "account.json");
|
|
25257
|
-
if (!
|
|
25888
|
+
if (!existsSync40(cfgPath)) continue;
|
|
25258
25889
|
try {
|
|
25259
|
-
const cfg = JSON.parse(
|
|
25890
|
+
const cfg = JSON.parse(readFileSync41(cfgPath, "utf-8"));
|
|
25260
25891
|
if (cfg.role !== "house") continue;
|
|
25261
25892
|
if (!Array.isArray(cfg.admins)) return [];
|
|
25262
25893
|
return cfg.admins.map(
|
|
@@ -25435,7 +26066,7 @@ app56.get("/", requireAdminSession, async (c) => {
|
|
|
25435
26066
|
var admin_users_list_default = app56;
|
|
25436
26067
|
|
|
25437
26068
|
// server/routes/admin/activity.ts
|
|
25438
|
-
import { readdirSync as
|
|
26069
|
+
import { readdirSync as readdirSync27 } from "fs";
|
|
25439
26070
|
var app57 = new Hono();
|
|
25440
26071
|
var TAG47 = "[activity]";
|
|
25441
26072
|
function resolveHouseAccountId(accounts) {
|
|
@@ -25451,7 +26082,7 @@ function requestToken() {
|
|
|
25451
26082
|
var short = (id) => id ? id.slice(0, 8) : "unset";
|
|
25452
26083
|
function countAccountDirsOnDisk() {
|
|
25453
26084
|
try {
|
|
25454
|
-
return
|
|
26085
|
+
return readdirSync27(ACCOUNTS_DIR, { withFileTypes: true }).filter(
|
|
25455
26086
|
(e) => e.isDirectory() && !e.name.startsWith(".")
|
|
25456
26087
|
).length;
|
|
25457
26088
|
} catch {
|
|
@@ -25855,7 +26486,7 @@ app61.route("/request-magic-link", request_magic_link_default);
|
|
|
25855
26486
|
var access_default = app61;
|
|
25856
26487
|
|
|
25857
26488
|
// server/routes/sites.ts
|
|
25858
|
-
import { existsSync as
|
|
26489
|
+
import { existsSync as existsSync41, readFileSync as readFileSync42, realpathSync as realpathSync8, statSync as statSync18 } from "fs";
|
|
25859
26490
|
import { resolve as resolve36 } from "path";
|
|
25860
26491
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
25861
26492
|
var MIME = {
|
|
@@ -25921,7 +26552,7 @@ app62.get("/:rel{.*}", (c) => {
|
|
|
25921
26552
|
}
|
|
25922
26553
|
let stat11;
|
|
25923
26554
|
try {
|
|
25924
|
-
stat11 =
|
|
26555
|
+
stat11 = existsSync41(filePath) ? statSync18(filePath) : null;
|
|
25925
26556
|
} catch {
|
|
25926
26557
|
stat11 = null;
|
|
25927
26558
|
}
|
|
@@ -25940,7 +26571,7 @@ app62.get("/:rel{.*}", (c) => {
|
|
|
25940
26571
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
25941
26572
|
return c.text("Forbidden", 403);
|
|
25942
26573
|
}
|
|
25943
|
-
if (!
|
|
26574
|
+
if (!existsSync41(filePath)) {
|
|
25944
26575
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
25945
26576
|
return c.text("Not found", 404);
|
|
25946
26577
|
}
|
|
@@ -25959,7 +26590,7 @@ app62.get("/:rel{.*}", (c) => {
|
|
|
25959
26590
|
}
|
|
25960
26591
|
let body;
|
|
25961
26592
|
try {
|
|
25962
|
-
body =
|
|
26593
|
+
body = readFileSync42(realPath);
|
|
25963
26594
|
} catch (err) {
|
|
25964
26595
|
const code = err?.code;
|
|
25965
26596
|
if (code === "EISDIR") {
|
|
@@ -25995,7 +26626,7 @@ var sites_default = app62;
|
|
|
25995
26626
|
|
|
25996
26627
|
// app/lib/visitor-token.ts
|
|
25997
26628
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
25998
|
-
import { mkdirSync as
|
|
26629
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync43, writeFileSync as writeFileSync15 } from "fs";
|
|
25999
26630
|
import { dirname as dirname14 } from "path";
|
|
26000
26631
|
var TOKEN_PREFIX = "v1.";
|
|
26001
26632
|
var SECRET_BYTES = 32;
|
|
@@ -26004,7 +26635,7 @@ var cachedSecret = null;
|
|
|
26004
26635
|
function getSecret() {
|
|
26005
26636
|
if (cachedSecret) return cachedSecret;
|
|
26006
26637
|
try {
|
|
26007
|
-
const hex2 =
|
|
26638
|
+
const hex2 = readFileSync43(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
26008
26639
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
26009
26640
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
26010
26641
|
return cachedSecret;
|
|
@@ -26013,12 +26644,12 @@ function getSecret() {
|
|
|
26013
26644
|
}
|
|
26014
26645
|
const fresh = randomBytes(SECRET_BYTES).toString("hex");
|
|
26015
26646
|
try {
|
|
26016
|
-
|
|
26017
|
-
|
|
26647
|
+
mkdirSync9(dirname14(VISITOR_TOKEN_SECRET_FILE), { recursive: true, mode: 448 });
|
|
26648
|
+
writeFileSync15(VISITOR_TOKEN_SECRET_FILE, fresh, { mode: 384, flag: "wx" });
|
|
26018
26649
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
26019
26650
|
} catch {
|
|
26020
26651
|
}
|
|
26021
|
-
const hex =
|
|
26652
|
+
const hex = readFileSync43(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
26022
26653
|
cachedSecret = Buffer.from(hex, "hex");
|
|
26023
26654
|
return cachedSecret;
|
|
26024
26655
|
}
|
|
@@ -26071,8 +26702,8 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
26071
26702
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
26072
26703
|
|
|
26073
26704
|
// app/lib/brand-config.ts
|
|
26074
|
-
import { existsSync as
|
|
26075
|
-
import { join as
|
|
26705
|
+
import { existsSync as existsSync42, readFileSync as readFileSync44 } from "fs";
|
|
26706
|
+
import { join as join44 } from "path";
|
|
26076
26707
|
var cached2 = null;
|
|
26077
26708
|
var cachedAttempted = false;
|
|
26078
26709
|
function readBrandConfig() {
|
|
@@ -26080,10 +26711,10 @@ function readBrandConfig() {
|
|
|
26080
26711
|
cachedAttempted = true;
|
|
26081
26712
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
|
|
26082
26713
|
if (!platformRoot5) return null;
|
|
26083
|
-
const brandPath =
|
|
26084
|
-
if (!
|
|
26714
|
+
const brandPath = join44(platformRoot5, "config", "brand.json");
|
|
26715
|
+
if (!existsSync42(brandPath)) return null;
|
|
26085
26716
|
try {
|
|
26086
|
-
cached2 = JSON.parse(
|
|
26717
|
+
cached2 = JSON.parse(readFileSync44(brandPath, "utf-8"));
|
|
26087
26718
|
return cached2;
|
|
26088
26719
|
} catch {
|
|
26089
26720
|
return null;
|
|
@@ -26581,13 +27212,13 @@ var visitor_event_default = app65;
|
|
|
26581
27212
|
|
|
26582
27213
|
// server/routes/session.ts
|
|
26583
27214
|
import { resolve as resolve37 } from "path";
|
|
26584
|
-
import { existsSync as
|
|
27215
|
+
import { existsSync as existsSync43, writeFileSync as writeFileSync16, mkdirSync as mkdirSync10 } from "fs";
|
|
26585
27216
|
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
27217
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
26587
27218
|
try {
|
|
26588
27219
|
const cacheDir = resolve37(MAXY_DIR, "branding-cache", accountId);
|
|
26589
|
-
|
|
26590
|
-
|
|
27220
|
+
mkdirSync10(cacheDir, { recursive: true });
|
|
27221
|
+
writeFileSync16(resolve37(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
26591
27222
|
} catch (err) {
|
|
26592
27223
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
26593
27224
|
}
|
|
@@ -26665,7 +27296,7 @@ app66.post("/", async (c) => {
|
|
|
26665
27296
|
let agentConfig = null;
|
|
26666
27297
|
if (account) {
|
|
26667
27298
|
const agentDir = resolve37(account.accountDir, "agents", agentSlug);
|
|
26668
|
-
if (!
|
|
27299
|
+
if (!existsSync43(agentDir) || !existsSync43(resolve37(agentDir, "config.json"))) {
|
|
26669
27300
|
return c.json({ error: "Agent not found" }, 404);
|
|
26670
27301
|
}
|
|
26671
27302
|
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
@@ -26925,7 +27556,7 @@ var calendar_public_default = app67;
|
|
|
26925
27556
|
// server/routes/portal-fetch.ts
|
|
26926
27557
|
import { createReadStream as createReadStream3 } from "fs";
|
|
26927
27558
|
import { stat as stat8, readFile as readFile9, realpath } from "fs/promises";
|
|
26928
|
-
import { join as
|
|
27559
|
+
import { join as join45, resolve as resolve38, sep as sep10 } from "path";
|
|
26929
27560
|
import { Readable as Readable3 } from "stream";
|
|
26930
27561
|
var app68 = new Hono();
|
|
26931
27562
|
var TAG50 = "[portal-fetch]";
|
|
@@ -26998,7 +27629,7 @@ function isValidAccountId2(value) {
|
|
|
26998
27629
|
}
|
|
26999
27630
|
async function readExposeFolders2(accountDir) {
|
|
27000
27631
|
try {
|
|
27001
|
-
const cfg = JSON.parse(await readFile9(
|
|
27632
|
+
const cfg = JSON.parse(await readFile9(join45(accountDir, "data-portal.json"), "utf8"));
|
|
27002
27633
|
return Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === "string") : [];
|
|
27003
27634
|
} catch {
|
|
27004
27635
|
return [];
|
|
@@ -27007,7 +27638,7 @@ async function readExposeFolders2(accountDir) {
|
|
|
27007
27638
|
async function resolveFetchTarget(accountDir, relPath, ownerId) {
|
|
27008
27639
|
let schemaText = null;
|
|
27009
27640
|
try {
|
|
27010
|
-
schemaText = await readFile9(
|
|
27641
|
+
schemaText = await readFile9(join45(accountDir, "SCHEMA.md"), "utf8");
|
|
27011
27642
|
} catch {
|
|
27012
27643
|
schemaText = null;
|
|
27013
27644
|
}
|
|
@@ -27177,8 +27808,8 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
27177
27808
|
}
|
|
27178
27809
|
|
|
27179
27810
|
// app/lib/ledger-census.ts
|
|
27180
|
-
import { readdirSync as
|
|
27181
|
-
import { join as
|
|
27811
|
+
import { readdirSync as readdirSync28, readFileSync as readFileSync45, statSync as statSync19 } from "fs";
|
|
27812
|
+
import { join as join46 } from "path";
|
|
27182
27813
|
|
|
27183
27814
|
// ../lib/ledger-core/dist/reconcile.js
|
|
27184
27815
|
var LEDGER_INDEX_NAMES = [
|
|
@@ -27226,16 +27857,16 @@ var DAY_MS = 864e5;
|
|
|
27226
27857
|
function countWriteRejects24h(logDir, now) {
|
|
27227
27858
|
let entries2;
|
|
27228
27859
|
try {
|
|
27229
|
-
entries2 =
|
|
27860
|
+
entries2 = readdirSync28(logDir).filter((f) => f.startsWith("mcp-ledger-") && f.endsWith(".log"));
|
|
27230
27861
|
} catch {
|
|
27231
27862
|
return 0;
|
|
27232
27863
|
}
|
|
27233
27864
|
let total = 0;
|
|
27234
27865
|
for (const name of entries2) {
|
|
27235
|
-
const path3 =
|
|
27866
|
+
const path3 = join46(logDir, name);
|
|
27236
27867
|
try {
|
|
27237
|
-
if (now -
|
|
27238
|
-
for (const line of
|
|
27868
|
+
if (now - statSync19(path3).mtimeMs > DAY_MS) continue;
|
|
27869
|
+
for (const line of readFileSync45(path3, "utf8").split("\n")) {
|
|
27239
27870
|
if (line.includes("[graph-write] reject")) total += 1;
|
|
27240
27871
|
}
|
|
27241
27872
|
} catch {
|
|
@@ -27730,8 +28361,8 @@ async function migrateUploads(opts = {}) {
|
|
|
27730
28361
|
|
|
27731
28362
|
// app/lib/migrate-admin-webchat-sidecars.ts
|
|
27732
28363
|
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
|
|
28364
|
+
import { existsSync as existsSync44 } from "fs";
|
|
28365
|
+
import { join as join47 } from "path";
|
|
27735
28366
|
var ADMIN_ROLE2 = "admin";
|
|
27736
28367
|
var WEBCHAT_CHANNEL2 = "webchat";
|
|
27737
28368
|
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 +28391,7 @@ async function writeRaw(path3, obj) {
|
|
|
27760
28391
|
await rename4(tmp, path3);
|
|
27761
28392
|
} catch (err) {
|
|
27762
28393
|
try {
|
|
27763
|
-
if (
|
|
28394
|
+
if (existsSync44(tmp)) await unlink4(tmp);
|
|
27764
28395
|
} catch {
|
|
27765
28396
|
}
|
|
27766
28397
|
throw err;
|
|
@@ -27776,7 +28407,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
27776
28407
|
}
|
|
27777
28408
|
for (const slug of slugs) {
|
|
27778
28409
|
if (!slug.isDirectory()) continue;
|
|
27779
|
-
const slugDir =
|
|
28410
|
+
const slugDir = join47(projectsRoot, slug.name);
|
|
27780
28411
|
let entries2;
|
|
27781
28412
|
try {
|
|
27782
28413
|
entries2 = await readdir7(slugDir, { withFileTypes: true });
|
|
@@ -27785,9 +28416,9 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
27785
28416
|
}
|
|
27786
28417
|
for (const entry of entries2) {
|
|
27787
28418
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
27788
|
-
out.push(
|
|
28419
|
+
out.push(join47(slugDir, entry.name));
|
|
27789
28420
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
27790
|
-
const subDir =
|
|
28421
|
+
const subDir = join47(slugDir, entry.name);
|
|
27791
28422
|
let subs;
|
|
27792
28423
|
try {
|
|
27793
28424
|
subs = await readdir7(subDir, { withFileTypes: true });
|
|
@@ -27795,7 +28426,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
27795
28426
|
continue;
|
|
27796
28427
|
}
|
|
27797
28428
|
for (const s of subs) {
|
|
27798
|
-
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(
|
|
28429
|
+
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join47(subDir, s.name));
|
|
27799
28430
|
}
|
|
27800
28431
|
}
|
|
27801
28432
|
}
|
|
@@ -27807,7 +28438,7 @@ function shortId(path3) {
|
|
|
27807
28438
|
return base.slice(0, 8);
|
|
27808
28439
|
}
|
|
27809
28440
|
async function migrateAdminWebchatSidecars(opts = {}) {
|
|
27810
|
-
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ?
|
|
28441
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join47(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
27811
28442
|
const usersFile = opts.usersFile ?? USERS_FILE;
|
|
27812
28443
|
const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
|
|
27813
28444
|
const resolveName = opts.resolveName ?? defaultResolveName;
|
|
@@ -30510,14 +31141,14 @@ function warnOnChannelAdminBindingDrift() {
|
|
|
30510
31141
|
}
|
|
30511
31142
|
|
|
30512
31143
|
// server/lib/booking-site-accounts.ts
|
|
30513
|
-
import { existsSync as
|
|
31144
|
+
import { existsSync as existsSync45, readFileSync as readFileSync46 } from "fs";
|
|
30514
31145
|
import { resolve as resolve43 } from "path";
|
|
30515
31146
|
function listBookingSiteAccounts(accounts) {
|
|
30516
31147
|
return accounts.filter((a) => {
|
|
30517
31148
|
const availPath = resolve43(a.accountDir, "calendar-availability.json");
|
|
30518
|
-
if (!
|
|
31149
|
+
if (!existsSync45(availPath)) return false;
|
|
30519
31150
|
try {
|
|
30520
|
-
const cfg = JSON.parse(
|
|
31151
|
+
const cfg = JSON.parse(readFileSync46(availPath, "utf-8"));
|
|
30521
31152
|
return typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
30522
31153
|
} catch {
|
|
30523
31154
|
return false;
|
|
@@ -30554,8 +31185,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
30554
31185
|
}
|
|
30555
31186
|
|
|
30556
31187
|
// ../lib/entitlement/src/index.ts
|
|
30557
|
-
import { createPublicKey, createHash as
|
|
30558
|
-
import { existsSync as
|
|
31188
|
+
import { createPublicKey, createHash as createHash7, verify as cryptoVerify } from "crypto";
|
|
31189
|
+
import { existsSync as existsSync46, readFileSync as readFileSync47, statSync as statSync20 } from "fs";
|
|
30559
31190
|
import { resolve as resolve44 } from "path";
|
|
30560
31191
|
|
|
30561
31192
|
// ../lib/entitlement/src/canonicalize.ts
|
|
@@ -30604,10 +31235,10 @@ function resolveEntitlement(brand, account) {
|
|
|
30604
31235
|
return logResolved(implicitTrust(account), null);
|
|
30605
31236
|
}
|
|
30606
31237
|
const entitlementPath = resolve44(brand.configDir, "entitlement.json");
|
|
30607
|
-
if (!
|
|
31238
|
+
if (!existsSync46(entitlementPath)) {
|
|
30608
31239
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
30609
31240
|
}
|
|
30610
|
-
const stat11 =
|
|
31241
|
+
const stat11 = statSync20(entitlementPath);
|
|
30611
31242
|
const key = memoKey(stat11.mtimeMs, account);
|
|
30612
31243
|
if (memo && memo.key === key) {
|
|
30613
31244
|
return memo.result;
|
|
@@ -30619,13 +31250,13 @@ function resolveEntitlement(brand, account) {
|
|
|
30619
31250
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
30620
31251
|
let pubkeyPem;
|
|
30621
31252
|
try {
|
|
30622
|
-
pubkeyPem =
|
|
31253
|
+
pubkeyPem = readFileSync47(pubkeyPath(brand), "utf-8");
|
|
30623
31254
|
} catch (err) {
|
|
30624
31255
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
30625
31256
|
reason: "pubkey-missing"
|
|
30626
31257
|
});
|
|
30627
31258
|
}
|
|
30628
|
-
const pubkeyHash =
|
|
31259
|
+
const pubkeyHash = createHash7("sha256").update(pubkeyPem).digest("hex");
|
|
30629
31260
|
if (pubkeyHash !== PUBKEY_SHA256) {
|
|
30630
31261
|
return logResolved(anonymousFallback("pubkey-tamper"), {
|
|
30631
31262
|
reason: "pubkey-tamper"
|
|
@@ -30633,7 +31264,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
30633
31264
|
}
|
|
30634
31265
|
let envelope;
|
|
30635
31266
|
try {
|
|
30636
|
-
envelope = JSON.parse(
|
|
31267
|
+
envelope = JSON.parse(readFileSync47(entitlementPath, "utf-8"));
|
|
30637
31268
|
} catch {
|
|
30638
31269
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
30639
31270
|
}
|
|
@@ -30795,14 +31426,14 @@ function clientFrom(c) {
|
|
|
30795
31426
|
}
|
|
30796
31427
|
installMediaDownloadGuard();
|
|
30797
31428
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
30798
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT8 ?
|
|
31429
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join48(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
30799
31430
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
30800
|
-
if (BRAND_JSON_PATH && !
|
|
31431
|
+
if (BRAND_JSON_PATH && !existsSync47(BRAND_JSON_PATH)) {
|
|
30801
31432
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
30802
31433
|
}
|
|
30803
|
-
if (BRAND_JSON_PATH &&
|
|
31434
|
+
if (BRAND_JSON_PATH && existsSync47(BRAND_JSON_PATH)) {
|
|
30804
31435
|
try {
|
|
30805
|
-
const parsed = JSON.parse(
|
|
31436
|
+
const parsed = JSON.parse(readFileSync48(BRAND_JSON_PATH, "utf-8"));
|
|
30806
31437
|
BRAND = { ...BRAND, ...parsed };
|
|
30807
31438
|
} catch (err) {
|
|
30808
31439
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -30834,11 +31465,11 @@ var brandLoginOpts = {
|
|
|
30834
31465
|
appleTouchIconPath: brandAppIcon512Path,
|
|
30835
31466
|
themeColor: BRAND.defaultColors?.primary
|
|
30836
31467
|
};
|
|
30837
|
-
var ALIAS_DOMAINS_PATH =
|
|
31468
|
+
var ALIAS_DOMAINS_PATH = join48(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
30838
31469
|
function loadAliasDomains() {
|
|
30839
31470
|
try {
|
|
30840
|
-
if (!
|
|
30841
|
-
const parsed = JSON.parse(
|
|
31471
|
+
if (!existsSync47(ALIAS_DOMAINS_PATH)) return null;
|
|
31472
|
+
const parsed = JSON.parse(readFileSync48(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
30842
31473
|
if (!Array.isArray(parsed)) {
|
|
30843
31474
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
30844
31475
|
return null;
|
|
@@ -30913,7 +31544,7 @@ var waGateway = new WaGateway({
|
|
|
30913
31544
|
fetchStandingRules: fetchAccountStandingRules,
|
|
30914
31545
|
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
30915
31546
|
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 ??
|
|
31547
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve45(process.env.MAXY_PLATFORM_ROOT ?? join48(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
30917
31548
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
30918
31549
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
30919
31550
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -30928,13 +31559,13 @@ var waGateway = new WaGateway({
|
|
|
30928
31559
|
caption,
|
|
30929
31560
|
accountId,
|
|
30930
31561
|
maxyAccountId,
|
|
30931
|
-
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ??
|
|
31562
|
+
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ?? join48(__dirname, ".."))
|
|
30932
31563
|
});
|
|
30933
31564
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
30934
31565
|
},
|
|
30935
31566
|
ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId, ownerSelfPhone, replyContext }) => {
|
|
30936
31567
|
if (passive) {
|
|
30937
|
-
const phoneHash =
|
|
31568
|
+
const phoneHash = createHash8("sha256").update(senderId).digest("hex").slice(0, 12);
|
|
30938
31569
|
const prompt = (text ?? "").trim();
|
|
30939
31570
|
if (prompt.length === 0) {
|
|
30940
31571
|
console.error(`[wa-passive] op=dropped account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"} reason=empty-text`);
|
|
@@ -31046,7 +31677,7 @@ function runDuplicateSenderAudit() {
|
|
|
31046
31677
|
try {
|
|
31047
31678
|
const configDir2 = process.env.CLAUDE_CONFIG_DIR;
|
|
31048
31679
|
if (!configDir2) return;
|
|
31049
|
-
const projectsRoot =
|
|
31680
|
+
const projectsRoot = join48(configDir2, "projects");
|
|
31050
31681
|
const records = [];
|
|
31051
31682
|
for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
|
|
31052
31683
|
if (isSubagent || archived) continue;
|
|
@@ -31621,14 +32252,14 @@ app69.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
31621
32252
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
31622
32253
|
return c.text("Forbidden", 403);
|
|
31623
32254
|
}
|
|
31624
|
-
if (!
|
|
32255
|
+
if (!existsSync47(filePath)) {
|
|
31625
32256
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
31626
32257
|
return c.text("Not found", 404);
|
|
31627
32258
|
}
|
|
31628
32259
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
31629
32260
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
31630
32261
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
31631
|
-
const body =
|
|
32262
|
+
const body = readFileSync48(filePath);
|
|
31632
32263
|
return c.body(body, 200, {
|
|
31633
32264
|
"Content-Type": contentType,
|
|
31634
32265
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -31651,14 +32282,14 @@ app69.get("/generated/:filename", (c) => {
|
|
|
31651
32282
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
31652
32283
|
return c.text("Forbidden", 403);
|
|
31653
32284
|
}
|
|
31654
|
-
if (!
|
|
32285
|
+
if (!existsSync47(filePath)) {
|
|
31655
32286
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
31656
32287
|
return c.text("Not found", 404);
|
|
31657
32288
|
}
|
|
31658
32289
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
31659
32290
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
31660
32291
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
31661
|
-
const body =
|
|
32292
|
+
const body = readFileSync48(filePath);
|
|
31662
32293
|
return c.body(body, 200, {
|
|
31663
32294
|
"Content-Type": contentType,
|
|
31664
32295
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -31671,9 +32302,9 @@ app69.route("/v", visitor_consent_default);
|
|
|
31671
32302
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
31672
32303
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
31673
32304
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
31674
|
-
if (BRAND_JSON_PATH &&
|
|
32305
|
+
if (BRAND_JSON_PATH && existsSync47(BRAND_JSON_PATH)) {
|
|
31675
32306
|
try {
|
|
31676
|
-
const fullBrand = JSON.parse(
|
|
32307
|
+
const fullBrand = JSON.parse(readFileSync48(BRAND_JSON_PATH, "utf-8"));
|
|
31677
32308
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
31678
32309
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
31679
32310
|
} catch {
|
|
@@ -31700,11 +32331,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
31700
32331
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
31701
32332
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
31702
32333
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
31703
|
-
(p) =>
|
|
32334
|
+
(p) => existsSync47(resolve45(process.cwd(), "public", p.replace(/^\//, "")))
|
|
31704
32335
|
);
|
|
31705
32336
|
var SW_SOURCE = (() => {
|
|
31706
32337
|
try {
|
|
31707
|
-
return
|
|
32338
|
+
return readFileSync48(resolve45(process.cwd(), "public", "sw.js"), "utf-8");
|
|
31708
32339
|
} catch {
|
|
31709
32340
|
return null;
|
|
31710
32341
|
}
|
|
@@ -31712,9 +32343,9 @@ var SW_SOURCE = (() => {
|
|
|
31712
32343
|
function readInstalledVersion() {
|
|
31713
32344
|
try {
|
|
31714
32345
|
if (!PLATFORM_ROOT8) return "unknown";
|
|
31715
|
-
const versionFile =
|
|
31716
|
-
if (!
|
|
31717
|
-
const content =
|
|
32346
|
+
const versionFile = join48(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
32347
|
+
if (!existsSync47(versionFile)) return "unknown";
|
|
32348
|
+
const content = readFileSync48(versionFile, "utf-8").trim();
|
|
31718
32349
|
return content || "unknown";
|
|
31719
32350
|
} catch {
|
|
31720
32351
|
return "unknown";
|
|
@@ -31755,7 +32386,7 @@ var clientErrorReporterScript = `<script>
|
|
|
31755
32386
|
function cachedHtml(file) {
|
|
31756
32387
|
let html = htmlCache.get(file);
|
|
31757
32388
|
if (!html) {
|
|
31758
|
-
html =
|
|
32389
|
+
html = readFileSync48(resolve45(process.cwd(), "public", file), "utf-8");
|
|
31759
32390
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
31760
32391
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
31761
32392
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -31772,13 +32403,13 @@ ${clientErrorReporterScript}
|
|
|
31772
32403
|
return html;
|
|
31773
32404
|
}
|
|
31774
32405
|
function loadBrandingCache(agentSlug) {
|
|
31775
|
-
const configDir2 =
|
|
32406
|
+
const configDir2 = join48(homedir4(), BRAND.configDir);
|
|
31776
32407
|
try {
|
|
31777
32408
|
const accountId = getDefaultAccountId();
|
|
31778
32409
|
if (!accountId) return null;
|
|
31779
|
-
const cachePath =
|
|
31780
|
-
if (!
|
|
31781
|
-
return JSON.parse(
|
|
32410
|
+
const cachePath = join48(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
32411
|
+
if (!existsSync47(cachePath)) return null;
|
|
32412
|
+
return JSON.parse(readFileSync48(cachePath, "utf-8"));
|
|
31782
32413
|
} catch {
|
|
31783
32414
|
return null;
|
|
31784
32415
|
}
|
|
@@ -31873,7 +32504,7 @@ app69.use("/vnc-popout.html", logViewerFetch);
|
|
|
31873
32504
|
app69.get("/vnc-popout.html", (c) => {
|
|
31874
32505
|
let html = htmlCache.get("vnc-popout.html");
|
|
31875
32506
|
if (!html) {
|
|
31876
|
-
html =
|
|
32507
|
+
html = readFileSync48(resolve45(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
31877
32508
|
const name = escapeHtml(BRAND.productName);
|
|
31878
32509
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
31879
32510
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -32025,10 +32656,10 @@ var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
|
32025
32656
|
var httpServer = serve({ fetch: app69.fetch, port, hostname });
|
|
32026
32657
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
32027
32658
|
{
|
|
32028
|
-
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32659
|
+
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join48(__dirname, "..");
|
|
32029
32660
|
const reconcileScript = resolve45(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
32030
32661
|
const runReconcile = (ctx) => {
|
|
32031
|
-
if (!
|
|
32662
|
+
if (!existsSync47(reconcileScript)) return;
|
|
32032
32663
|
try {
|
|
32033
32664
|
const child = spawn3(process.execPath, [reconcileScript], {
|
|
32034
32665
|
env: { ...process.env, PLATFORM_ROOT: reconcilePlatformRoot },
|
|
@@ -32048,7 +32679,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32048
32679
|
detached: true,
|
|
32049
32680
|
run: runReconcile
|
|
32050
32681
|
});
|
|
32051
|
-
const reconcileStatePath =
|
|
32682
|
+
const reconcileStatePath = join48(MAXY_DIR, "booking-reconcile-state.json");
|
|
32052
32683
|
registerLoop({
|
|
32053
32684
|
name: "calendar-reconcile-liveness",
|
|
32054
32685
|
intervalMs: 6e5,
|
|
@@ -32066,7 +32697,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32066
32697
|
}
|
|
32067
32698
|
{
|
|
32068
32699
|
const DREAM_CYCLE_SWEEP_INTERVAL_MS = 36e5;
|
|
32069
|
-
const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32700
|
+
const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join48(__dirname, "..");
|
|
32070
32701
|
const dreamSweepScript = resolve45(dreamPlatformRoot, "plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js");
|
|
32071
32702
|
registerLoop({
|
|
32072
32703
|
name: "dream-cycle-sweep",
|
|
@@ -32074,7 +32705,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32074
32705
|
firstRunDelayMs: 3e4,
|
|
32075
32706
|
detached: true,
|
|
32076
32707
|
run: (ctx) => {
|
|
32077
|
-
if (!
|
|
32708
|
+
if (!existsSync47(dreamSweepScript)) return;
|
|
32078
32709
|
try {
|
|
32079
32710
|
const child = spawn3(process.execPath, [dreamSweepScript], {
|
|
32080
32711
|
env: { ...process.env, PLATFORM_ROOT: dreamPlatformRoot },
|
|
@@ -32090,11 +32721,11 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32090
32721
|
});
|
|
32091
32722
|
}
|
|
32092
32723
|
{
|
|
32093
|
-
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32724
|
+
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join48(__dirname, "..");
|
|
32094
32725
|
const outlookScript = resolve45(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
|
|
32095
32726
|
const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
|
|
32096
32727
|
const runOutlookComplete = (ctx) => {
|
|
32097
|
-
if (!
|
|
32728
|
+
if (!existsSync47(outlookScript)) return;
|
|
32098
32729
|
try {
|
|
32099
32730
|
const child = spawn3(process.execPath, [outlookScript], {
|
|
32100
32731
|
env: { ...process.env, PLATFORM_ROOT: outlookPlatformRoot },
|
|
@@ -32116,20 +32747,20 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32116
32747
|
});
|
|
32117
32748
|
}
|
|
32118
32749
|
{
|
|
32119
|
-
const auditRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32750
|
+
const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join48(__dirname, "..");
|
|
32120
32751
|
const strandedAccountsDir = resolve45(auditRoot, "..", "data/accounts");
|
|
32121
32752
|
const STRANDED_AUDIT_INTERVAL_MS = 3e5;
|
|
32122
32753
|
const STRANDED_AGE_MS = 16 * 6e4;
|
|
32123
32754
|
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
32755
|
const runStrandedAudit = () => {
|
|
32125
32756
|
try {
|
|
32126
|
-
if (!
|
|
32757
|
+
if (!existsSync47(strandedAccountsDir)) return;
|
|
32127
32758
|
const now = Date.now();
|
|
32128
|
-
for (const name of
|
|
32759
|
+
for (const name of readdirSync29(strandedAccountsDir)) {
|
|
32129
32760
|
if (!STRANDED_UUID_RE.test(name)) continue;
|
|
32130
32761
|
const pendingPath2 = resolve45(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
|
|
32131
|
-
if (!
|
|
32132
|
-
const ageMs2 = now -
|
|
32762
|
+
if (!existsSync47(pendingPath2)) continue;
|
|
32763
|
+
const ageMs2 = now - statSync21(pendingPath2).mtimeMs;
|
|
32133
32764
|
if (ageMs2 > STRANDED_AGE_MS) {
|
|
32134
32765
|
console.error(`[outlook-mcp] devicecode-stranded account=${name} ageSec=${Math.floor(ageMs2 / 1e3)}`);
|
|
32135
32766
|
}
|
|
@@ -32146,7 +32777,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32146
32777
|
});
|
|
32147
32778
|
}
|
|
32148
32779
|
{
|
|
32149
|
-
const googleRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32780
|
+
const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join48(__dirname, "..");
|
|
32150
32781
|
const googleAccountsDir = resolve45(googleRoot, "..", "data/accounts");
|
|
32151
32782
|
const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
|
|
32152
32783
|
const runGooglePendingAuditSafe = () => {
|
|
@@ -32165,7 +32796,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32165
32796
|
const googleAuditScript = resolve45(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
|
|
32166
32797
|
const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
|
|
32167
32798
|
const runGoogleAccountAudit = (ctx) => {
|
|
32168
|
-
if (!
|
|
32799
|
+
if (!existsSync47(googleAuditScript)) return;
|
|
32169
32800
|
try {
|
|
32170
32801
|
const child = spawn3(process.execPath, [googleAuditScript], {
|
|
32171
32802
|
env: { ...process.env, PLATFORM_ROOT: googleRoot },
|
|
@@ -32190,7 +32821,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32190
32821
|
});
|
|
32191
32822
|
}
|
|
32192
32823
|
{
|
|
32193
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32824
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join48(__dirname, "..");
|
|
32194
32825
|
const STORAGE_AUDIT_INTERVAL_MS = 3e5;
|
|
32195
32826
|
const runStorageAuditSafe = () => runStorageAudit(auditPlatformRoot).catch((err) => {
|
|
32196
32827
|
console.error(`[storage-audit] error="${err.message}"`);
|
|
@@ -32236,7 +32867,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32236
32867
|
});
|
|
32237
32868
|
}
|
|
32238
32869
|
{
|
|
32239
|
-
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32870
|
+
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join48(__dirname, "..");
|
|
32240
32871
|
const publishScript = resolve45(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
32241
32872
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
32242
32873
|
const bookingAccounts = () => {
|
|
@@ -32248,7 +32879,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32248
32879
|
}
|
|
32249
32880
|
};
|
|
32250
32881
|
const spawnPublish = (account, ctx) => {
|
|
32251
|
-
if (!
|
|
32882
|
+
if (!existsSync47(publishScript)) return;
|
|
32252
32883
|
try {
|
|
32253
32884
|
const child = spawn3(process.execPath, [publishScript], {
|
|
32254
32885
|
env: {
|
|
@@ -32270,8 +32901,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32270
32901
|
const availPath = resolve45(account.accountDir, "calendar-availability.json");
|
|
32271
32902
|
let hasBookingSite = false;
|
|
32272
32903
|
try {
|
|
32273
|
-
if (
|
|
32274
|
-
const cfg = JSON.parse(
|
|
32904
|
+
if (existsSync47(availPath)) {
|
|
32905
|
+
const cfg = JSON.parse(readFileSync48(availPath, "utf-8"));
|
|
32275
32906
|
hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
32276
32907
|
}
|
|
32277
32908
|
} catch {
|
|
@@ -32279,9 +32910,9 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32279
32910
|
if (!hasBookingSite) return;
|
|
32280
32911
|
const statePath = resolve45(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
32281
32912
|
let lastSuccessAt = null;
|
|
32282
|
-
if (
|
|
32913
|
+
if (existsSync47(statePath)) {
|
|
32283
32914
|
try {
|
|
32284
|
-
const rec = JSON.parse(
|
|
32915
|
+
const rec = JSON.parse(readFileSync48(statePath, "utf-8"));
|
|
32285
32916
|
lastSuccessAt = rec.lastSuccessAt ?? null;
|
|
32286
32917
|
} catch {
|
|
32287
32918
|
console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
|
|
@@ -32313,12 +32944,12 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32313
32944
|
startTimeEntryCensus(() => getSession());
|
|
32314
32945
|
startLedgerCensus(() => getSession());
|
|
32315
32946
|
{
|
|
32316
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
32947
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join48(__dirname, "..");
|
|
32317
32948
|
const auditScript = resolve45(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
32318
32949
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
32319
32950
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
32320
32951
|
const runConnectorAudit = (ctx) => {
|
|
32321
|
-
if (!
|
|
32952
|
+
if (!existsSync47(auditScript)) return;
|
|
32322
32953
|
try {
|
|
32323
32954
|
const child = spawn3(process.execPath, [auditScript], {
|
|
32324
32955
|
env: { ...process.env, PLATFORM_ROOT: auditPlatformRoot, LOG_DIR: auditLogDir },
|
|
@@ -32438,11 +33069,11 @@ try {
|
|
|
32438
33069
|
var ADMINUSER_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
32439
33070
|
async function runAdminUserReconcileTick() {
|
|
32440
33071
|
try {
|
|
32441
|
-
if (!
|
|
33072
|
+
if (!existsSync47(USERS_FILE)) {
|
|
32442
33073
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
32443
33074
|
return;
|
|
32444
33075
|
}
|
|
32445
|
-
const usersRaw =
|
|
33076
|
+
const usersRaw = readFileSync48(USERS_FILE, "utf-8").trim();
|
|
32446
33077
|
if (!usersRaw) {
|
|
32447
33078
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
32448
33079
|
return;
|
|
@@ -32489,8 +33120,8 @@ registerLoop({
|
|
|
32489
33120
|
});
|
|
32490
33121
|
var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
32491
33122
|
function countUsersRows() {
|
|
32492
|
-
if (!
|
|
32493
|
-
const raw =
|
|
33123
|
+
if (!existsSync47(USERS_FILE)) return 0;
|
|
33124
|
+
const raw = readFileSync48(USERS_FILE, "utf-8").trim();
|
|
32494
33125
|
if (!raw) return 0;
|
|
32495
33126
|
const users = JSON.parse(raw);
|
|
32496
33127
|
return users.filter((u) => typeof u.userId === "string").length;
|
|
@@ -32623,7 +33254,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
32623
33254
|
}
|
|
32624
33255
|
init({
|
|
32625
33256
|
configDir: configDirForWhatsApp,
|
|
32626
|
-
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ??
|
|
33257
|
+
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ?? join48(__dirname, "..")),
|
|
32627
33258
|
accountConfig: bootAccountConfig,
|
|
32628
33259
|
onMessage: async (msg) => {
|
|
32629
33260
|
if (isObserveAccount(msg.accountId)) {
|