@rubytech/create-maxy-code 0.1.74 → 0.1.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/plugin-install.test.js +2 -2
- package/dist/lib/plugin-install.js +1 -1
- package/package.json +1 -1
- package/payload/platform/config/brand.json +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +1 -1
- package/payload/platform/lib/graph-write/src/index.ts +1 -1
- package/payload/platform/package.json +1 -1
- package/payload/platform/plugins/.claude-plugin/marketplace.json +101 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +112 -8
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/commitment-followthrough/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/session-management/SKILL.md +1 -1
- package/payload/platform/plugins/business-assistant/PLUGIN.md +1 -1
- package/payload/platform/plugins/business-assistant/references/task-management.md +13 -13
- package/payload/platform/plugins/contacts/mcp/dist/index.js +36 -5
- package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/docs/references/admin-session.md +7 -6
- package/payload/platform/plugins/docs/references/internals.md +2 -2
- package/payload/platform/plugins/email/mcp/dist/index.js +34 -3
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +78 -3
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.js +57 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js +9 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +2 -0
- package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +2 -2
- package/payload/platform/plugins/outlook/mcp/dist/index.js +37 -5
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/projects/PLUGIN.md +3 -3
- package/payload/platform/plugins/projects/references/sprint.md +2 -2
- package/payload/platform/plugins/replicate/mcp/dist/index.js +27 -6
- package/payload/platform/plugins/replicate/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +32 -3
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/scheduling/skills/briefing/SKILL.md +2 -2
- package/payload/platform/plugins/tasks/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/tasks/mcp/dist/index.js +44 -3
- package/payload/platform/plugins/tasks/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/waitlist/mcp/dist/index.js +30 -3
- package/payload/platform/plugins/waitlist/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/work/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/{tasks → work}/.mcp.json +2 -2
- package/payload/platform/plugins/work/PLUGIN.md +120 -0
- package/payload/platform/plugins/work/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/work/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/index.js +543 -0
- package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.d.ts +7 -0
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.js +24 -0
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/work/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/lib/neo4j.js +40 -0
- package/payload/platform/plugins/work/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-complete.d.ts +17 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-complete.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-complete.js +76 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-complete.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.d.ts +29 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.js +235 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-get.d.ts +40 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-get.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-get.js +125 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-get.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-list.d.ts +26 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-list.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-list.js +81 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-list.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-update.d.ts +19 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-update.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-update.js +102 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-update.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.d.ts +20 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.js +37 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-name.d.ts +12 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-name.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-name.js +28 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-name.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-complete.d.ts +16 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-complete.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-complete.js +33 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-complete.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts +63 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js +141 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-get.d.ts +19 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-get.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-get.js +51 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-get.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-list.d.ts +18 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-list.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-list.js +66 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-list.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-ready.d.ts +21 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-ready.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-ready.js +54 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-ready.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-relate.d.ts +12 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-relate.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-relate.js +59 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-relate.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-update.d.ts +32 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-update.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-update.js +112 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-update.js.map +1 -0
- package/payload/platform/plugins/{tasks → work}/mcp/package.json +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/index.js +32 -3
- package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -1
- package/payload/platform/scripts/conversation-id-allowlist.txt +4 -4
- package/payload/platform/scripts/seed-neo4j.sh +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +34 -5
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +32 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +33 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +170 -39
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.js +10 -4
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +22 -38
- package/payload/platform/templates/agents/admin/SOUL.md +0 -38
- package/payload/platform/templates/specialists/agents/database-operator.md +1 -1
- package/payload/platform/templates/specialists/agents/librarian.md +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/platform/templates/specialists/agents/project-manager.md +3 -3
- package/payload/premium-plugins/real-agent/plugins/leads/PLUGIN.md +1 -1
- package/payload/server/{chunk-ITQ55PCQ.js → chunk-TFEIFU7F.js} +1 -1
- package/payload/server/{chunk-HZ77KMTV.js → chunk-W33UUYYR.js} +1 -4
- package/payload/server/{cloudflare-task-tracker-PZBX7X3D.js → cloudflare-task-tracker-7P355PSD.js} +1 -1
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/{admin-DcqVybWZ.js → admin-Bn3SKHdv.js} +1 -1
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-C6vU0NST.js → architectureDiagram-Q4EWVU46-VMeuB5OP.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-CVaR8-w2.js → blockDiagram-DXYQGD6D-B7mq0Me0.js} +1 -1
- package/payload/server/public/assets/{brand-1AQwHifp.css → brand-CZQNuuLo.css} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-DnFHlz0P.js → c4Diagram-AHTNJAMY-Btu6ejD1.js} +1 -1
- package/payload/server/public/assets/channel-C_bSLH4h.js +1 -0
- package/payload/server/public/assets/{chunk-336JU56O-B6a2-iht.js → chunk-336JU56O-DAL_d_Xa.js} +2 -2
- package/payload/server/public/assets/{chunk-426QAEUC-DxIW6EIp.js → chunk-426QAEUC-DPD9ot_N.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-Bd_K_Vm8.js → chunk-4TB4RGXK-BexM9T-f.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-BrcNLOWO.js → chunk-5FUZZQ4R-EwDdji3T.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-VlQyk4ta.js → chunk-5PVQY5BW-BP9uoVws.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-BR-zR5uE.js → chunk-EDXVE4YY-DSFmUMEW.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-SZeptTG4.js → chunk-ENJZ2VHE-IVAy2Qk9.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-CtefGo4C.js → chunk-ICPOFSXX-DkUupPox.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-BozNOEbo.js → chunk-OYMX7WX6-BPnzam8G.js} +1 -1
- package/payload/server/public/assets/{chunk-U2HBQHQK-D96MbvO_.js → chunk-U2HBQHQK-xYNchOVT.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-gwEYUG8y.js → chunk-X2U36JSP-C1V_V8v8.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-DDIFkTtt.js → chunk-YZCP3GAM-CUibW87w.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-C75vepuo.js → chunk-ZZ45TVLE-HkKx4xBt.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-B6oW43xg.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-D0237d4h.js +1 -0
- package/payload/server/public/assets/clone-AMRCmnen.js +1 -0
- package/payload/server/public/assets/{dagre-DwC2Bsrq.js → dagre-DIaJ9oMM.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-DuukLv5C.js → dagre-KV5264BT-Oyox5ACd.js} +1 -1
- package/payload/server/public/assets/data-Mm3pTL1b.js +1 -0
- package/payload/server/public/assets/{device-url-actions-DDsULUXs.js → device-url-actions-BjDrEVoY.js} +1 -1
- package/payload/server/public/assets/{diagram-5BDNPKRD-DQWQd3WX.js → diagram-5BDNPKRD-DoGhkzBd.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-DbS_Ih5q.js → diagram-G4DWMVQ6-Dc3Ix-Dh.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-BFDfdYgV.js → diagram-MMDJMWI5-CFM9_Ggj.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-Dp48BsHY.js → diagram-TYMM5635-nMWgzxC3.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-CKA6q8Cy.js → erDiagram-SMLLAGMA-DcQWiGgF.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BVva2cwM.js → flowDiagram-DWJPFMVM-CcX5waal.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-BuzV7Yj-.js → ganttDiagram-T4ZO3ILL-BTQWoD8W.js} +1 -1
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-B188dI5e.js → gitGraphDiagram-UUTBAWPF-BHZcRznS.js} +1 -1
- package/payload/server/public/assets/graph-Cd4jsDi3.js +1 -0
- package/payload/server/public/assets/{graph-labels-Cp9MdDRy.js → graph-labels-CqsRzbp0.js} +1 -1
- package/payload/server/public/assets/{graphlib-D-7uYmP9.js → graphlib-BpUb9z9y.js} +1 -1
- package/payload/server/public/assets/{infoDiagram-42DDH7IO-CLvYxjeZ.js → infoDiagram-42DDH7IO-CBoiO8yF.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-B9F97qbc.js → ishikawaDiagram-UXIWVN3A-mTDI_0As.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CWg6xF0d.js → journeyDiagram-VCZTEJTY-CcuU_0rp.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-CUM2Zro1.js → kanban-definition-6JOO6SKY-BWA7Do5Y.js} +1 -1
- package/payload/server/public/assets/{line-B_dtxHKd.js → line-BlGptbDR.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-q8nXkWvq.js → mermaid-parser.core-hslUiqJ-.js} +1 -1
- package/payload/server/public/assets/{mermaid.core-BIZvK8d3.js → mermaid.core-DWU_5Faf.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-DOl7v8YY.js → mindmap-definition-QFDTVHPH-B3_sQr-2.js} +1 -1
- package/payload/server/public/assets/{page-HcHeXd21.js → page-eQPbgLXP.js} +1 -1
- package/payload/server/public/assets/{page-Ybq9fvmk.js → page-lPEIoiHs.js} +1 -1
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-CVFDHS9S.js → pieDiagram-DEJITSTG-02u2dI6Z.js} +1 -1
- package/payload/server/public/assets/{public-B_FEURBo.js → public-j7U67c6E.js} +3 -3
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-DIx-sbIM.js → quadrantDiagram-34T5L4WZ-BfgiMQiy.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-C9yrlgJP.js → requirementDiagram-MS252O5E-JKZoibIp.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-Dn2R2x6-.js → sankeyDiagram-XADWPNL6-N1iaY19u.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-C9QMqZZw.js → sequenceDiagram-FGHM5R23-DjU3oQ0_.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DPzHXWge.js → stateDiagram-FHFEXIEX-BuFcldiV.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-rZLYem3J.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-gRq4Cvlk.js → timeline-definition-GMOUNBTQ-3vztYjFm.js} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CJGA-zAB.js → vennDiagram-DHZGUBPP-Zdv9WVqJ.js} +1 -1
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BFS9eK0s.js → wardleyDiagram-NUSXRM2D-CkTAZI7f.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-D5Kqs-jt.js → xychartDiagram-5P7HB3ND-CgMCxpms.js} +1 -1
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +5 -5
- package/payload/server/public/index.html +7 -7
- package/payload/server/public/public.html +4 -4
- package/payload/server/server.js +50 -18
- package/payload/platform/plugins/tasks/PLUGIN.md +0 -120
- package/payload/server/public/assets/channel-Ccfjb07z.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-Ci-ABevb.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BDQ3yWGx.js +0 -1
- package/payload/server/public/assets/clone-CuYGwTRT.js +0 -1
- package/payload/server/public/assets/data-DbNK3_l-.js +0 -1
- package/payload/server/public/assets/graph-yTqw4J01.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CcDXqSsh.js +0 -1
- /package/payload/server/public/assets/{brand-lfvUn_r6.js → brand-BnMilSyt.js} +0 -0
|
@@ -7,7 +7,7 @@ import assert from "node:assert/strict";
|
|
|
7
7
|
import { parsePluginList, computeInstallActions, computeConfigureActions, parseExternalPlugins, } from "../lib/plugin-install.js";
|
|
8
8
|
test("parsePluginList extracts name@marketplace tuples", () => {
|
|
9
9
|
const stdout = `
|
|
10
|
-
❯
|
|
10
|
+
❯ work@maxy-platform
|
|
11
11
|
Version: 0.1.0
|
|
12
12
|
Scope: local
|
|
13
13
|
|
|
@@ -16,7 +16,7 @@ test("parsePluginList extracts name@marketplace tuples", () => {
|
|
|
16
16
|
`;
|
|
17
17
|
const out = parsePluginList(stdout);
|
|
18
18
|
assert.deepEqual(out, [
|
|
19
|
-
{ name: "
|
|
19
|
+
{ name: "work", marketplace: "maxy-platform" },
|
|
20
20
|
{ name: "telegram", marketplace: "claude-plugins-official" },
|
|
21
21
|
]);
|
|
22
22
|
});
|
package/package.json
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
|
|
49
49
|
"plugins": {
|
|
50
|
-
"core": ["admin", "memory", "docs", "cloudflare", "anthropic", "workflows", "
|
|
50
|
+
"core": ["admin", "memory", "docs", "cloudflare", "anthropic", "workflows", "work", "scheduling", "email", "contacts", "projects"],
|
|
51
51
|
"defaultEnabled": ["business-assistant", "sales"],
|
|
52
52
|
"available": ["telegram", "waitlist", "deep-research", "whatsapp", "replicate", "linkedin-import"],
|
|
53
53
|
"excluded": []
|
|
@@ -222,7 +222,7 @@ async function writeNodeWithEdges(params) {
|
|
|
222
222
|
.filter((m) => m !== null);
|
|
223
223
|
if (matched.length === 0) {
|
|
224
224
|
process.stderr.write(`[graph-write] reject reason=missing-provenance labels=${labelCsv} agent=${agentLabel}\n`);
|
|
225
|
-
throw new Error(`missing-provenance: write to ${labelCsv} requires an inbound :PRODUCED edge from a :Task, :Conversation, or :Message (createdBy.agent='${agentLabel}'). Either pass producedByTaskId on the write (autonomous workflows: call
|
|
225
|
+
throw new Error(`missing-provenance: write to ${labelCsv} requires an inbound :PRODUCED edge from a :Task, :Conversation, or :Message (createdBy.agent='${agentLabel}'). Either pass producedByTaskId on the write (autonomous workflows: call work-create at the start of the flow and thread the returned taskId), or rely on the MCP wrapper's CONVERSATION_NODE_ID env-stamp injection (direct admin asks).`);
|
|
226
226
|
}
|
|
227
227
|
provenanceSourceId = matched[0].rel.targetNodeId;
|
|
228
228
|
provenanceSourceLabel = matched[0].sourceLabel;
|
|
@@ -300,7 +300,7 @@ export async function writeNodeWithEdges(
|
|
|
300
300
|
`[graph-write] reject reason=missing-provenance labels=${labelCsv} agent=${agentLabel}\n`
|
|
301
301
|
);
|
|
302
302
|
throw new Error(
|
|
303
|
-
`missing-provenance: write to ${labelCsv} requires an inbound :PRODUCED edge from a :Task, :Conversation, or :Message (createdBy.agent='${agentLabel}'). Either pass producedByTaskId on the write (autonomous workflows: call
|
|
303
|
+
`missing-provenance: write to ${labelCsv} requires an inbound :PRODUCED edge from a :Task, :Conversation, or :Message (createdBy.agent='${agentLabel}'). Either pass producedByTaskId on the write (autonomous workflows: call work-create at the start of the flow and thread the returned taskId), or rely on the MCP wrapper's CONVERSATION_NODE_ID env-stamp injection (direct admin asks).`
|
|
304
304
|
);
|
|
305
305
|
}
|
|
306
306
|
provenanceSourceId = matched[0].rel.targetNodeId;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"build:telegram": "tsc -p plugins/telegram/mcp/tsconfig.json",
|
|
14
14
|
"build:admin": "tsc -p plugins/admin/mcp/tsconfig.json",
|
|
15
15
|
"build:cloudflare": "tsc -p plugins/cloudflare/mcp/tsconfig.json",
|
|
16
|
-
"build:
|
|
16
|
+
"build:work": "tsc -p plugins/work/mcp/tsconfig.json",
|
|
17
17
|
"build:email": "tsc -p plugins/email/mcp/tsconfig.json",
|
|
18
18
|
"build:outlook": "tsc -p plugins/outlook/mcp/tsconfig.json",
|
|
19
19
|
"build:workflows": "tsc -p plugins/workflows/mcp/tsconfig.json",
|
|
@@ -1,14 +1,114 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "maxy-
|
|
2
|
+
"name": "maxy-platform",
|
|
3
3
|
"owner": {
|
|
4
4
|
"name": "Rubytech LLC"
|
|
5
5
|
},
|
|
6
6
|
"plugins": [
|
|
7
|
+
{
|
|
8
|
+
"name": "admin",
|
|
9
|
+
"source": "./admin",
|
|
10
|
+
"version": "0.1.0"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "business-assistant",
|
|
14
|
+
"source": "./business-assistant",
|
|
15
|
+
"version": "0.1.0"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "cloudflare",
|
|
19
|
+
"source": "./cloudflare",
|
|
20
|
+
"version": "0.1.0"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "contacts",
|
|
24
|
+
"source": "./contacts",
|
|
25
|
+
"version": "0.1.0"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "deep-research",
|
|
29
|
+
"source": "./deep-research",
|
|
30
|
+
"version": "0.1.0"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "docs",
|
|
34
|
+
"source": "./docs",
|
|
35
|
+
"version": "0.1.0"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "email",
|
|
39
|
+
"source": "./email",
|
|
40
|
+
"version": "0.1.0"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "linkedin-import",
|
|
44
|
+
"source": "./linkedin-import",
|
|
45
|
+
"version": "0.1.0"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "memory",
|
|
49
|
+
"source": "./memory",
|
|
50
|
+
"version": "0.1.0"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "outlook",
|
|
54
|
+
"source": "./outlook",
|
|
55
|
+
"version": "0.1.0"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "projects",
|
|
59
|
+
"source": "./projects",
|
|
60
|
+
"version": "0.1.0"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "replicate",
|
|
64
|
+
"source": "./replicate",
|
|
65
|
+
"version": "0.1.0"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "sales",
|
|
69
|
+
"source": "./sales",
|
|
70
|
+
"version": "0.1.0"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "scheduling",
|
|
74
|
+
"source": "./scheduling",
|
|
75
|
+
"version": "0.1.0"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "tasks",
|
|
79
|
+
"source": "./tasks",
|
|
80
|
+
"version": "0.1.0"
|
|
81
|
+
},
|
|
7
82
|
{
|
|
8
83
|
"name": "teaching",
|
|
9
84
|
"source": "./teaching",
|
|
10
85
|
"version": "0.1.0"
|
|
11
86
|
},
|
|
87
|
+
{
|
|
88
|
+
"name": "telegram",
|
|
89
|
+
"source": "./telegram",
|
|
90
|
+
"version": "0.1.0"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "waitlist",
|
|
94
|
+
"source": "./waitlist",
|
|
95
|
+
"version": "0.1.0"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "whatsapp",
|
|
99
|
+
"source": "./whatsapp",
|
|
100
|
+
"version": "0.1.0"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "work",
|
|
104
|
+
"source": "./work",
|
|
105
|
+
"version": "0.1.0"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "workflows",
|
|
109
|
+
"source": "./workflows",
|
|
110
|
+
"version": "0.1.0"
|
|
111
|
+
},
|
|
12
112
|
{
|
|
13
113
|
"name": "writer-craft",
|
|
14
114
|
"source": "./writer-craft",
|
|
@@ -25,17 +25,42 @@ const server = new McpServer({
|
|
|
25
25
|
version: "0.1.0",
|
|
26
26
|
});
|
|
27
27
|
const PLATFORM_ROOT = process.env.PLATFORM_ROOT ?? resolve(import.meta.dirname, "../../../..");
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
// Plugin-system boot tolerance — see Task 202.
|
|
29
|
+
// Module-init must never throw; plugin-system spawn enumerates tools/list
|
|
30
|
+
// before any per-account env is set. Per-call refusal preserves account
|
|
31
|
+
// isolation in metadata-only mode.
|
|
32
|
+
const ACCOUNT_ID = process.env.ACCOUNT_ID ?? null;
|
|
33
|
+
process.stderr.write(`[admin] boot accountId=${ACCOUNT_ID ?? "null"}\n`);
|
|
34
|
+
function refuseNoAccount(toolName) {
|
|
35
|
+
process.stderr.write(`[admin] tool=${toolName} refuse reason=no-account-context\n`);
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: "text",
|
|
40
|
+
text: `${toolName} cannot execute: this MCP instance was launched without ACCOUNT_ID (plugin-system metadata-only mode). Per-account admin tools require ACCOUNT_ID at spawn.`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
isError: true,
|
|
44
|
+
};
|
|
31
45
|
}
|
|
32
46
|
// Task 142 — `PLATFORM_PORT` is the internal loopback port, stamped on
|
|
33
47
|
// the claude-session-manager systemd unit and forwarded to this MCP
|
|
34
48
|
// process via the per-spawn `maxy-mcp-*.json` env block written by
|
|
35
49
|
// `pty-spawner.writePerSpawnMcpConfig`. `MAXY_BRAND_PORT` is the brand's
|
|
36
50
|
// public UI port, used only for display-surface `.local` URLs.
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
// Ports are best-effort at boot: plugin-system metadata-only spawn has no
|
|
52
|
+
// port env, so we capture the loud-fail message instead of crashing. Real
|
|
53
|
+
// per-account spawns supply both; any handler reaching the port-using code
|
|
54
|
+
// paths is gated upstream by the accountId refusal.
|
|
55
|
+
let PLATFORM_PORT = "";
|
|
56
|
+
let MAXY_BRAND_PORT = "";
|
|
57
|
+
try {
|
|
58
|
+
PLATFORM_PORT = String(requirePortEnv("PLATFORM_PORT", { tag: "admin:module-init" }));
|
|
59
|
+
MAXY_BRAND_PORT = String(requirePortEnv("MAXY_BRAND_PORT", { tag: "admin:module-init" }));
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
process.stderr.write(`[admin] boot port-env unavailable — ${err.message}\n`);
|
|
63
|
+
}
|
|
39
64
|
// Brand-aware config — reads configDir, productName, and commercialMode from
|
|
40
65
|
// brand.json stamped at install time. commercialMode gates the
|
|
41
66
|
// entitlement verifier: false (default) preserves personal-mode installs;
|
|
@@ -114,6 +139,9 @@ async function currentEntitlement() {
|
|
|
114
139
|
const REMOTE_PASSWORD_FILE = resolve(CONFIG_DIR, ".remote-password");
|
|
115
140
|
// Resolve account directory
|
|
116
141
|
function getAccountDir() {
|
|
142
|
+
if (!ACCOUNT_ID) {
|
|
143
|
+
throw new Error("getAccountDir() called without ACCOUNT_ID — handler guard missing");
|
|
144
|
+
}
|
|
117
145
|
const dir = resolve(PLATFORM_ROOT, "..", "data/accounts", ACCOUNT_ID);
|
|
118
146
|
if (!existsSync(dir)) {
|
|
119
147
|
throw new Error(`Account directory not found: ${dir}`);
|
|
@@ -220,6 +248,8 @@ function checkPort(port, timeoutMs = 1000) {
|
|
|
220
248
|
});
|
|
221
249
|
}
|
|
222
250
|
eagerTool(server, "system-status", "Check health of all Maxy platform services: Neo4j, Ollama, Cloudflare tunnel, crontab (Maxy cron entries), VNC, Chrome (CDP), specialist agents, and deployed brand identity.", {}, async () => {
|
|
251
|
+
if (!ACCOUNT_ID)
|
|
252
|
+
return refuseNoAccount("system-status");
|
|
223
253
|
const checks = {};
|
|
224
254
|
try {
|
|
225
255
|
// NEO4J_URI must be explicit. The outer try/catch surfaces the
|
|
@@ -407,6 +437,8 @@ eagerTool(server, "system-status", "Check health of all Maxy platform services:
|
|
|
407
437
|
return { content: [{ type: "text", text: formatted }] };
|
|
408
438
|
});
|
|
409
439
|
eagerTool(server, "public-hostname", "Resolve this account's canonical public hostname. Reads cloudflared ingress + alias-domains.json — the same files the platform server trusts to route. Returns a single deterministic answer; use this immediately after publish-site to construct the full URL.", {}, async () => {
|
|
440
|
+
if (!ACCOUNT_ID)
|
|
441
|
+
return refuseNoAccount("public-hostname");
|
|
410
442
|
const TAG = "[admin:public-hostname]";
|
|
411
443
|
try {
|
|
412
444
|
const result = resolvePublicHostname(CONFIG_DIR);
|
|
@@ -437,6 +469,8 @@ eagerTool(server, "public-hostname", "Resolve this account's canonical public ho
|
|
|
437
469
|
}
|
|
438
470
|
});
|
|
439
471
|
eagerTool(server, "remote-auth-status", "Check whether the remote access password is configured. When not configured, emits a device-bound URL affordance (maxy-device-url fenced block) pointing at the password setup page — this URL opens on the device's own screen when the operator clicks it. The agent never constructs the password file path or runs shell commands — this tool is the single authority.", {}, async () => {
|
|
472
|
+
if (!ACCOUNT_ID)
|
|
473
|
+
return refuseNoAccount("remote-auth-status");
|
|
440
474
|
const TAG = "[remote-auth-status]";
|
|
441
475
|
// Task 142: `.local` URL is a display surface — read the brand's
|
|
442
476
|
// public port. The loopback fetch reads PLATFORM_PORT (internal).
|
|
@@ -476,6 +510,8 @@ eagerTool(server, "remote-auth-status", "Check whether the remote access passwor
|
|
|
476
510
|
}
|
|
477
511
|
});
|
|
478
512
|
eagerTool(server, "brand-settings", "Read the brand/styling configuration (name, tagline, colours, fonts, plugin sets). Reads from config/brand.json (stamped by the bundler at install time).", {}, async () => {
|
|
513
|
+
if (!ACCOUNT_ID)
|
|
514
|
+
return refuseNoAccount("brand-settings");
|
|
479
515
|
try {
|
|
480
516
|
const brandPath = resolve(PLATFORM_ROOT, "config", "brand.json");
|
|
481
517
|
if (!existsSync(brandPath)) {
|
|
@@ -492,6 +528,8 @@ eagerTool(server, "brand-settings", "Read the brand/styling configuration (name,
|
|
|
492
528
|
}
|
|
493
529
|
});
|
|
494
530
|
eagerTool(server, "account-manage", "Read the account configuration (tier, domains, settings).", {}, async () => {
|
|
531
|
+
if (!ACCOUNT_ID)
|
|
532
|
+
return refuseNoAccount("account-manage");
|
|
495
533
|
try {
|
|
496
534
|
const config = await readAccountConfig();
|
|
497
535
|
return {
|
|
@@ -509,6 +547,8 @@ eagerTool(server, "account-update", "Update a user-configurable setting in accou
|
|
|
509
547
|
field: z.enum(["outputStyle", "thinkingView", "effort", "adminModel", "publicModel", "defaultAgent"]),
|
|
510
548
|
value: z.string(),
|
|
511
549
|
}, async ({ field, value }) => {
|
|
550
|
+
if (!ACCOUNT_ID)
|
|
551
|
+
return refuseNoAccount("account-update");
|
|
512
552
|
const VALID = {
|
|
513
553
|
outputStyle: ["default", "explanatory"],
|
|
514
554
|
thinkingView: ["default", "expanded", "collapsed"],
|
|
@@ -588,6 +628,8 @@ server.tool("plugin-toggle-enabled", "Enable or disable a plugin in this account
|
|
|
588
628
|
pluginName: z.string().describe("Plugin slug (lowercase a-z0-9-)."),
|
|
589
629
|
action: z.enum(["enable", "disable"]).describe("enable adds to enabledPlugins; disable removes."),
|
|
590
630
|
}, async ({ pluginName, action }) => {
|
|
631
|
+
if (!ACCOUNT_ID)
|
|
632
|
+
return refuseNoAccount("plugin-toggle-enabled");
|
|
591
633
|
const TAG = "[admin:plugin-toggle-enabled]";
|
|
592
634
|
if (!VALID_PLUGIN_NAME.test(pluginName)) {
|
|
593
635
|
return { content: [{ type: "text", text: `${TAG} Invalid plugin name "${pluginName}". Must match ${VALID_PLUGIN_NAME}.` }], isError: true };
|
|
@@ -632,6 +674,8 @@ eagerTool(server, "admin-add", "Add a new admin user to this account. Creates a
|
|
|
632
674
|
name: z.string().describe("Display name for the new admin (stored on the AdminUser node in Neo4j)."),
|
|
633
675
|
pin: z.string().optional().describe("Optional PIN (minimum 4 digits). If omitted, a unique 4-digit PIN is generated."),
|
|
634
676
|
}, async ({ name, pin: rawPin }) => {
|
|
677
|
+
if (!ACCOUNT_ID)
|
|
678
|
+
return refuseNoAccount("admin-add");
|
|
635
679
|
const TAG = "[admin]";
|
|
636
680
|
if (!name.trim()) {
|
|
637
681
|
return { content: [{ type: "text", text: `${TAG} Name is required.` }], isError: true };
|
|
@@ -818,6 +862,8 @@ eagerTool(server, "admin-add", "Add a new admin user to this account. Creates a
|
|
|
818
862
|
eagerTool(server, "admin-remove", "Remove an admin from this account. Removes them from the account's admins list (account.json) and deletes the ADMIN_OF relationship in Neo4j. Does NOT remove the device-level user entry (they may admin other accounts). Cannot remove the last admin on the account.", {
|
|
819
863
|
userId: z.string().describe("The userId of the admin to remove (use admin-list to find userIds)"),
|
|
820
864
|
}, async ({ userId }) => {
|
|
865
|
+
if (!ACCOUNT_ID)
|
|
866
|
+
return refuseNoAccount("admin-remove");
|
|
821
867
|
const TAG = "[admin]";
|
|
822
868
|
const callerUserId = process.env.USER_ID;
|
|
823
869
|
// Read account.json and validate
|
|
@@ -892,6 +938,8 @@ eagerTool(server, "admin-remove", "Remove an admin from this account. Removes th
|
|
|
892
938
|
};
|
|
893
939
|
});
|
|
894
940
|
eagerTool(server, "admin-list", "List all admins for this account with their names and roles.", {}, async () => {
|
|
941
|
+
if (!ACCOUNT_ID)
|
|
942
|
+
return refuseNoAccount("admin-list");
|
|
895
943
|
const TAG = "[admin]";
|
|
896
944
|
let admins;
|
|
897
945
|
try {
|
|
@@ -937,6 +985,8 @@ eagerTool(server, "admin-update-pin", "Update an existing admin user's PIN. Defa
|
|
|
937
985
|
userId: z.string().optional().describe("The userId of the admin whose PIN to update. Defaults to the caller (the admin invoking this tool)."),
|
|
938
986
|
newPin: z.string().describe("The new PIN. Minimum 4 digits, no upper bound."),
|
|
939
987
|
}, async ({ userId: targetUserId, newPin }) => {
|
|
988
|
+
if (!ACCOUNT_ID)
|
|
989
|
+
return refuseNoAccount("admin-update-pin");
|
|
940
990
|
const TAG = "[admin-update-pin]";
|
|
941
991
|
const callerUserId = process.env.USER_ID;
|
|
942
992
|
const userId = targetUserId ?? callerUserId;
|
|
@@ -994,6 +1044,8 @@ server.tool("agent-image", "Upload, update, or remove an agent's image. action=s
|
|
|
994
1044
|
filePath: z.string().optional().describe("Absolute path to the image file (required for action=set)"),
|
|
995
1045
|
imageShape: z.enum(["circle", "rounded"]).optional().describe("Image shape: circle for avatars, rounded for logos (required for action=set)"),
|
|
996
1046
|
}, async ({ agentSlug, action, filePath, imageShape }) => {
|
|
1047
|
+
if (!ACCOUNT_ID)
|
|
1048
|
+
return refuseNoAccount("agent-image");
|
|
997
1049
|
const VALID_IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"];
|
|
998
1050
|
try {
|
|
999
1051
|
const accountDir = getAccountDir();
|
|
@@ -1104,6 +1156,8 @@ server.tool("agent-image", "Upload, update, or remove an agent's image. action=s
|
|
|
1104
1156
|
server.tool("agent-config-read", "Read the full config.json for a specific public agent by slug. Returns the complete configuration including slug, displayName, model, plugins, status, liveMemory, and any other fields.", {
|
|
1105
1157
|
slug: z.string().describe("Agent slug (directory name under agents/)"),
|
|
1106
1158
|
}, async ({ slug }) => {
|
|
1159
|
+
if (!ACCOUNT_ID)
|
|
1160
|
+
return refuseNoAccount("agent-config-read");
|
|
1107
1161
|
const TAG = "[admin:agent-config-read]";
|
|
1108
1162
|
// Path traversal guard
|
|
1109
1163
|
if (slug.includes("/") || slug.includes("\\") || slug.includes("..")) {
|
|
@@ -1138,6 +1192,8 @@ server.tool("agent-config-read", "Read the full config.json for a specific publi
|
|
|
1138
1192
|
}
|
|
1139
1193
|
});
|
|
1140
1194
|
server.tool("agent-list", "List all public (non-admin) agents with their full configuration: slug, displayName, model, plugins, status, liveMemory, and whether each is the account's default agent.", {}, async () => {
|
|
1195
|
+
if (!ACCOUNT_ID)
|
|
1196
|
+
return refuseNoAccount("agent-list");
|
|
1141
1197
|
const TAG = "[admin:agent-list]";
|
|
1142
1198
|
try {
|
|
1143
1199
|
const accountDir = getAccountDir();
|
|
@@ -1202,6 +1258,8 @@ eagerTool(server, "logs-read", "Read recent logs. Stream logs (type=agent-stream
|
|
|
1202
1258
|
sessionKey: z.string().optional(),
|
|
1203
1259
|
conversationId: z.string().optional(),
|
|
1204
1260
|
}, async ({ type, lines = 50, sessionKey, conversationId }) => {
|
|
1261
|
+
if (!ACCOUNT_ID)
|
|
1262
|
+
return refuseNoAccount("logs-read");
|
|
1205
1263
|
try {
|
|
1206
1264
|
const LOG_DIR = resolve(getAccountDir(), "logs");
|
|
1207
1265
|
// sessionKey is the single identifier on disk.
|
|
@@ -1418,6 +1476,8 @@ eagerTool(server, "store-skill", "Save an operator-authored skill on disk as par
|
|
|
1418
1476
|
content: z.string().describe("Reference file body content."),
|
|
1419
1477
|
})).default([]).describe("Optional reference files written under skills/<skillName>/references/."),
|
|
1420
1478
|
}, async ({ pluginName, skillName, description, publicEmbed, body, references }) => {
|
|
1479
|
+
if (!ACCOUNT_ID)
|
|
1480
|
+
return refuseNoAccount("store-skill");
|
|
1421
1481
|
const KEBAB_RE = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
|
|
1422
1482
|
const REF_RE = /^[a-z0-9-]+\.md$/;
|
|
1423
1483
|
if (!KEBAB_RE.test(pluginName)) {
|
|
@@ -1531,6 +1591,8 @@ eagerTool(server, "plugin-read", "Read a plugin definition (PLUGIN.md) or one of
|
|
|
1531
1591
|
pluginName: z.string().describe("Name of the plugin directory (e.g. 'sales', 'business-assistant')"),
|
|
1532
1592
|
file: z.string().optional().describe("Specific file to read (e.g. 'references/pricing.md'). Defaults to PLUGIN.md."),
|
|
1533
1593
|
}, async ({ pluginName, file }) => {
|
|
1594
|
+
if (!ACCOUNT_ID)
|
|
1595
|
+
return refuseNoAccount("plugin-read");
|
|
1534
1596
|
try {
|
|
1535
1597
|
const resolvedFile = file ?? "PLUGIN.md";
|
|
1536
1598
|
const pluginPath = resolve(PLATFORM_ROOT, "plugins", pluginName, resolvedFile);
|
|
@@ -1614,6 +1676,8 @@ eagerTool(server, "plugin-read", "Read a plugin definition (PLUGIN.md) or one of
|
|
|
1614
1676
|
server.tool("skill-find", "Find which plugin owns a skill by name. Walks plugins/*/skills/<skillName>/SKILL.md and returns the owning plugin (unique), every candidate (ambiguous), or not-found. For the common resolve-and-read case, prefer `skill-load skillName=<name>` — it returns the SKILL.md body in the same call.", {
|
|
1615
1677
|
skillName: z.string().regex(/^[a-z0-9][a-z0-9-]*$/, "skillName must be lowercase kebab-case (a-z, 0-9, -)"),
|
|
1616
1678
|
}, async ({ skillName }) => {
|
|
1679
|
+
if (!ACCOUNT_ID)
|
|
1680
|
+
return refuseNoAccount("skill-find");
|
|
1617
1681
|
const start = Date.now();
|
|
1618
1682
|
const result = findSkillOwners(PLATFORM_ROOT, skillName);
|
|
1619
1683
|
const ms = Date.now() - start;
|
|
@@ -1657,6 +1721,8 @@ server.tool("skill-find", "Find which plugin owns a skill by name. Walks plugins
|
|
|
1657
1721
|
eagerTool(server, "skill-load", "Load a plugin skill's SKILL.md body by skill name. One call: resolves the owning plugin and reads the body, with brand placeholders substituted. Returns the SKILL.md body when exactly one plugin owns the skill, a candidate list when multiple plugins own it (ambiguous), or an error when no plugin owns it.", {
|
|
1658
1722
|
skillName: z.string().regex(/^[a-z0-9][a-z0-9-]*$/, "skillName must be lowercase kebab-case (a-z, 0-9, -)"),
|
|
1659
1723
|
}, async ({ skillName }) => {
|
|
1724
|
+
if (!ACCOUNT_ID)
|
|
1725
|
+
return refuseNoAccount("skill-load");
|
|
1660
1726
|
const start = Date.now();
|
|
1661
1727
|
try {
|
|
1662
1728
|
const outcome = await loadSkill(PLATFORM_ROOT, skillName);
|
|
@@ -1693,7 +1759,7 @@ eagerTool(server, "skill-load", "Load a plugin skill's SKILL.md body by skill na
|
|
|
1693
1759
|
if (requiredInputs) {
|
|
1694
1760
|
const slotLabels = requiredInputs.map(s => s.join(" | ")).join(", ");
|
|
1695
1761
|
const marker = JSON.stringify(requiredInputs);
|
|
1696
|
-
const notice = `<!-- skill-input-contract requiredInputs=${marker} -->\nREQUIRED INPUTS: ${slotLabels}\nIf the operator has not supplied each of these inputs, your next tool call MUST be AskUserQuestion. Do not use memory-search, conversation-search, graph-read, or
|
|
1762
|
+
const notice = `<!-- skill-input-contract requiredInputs=${marker} -->\nREQUIRED INPUTS: ${slotLabels}\nIf the operator has not supplied each of these inputs, your next tool call MUST be AskUserQuestion. Do not use memory-search, conversation-search, graph-read, or work-get to guess the missing input.\n<!-- /skill-input-contract -->\n\n`;
|
|
1697
1763
|
bodyOut = notice + content;
|
|
1698
1764
|
}
|
|
1699
1765
|
const ms = Date.now() - start;
|
|
@@ -1721,14 +1787,24 @@ eagerTool(server, "skill-load", "Load a plugin skill's SKILL.md body by skill na
|
|
|
1721
1787
|
// already carry the rows; no new conversations write to it.
|
|
1722
1788
|
eagerTool(server, "session-reset", "Reset the current session. Compacts conversation history to memory, clears the visible conversation, " +
|
|
1723
1789
|
"and starts a fresh session with a new greeting. Call when the user asks to start a new session, " +
|
|
1724
|
-
"clear the conversation, or start fresh.", {}, async () =>
|
|
1790
|
+
"clear the conversation, or start fresh.", {}, async () => {
|
|
1791
|
+
if (!ACCOUNT_ID)
|
|
1792
|
+
return refuseNoAccount("session-reset");
|
|
1793
|
+
return { content: [{ type: "text", text: "reset" }] };
|
|
1794
|
+
});
|
|
1725
1795
|
eagerTool(server, "session-resume", "Resume a previous session. Loads the selected session's message history into the chat timeline " +
|
|
1726
1796
|
"and routes new messages to that conversation. Call after the user selects a session from the " +
|
|
1727
|
-
"session-list results. Pass the conversationId from the selected session.", { conversationId: z.string().uuid().describe("The conversationId of the session to resume") }, async () =>
|
|
1797
|
+
"session-list results. Pass the conversationId from the selected session.", { conversationId: z.string().uuid().describe("The conversationId of the session to resume") }, async () => {
|
|
1798
|
+
if (!ACCOUNT_ID)
|
|
1799
|
+
return refuseNoAccount("session-resume");
|
|
1800
|
+
return { content: [{ type: "text", text: "resumed" }] };
|
|
1801
|
+
});
|
|
1728
1802
|
server.tool("remote-auth-set-password", "Set the remote access password. Hashes with scrypt and writes to the platform's brand-specific config directory with mode 0600. " +
|
|
1729
1803
|
"Validates strength (8+ chars, digit, special character, no spaces anywhere). " +
|
|
1730
1804
|
"Protects the admin interface when exposed over the tunnel — has no effect on the public endpoint or the tunnel itself. " +
|
|
1731
1805
|
"Set before `setup-tunnel.sh` — the script's post-restart verification curls the admin hostname and fails if remote-auth is not configured.", { password: z.string() }, async ({ password }) => {
|
|
1806
|
+
if (!ACCOUNT_ID)
|
|
1807
|
+
return refuseNoAccount("remote-auth-set-password");
|
|
1732
1808
|
// Validate strength — same rules as the web server's validatePasswordStrength
|
|
1733
1809
|
const checks = [
|
|
1734
1810
|
{ label: "at least 8 characters", met: password.length >= 8 },
|
|
@@ -1777,6 +1853,8 @@ eagerTool(server, "qr-generate", "Generate a QR code from text or a URL. Returns
|
|
|
1777
1853
|
.describe("File path to save the PNG. If omitted, returns a data URI."),
|
|
1778
1854
|
width: z.number().optional().describe("Width in pixels (default 300)"),
|
|
1779
1855
|
}, async ({ data, outputPath, width }) => {
|
|
1856
|
+
if (!ACCOUNT_ID)
|
|
1857
|
+
return refuseNoAccount("qr-generate");
|
|
1780
1858
|
try {
|
|
1781
1859
|
const options = { width: width ?? 300, margin: 2 };
|
|
1782
1860
|
if (outputPath) {
|
|
@@ -1831,6 +1909,8 @@ eagerTool(server, "wifi", "Manage WiFi connections on this device. Actions: " +
|
|
|
1831
1909
|
ssid: z.string().optional().describe("Network name — required for connect and forget actions"),
|
|
1832
1910
|
password: z.string().optional().describe("Network password — required for connect action (WPA/WPA2/WPA3 PSK)"),
|
|
1833
1911
|
}, async ({ action, ssid, password }) => {
|
|
1912
|
+
if (!ACCOUNT_ID)
|
|
1913
|
+
return refuseNoAccount("wifi");
|
|
1834
1914
|
/** Parse nmcli terse output into rows of colon-separated fields.
|
|
1835
1915
|
* nmcli -t escapes literal colons in values as \: — split on
|
|
1836
1916
|
* unescaped colons only, then unescape the fields. */
|
|
@@ -2084,6 +2164,8 @@ eagerTool(server, "wifi", "Manage WiFi connections on this device. Actions: " +
|
|
|
2084
2164
|
server.tool("premium-list", "List available premium plugins and their delivery status. Returns structured data " +
|
|
2085
2165
|
"for each premium plugin in the staging area: name, type (standalone/bundle), description, " +
|
|
2086
2166
|
"purchase status, and delivery status (per sub-plugin for bundles).", {}, async () => {
|
|
2167
|
+
if (!ACCOUNT_ID)
|
|
2168
|
+
return refuseNoAccount("premium-list");
|
|
2087
2169
|
try {
|
|
2088
2170
|
if (!existsSync(STAGING_ROOT)) {
|
|
2089
2171
|
return {
|
|
@@ -2158,6 +2240,8 @@ server.tool("premium-deliver", "Deliver a purchased premium plugin. Copies plugi
|
|
|
2158
2240
|
"status and available templates.", {
|
|
2159
2241
|
pluginName: z.string().describe("Name of the premium plugin to deliver (e.g. 'real-agent', 'teaching')"),
|
|
2160
2242
|
}, async ({ pluginName }) => {
|
|
2243
|
+
if (!ACCOUNT_ID)
|
|
2244
|
+
return refuseNoAccount("premium-deliver");
|
|
2161
2245
|
const TAG = "[premium-deliver]";
|
|
2162
2246
|
// --- Validate plugin name ---
|
|
2163
2247
|
if (!pluginName || !VALID_PLUGIN_NAME.test(pluginName)) {
|
|
@@ -2404,6 +2488,8 @@ eagerTool(server, "file-attach", "Attach a file from the account directory for d
|
|
|
2404
2488
|
"and size on screen.", {
|
|
2405
2489
|
filePath: z.string().describe("Absolute path to the file to attach"),
|
|
2406
2490
|
}, async ({ filePath }) => {
|
|
2491
|
+
if (!ACCOUNT_ID)
|
|
2492
|
+
return refuseNoAccount("file-attach");
|
|
2407
2493
|
const TAG = "[file-attach]";
|
|
2408
2494
|
if (!filePath || !filePath.trim()) {
|
|
2409
2495
|
return {
|
|
@@ -2572,6 +2658,13 @@ async function persistApprovalToolCall(opts) {
|
|
|
2572
2658
|
console.error(`[approval] ToolCall skipped (no conversationId): tool=${opts.toolName} state=${opts.approvalState}`);
|
|
2573
2659
|
return;
|
|
2574
2660
|
}
|
|
2661
|
+
if (!ACCOUNT_ID) {
|
|
2662
|
+
// Boot-tolerance refusal handlers gate every caller, so reaching here is
|
|
2663
|
+
// a guard regression rather than a steady-state condition. Throw loud so
|
|
2664
|
+
// the missing handler-level guard is identified, not silently default to
|
|
2665
|
+
// empty-string accountId (which would mint orphan ToolCall nodes).
|
|
2666
|
+
throw new Error(`[approval] persistApprovalToolCall reached without ACCOUNT_ID — handler guard missing for tool=${opts.toolName}`);
|
|
2667
|
+
}
|
|
2575
2668
|
const session = getSession();
|
|
2576
2669
|
try {
|
|
2577
2670
|
const optionalFields = [
|
|
@@ -2642,6 +2735,9 @@ async function dispatchApprovedAction(plugin, tool, args, timeoutMs = 30_000) {
|
|
|
2642
2735
|
env[k] = v;
|
|
2643
2736
|
}
|
|
2644
2737
|
env.PLATFORM_ROOT = PLATFORM_ROOT;
|
|
2738
|
+
if (!ACCOUNT_ID) {
|
|
2739
|
+
throw new Error("dispatchApprovedAction called without ACCOUNT_ID — handler guard missing");
|
|
2740
|
+
}
|
|
2645
2741
|
env.ACCOUNT_ID = ACCOUNT_ID;
|
|
2646
2742
|
const transport = new StdioClientTransport({
|
|
2647
2743
|
command: process.execPath,
|
|
@@ -2690,6 +2786,8 @@ async function dispatchApprovedAction(plugin, tool, args, timeoutMs = 30_000) {
|
|
|
2690
2786
|
eagerTool(server, "action-pending", "List actions that are queued for human approval. Returns each pending action's ID, " +
|
|
2691
2787
|
"tool name, input summary, and when it was queued. Use this to review what the agent " +
|
|
2692
2788
|
"wanted to do before approving or rejecting.", {}, async () => {
|
|
2789
|
+
if (!ACCOUNT_ID)
|
|
2790
|
+
return refuseNoAccount("action-pending");
|
|
2693
2791
|
const actions = readPendingActions();
|
|
2694
2792
|
if (actions.length === 0) {
|
|
2695
2793
|
return {
|
|
@@ -2710,6 +2808,8 @@ eagerTool(server, "action-approve", "Approve a pending action and execute it imm
|
|
|
2710
2808
|
actionId: z.string().describe("The action ID to approve (from action-pending)"),
|
|
2711
2809
|
conversationId: z.string().optional().describe("Current conversation ID for audit trail linking"),
|
|
2712
2810
|
}, async ({ actionId, conversationId }) => {
|
|
2811
|
+
if (!ACCOUNT_ID)
|
|
2812
|
+
return refuseNoAccount("action-approve");
|
|
2713
2813
|
const action = readPendingAction(actionId);
|
|
2714
2814
|
if (!action) {
|
|
2715
2815
|
return {
|
|
@@ -2780,6 +2880,8 @@ eagerTool(server, "action-reject", "Reject a pending action. The action is not e
|
|
|
2780
2880
|
reason: z.string().optional().describe("Optional reason for rejection"),
|
|
2781
2881
|
conversationId: z.string().optional().describe("Current conversation ID for audit trail linking"),
|
|
2782
2882
|
}, async ({ actionId, reason, conversationId }) => {
|
|
2883
|
+
if (!ACCOUNT_ID)
|
|
2884
|
+
return refuseNoAccount("action-reject");
|
|
2783
2885
|
const action = readPendingAction(actionId);
|
|
2784
2886
|
if (!action) {
|
|
2785
2887
|
return {
|
|
@@ -2822,6 +2924,8 @@ eagerTool(server, "action-edit", "Edit a pending action's input and then execute
|
|
|
2822
2924
|
editedInput: z.string().describe("The modified tool input as a JSON string"),
|
|
2823
2925
|
conversationId: z.string().optional().describe("Current conversation ID for audit trail linking"),
|
|
2824
2926
|
}, async ({ actionId, editedInput, conversationId }) => {
|
|
2927
|
+
if (!ACCOUNT_ID)
|
|
2928
|
+
return refuseNoAccount("action-edit");
|
|
2825
2929
|
const action = readPendingAction(actionId);
|
|
2826
2930
|
if (!action) {
|
|
2827
2931
|
return {
|