@rubytech/create-maxy-code 0.1.0
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__/account-id-env.test.js +48 -0
- package/dist/__tests__/apt-resolve.test.js +179 -0
- package/dist/__tests__/brew-install.test.js +141 -0
- package/dist/__tests__/brew-resolve.test.js +103 -0
- package/dist/__tests__/cdp-port-no-silent-fallback.test.js +53 -0
- package/dist/__tests__/launchd-plist.test.js +149 -0
- package/dist/__tests__/macos-version.test.js +96 -0
- package/dist/__tests__/peer-brand-detect.test.js +103 -0
- package/dist/__tests__/platform-detect.test.js +50 -0
- package/dist/__tests__/port-canonicalisation.test.js +200 -0
- package/dist/__tests__/preflight-port-classifier.test.js +330 -0
- package/dist/__tests__/snap-chromium.test.js +115 -0
- package/dist/apt-resolve.js +73 -0
- package/dist/brew-install.js +175 -0
- package/dist/brew-resolve.js +68 -0
- package/dist/index.js +3325 -0
- package/dist/launchd-plist.js +68 -0
- package/dist/macos-version.js +53 -0
- package/dist/peer-brand-detect.js +39 -0
- package/dist/pinned-binaries.js +12 -0
- package/dist/platform-detect.js +36 -0
- package/dist/port-resolution.js +153 -0
- package/dist/preflight-port-classifier.js +222 -0
- package/dist/snap-chromium.js +89 -0
- package/dist/uninstall.js +861 -0
- package/package.json +32 -0
- package/payload/platform/config/brand-registry.json +20 -0
- package/payload/platform/config/brand.json +54 -0
- package/payload/platform/lib/account-enumeration/dist/__tests__/enumerate.test.d.ts +2 -0
- package/payload/platform/lib/account-enumeration/dist/__tests__/enumerate.test.d.ts.map +1 -0
- package/payload/platform/lib/account-enumeration/dist/__tests__/enumerate.test.js +88 -0
- package/payload/platform/lib/account-enumeration/dist/__tests__/enumerate.test.js.map +1 -0
- package/payload/platform/lib/account-enumeration/dist/__tests__/validate-env.test.d.ts +2 -0
- package/payload/platform/lib/account-enumeration/dist/__tests__/validate-env.test.d.ts.map +1 -0
- package/payload/platform/lib/account-enumeration/dist/__tests__/validate-env.test.js +55 -0
- package/payload/platform/lib/account-enumeration/dist/__tests__/validate-env.test.js.map +1 -0
- package/payload/platform/lib/account-enumeration/dist/index.d.ts +49 -0
- package/payload/platform/lib/account-enumeration/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/account-enumeration/dist/index.js +109 -0
- package/payload/platform/lib/account-enumeration/dist/index.js.map +1 -0
- package/payload/platform/lib/account-enumeration/src/__tests__/enumerate.test.ts +94 -0
- package/payload/platform/lib/account-enumeration/src/__tests__/validate-env.test.ts +57 -0
- package/payload/platform/lib/account-enumeration/src/index.ts +140 -0
- package/payload/platform/lib/account-enumeration/tsconfig.json +8 -0
- package/payload/platform/lib/admins-write/dist/index.d.ts +87 -0
- package/payload/platform/lib/admins-write/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/admins-write/dist/index.js +248 -0
- package/payload/platform/lib/admins-write/dist/index.js.map +1 -0
- package/payload/platform/lib/admins-write/src/index.ts +311 -0
- package/payload/platform/lib/admins-write/tsconfig.json +8 -0
- package/payload/platform/lib/anthropic-key/dist/index.d.ts +22 -0
- package/payload/platform/lib/anthropic-key/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/anthropic-key/dist/index.js +232 -0
- package/payload/platform/lib/anthropic-key/dist/index.js.map +1 -0
- package/payload/platform/lib/brand-templating/dist/index.d.ts +18 -0
- package/payload/platform/lib/brand-templating/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/brand-templating/dist/index.js +69 -0
- package/payload/platform/lib/brand-templating/dist/index.js.map +1 -0
- package/payload/platform/lib/brand-templating/src/index.ts +76 -0
- package/payload/platform/lib/brand-templating/tsconfig.json +8 -0
- package/payload/platform/lib/device-url/dist/index.d.ts +44 -0
- package/payload/platform/lib/device-url/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/device-url/dist/index.js +68 -0
- package/payload/platform/lib/device-url/dist/index.js.map +1 -0
- package/payload/platform/lib/device-url/src/index.ts +78 -0
- package/payload/platform/lib/device-url/tsconfig.json +8 -0
- package/payload/platform/lib/entitlement/PUBKEY-HASH.txt +1 -0
- package/payload/platform/lib/entitlement/dist/canonicalize.d.ts +26 -0
- package/payload/platform/lib/entitlement/dist/canonicalize.d.ts.map +1 -0
- package/payload/platform/lib/entitlement/dist/canonicalize.js +54 -0
- package/payload/platform/lib/entitlement/dist/canonicalize.js.map +1 -0
- package/payload/platform/lib/entitlement/dist/index.d.ts +76 -0
- package/payload/platform/lib/entitlement/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/entitlement/dist/index.js +293 -0
- package/payload/platform/lib/entitlement/dist/index.js.map +1 -0
- package/payload/platform/lib/entitlement/rubytech-pubkey.pem +3 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate-write.test.d.ts +2 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate-write.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate-write.test.js +97 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate-write.test.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.d.ts +2 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.js +112 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.d.ts +2 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.js +163 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.d.ts +2 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.js +89 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/warnings-envelope.test.d.ts +2 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/warnings-envelope.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/warnings-envelope.test.js +140 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/warnings-envelope.test.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-rewrite-stamp.d.ts +37 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-rewrite-stamp.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-rewrite-stamp.js +333 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-rewrite-stamp.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-shim-read.d.ts +85 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-shim-read.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-shim-read.js +93 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-shim-read.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-shim-write.d.ts +71 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-shim-write.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-shim-write.js +168 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-shim-write.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-validate.d.ts +50 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-validate.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-validate.js +197 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-validate.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/index.d.ts +26 -0
- package/payload/platform/lib/graph-mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/index.js +847 -0
- package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts +75 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cache.js +217 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cache.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.d.ts +42 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.js +87 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.js.map +1 -0
- package/payload/platform/lib/graph-mcp/src/__tests__/cypher-validate-write.test.ts +150 -0
- package/payload/platform/lib/graph-mcp/src/__tests__/cypher-validate.test.ts +141 -0
- package/payload/platform/lib/graph-mcp/src/__tests__/schema-cache.test.ts +169 -0
- package/payload/platform/lib/graph-mcp/src/__tests__/schema-cypher-parser.test.ts +99 -0
- package/payload/platform/lib/graph-mcp/src/__tests__/warnings-envelope.test.ts +151 -0
- package/payload/platform/lib/graph-mcp/src/cypher-rewrite-stamp.ts +349 -0
- package/payload/platform/lib/graph-mcp/src/cypher-shim-read.ts +141 -0
- package/payload/platform/lib/graph-mcp/src/cypher-shim-write.ts +240 -0
- package/payload/platform/lib/graph-mcp/src/cypher-validate.ts +249 -0
- package/payload/platform/lib/graph-mcp/src/index.ts +1076 -0
- package/payload/platform/lib/graph-mcp/src/schema-cache.ts +242 -0
- package/payload/platform/lib/graph-mcp/src/schema-cypher-parser.ts +84 -0
- package/payload/platform/lib/graph-mcp/tsconfig.json +8 -0
- package/payload/platform/lib/graph-search/dist/index.d.ts +227 -0
- package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/graph-search/dist/index.js +525 -0
- package/payload/platform/lib/graph-search/dist/index.js.map +1 -0
- package/payload/platform/lib/graph-search/src/__tests__/bm25-label-gate.test.ts +88 -0
- package/payload/platform/lib/graph-search/src/__tests__/bm25-only.test.ts +129 -0
- package/payload/platform/lib/graph-search/src/__tests__/bm25-strong-bypass-threshold.test.ts +126 -0
- package/payload/platform/lib/graph-search/src/__tests__/brochure-threshold.test.ts +136 -0
- package/payload/platform/lib/graph-search/src/__tests__/escape-and-normalise.test.ts +53 -0
- package/payload/platform/lib/graph-search/src/__tests__/expand-batch.test.ts +206 -0
- package/payload/platform/lib/graph-search/src/__tests__/fulltext-coverage.test.ts +280 -0
- package/payload/platform/lib/graph-search/src/__tests__/hybrid.test.ts +262 -0
- package/payload/platform/lib/graph-search/src/__tests__/vector-threshold.test.ts +170 -0
- package/payload/platform/lib/graph-search/src/index.ts +718 -0
- package/payload/platform/lib/graph-search/tsconfig.json +9 -0
- package/payload/platform/lib/graph-search/vitest.config.ts +9 -0
- package/payload/platform/lib/graph-trash/dist/index.d.ts +99 -0
- package/payload/platform/lib/graph-trash/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/graph-trash/dist/index.js +333 -0
- package/payload/platform/lib/graph-trash/dist/index.js.map +1 -0
- package/payload/platform/lib/graph-trash/src/index.ts +475 -0
- package/payload/platform/lib/graph-trash/tsconfig.json +8 -0
- package/payload/platform/lib/graph-write/dist/__tests__/account-id-gate.test.d.ts +2 -0
- package/payload/platform/lib/graph-write/dist/__tests__/account-id-gate.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/__tests__/account-id-gate.test.js +165 -0
- package/payload/platform/lib/graph-write/dist/__tests__/account-id-gate.test.js.map +1 -0
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.d.ts +2 -0
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js +226 -0
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js.map +1 -0
- package/payload/platform/lib/graph-write/dist/__tests__/audit.test.d.ts +2 -0
- package/payload/platform/lib/graph-write/dist/__tests__/audit.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/__tests__/audit.test.js +147 -0
- package/payload/platform/lib/graph-write/dist/__tests__/audit.test.js.map +1 -0
- package/payload/platform/lib/graph-write/dist/audit.d.ts +84 -0
- package/payload/platform/lib/graph-write/dist/audit.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/audit.js +129 -0
- package/payload/platform/lib/graph-write/dist/audit.js.map +1 -0
- package/payload/platform/lib/graph-write/dist/conversation-provenance.d.ts +26 -0
- package/payload/platform/lib/graph-write/dist/conversation-provenance.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js +81 -0
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js.map +1 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts +124 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/index.js +288 -0
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -0
- package/payload/platform/lib/graph-write/src/__tests__/account-id-gate.test.ts +189 -0
- package/payload/platform/lib/graph-write/src/__tests__/action-provenance-gate.test.ts +252 -0
- package/payload/platform/lib/graph-write/src/__tests__/audit.test.ts +162 -0
- package/payload/platform/lib/graph-write/src/audit.ts +182 -0
- package/payload/platform/lib/graph-write/src/conversation-provenance.ts +140 -0
- package/payload/platform/lib/graph-write/src/index.ts +386 -0
- package/payload/platform/lib/graph-write/tsconfig.json +8 -0
- package/payload/platform/lib/mcp-eager/dist/index.d.ts +61 -0
- package/payload/platform/lib/mcp-eager/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/mcp-eager/dist/index.js +49 -0
- package/payload/platform/lib/mcp-eager/dist/index.js.map +1 -0
- package/payload/platform/lib/mcp-eager/src/index.ts +78 -0
- package/payload/platform/lib/mcp-eager/tsconfig.json +8 -0
- package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts +53 -0
- package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/mcp-spawn-tee/dist/index.js +132 -0
- package/payload/platform/lib/mcp-spawn-tee/dist/index.js.map +1 -0
- package/payload/platform/lib/mcp-spawn-tee/src/index.ts +134 -0
- package/payload/platform/lib/mcp-spawn-tee/tsconfig.json +8 -0
- package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts +51 -0
- package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/mcp-stderr-tee/dist/index.js +196 -0
- package/payload/platform/lib/mcp-stderr-tee/dist/index.js.map +1 -0
- package/payload/platform/lib/mcp-stderr-tee/src/index.ts +206 -0
- package/payload/platform/lib/mcp-stderr-tee/tsconfig.json +8 -0
- package/payload/platform/lib/models/dist/index.d.ts +7 -0
- package/payload/platform/lib/models/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/models/dist/index.js +20 -0
- package/payload/platform/lib/models/dist/index.js.map +1 -0
- package/payload/platform/lib/models/src/index.ts +18 -0
- package/payload/platform/lib/models/tsconfig.json +8 -0
- package/payload/platform/lib/oauth-llm/dist/index.d.ts +118 -0
- package/payload/platform/lib/oauth-llm/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/oauth-llm/dist/index.js +388 -0
- package/payload/platform/lib/oauth-llm/dist/index.js.map +1 -0
- package/payload/platform/lib/oauth-llm/src/index.ts +585 -0
- package/payload/platform/lib/oauth-llm/tsconfig.json +8 -0
- package/payload/platform/lib/persistent-components/dist/index.d.ts +21 -0
- package/payload/platform/lib/persistent-components/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/persistent-components/dist/index.js +32 -0
- package/payload/platform/lib/persistent-components/dist/index.js.map +1 -0
- package/payload/platform/lib/persistent-components/src/index.ts +28 -0
- package/payload/platform/lib/persistent-components/tsconfig.json +8 -0
- package/payload/platform/lib/screening-patterns/dist/index.d.ts +29 -0
- package/payload/platform/lib/screening-patterns/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/screening-patterns/dist/index.js +48 -0
- package/payload/platform/lib/screening-patterns/dist/index.js.map +1 -0
- package/payload/platform/lib/screening-patterns/src/index.ts +51 -0
- package/payload/platform/lib/screening-patterns/tsconfig.json +8 -0
- package/payload/platform/lib/task-secrets/dist/index.d.ts +40 -0
- package/payload/platform/lib/task-secrets/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/task-secrets/dist/index.js +44 -0
- package/payload/platform/lib/task-secrets/dist/index.js.map +1 -0
- package/payload/platform/lib/task-secrets/src/__tests__/redact-secrets.test.ts +127 -0
- package/payload/platform/lib/task-secrets/src/index.ts +77 -0
- package/payload/platform/lib/task-secrets/tsconfig.json +9 -0
- package/payload/platform/lib/task-secrets/vitest.config.ts +9 -0
- package/payload/platform/neo4j/edge-annotations.json +154 -0
- package/payload/platform/neo4j/schema.cypher +1104 -0
- package/payload/platform/package-lock.json +3576 -0
- package/payload/platform/package.json +25 -0
- package/payload/platform/plugins/admin/PLUGIN.md +81 -0
- package/payload/platform/plugins/admin/hooks/__tests__/archive-ingest-surface-gate.test.sh +191 -0
- package/payload/platform/plugins/admin/hooks/__tests__/playwright-file-guard.test.sh +278 -0
- package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-base64-guard.test.sh +204 -0
- package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +225 -0
- package/payload/platform/plugins/admin/hooks/playwright-file-guard.sh +214 -0
- package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +294 -0
- package/payload/platform/plugins/admin/hooks/webfetch-preflight.mjs +363 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.js +91 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/public-hostname.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/public-hostname.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/public-hostname.test.js +98 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/public-hostname.test.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load-required-inputs.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load-required-inputs.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load-required-inputs.test.js +141 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load-required-inputs.test.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load.test.js +88 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-load.test.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +3495 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/neo4j.js +40 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.d.ts +39 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.js +249 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/public-hostname.d.ts +15 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/public-hostname.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/public-hostname.js +73 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/public-hostname.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts +35 -0
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js +140 -0
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/package.json +23 -0
- package/payload/platform/plugins/admin/mcp/vitest.config.ts +9 -0
- package/payload/platform/plugins/admin/references/chat-ui-guide.md +31 -0
- package/payload/platform/plugins/admin/references/contextual-ui.md +107 -0
- package/payload/platform/plugins/admin/skills/a4-print-documents/SKILL.md +241 -0
- package/payload/platform/plugins/admin/skills/access-manager/SKILL.md +28 -0
- package/payload/platform/plugins/admin/skills/access-manager/references/operations.md +197 -0
- package/payload/platform/plugins/admin/skills/business-profile/SKILL.md +53 -0
- package/payload/platform/plugins/admin/skills/datetime/SKILL.md +91 -0
- package/payload/platform/plugins/admin/skills/deck-pages/SKILL.md +418 -0
- package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +255 -0
- package/payload/platform/plugins/admin/skills/plainly/SKILL.md +105 -0
- package/payload/platform/plugins/admin/skills/plainly/references/worked-examples.md +301 -0
- package/payload/platform/plugins/admin/skills/plugin-management/SKILL.md +99 -0
- package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +277 -0
- package/payload/platform/plugins/admin/skills/publish-site/SKILL.md +72 -0
- package/payload/platform/plugins/admin/skills/qr-code/SKILL.md +35 -0
- package/payload/platform/plugins/admin/skills/qr-code/references/data-formats.md +113 -0
- package/payload/platform/plugins/admin/skills/skill-builder/SKILL.md +113 -0
- package/payload/platform/plugins/admin/skills/skill-builder/references/lean-pattern.md +110 -0
- package/payload/platform/plugins/admin/skills/skill-builder/references/pdf-generation.md +30 -0
- package/payload/platform/plugins/admin/skills/specialist-management/SKILL.md +44 -0
- package/payload/platform/plugins/admin/skills/stream-log-review/SKILL.md +71 -0
- package/payload/platform/plugins/admin/skills/stream-log-review/references/analysis-patterns.md +189 -0
- package/payload/platform/plugins/admin/skills/unzip-attachment/SKILL.md +79 -0
- package/payload/platform/plugins/admin/skills/unzip-attachment/__tests__/preflight.sh +148 -0
- package/payload/platform/plugins/admin/skills/unzip-attachment/references/safety.md +116 -0
- package/payload/platform/plugins/admin/skills/update-knowledge/SKILL.md +47 -0
- package/payload/platform/plugins/anthropic/PLUGIN.md +40 -0
- package/payload/platform/plugins/anthropic/references/console-api.md +186 -0
- package/payload/platform/plugins/anthropic/references/setup-guide.md +36 -0
- package/payload/platform/plugins/anthropic/skills/get-api-key/SKILL.md +138 -0
- package/payload/platform/plugins/business-assistant/PLUGIN.md +59 -0
- package/payload/platform/plugins/business-assistant/references/crm.md +112 -0
- package/payload/platform/plugins/business-assistant/references/document-management.md +96 -0
- package/payload/platform/plugins/business-assistant/references/escalation.md +126 -0
- package/payload/platform/plugins/business-assistant/references/invoicing.md +163 -0
- package/payload/platform/plugins/business-assistant/references/profiling.md +50 -0
- package/payload/platform/plugins/business-assistant/references/quoting.md +56 -0
- package/payload/platform/plugins/business-assistant/references/scheduling.md +127 -0
- package/payload/platform/plugins/business-assistant/references/task-management.md +163 -0
- package/payload/platform/plugins/cloudflare/PLUGIN.md +73 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js +29 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +283 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +1155 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +1 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.d.ts +90 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.d.ts.map +1 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.js +550 -0
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.js.map +1 -0
- package/payload/platform/plugins/cloudflare/mcp/package.json +18 -0
- package/payload/platform/plugins/cloudflare/mcp/vitest.config.ts +10 -0
- package/payload/platform/plugins/cloudflare/references/dashboard-guide.md +108 -0
- package/payload/platform/plugins/cloudflare/references/manual-setup.md +481 -0
- package/payload/platform/plugins/cloudflare/references/reset-guide.md +118 -0
- package/payload/platform/plugins/cloudflare/scripts/_stream-log.sh +154 -0
- package/payload/platform/plugins/cloudflare/scripts/list-cf-domains.sh +98 -0
- package/payload/platform/plugins/cloudflare/scripts/list-cf-domains.ts +751 -0
- package/payload/platform/plugins/cloudflare/scripts/reset-tunnel.sh +107 -0
- package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +826 -0
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +107 -0
- package/payload/platform/plugins/contacts/PLUGIN.md +31 -0
- package/payload/platform/plugins/contacts/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/contacts/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/index.js +433 -0
- package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.js +40 -0
- package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.d.ts +33 -0
- package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.js +53 -0
- package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts +23 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +123 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.d.ts +28 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.js +39 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts +41 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js +142 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.d.ts +52 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.js +119 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.d.ts +23 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.js +49 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +21 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +70 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.d.ts +14 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.js +43 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts +18 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js +95 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.d.ts +15 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js +72 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/package.json +19 -0
- package/payload/platform/plugins/deep-research/PLUGIN.md +13 -0
- package/payload/platform/plugins/deep-research/skills/deep-research/SKILL.md +46 -0
- package/payload/platform/plugins/deep-research/skills/deep-research/references/citation-styles.md +52 -0
- package/payload/platform/plugins/deep-research/skills/deep-research/references/research-modes.md +22 -0
- package/payload/platform/plugins/deep-research/skills/deep-research/references/search-strategy.md +24 -0
- package/payload/platform/plugins/docs/PLUGIN.md +52 -0
- package/payload/platform/plugins/docs/references/access-control.md +73 -0
- package/payload/platform/plugins/docs/references/admin-session.md +80 -0
- package/payload/platform/plugins/docs/references/attachments.md +44 -0
- package/payload/platform/plugins/docs/references/cloudflare.md +111 -0
- package/payload/platform/plugins/docs/references/contacts-guide.md +102 -0
- package/payload/platform/plugins/docs/references/deployment.md +150 -0
- package/payload/platform/plugins/docs/references/getting-started.md +82 -0
- package/payload/platform/plugins/docs/references/graph.md +149 -0
- package/payload/platform/plugins/docs/references/internals.md +512 -0
- package/payload/platform/plugins/docs/references/memory-guide.md +119 -0
- package/payload/platform/plugins/docs/references/migration-guide.md +90 -0
- package/payload/platform/plugins/docs/references/outlook-guide.md +69 -0
- package/payload/platform/plugins/docs/references/platform.md +111 -0
- package/payload/platform/plugins/docs/references/plugins-guide.md +174 -0
- package/payload/platform/plugins/docs/references/projects-guide.md +73 -0
- package/payload/platform/plugins/docs/references/settings.md +82 -0
- package/payload/platform/plugins/docs/references/telegram-guide.md +58 -0
- package/payload/platform/plugins/docs/references/troubleshooting.md +532 -0
- package/payload/platform/plugins/email/PLUGIN.md +49 -0
- package/payload/platform/plugins/email/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/index.js +291 -0
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts +118 -0
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.js +364 -0
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/embedding.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/lib/embedding.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/embedding.js +24 -0
- package/payload/platform/plugins/email/mcp/dist/lib/embedding.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts +87 -0
- package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/graph.js +324 -0
- package/payload/platform/plugins/email/mcp/dist/lib/graph.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +215 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js +735 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/email/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/neo4j.js +40 -0
- package/payload/platform/plugins/email/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/providers.d.ts +32 -0
- package/payload/platform/plugins/email/mcp/dist/lib/providers.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/providers.js +569 -0
- package/payload/platform/plugins/email/mcp/dist/lib/providers.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/screening.d.ts +29 -0
- package/payload/platform/plugins/email/mcp/dist/lib/screening.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/screening.js +105 -0
- package/payload/platform/plugins/email/mcp/dist/lib/screening.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts +21 -0
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js +77 -0
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.d.ts +38 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js +894 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.d.ts +25 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js +227 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.d.ts +19 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.js +151 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.d.ts +22 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.js +188 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-otp-extract.d.ts +15 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-otp-extract.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-otp-extract.js +142 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-otp-extract.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-read.d.ts +14 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-read.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-read.js +75 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-read.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts +10 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js +83 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-search.d.ts +15 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-search.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-search.js +63 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-search.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts +10 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.js +31 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.d.ts +22 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js +183 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-status.d.ts +6 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-status.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-status.js +43 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-status.js.map +1 -0
- package/payload/platform/plugins/email/mcp/package.json +23 -0
- package/payload/platform/plugins/email/references/email-reference.md +204 -0
- package/payload/platform/plugins/linkedin-import/PLUGIN.md +27 -0
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md +142 -0
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/connections.md +135 -0
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/profile.md +95 -0
- package/payload/platform/plugins/memory/PLUGIN.md +146 -0
- package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +879 -0
- package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +138 -0
- package/payload/platform/plugins/memory/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js +1813 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.js +92 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js +225 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js +100 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js +448 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/attachments.d.ts +37 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/attachments.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/attachments.js +69 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/attachments.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts +5 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js +30 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.d.ts +49 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.js +35 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts +47 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js +31 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.d.ts +3 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.js +155 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.d.ts +11 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.js +20 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts +14 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js +38 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.d.ts +16 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.js +59 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.d.ts +9 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.js +32 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.d.ts +3 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.js +29 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.d.ts +45 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.js +125 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.d.ts +9 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.js +61 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts +3 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js +29 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.d.ts +36 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.js +86 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.d.ts +42 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.js +114 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts +38 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js +89 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.d.ts +136 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.js +180 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +246 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +828 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts +63 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js +210 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js +40 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts +113 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js +455 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.d.ts +83 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.js +209 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/uuid.d.ts +3 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/uuid.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/uuid.js +12 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/uuid.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js +97 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js +184 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js +73 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.js +109 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js +84 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js +106 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-not-applicable.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-not-applicable.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-not-applicable.test.js +87 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-not-applicable.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.js +148 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts +89 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +542 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.d.ts +41 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.js +116 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.d.ts +8 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.js +7 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.d.ts +7 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.js +28 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-list.d.ts +7 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-list.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-list.js +7 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-list.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.d.ts +7 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.js +27 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +54 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +231 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.d.ts +34 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js +58 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts +57 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js +106 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.d.ts +16 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.js +91 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts +22 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js +36 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts +58 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js +125 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.d.ts +28 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js +93 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.d.ts +20 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.js +87 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +129 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +827 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-list-attachments.d.ts +19 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-list-attachments.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-list-attachments.js +125 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-list-attachments.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.d.ts +61 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js +102 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-read-attachment.d.ts +12 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-read-attachment.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-read-attachment.js +100 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-read-attachment.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts +9 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js +84 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-rename-attachment.d.ts +13 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-rename-attachment.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-rename-attachment.js +63 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-rename-attachment.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.d.ts +24 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.js +40 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts +5 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +42 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts +13 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +77 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +47 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +173 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts +24 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js +31 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts +44 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +322 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +65 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +369 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/package.json +24 -0
- package/payload/platform/plugins/memory/mcp/scripts/boot-smoke.sh +69 -0
- package/payload/platform/plugins/memory/mcp/scripts/graph/accept.sh +217 -0
- package/payload/platform/plugins/memory/mcp/scripts/graph/fixture.cypher +59 -0
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +15 -0
- package/payload/platform/plugins/memory/references/graph-primitives.md +342 -0
- package/payload/platform/plugins/memory/references/schema-base.md +234 -0
- package/payload/platform/plugins/memory/references/schema-creator.md +35 -0
- package/payload/platform/plugins/memory/references/schema-estate-agent.md +35 -0
- package/payload/platform/plugins/memory/references/schema-food-beverage.md +32 -0
- package/payload/platform/plugins/memory/references/schema-hospitality.md +31 -0
- package/payload/platform/plugins/memory/references/schema-logistics.md +30 -0
- package/payload/platform/plugins/memory/references/schema-professional-services.md +33 -0
- package/payload/platform/plugins/memory/references/schema-retail.md +33 -0
- package/payload/platform/plugins/memory/references/schema-trades.md +36 -0
- package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +202 -0
- package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +159 -0
- package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +108 -0
- package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +267 -0
- package/payload/platform/plugins/outlook/PLUGIN.md +48 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js +94 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.js +31 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js +213 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js +130 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts +65 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js +261 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts +61 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js +170 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.d.ts +18 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.js +152 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +60 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +189 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/log.d.ts +23 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/log.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/log.js +53 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/log.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts +26 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js +50 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.d.ts +12 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.js +32 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.d.ts +59 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.js +54 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.d.ts +14 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.js +45 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts +15 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js +48 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts +8 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js +49 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts +19 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js +58 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/package.json +20 -0
- package/payload/platform/plugins/outlook/mcp/scripts/verify-doc-impl.sh +109 -0
- package/payload/platform/plugins/outlook/references/auth.md +118 -0
- package/payload/platform/plugins/outlook/references/graph-surfaces.md +114 -0
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +65 -0
- package/payload/platform/plugins/projects/PLUGIN.md +90 -0
- package/payload/platform/plugins/projects/references/investigation.md +63 -0
- package/payload/platform/plugins/projects/references/retrospective.md +71 -0
- package/payload/platform/plugins/projects/references/review.md +51 -0
- package/payload/platform/plugins/projects/references/sprint.md +168 -0
- package/payload/platform/plugins/replicate/PLUGIN.md +23 -0
- package/payload/platform/plugins/replicate/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/replicate/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/replicate/mcp/dist/index.js +106 -0
- package/payload/platform/plugins/replicate/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/replicate/mcp/dist/lib/replicate-key.d.ts +15 -0
- package/payload/platform/plugins/replicate/mcp/dist/lib/replicate-key.d.ts.map +1 -0
- package/payload/platform/plugins/replicate/mcp/dist/lib/replicate-key.js +73 -0
- package/payload/platform/plugins/replicate/mcp/dist/lib/replicate-key.js.map +1 -0
- package/payload/platform/plugins/replicate/mcp/dist/tools/image-generate.d.ts +14 -0
- package/payload/platform/plugins/replicate/mcp/dist/tools/image-generate.d.ts.map +1 -0
- package/payload/platform/plugins/replicate/mcp/dist/tools/image-generate.js +191 -0
- package/payload/platform/plugins/replicate/mcp/dist/tools/image-generate.js.map +1 -0
- package/payload/platform/plugins/replicate/mcp/package.json +21 -0
- package/payload/platform/plugins/sales/PLUGIN.md +106 -0
- package/payload/platform/plugins/sales/references/close-tracking.md +69 -0
- package/payload/platform/plugins/sales/references/comparisons.md +99 -0
- package/payload/platform/plugins/sales/references/competitive-positioning.md +51 -0
- package/payload/platform/plugins/sales/references/faq.md +77 -0
- package/payload/platform/plugins/sales/references/objection-handling.md +157 -0
- package/payload/platform/plugins/sales/references/pricing.md +101 -0
- package/payload/platform/plugins/scheduling/PLUGIN.md +103 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/time-resolve-description.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/time-resolve-description.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/time-resolve-description.test.js +16 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/time-resolve-description.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +315 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.js +119 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/embedding.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/embedding.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/embedding.js +19 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/embedding.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.d.ts +47 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.js +362 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.d.ts +30 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.js +89 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/time-format.d.ts +48 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/time-format.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/time-format.js +140 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/time-format.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.d.ts +20 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +568 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts +7 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js +23 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +25 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +121 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-export-ics.d.ts +9 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-export-ics.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-export-ics.js +76 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-export-ics.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts +25 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js +53 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-import-ics.d.ts +8 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-import-ics.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-import-ics.js +48 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-import-ics.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts +20 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js +76 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts +18 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +167 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/package.json +23 -0
- package/payload/platform/plugins/scheduling/mcp/vitest.config.ts +9 -0
- package/payload/platform/plugins/tasks/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/tasks/.mcp.json +13 -0
- package/payload/platform/plugins/tasks/PLUGIN.md +81 -0
- package/payload/platform/plugins/tasks/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/tasks/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/index.js +503 -0
- package/payload/platform/plugins/tasks/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/lib/embeddings.d.ts +7 -0
- package/payload/platform/plugins/tasks/mcp/dist/lib/embeddings.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/lib/embeddings.js +24 -0
- package/payload/platform/plugins/tasks/mcp/dist/lib/embeddings.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/tasks/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/lib/neo4j.js +40 -0
- package/payload/platform/plugins/tasks/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-complete.d.ts +17 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-complete.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-complete.js +76 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-complete.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-create.d.ts +29 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-create.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-create.js +235 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-create.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-get.d.ts +40 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-get.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-get.js +125 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-get.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-list.d.ts +26 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-list.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-list.js +81 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-list.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-update.d.ts +19 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-update.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-update.js +102 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/project-update.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/session-list.d.ts +20 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/session-list.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/session-list.js +37 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/session-list.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/session-name.d.ts +12 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/session-name.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/session-name.js +28 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/session-name.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-complete.d.ts +16 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-complete.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-complete.js +33 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-complete.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.d.ts +63 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.js +141 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-get.d.ts +19 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-get.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-get.js +51 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-get.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-list.d.ts +18 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-list.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-list.js +66 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-list.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-ready.d.ts +21 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-ready.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-ready.js +54 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-ready.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-relate.d.ts +12 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-relate.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-relate.js +59 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-relate.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.d.ts +32 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.d.ts.map +1 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.js +112 -0
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.js.map +1 -0
- package/payload/platform/plugins/tasks/mcp/package.json +20 -0
- package/payload/platform/plugins/telegram/PLUGIN.md +35 -0
- package/payload/platform/plugins/telegram/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/telegram/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/index.js +198 -0
- package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts +41 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js +70 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.d.ts +16 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js +68 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts +20 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.js +34 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/package.json +19 -0
- package/payload/platform/plugins/telegram/references/setup-guide.md +50 -0
- package/payload/platform/plugins/waitlist/PLUGIN.md +63 -0
- package/payload/platform/plugins/waitlist/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/waitlist/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/index.js +328 -0
- package/payload/platform/plugins/waitlist/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/lib/embedding.d.ts +3 -0
- package/payload/platform/plugins/waitlist/mcp/dist/lib/embedding.d.ts.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/lib/embedding.js +48 -0
- package/payload/platform/plugins/waitlist/mcp/dist/lib/embedding.js.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/waitlist/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/lib/neo4j.js +40 -0
- package/payload/platform/plugins/waitlist/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/__tests__/waitlist-persist.test.d.ts +2 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/__tests__/waitlist-persist.test.d.ts.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/__tests__/waitlist-persist.test.js +70 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/__tests__/waitlist-persist.test.js.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-heal.d.ts +33 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-heal.d.ts.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-heal.js +124 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-heal.js.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-list.d.ts +23 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-list.d.ts.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-list.js +58 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-list.js.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-persist.d.ts +83 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-persist.d.ts.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-persist.js +433 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-persist.js.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-review.d.ts +19 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-review.d.ts.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-review.js +81 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-review.js.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-scan.d.ts +28 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-scan.d.ts.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-scan.js +50 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-scan.js.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-setup.d.ts +33 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-setup.d.ts.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-setup.js +335 -0
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-setup.js.map +1 -0
- package/payload/platform/plugins/waitlist/mcp/package.json +23 -0
- package/payload/platform/plugins/waitlist/mcp/vitest.config.ts +9 -0
- package/payload/platform/plugins/whatsapp/PLUGIN.md +71 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/call-api.d.ts +14 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/call-api.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/call-api.js +42 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/call-api.js.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.d.ts +8 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +469 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/package.json +19 -0
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +257 -0
- package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +82 -0
- package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +99 -0
- package/payload/platform/plugins/workflows/.claude-plugin/plugin.json +16 -0
- package/payload/platform/plugins/workflows/.mcp.json +12 -0
- package/payload/platform/plugins/workflows/PLUGIN.md +297 -0
- package/payload/platform/plugins/workflows/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/workflows/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/index.js +536 -0
- package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.d.ts +38 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.js +83 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/embeddings.d.ts +2 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/embeddings.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/embeddings.js +19 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/embeddings.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.d.ts +151 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.js +299 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/neo4j.js +40 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.d.ts +153 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.js +273 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/validate-capabilities.d.ts +32 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/validate-capabilities.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/validate-capabilities.js +97 -0
- package/payload/platform/plugins/workflows/mcp/dist/lib/validate-capabilities.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.d.ts +48 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js +166 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.d.ts +28 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.js +57 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.d.ts +64 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js +605 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.d.ts +29 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.js +55 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.d.ts +16 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.js +44 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.d.ts +28 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.js +71 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.d.ts +11 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.js +198 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.d.ts +13 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.d.ts.map +1 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.js +71 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.js.map +1 -0
- package/payload/platform/plugins/workflows/mcp/package.json +20 -0
- package/payload/platform/plugins/workflows/mcp/test-runner.mjs +710 -0
- package/payload/platform/plugins/workflows/mcp/test-workflows.sh +77 -0
- package/payload/platform/plugins/workflows/skills/workflow-manager/SKILL.md +88 -0
- package/payload/platform/scripts/__tests__/admin-persist-audit.test.ts +182 -0
- package/payload/platform/scripts/__tests__/first-token-creates-stream-log.test.sh +37 -0
- package/payload/platform/scripts/__tests__/logs-read-prefix.sh +237 -0
- package/payload/platform/scripts/admin-conversation-recover.mjs +386 -0
- package/payload/platform/scripts/admin-persist-audit.ts +217 -0
- package/payload/platform/scripts/check-no-conversation-id-leaks.mjs +165 -0
- package/payload/platform/scripts/check-no-task-id-leaks.mjs +110 -0
- package/payload/platform/scripts/check-sdk-oauth.mjs +185 -0
- package/payload/platform/scripts/check-skill-load-coverage.mjs +100 -0
- package/payload/platform/scripts/component-knowledgedoc-backfill.ts +214 -0
- package/payload/platform/scripts/conversation-id-allowlist.txt +151 -0
- package/payload/platform/scripts/dedupe-userprofile-ghosts.sh +388 -0
- package/payload/platform/scripts/generate-entitlement-fixture.mjs +152 -0
- package/payload/platform/scripts/installer-device-verify.sh +249 -0
- package/payload/platform/scripts/lib/resolve-account-dir.sh +186 -0
- package/payload/platform/scripts/log-adherence-check.sh +125 -0
- package/payload/platform/scripts/logs-read.sh +577 -0
- package/payload/platform/scripts/logs-read.test.sh +159 -0
- package/payload/platform/scripts/migrate-import.sh +437 -0
- package/payload/platform/scripts/redact-install-logs.sh +87 -0
- package/payload/platform/scripts/resume-tunnel.sh +117 -0
- package/payload/platform/scripts/seed-neo4j.sh +590 -0
- package/payload/platform/scripts/taskmaster-export.sh +388 -0
- package/payload/platform/scripts/test-laptop-vnc-boot.sh +88 -0
- package/payload/platform/scripts/verify-skill-tool-surface.sh +255 -0
- package/payload/platform/scripts/vnc.sh +475 -0
- package/payload/platform/scripts/wifi-provision-server/server.js +743 -0
- package/payload/platform/scripts/wifi-provision.sh +492 -0
- package/payload/platform/services/claude-session-manager/dist/config.d.ts +12 -0
- package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/config.js +27 -0
- package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +10 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js +186 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/index.d.ts +2 -0
- package/payload/platform/services/claude-session-manager/dist/index.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/index.js +64 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +4 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +31 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +34 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +91 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/session-store.d.ts +23 -0
- package/payload/platform/services/claude-session-manager/dist/session-store.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/session-store.js +31 -0
- package/payload/platform/services/claude-session-manager/dist/session-store.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/types.d.ts +33 -0
- package/payload/platform/services/claude-session-manager/dist/types.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/types.js +2 -0
- package/payload/platform/services/claude-session-manager/dist/types.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/url-capture.d.ts +12 -0
- package/payload/platform/services/claude-session-manager/dist/url-capture.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/url-capture.js +68 -0
- package/payload/platform/services/claude-session-manager/dist/url-capture.js.map +1 -0
- package/payload/platform/services/claude-session-manager/package.json +22 -0
- package/payload/platform/templates/account.json +12 -0
- package/payload/platform/templates/agents/admin/AGENTS.md +12 -0
- package/payload/platform/templates/agents/admin/IDENTITY.md +320 -0
- package/payload/platform/templates/agents/admin/LEARNINGS.md +3 -0
- package/payload/platform/templates/agents/admin/SOUL.md +23 -0
- package/payload/platform/templates/agents/public/IDENTITY.md +56 -0
- package/payload/platform/templates/agents/public/SOUL.md +19 -0
- package/payload/platform/templates/agents/public/config.json +9 -0
- package/payload/platform/templates/specialists/.claude-plugin/plugin.json +4 -0
- package/payload/platform/templates/specialists/agents/content-producer.md +104 -0
- package/payload/platform/templates/specialists/agents/database-operator.md +199 -0
- package/payload/platform/templates/specialists/agents/personal-assistant.md +209 -0
- package/payload/platform/templates/specialists/agents/project-manager.md +132 -0
- package/payload/platform/templates/specialists/agents/research-assistant.md +115 -0
- package/payload/platform/templates/systemd/edge.service.template +38 -0
- package/payload/platform/tsconfig.base.json +18 -0
- package/payload/premium-plugins/real-agency/BUNDLE.md +44 -0
- package/payload/premium-plugins/real-agency/agents/buyer-enquiry/IDENTITY.md +13 -0
- package/payload/premium-plugins/real-agency/agents/buyer-enquiry/SOUL.md +9 -0
- package/payload/premium-plugins/real-agency/agents/buyer-enquiry/template.json +9 -0
- package/payload/premium-plugins/real-agency/agents/compliance.md +303 -0
- package/payload/premium-plugins/real-agency/agents/negotiator.md +145 -0
- package/payload/premium-plugins/real-agency/agents/valuer.md +140 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/PLUGIN.md +36 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/commands/make-brochure.md +11 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/a4-print-documents/SKILL.md +478 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/brand-design/SKILL.md +192 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/make-brochure/SKILL.md +354 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/make-brochure/references/seller-brief-template.md +115 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/SKILL.md +119 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/build.md +270 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/copy.md +211 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/images.md +166 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/page-landing.md +376 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/page.html +1288 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/placeholders.md +250 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/registers.md +47 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/seller-brief.md +56 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/structure.md +249 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-brochure/references/template.html +2370 -0
- package/payload/premium-plugins/real-agency/plugins/brochures/skills/property-extract/SKILL.md +372 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/PLUGIN.md +35 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-feedback/SKILL.md +109 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/SKILL.md +42 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/buyer-qualification-questions.md +16 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/buyer-qualification.md +59 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/buyer-scripts.md +63 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/buyer-working-scripts.md +54 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/feedback-collection.md +42 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/offer-capture.md +38 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/viewing-booking.md +32 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-management/references/viewing-management.md +52 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/SKILL.md +407 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/care-fees-guide.md +68 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/divorce-sales-guide.md +61 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/downsizing-guide.md +45 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/first-time-buyers.md +92 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/first-time-sellers.md +78 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/probate-guide.md +53 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/buyer-seller-guides/references/upsizing-guide.md +41 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/property-enquiry/SKILL.md +126 -0
- package/payload/premium-plugins/real-agency/plugins/buyers/skills/viewing-management/SKILL.md +111 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/PLUGIN.md +34 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/SKILL.md +133 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/buy-back-your-time.md +37 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/firewave-gost-scorecards.md +14 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/keller-org-model.md +17 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/lencioni-team-models.md +22 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/listing-management-system.md +11 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/net-figure-form.md +11 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/serhant-bizinbox-notes.md +13 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/team-roles-commission.md +14 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/va-2026-ops.md +43 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-growth/references/wingman-structure.md +13 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-operations/SKILL.md +32 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-operations/references/crm-systems.md +57 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-operations/references/hiring-guide.md +59 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-operations/references/impact-framework.md +47 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-operations/references/minutes-equal-money.md +55 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/business-operations/references/team-management.md +48 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/SKILL.md +52 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/12-reasons.md +39 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/95-5-system.md +66 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/agent-attraction-scripts.md +90 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/business-partnership.md +92 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/exp-model-overview.md +66 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/model-comparison.md +66 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/exp-partnership/references/revenue-share-explained.md +57 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/SKILL.md +117 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/attraction-agent-notes.md +31 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/attraction-agent.md +58 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/authenticity-boundaries.md +28 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/become-a-brand-leader-notes.md +19 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/blast-formula.md +42 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/brand-leader.md +48 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/brand-strategy-system.md +59 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/content-engine.md +49 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/firewave-blast-and-blogging.md +23 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/gary-v-content.md +52 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/gary-v-principles.md +20 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/oversubscribed-positioning.md +18 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/platforms.md +41 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/priestley-oversubscribed.md +54 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/storeys-style-examples.md +25 -0
- package/payload/premium-plugins/real-agency/plugins/estate-business/skills/personal-branding/references/visual-identity.md +27 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/PLUGIN.md +55 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/agent-performance/SKILL.md +371 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/agent-performance/references/atomic-habits.md +52 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/agent-performance/references/daily-routine-scorecard.md +104 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/agent-performance/references/hp6-model.md +63 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/agent-performance/references/twelve-week-year.md +71 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/bespoke-coaching/SKILL.md +36 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/bespoke-coaching/references/coaching-boundaries.md +56 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/bespoke-coaching/references/feedback-framework.md +61 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/bespoke-coaching/references/performance-framework.md +109 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/coaching-toolkit/SKILL.md +421 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/coaching-toolkit/references/coaching-exercises.md +86 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/coaching-toolkit/references/goal-setting.md +78 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/coaching-toolkit/references/one-to-one-framework.md +92 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/coaching-toolkit/references/soi-workbook.md +103 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/serhant-training/SKILL.md +410 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/serhant-training/references/agent-training-guide.md +70 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/serhant-training/references/business-in-a-box.md +72 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/serhant-training/references/buyers-guide.md +53 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/serhant-training/references/codo-method.md +72 -0
- package/payload/premium-plugins/real-agency/plugins/estate-coaching/skills/serhant-training/references/website-planning-guide.md +79 -0
- package/payload/premium-plugins/real-agency/plugins/estate-onboarding/PLUGIN.md +31 -0
- package/payload/premium-plugins/real-agency/plugins/estate-onboarding/skills/bootstrap/SKILL.md +26 -0
- package/payload/premium-plugins/real-agency/plugins/estate-onboarding/skills/bootstrap/references/onboarding-flow.md +63 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/PLUGIN.md +34 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/negotiation/SKILL.md +35 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/negotiation/references/deal-saving.md +47 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/negotiation/references/negotiation-deep-guide.md +64 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/negotiation/references/negotiation-prep-principles.md +29 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/negotiation/references/negotiation-techniques.md +42 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/negotiation/references/offer-presentation.md +43 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-closer/SKILL.md +24 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-closer/references/serhant-emotion-stages.md +36 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/SKILL.md +30 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/chris-voss-discovery.md +88 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/firewave-gost-journey.md +68 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/phil-jones-openers.md +78 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/pre-listing-checklist.md +77 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/serhant-improv.md +22 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/tom-ferry-discovery.md +103 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-discovery/references/vendor-motivation-competitor.md +52 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-negotiation/SKILL.md +29 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-negotiation/references/chris-voss-negotiation.md +70 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-negotiation/references/phil-jones-price-words.md +40 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-negotiation/references/serhant-negotiation-plus.md +55 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-negotiation/references/tom-panos-commission-pricing.md +57 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-negotiation/references/tony-morris-questioning.md +54 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-progression/SKILL.md +27 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-progression/references/conveyancing-guide.md +54 -0
- package/payload/premium-plugins/real-agency/plugins/estate-sales/skills/sales-progression/references/transaction-tracking.md +66 -0
- package/payload/premium-plugins/real-agency/plugins/estate-teaching/PLUGIN.md +31 -0
- package/payload/premium-plugins/real-agency/plugins/estate-teaching/skills/content-directory/SKILL.md +39 -0
- package/payload/premium-plugins/real-agency/plugins/estate-teaching/skills/content-directory/references/module-delivery.md +65 -0
- package/payload/premium-plugins/real-agency/plugins/estate-teaching/skills/content-directory/references/progress-tracking.md +47 -0
- package/payload/premium-plugins/real-agency/plugins/leads/PLUGIN.md +32 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/SKILL.md +137 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/buyer-search-letter.md +28 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/buyer-search-letters.md +37 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/database-reactivation.md +30 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/email-nurture-sequences.md +45 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/facebook-referrals.md +30 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/firewave-email-nurture-sequences.md +41 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/keller-33-touch.md +34 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/neighbour-letters.md +31 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/neighbour-notification-letter.md +20 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/ofi-follow-up-dialogue.md +22 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/ofi-follow-up.md +26 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/serhant-three-fs-plus.md +21 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/sharran-10x10x10.md +18 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/sms-templates.md +40 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/sphere-of-influence-notes.md +34 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/sphere-of-influence.md +60 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/lead-nurturing/references/tom-panos-sms-templates.md +59 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/prospecting/SKILL.md +33 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/prospecting/references/database-matching.md +30 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/prospecting/references/database-value.md +53 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/prospecting/references/prospecting-dialogues.md +24 -0
- package/payload/premium-plugins/real-agency/plugins/leads/skills/prospecting/references/reactivation.md +34 -0
- package/payload/premium-plugins/real-agency/plugins/listings/PLUGIN.md +33 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/home-preparation/SKILL.md +28 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/home-preparation/references/kerb-appeal.md +38 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/home-preparation/references/photo-day.md +59 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/home-preparation/references/situational-tips.md +50 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/home-preparation/references/staging-guide.md +52 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/SKILL.md +286 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/references/booking-script.md +51 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/references/objection-scripts.md +193 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/references/penhaul-presentation.md +123 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/references/pre-listing-kit.md +139 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/references/set-to-sell.md +55 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/listing-presentation/references/sharran-frameworks.md +107 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/property-marketing/SKILL.md +337 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/property-marketing/references/auction-report-template.md +41 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/property-marketing/references/coming-soon-campaign.md +43 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/property-marketing/references/direct-mail-templates.md +121 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/property-marketing/references/eoi-form-template.md +62 -0
- package/payload/premium-plugins/real-agency/plugins/listings/skills/property-marketing/references/monthly-scorecard.md +63 -0
- package/payload/premium-plugins/real-agency/plugins/loop/PLUGIN.md +73 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/index.d.ts +2 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/index.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/index.js +293 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/index.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/crypto.d.ts +10 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/crypto.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/crypto.js +88 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/crypto.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/loop-api.d.ts +82 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/loop-api.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/loop-api.js +427 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/loop-api.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/neo4j.js +40 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/customer-preferences.d.ts +10 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/customer-preferences.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/customer-preferences.js +24 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/customer-preferences.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/feedback.d.ts +16 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/feedback.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/feedback.js +35 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/feedback.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-deregister.d.ts +5 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-deregister.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-deregister.js +19 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-deregister.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-list.d.ts +4 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-list.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-list.js +14 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-list.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-register.d.ts +9 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-register.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-register.js +60 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/key-register.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-enquiry.d.ts +13 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-enquiry.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-enquiry.js +41 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-enquiry.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-batch.d.ts +9 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-batch.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-batch.js +16 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-batch.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-request.d.ts +15 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-request.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-request.js +11 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match-request.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match.d.ts +10 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match.js +39 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/marketing-match.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-detail.d.ts +9 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-detail.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-detail.js +125 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-detail.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-search.d.ts +18 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-search.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-search.js +87 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/people-search.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-detail.d.ts +10 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-detail.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-detail.js +82 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-detail.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-listed.d.ts +12 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-listed.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-listed.js +32 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-listed.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-request.d.ts +15 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-request.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-request.js +11 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-request.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-search.d.ts +16 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-search.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-search.js +41 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/property-search.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/supplier.d.ts +13 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/supplier.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/supplier.js +49 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/supplier.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-availability.d.ts +7 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-availability.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-availability.js +19 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-availability.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-info.d.ts +5 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-info.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-info.js +32 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/team-info.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-create.d.ts +14 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-create.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-create.js +11 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-create.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-detail.d.ts +9 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-detail.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-detail.js +85 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-detail.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-search.d.ts +13 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-search.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-search.js +44 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-search.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-update.d.ts +14 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-update.d.ts.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-update.js +18 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/dist/tools/viewing-update.js.map +1 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/package-lock.json +2549 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/package.json +21 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/__tests__/loop-swagger.snapshot.json +26467 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/__tests__/swagger-write-coverage.test.ts +153 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/index.ts +444 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/lib/crypto.ts +105 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/lib/loop-api.ts +604 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/lib/neo4j.ts +51 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/customer-preferences.ts +66 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/feedback.ts +86 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/key-deregister.ts +27 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/key-list.ts +19 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/key-register.ts +95 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/marketing-enquiry.ts +113 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/marketing-match-batch.ts +53 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/marketing-match-request.ts +42 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/marketing-match.ts +84 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/people-detail.ts +245 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/people-search.ts +180 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/property-detail.ts +145 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/property-listed.ts +88 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/property-request.ts +42 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/property-search.ts +92 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/supplier.ts +129 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/team-availability.ts +52 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/team-info.ts +95 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/viewing-create.ts +41 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/viewing-detail.ts +171 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/viewing-search.ts +92 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/src/tools/viewing-update.ts +53 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/tsconfig.json +20 -0
- package/payload/premium-plugins/real-agency/plugins/loop/mcp/vitest.config.ts +9 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/PLUGIN.md +34 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/SKILL.md +42 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/fee-protection-and-agenda.md +28 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/listing-scripts.md +44 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/negotiation-deep-guide.md +70 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/price-alignment-scripts.md +33 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/price-alignment.md +34 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/scenario-scripts.md +38 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/seller-engagement.md +51 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/valuation-booking.md +76 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/vendor-scripts.md +63 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-communication/references/vendor-updates.md +41 -0
- package/payload/premium-plugins/real-agency/plugins/vendors/skills/vendor-updates/SKILL.md +153 -0
- package/payload/premium-plugins/teaching/PLUGIN.md +57 -0
- package/payload/premium-plugins/teaching/skills/interactive-tutor/SKILL.md +59 -0
- package/payload/premium-plugins/teaching/skills/interactive-tutor/references/assessment.md +70 -0
- package/payload/premium-plugins/teaching/skills/interactive-tutor/references/classroom-conduct.md +43 -0
- package/payload/premium-plugins/teaching/skills/interactive-tutor/references/teaching-modes.md +83 -0
- package/payload/premium-plugins/teaching/skills/lesson-planner/SKILL.md +48 -0
- package/payload/premium-plugins/teaching/skills/lesson-planner/references/context-gathering.md +41 -0
- package/payload/premium-plugins/teaching/skills/lesson-planner/references/plan-structure.md +94 -0
- package/payload/premium-plugins/teaching/skills/study-pack-builder/SKILL.md +52 -0
- package/payload/premium-plugins/teaching/skills/study-pack-builder/references/disaggregation.md +49 -0
- package/payload/premium-plugins/teaching/skills/study-pack-builder/references/materials.md +116 -0
- package/payload/premium-plugins/writer-craft/PLUGIN.md +87 -0
- package/payload/premium-plugins/writer-craft/agents/writer-craft--manuscript-reviewer.md +92 -0
- package/payload/premium-plugins/writer-craft/skills/citation-style/SKILL.md +94 -0
- package/payload/premium-plugins/writer-craft/skills/citation-style/references/book-and-chapter-models.md +77 -0
- package/payload/premium-plugins/writer-craft/skills/citation-style/references/citation-rules.md +103 -0
- package/payload/premium-plugins/writer-craft/skills/citation-style/references/journal-article-models.md +74 -0
- package/payload/premium-plugins/writer-craft/skills/citation-style/references/other-source-models.md +146 -0
- package/payload/premium-plugins/writer-craft/skills/citation-style/references/reference-list-rules.md +70 -0
- package/payload/premium-plugins/writer-craft/skills/editorial-practice/SKILL.md +108 -0
- package/payload/premium-plugins/writer-craft/skills/editorial-practice/references/copyediting.md +73 -0
- package/payload/premium-plugins/writer-craft/skills/editorial-practice/references/developmental-editing.md +85 -0
- package/payload/premium-plugins/writer-craft/skills/editorial-practice/references/genre-specific-editing.md +78 -0
- package/payload/premium-plugins/writer-craft/skills/editorial-practice/references/line-editing.md +55 -0
- package/payload/premium-plugins/writer-craft/skills/editorial-practice/references/self-editing.md +89 -0
- package/payload/premium-plugins/writer-craft/skills/persuasive-storytelling/SKILL.md +114 -0
- package/payload/premium-plugins/writer-craft/skills/persuasive-storytelling/references/audience-analysis.md +73 -0
- package/payload/premium-plugins/writer-craft/skills/persuasive-storytelling/references/crafting-persuasive-story.md +76 -0
- package/payload/premium-plugins/writer-craft/skills/persuasive-storytelling/references/persuasion-case-studies.md +67 -0
- package/payload/premium-plugins/writer-craft/skills/persuasive-storytelling/references/transformation-framework.md +86 -0
- package/payload/premium-plugins/writer-craft/skills/point-of-view/SKILL.md +97 -0
- package/payload/premium-plugins/writer-craft/skills/point-of-view/references/indirect-narration.md +72 -0
- package/payload/premium-plugins/writer-craft/skills/point-of-view/references/pov-types-and-voice.md +91 -0
- package/payload/premium-plugins/writer-craft/skills/point-of-view/references/protagonist-filter.md +71 -0
- package/payload/premium-plugins/writer-craft/skills/point-of-view/references/tense-and-person.md +85 -0
- package/payload/premium-plugins/writer-craft/skills/prose-craft/SKILL.md +100 -0
- package/payload/premium-plugins/writer-craft/skills/prose-craft/references/punctuation-and-grammar.md +72 -0
- package/payload/premium-plugins/writer-craft/skills/prose-craft/references/repetition.md +71 -0
- package/payload/premium-plugins/writer-craft/skills/prose-craft/references/sound-and-rhythm.md +64 -0
- package/payload/premium-plugins/writer-craft/skills/prose-craft/references/word-economy.md +93 -0
- package/payload/premium-plugins/writer-craft/skills/reader-engagement/SKILL.md +100 -0
- package/payload/premium-plugins/writer-craft/skills/reader-engagement/references/cause-effect-setup-payoff.md +79 -0
- package/payload/premium-plugins/writer-craft/skills/reader-engagement/references/conflict-escalation.md +81 -0
- package/payload/premium-plugins/writer-craft/skills/reader-engagement/references/hooking-readers.md +67 -0
- package/payload/premium-plugins/writer-craft/skills/reader-engagement/references/neurochemistry-of-engagement.md +94 -0
- package/payload/premium-plugins/writer-craft/skills/review-manuscript/SKILL.md +111 -0
- package/payload/premium-plugins/writer-craft/skills/review-manuscript/references/review-manuscript-checklist.md +119 -0
- package/payload/premium-plugins/writer-craft/skills/review-prose/SKILL.md +99 -0
- package/payload/premium-plugins/writer-craft/skills/review-prose/references/prose-review-checklist.md +112 -0
- package/payload/premium-plugins/writer-craft/skills/review-scene/SKILL.md +99 -0
- package/payload/premium-plugins/writer-craft/skills/review-scene/references/scene-analysis-framework.md +95 -0
- package/payload/premium-plugins/writer-craft/skills/story-architecture/SKILL.md +106 -0
- package/payload/premium-plugins/writer-craft/skills/story-architecture/references/blueprinting-and-scene-cards.md +118 -0
- package/payload/premium-plugins/writer-craft/skills/story-architecture/references/inner-issue-and-protagonist-goal.md +66 -0
- package/payload/premium-plugins/writer-craft/skills/story-architecture/references/misbelief-desire-worldview.md +87 -0
- package/payload/premium-plugins/writer-craft/skills/story-architecture/references/origin-scenes-and-escalation.md +82 -0
- package/payload/premium-plugins/writer-craft/skills/story-blueprint/SKILL.md +133 -0
- package/payload/premium-plugins/writer-craft/skills/story-blueprint/references/blueprinting-exercises.md +118 -0
- package/payload/premium-plugins/writer-craft/skills/story-blueprint/references/blueprinting-process.md +128 -0
- package/payload/server/adminuser-self-heal-QAWOZ3JV.js +45 -0
- package/payload/server/chunk-5FM432JB.js +4148 -0
- package/payload/server/chunk-6S5JTXAN.js +1544 -0
- package/payload/server/chunk-JSBRDJBE.js +30 -0
- package/payload/server/chunk-RNW625CL.js +759 -0
- package/payload/server/cloudflare-task-tracker-VC7QVU5H.js +22 -0
- package/payload/server/maxy-edge.js +1021 -0
- package/payload/server/package.json +13 -0
- package/payload/server/public/assets/Checkbox-C6ZCsPvl.js +1 -0
- package/payload/server/public/assets/_baseFor-BHtDrjIo.js +1 -0
- package/payload/server/public/assets/admin-CWMpccrR.css +1 -0
- package/payload/server/public/assets/admin-DVGJmN-k.js +216 -0
- package/payload/server/public/assets/arc-DMDAZHAN.js +1 -0
- package/payload/server/public/assets/architecture-YZFGNWBL-COhEvUpo.js +1 -0
- package/payload/server/public/assets/architectureDiagram-Q4EWVU46-DwN6H0y2.js +36 -0
- package/payload/server/public/assets/array-DetWRiSa.js +1 -0
- package/payload/server/public/assets/blockDiagram-DXYQGD6D-TUk_F7H6.js +132 -0
- package/payload/server/public/assets/c4Diagram-AHTNJAMY-CTjGko0X.js +10 -0
- package/payload/server/public/assets/channel-Cv-65bLZ.js +1 -0
- package/payload/server/public/assets/chunk-2KRD3SAO-Di4bO8ir.js +1 -0
- package/payload/server/public/assets/chunk-336JU56O-DulT46bV.js +2 -0
- package/payload/server/public/assets/chunk-426QAEUC-BwKj8yqp.js +1 -0
- package/payload/server/public/assets/chunk-4BX2VUAB-DyEhFk-Z.js +1 -0
- package/payload/server/public/assets/chunk-4TB4RGXK-CewO8YaZ.js +206 -0
- package/payload/server/public/assets/chunk-55IACEB6-BRJOZLpU.js +1 -0
- package/payload/server/public/assets/chunk-5FUZZQ4R-B3IWYz-k.js +62 -0
- package/payload/server/public/assets/chunk-5PVQY5BW-DyiDEtXY.js +2 -0
- package/payload/server/public/assets/chunk-67CJDMHE-BG6-9r6c.js +1 -0
- package/payload/server/public/assets/chunk-7N4EOEYR-BvMbitX7.js +1 -0
- package/payload/server/public/assets/chunk-AA7GKIK3-CE8mGBD5.js +1 -0
- package/payload/server/public/assets/chunk-BSJP7CBP-DP7LTBll.js +1 -0
- package/payload/server/public/assets/chunk-CIAEETIT-VfnIdN-h.js +1 -0
- package/payload/server/public/assets/chunk-DD-I1_y5.js +1 -0
- package/payload/server/public/assets/chunk-EDXVE4YY-B3u7wU36.js +1 -0
- package/payload/server/public/assets/chunk-ENJZ2VHE-BesS5YY4.js +10 -0
- package/payload/server/public/assets/chunk-FMBD7UC4-w-yBZsN2.js +15 -0
- package/payload/server/public/assets/chunk-FOC6F5B3-f9cFywhd.js +1 -0
- package/payload/server/public/assets/chunk-ICPOFSXX-C5_hbB6H.js +122 -0
- package/payload/server/public/assets/chunk-K5T4RW27-B_ZUrFUq.js +94 -0
- package/payload/server/public/assets/chunk-KGLVRYIC-CcWTvRlI.js +1 -0
- package/payload/server/public/assets/chunk-LIHQZDEY-D-5-peQw.js +1 -0
- package/payload/server/public/assets/chunk-ORNJ4GCN-B4Z5L25I.js +1 -0
- package/payload/server/public/assets/chunk-OYMX7WX6-CrL4rhBa.js +231 -0
- package/payload/server/public/assets/chunk-QZHKN3VN-CWh_0JsP.js +1 -0
- package/payload/server/public/assets/chunk-U2HBQHQK-CQpbcqRS.js +70 -0
- package/payload/server/public/assets/chunk-X2U36JSP-1HG7T4gX.js +1 -0
- package/payload/server/public/assets/chunk-XPW4576I-m1Y_r88I.js +32 -0
- package/payload/server/public/assets/chunk-YZCP3GAM-boN5wjX9.js +1 -0
- package/payload/server/public/assets/chunk-ZZ45TVLE-D6CiPO0Q.js +1 -0
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-wQ2-BRyB.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-B_YLNNHy.js +1 -0
- package/payload/server/public/assets/clone-VLK-GPZZ.js +1 -0
- package/payload/server/public/assets/cormorant-cyrillic-300-normal-CzPHYadL.woff +0 -0
- package/payload/server/public/assets/cormorant-cyrillic-300-normal-DFUoTmrg.woff2 +0 -0
- package/payload/server/public/assets/cormorant-cyrillic-400-normal-C8QS47vb.woff2 +0 -0
- package/payload/server/public/assets/cormorant-cyrillic-400-normal-D3EsxgFc.woff +0 -0
- package/payload/server/public/assets/cormorant-cyrillic-500-normal-B7dJQtg-.woff +0 -0
- package/payload/server/public/assets/cormorant-cyrillic-500-normal-BLlg2W5x.woff2 +0 -0
- package/payload/server/public/assets/cormorant-cyrillic-ext-300-normal-BXl3lXsi.woff2 +0 -0
- package/payload/server/public/assets/cormorant-cyrillic-ext-300-normal-DmxSOTe3.woff +0 -0
- package/payload/server/public/assets/cormorant-cyrillic-ext-400-normal-Bgrpe4p1.woff +0 -0
- package/payload/server/public/assets/cormorant-cyrillic-ext-400-normal-BlcaxZtM.woff2 +0 -0
- package/payload/server/public/assets/cormorant-cyrillic-ext-500-normal-CdQuyvtc.woff +0 -0
- package/payload/server/public/assets/cormorant-cyrillic-ext-500-normal-pZw22qtS.woff2 +0 -0
- package/payload/server/public/assets/cormorant-latin-300-normal-CJ5dfen0.woff2 +0 -0
- package/payload/server/public/assets/cormorant-latin-300-normal-DQZObO_3.woff +0 -0
- package/payload/server/public/assets/cormorant-latin-400-normal-BGH8Vunh.woff2 +0 -0
- package/payload/server/public/assets/cormorant-latin-400-normal-C3_-2Ua-.woff +0 -0
- package/payload/server/public/assets/cormorant-latin-500-normal-Dj3SQ6fR.woff +0 -0
- package/payload/server/public/assets/cormorant-latin-500-normal-EBdSCOD3.woff2 +0 -0
- package/payload/server/public/assets/cormorant-latin-ext-300-normal-CkiUx0UG.woff +0 -0
- package/payload/server/public/assets/cormorant-latin-ext-300-normal-De3D72RL.woff2 +0 -0
- package/payload/server/public/assets/cormorant-latin-ext-400-normal-DuQ88yz3.woff2 +0 -0
- package/payload/server/public/assets/cormorant-latin-ext-400-normal-DuXFa1Dr.woff +0 -0
- package/payload/server/public/assets/cormorant-latin-ext-500-normal-AH9qog1s.woff2 +0 -0
- package/payload/server/public/assets/cormorant-latin-ext-500-normal-DAuUCO41.woff +0 -0
- package/payload/server/public/assets/cormorant-vietnamese-300-normal-BVqIp_mg.woff2 +0 -0
- package/payload/server/public/assets/cormorant-vietnamese-300-normal-CEMS9Pw-.woff +0 -0
- package/payload/server/public/assets/cormorant-vietnamese-400-normal-C-RiYxEf.woff2 +0 -0
- package/payload/server/public/assets/cormorant-vietnamese-400-normal-DmUuA7Y2.woff +0 -0
- package/payload/server/public/assets/cormorant-vietnamese-500-normal-DsPuwQHi.woff2 +0 -0
- package/payload/server/public/assets/cormorant-vietnamese-500-normal-tGBW_mI7.woff +0 -0
- package/payload/server/public/assets/cose-bilkent-S5V4N54A-BhtgY3T7.js +1 -0
- package/payload/server/public/assets/cytoscape.esm-C9yNhe1u.js +321 -0
- package/payload/server/public/assets/dagre-CncXYNX1.js +1 -0
- package/payload/server/public/assets/dagre-KV5264BT-C0CcgCHW.js +4 -0
- package/payload/server/public/assets/data-Bt4Wsocg.js +1 -0
- package/payload/server/public/assets/defaultLocale-_WRwicXn.js +1 -0
- package/payload/server/public/assets/diagram-5BDNPKRD-CkUlWbsI.js +10 -0
- package/payload/server/public/assets/diagram-G4DWMVQ6-DVZBG1Ul.js +24 -0
- package/payload/server/public/assets/diagram-MMDJMWI5-BIb06ZkK.js +43 -0
- package/payload/server/public/assets/diagram-TYMM5635-BcT0_WR8.js +24 -0
- package/payload/server/public/assets/dist-Bd4S37oi.js +1 -0
- package/payload/server/public/assets/dm-sans-latin-400-normal-BwCSEQnW.woff +0 -0
- package/payload/server/public/assets/dm-sans-latin-400-normal-CW0RaeGs.woff2 +0 -0
- package/payload/server/public/assets/dm-sans-latin-500-normal-B9HHJjqV.woff2 +0 -0
- package/payload/server/public/assets/dm-sans-latin-500-normal-Dr3UlScf.woff +0 -0
- package/payload/server/public/assets/dm-sans-latin-ext-400-normal-BjWJ59Pq.woff +0 -0
- package/payload/server/public/assets/dm-sans-latin-ext-400-normal-BtiwyxMk.woff2 +0 -0
- package/payload/server/public/assets/dm-sans-latin-ext-500-normal-BJfUCQsA.woff2 +0 -0
- package/payload/server/public/assets/dm-sans-latin-ext-500-normal-DR84L5F-.woff +0 -0
- package/payload/server/public/assets/erDiagram-SMLLAGMA-BHk6lxIT.js +85 -0
- package/payload/server/public/assets/flatten-BsWEYbBB.js +1 -0
- package/payload/server/public/assets/flowDiagram-DWJPFMVM-CjeJn490.js +162 -0
- package/payload/server/public/assets/ganttDiagram-T4ZO3ILL-BGYvX3Lv.js +292 -0
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-DeTNsAO0.js +1 -0
- package/payload/server/public/assets/gitGraphDiagram-UUTBAWPF-Br4WLGzW.js +106 -0
- package/payload/server/public/assets/graph-CRSLozxc.js +1 -0
- package/payload/server/public/assets/graph-labels-CQyZQ0u6.js +1 -0
- package/payload/server/public/assets/graphlib-BWd9sMeP.js +1 -0
- package/payload/server/public/assets/info-OMHHGYJF-DJJ9GlS6.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-BjZeQoNZ.js +2 -0
- package/payload/server/public/assets/init-sTEcj9YX.js +1 -0
- package/payload/server/public/assets/isEmpty-BWl67LAZ.js +1 -0
- package/payload/server/public/assets/ishikawaDiagram-UXIWVN3A-POMae6Ni.js +70 -0
- package/payload/server/public/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/payload/server/public/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/payload/server/public/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
- package/payload/server/public/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
- package/payload/server/public/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/payload/server/public/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/payload/server/public/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
- package/payload/server/public/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
- package/payload/server/public/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/payload/server/public/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/payload/server/public/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/payload/server/public/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- package/payload/server/public/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/payload/server/public/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/payload/server/public/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
- package/payload/server/public/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
- package/payload/server/public/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/payload/server/public/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
- package/payload/server/public/assets/journeyDiagram-VCZTEJTY-ledtLV6x.js +139 -0
- package/payload/server/public/assets/jsx-runtime-DvanDPKm.css +1 -0
- package/payload/server/public/assets/jsx-runtime-vPsBTwUp.js +9 -0
- package/payload/server/public/assets/kanban-definition-6JOO6SKY-DI0T4W9z.js +89 -0
- package/payload/server/public/assets/katex-s61Rgv6l.js +257 -0
- package/payload/server/public/assets/lib-W5Jcz4p8.js +33 -0
- package/payload/server/public/assets/line-D1281H12.js +1 -0
- package/payload/server/public/assets/linear-0O14Y6uf.js +1 -0
- package/payload/server/public/assets/mermaid-parser.core-Dt95U7zk.js +4 -0
- package/payload/server/public/assets/mermaid.core-BuYSs1fU.js +11 -0
- package/payload/server/public/assets/mindmap-definition-QFDTVHPH-CFE1lmfX.js +96 -0
- package/payload/server/public/assets/ordinal-krseTxxN.js +1 -0
- package/payload/server/public/assets/packet-4T2RLAQJ-CGbvGkvF.js +1 -0
- package/payload/server/public/assets/page-CSUcuVW0.js +1 -0
- package/payload/server/public/assets/page-TARBO-Yr.js +50 -0
- package/payload/server/public/assets/path-B0Ik7Tu9.js +1 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-BZy8rjFn.js +1 -0
- package/payload/server/public/assets/pieDiagram-DEJITSTG-BSd9xa7v.js +30 -0
- package/payload/server/public/assets/public-5r6aRXrb.js +8 -0
- package/payload/server/public/assets/quadrantDiagram-34T5L4WZ-xYehPVw5.js +7 -0
- package/payload/server/public/assets/radar-PYXPWWZC-DcfWIVXr.js +1 -0
- package/payload/server/public/assets/reduce-tk-xY6Fv.js +1 -0
- package/payload/server/public/assets/requirementDiagram-MS252O5E-C6n77V1S.js +84 -0
- package/payload/server/public/assets/rough.esm-DKRO8IF-.js +1 -0
- package/payload/server/public/assets/sankeyDiagram-XADWPNL6-CWIfeO1M.js +10 -0
- package/payload/server/public/assets/sequenceDiagram-FGHM5R23-DDv2DuMo.js +157 -0
- package/payload/server/public/assets/src-B6XdH6xq.js +1 -0
- package/payload/server/public/assets/stateDiagram-FHFEXIEX-BPZdmsww.js +1 -0
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-33eC4TwE.js +1 -0
- package/payload/server/public/assets/timeline-definition-GMOUNBTQ-BpRT_wSX.js +120 -0
- package/payload/server/public/assets/treeView-SZITEDCU-3WugwVdj.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-Cf5mDLlu.js +1 -0
- package/payload/server/public/assets/vennDiagram-DHZGUBPP-BG8ubucH.js +34 -0
- package/payload/server/public/assets/wardley-RL74JXVD-Bv4md4b3.js +1 -0
- package/payload/server/public/assets/wardleyDiagram-NUSXRM2D-D4E7PU1B.js +20 -0
- package/payload/server/public/assets/xychartDiagram-5P7HB3ND-Ur2xVM-c.js +7 -0
- package/payload/server/public/brand/claude.png +0 -0
- package/payload/server/public/brand/favicon.ico +0 -0
- package/payload/server/public/brand/maxy-black.png +0 -0
- package/payload/server/public/brand/maxy-horizontal.png +0 -0
- package/payload/server/public/brand/maxy-monochrome.png +0 -0
- package/payload/server/public/brand/maxy-square.png +0 -0
- package/payload/server/public/brand/maxy.png +0 -0
- package/payload/server/public/brand/star.png +0 -0
- package/payload/server/public/brand-constants.json +8 -0
- package/payload/server/public/brand-defaults.css +12 -0
- package/payload/server/public/data.html +18 -0
- package/payload/server/public/favicon.ico +0 -0
- package/payload/server/public/graph.html +19 -0
- package/payload/server/public/index.html +23 -0
- package/payload/server/public/public.html +19 -0
- package/payload/server/public/robots.txt +5 -0
- package/payload/server/public/vnc-popout.html +63 -0
- package/payload/server/server-init.cjs +115 -0
- package/payload/server/server.js +14960 -0
|
@@ -0,0 +1,3495 @@
|
|
|
1
|
+
import { initStderrTee } from "../../../../lib/mcp-stderr-tee/dist/index.js";
|
|
2
|
+
initStderrTee("admin");
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { eagerTool } from "../../../../lib/mcp-eager/dist/index.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
8
|
+
import { resolve, join } from "node:path";
|
|
9
|
+
import { execFileSync } from "node:child_process";
|
|
10
|
+
import { appendFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { writeKey, validateKey, hasKey, keyFilePath, deleteKey } from "../../../../lib/anthropic-key/dist/index.js";
|
|
12
|
+
import { writeAdminEntry, removeAdminFromAccount } from "../../../../lib/admins-write/dist/index.js";
|
|
13
|
+
import { isPersistentComponent } from "../../../../lib/persistent-components/dist/index.js";
|
|
14
|
+
import { deviceUrlBlock } from "../../../../lib/device-url/dist/index.js";
|
|
15
|
+
import { substituteBrandPlaceholders } from "../../../../lib/brand-templating/dist/index.js";
|
|
16
|
+
import { resolveEntitlement } from "../../../../lib/entitlement/dist/index.js";
|
|
17
|
+
import { createHash, randomInt, randomUUID } from "node:crypto";
|
|
18
|
+
import { createConnection } from "node:net";
|
|
19
|
+
import { homedir, hostname as osHostname } from "node:os";
|
|
20
|
+
import QRCode from "qrcode";
|
|
21
|
+
import { getSession, closeDriver } from "./lib/neo4j.js";
|
|
22
|
+
import { getOnboardingState, completeOnboardingStep } from "./lib/onboarding.js";
|
|
23
|
+
import { findSkillOwners, computePluginReadHint, loadSkill, parseRequiredInputs } from "./skill-resolution.js";
|
|
24
|
+
import { resolvePublicHostname } from "./lib/public-hostname.js";
|
|
25
|
+
const server = new McpServer({
|
|
26
|
+
name: "admin",
|
|
27
|
+
version: "0.1.0",
|
|
28
|
+
});
|
|
29
|
+
const PLATFORM_ROOT = process.env.PLATFORM_ROOT ?? resolve(import.meta.dirname, "../../../..");
|
|
30
|
+
const ACCOUNT_ID = process.env.ACCOUNT_ID;
|
|
31
|
+
if (!ACCOUNT_ID) {
|
|
32
|
+
throw new Error("ACCOUNT_ID environment variable is required");
|
|
33
|
+
}
|
|
34
|
+
const PLATFORM_PORT = process.env.PLATFORM_PORT;
|
|
35
|
+
if (!PLATFORM_PORT) {
|
|
36
|
+
throw new Error("PLATFORM_PORT environment variable is required — set by getMcpServers() in claude-agent.ts");
|
|
37
|
+
}
|
|
38
|
+
// Brand-aware config — reads configDir, productName, and commercialMode from
|
|
39
|
+
// brand.json stamped at install time. commercialMode (Task 831) gates the
|
|
40
|
+
// entitlement verifier: false (default) preserves personal-mode installs;
|
|
41
|
+
// true requires a Rubytech-signed entitlement.json or the install runs locked.
|
|
42
|
+
// No fallback: if brand.json is missing or incomplete, the platform wasn't properly installed.
|
|
43
|
+
function resolveBrandConfig() {
|
|
44
|
+
const brandPath = resolve(PLATFORM_ROOT, "config", "brand.json");
|
|
45
|
+
if (!existsSync(brandPath)) {
|
|
46
|
+
throw new Error(`brand.json not found at ${brandPath} — platform not properly installed`);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
|
|
50
|
+
if (!brand.configDir) {
|
|
51
|
+
throw new Error(`brand.json at ${brandPath} is missing the configDir field`);
|
|
52
|
+
}
|
|
53
|
+
if (!brand.productName) {
|
|
54
|
+
throw new Error(`brand.json at ${brandPath} is missing the productName field`);
|
|
55
|
+
}
|
|
56
|
+
// Task 924 + 959 — brand.json is the single source of truth for the
|
|
57
|
+
// four brand-scoped port fields. An admin MCP probe must see the exact
|
|
58
|
+
// same numbers the rest of the platform binds; silent vncDisplay-derived
|
|
59
|
+
// fallback was a recurrence-class silent-fallback-masks-root-cause
|
|
60
|
+
// violation (Task 959) and is now loud-fail.
|
|
61
|
+
const brandLabel = String(brand.configDir).replace(/^\./, "");
|
|
62
|
+
if (typeof brand.vncDisplay !== "number") {
|
|
63
|
+
console.error(`[mcp:admin] error reason=cdp-port-unresolved brand=${brandLabel} path=${brandPath} field=vncDisplay json_keys=${Object.keys(brand).join(",")}`);
|
|
64
|
+
throw new Error(`brand.json at ${brandPath} missing required field: vncDisplay`);
|
|
65
|
+
}
|
|
66
|
+
for (const field of ["rfbPort", "websockifyPort", "cdpPort"]) {
|
|
67
|
+
if (typeof brand[field] !== "number") {
|
|
68
|
+
console.error(`[mcp:admin] error reason=cdp-port-unresolved brand=${brandLabel} path=${brandPath} field=${field} json_keys=${Object.keys(brand).join(",")}`);
|
|
69
|
+
throw new Error(`brand.json at ${brandPath} missing required field: ${field}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
configDir: brand.configDir,
|
|
74
|
+
productName: brand.productName,
|
|
75
|
+
commercialMode: brand.commercialMode === true,
|
|
76
|
+
vncDisplay: brand.vncDisplay,
|
|
77
|
+
rfbPort: brand.rfbPort,
|
|
78
|
+
websockifyPort: brand.websockifyPort,
|
|
79
|
+
cdpPort: brand.cdpPort,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
if (err instanceof SyntaxError) {
|
|
84
|
+
throw new Error(`brand.json at ${brandPath} is not valid JSON: ${err.message}`);
|
|
85
|
+
}
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const BRAND_CONFIG = resolveBrandConfig();
|
|
90
|
+
const CONFIG_DIR = resolve(homedir(), BRAND_CONFIG.configDir);
|
|
91
|
+
const BRAND_NAME = BRAND_CONFIG.productName;
|
|
92
|
+
// Entitlement input shape for the verifier. configDir is rooted under $HOME
|
|
93
|
+
// (where entitlement.json is delivered post-purchase). platformRoot is needed
|
|
94
|
+
// because the verifier runs in two bundle contexts (CJS dist for MCP, ESM tsup
|
|
95
|
+
// bundle for UI) — caller passes the root rather than the verifier guessing.
|
|
96
|
+
const ENTITLEMENT_BRAND = {
|
|
97
|
+
configDir: CONFIG_DIR,
|
|
98
|
+
platformRoot: PLATFORM_ROOT,
|
|
99
|
+
commercialMode: BRAND_CONFIG.commercialMode,
|
|
100
|
+
};
|
|
101
|
+
/** Resolve current effective entitlement. Memoized inside the verifier. */
|
|
102
|
+
async function currentEntitlement() {
|
|
103
|
+
const config = await readAccountConfig();
|
|
104
|
+
return resolveEntitlement(ENTITLEMENT_BRAND, {
|
|
105
|
+
accountId: typeof config.accountId === "string" ? config.accountId : "",
|
|
106
|
+
customerEmail: typeof config.customerEmail === "string" ? config.customerEmail : undefined,
|
|
107
|
+
tier: typeof config.tier === "string" ? config.tier : undefined,
|
|
108
|
+
purchasedPlugins: Array.isArray(config.purchasedPlugins)
|
|
109
|
+
? config.purchasedPlugins
|
|
110
|
+
: undefined,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const REMOTE_PASSWORD_FILE = resolve(CONFIG_DIR, ".remote-password");
|
|
114
|
+
// Resolve account directory
|
|
115
|
+
function getAccountDir() {
|
|
116
|
+
const dir = resolve(PLATFORM_ROOT, "..", "data/accounts", ACCOUNT_ID);
|
|
117
|
+
if (!existsSync(dir)) {
|
|
118
|
+
throw new Error(`Account directory not found: ${dir}`);
|
|
119
|
+
}
|
|
120
|
+
return dir;
|
|
121
|
+
}
|
|
122
|
+
async function readAccountConfig() {
|
|
123
|
+
const configPath = join(getAccountDir(), "account.json");
|
|
124
|
+
const content = await readFile(configPath, "utf-8");
|
|
125
|
+
return JSON.parse(content);
|
|
126
|
+
}
|
|
127
|
+
const STAGING_ROOT = resolve(PLATFORM_ROOT, "../premium-plugins");
|
|
128
|
+
const PLUGINS_DIR = resolve(PLATFORM_ROOT, "plugins");
|
|
129
|
+
const CORE_PLUGINS = ["admin", "memory", "docs", "cloudflare", "anthropic"];
|
|
130
|
+
// users.json lives under CONFIG_DIR (persistent) — see paths.ts comment for
|
|
131
|
+
// the regression that motivated the move. Both platform/ui and this MCP plugin
|
|
132
|
+
// resolve the same file via brand-aware $HOME/<configDir>, so the device-level
|
|
133
|
+
// admin auth store is single-pathed across both processes (Task 904).
|
|
134
|
+
const USERS_FILE = resolve(CONFIG_DIR, "users.json");
|
|
135
|
+
/** Maximum number of admin users allowed per tier. */
|
|
136
|
+
const MAX_ADMINS_BY_TIER = {
|
|
137
|
+
solo: 1,
|
|
138
|
+
family: 5,
|
|
139
|
+
pro: 5,
|
|
140
|
+
};
|
|
141
|
+
const MAX_ADMINS_DEFAULT = 5;
|
|
142
|
+
function hashPin(pin) {
|
|
143
|
+
return createHash("sha256").update(pin).digest("hex");
|
|
144
|
+
}
|
|
145
|
+
function readUsersJson() {
|
|
146
|
+
if (!existsSync(USERS_FILE)) {
|
|
147
|
+
throw new Error("users.json not found — run the seed script first");
|
|
148
|
+
}
|
|
149
|
+
const raw = readFileSync(USERS_FILE, "utf-8").trim();
|
|
150
|
+
if (!raw)
|
|
151
|
+
return [];
|
|
152
|
+
return JSON.parse(raw);
|
|
153
|
+
}
|
|
154
|
+
function writeUsersJson(users) {
|
|
155
|
+
writeFileSync(USERS_FILE, JSON.stringify(users, null, 2) + "\n", "utf-8");
|
|
156
|
+
}
|
|
157
|
+
function generateUniquePin(users, maxRetries = 10) {
|
|
158
|
+
const existingPins = new Set(users.map(u => u.pin));
|
|
159
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
160
|
+
const raw = String(randomInt(0, 10000)).padStart(4, "0");
|
|
161
|
+
const hashed = hashPin(raw);
|
|
162
|
+
if (!existingPins.has(hashed)) {
|
|
163
|
+
return raw;
|
|
164
|
+
}
|
|
165
|
+
console.error(`[admin] PIN collision during admin-add: rejecting duplicate (attempt ${i + 1}/${maxRetries})`);
|
|
166
|
+
}
|
|
167
|
+
throw new Error("Could not generate a unique PIN after multiple attempts. Specify a PIN manually.");
|
|
168
|
+
}
|
|
169
|
+
const VALID_PLUGIN_NAME = /^[a-z0-9-]+$/;
|
|
170
|
+
/** Parse YAML frontmatter from markdown content. Returns key-value pairs. */
|
|
171
|
+
function parseFrontmatter(content) {
|
|
172
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
173
|
+
if (!match)
|
|
174
|
+
return {};
|
|
175
|
+
const result = {};
|
|
176
|
+
let currentKey = "";
|
|
177
|
+
let currentArray = null;
|
|
178
|
+
for (const line of match[1].split("\n")) {
|
|
179
|
+
const kvMatch = line.match(/^(\w[\w-]*):\s*(.*)$/);
|
|
180
|
+
if (kvMatch) {
|
|
181
|
+
if (currentArray && currentKey) {
|
|
182
|
+
result[currentKey] = currentArray;
|
|
183
|
+
currentArray = null;
|
|
184
|
+
}
|
|
185
|
+
const [, key, rawValue] = kvMatch;
|
|
186
|
+
const value = rawValue.replace(/^["']|["']$/g, "").trim();
|
|
187
|
+
if (value) {
|
|
188
|
+
result[key] = value;
|
|
189
|
+
currentKey = key;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
// Key with no inline value — next lines may be array items
|
|
193
|
+
currentKey = key;
|
|
194
|
+
currentArray = [];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else if (currentArray !== null) {
|
|
198
|
+
const itemMatch = line.match(/^\s*-\s+(.+)$/);
|
|
199
|
+
if (itemMatch) {
|
|
200
|
+
currentArray.push(itemMatch[1].trim());
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (currentArray && currentKey) {
|
|
205
|
+
result[currentKey] = currentArray;
|
|
206
|
+
}
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
// ===================================================================
|
|
210
|
+
// System tools
|
|
211
|
+
// ===================================================================
|
|
212
|
+
function checkPort(port, timeoutMs = 1000) {
|
|
213
|
+
return new Promise((res) => {
|
|
214
|
+
const socket = createConnection(port, "127.0.0.1");
|
|
215
|
+
socket.setTimeout(timeoutMs);
|
|
216
|
+
socket.once("connect", () => { socket.destroy(); res(true); });
|
|
217
|
+
socket.once("error", () => { socket.destroy(); res(false); });
|
|
218
|
+
socket.once("timeout", () => { socket.destroy(); res(false); });
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
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 () => {
|
|
222
|
+
const checks = {};
|
|
223
|
+
try {
|
|
224
|
+
// Task 580: NEO4J_URI must be explicit. The outer try/catch surfaces the
|
|
225
|
+
// unset condition as `checks.neo4j = "unreachable: NEO4J_URI unset..."`
|
|
226
|
+
// so system-status still reports cleanly — the misconfig appears in the
|
|
227
|
+
// check output instead of taking down the whole tool.
|
|
228
|
+
const neo4jUri = process.env.NEO4J_URI;
|
|
229
|
+
if (!neo4jUri) {
|
|
230
|
+
throw new Error("NEO4J_URI unset (Task 580 — no silent default)");
|
|
231
|
+
}
|
|
232
|
+
// NOTE: the port-replace below only works when the URI ends in :7687.
|
|
233
|
+
// For non-default bolt ports (e.g. :7688 on realagent) the HTTP port is
|
|
234
|
+
// bolt - 213 (see graph-proxy.ts). Pre-existing fragility — flagged as a
|
|
235
|
+
// follow-up in .docs/neo4j-uri-audit.md. Not in scope for Task 580.
|
|
236
|
+
const res = await fetch(neo4jUri.replace("bolt://", "http://").replace("7687", "7474"), { signal: AbortSignal.timeout(3000) });
|
|
237
|
+
checks.neo4j = res.ok ? "healthy" : `unhealthy (${res.status})`;
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
checks.neo4j = `unreachable: ${err instanceof Error ? err.message : String(err)}`;
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
const ollamaUrl = process.env.OLLAMA_URL ?? "http://localhost:11434";
|
|
244
|
+
const res = await fetch(`${ollamaUrl}/api/tags`, {
|
|
245
|
+
signal: AbortSignal.timeout(3000),
|
|
246
|
+
});
|
|
247
|
+
if (res.ok) {
|
|
248
|
+
const data = (await res.json());
|
|
249
|
+
const models = data.models?.map((m) => m.name).join(", ") ?? "none";
|
|
250
|
+
checks.ollama = `healthy (models: ${models})`;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
checks.ollama = `unhealthy (${res.status})`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
checks.ollama = `unreachable: ${err instanceof Error ? err.message : String(err)}`;
|
|
258
|
+
}
|
|
259
|
+
// Cloudflare tunnel: lightweight process check (full status via tunnel-status in cloudflare plugin)
|
|
260
|
+
try {
|
|
261
|
+
execFileSync("pgrep", ["-x", "cloudflared"], { timeout: 2000 });
|
|
262
|
+
checks.cloudflare = "running";
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
// pgrep exits non-zero if no process found
|
|
266
|
+
try {
|
|
267
|
+
execFileSync("which", ["cloudflared"], { encoding: "utf-8", timeout: 3000 });
|
|
268
|
+
checks.cloudflare = "installed but not running";
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
checks.cloudflare = "not installed";
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// Crontab — brand cron entries registered by the installer
|
|
275
|
+
try {
|
|
276
|
+
const crontabOutput = execFileSync("crontab", ["-l"], {
|
|
277
|
+
encoding: "utf-8",
|
|
278
|
+
timeout: 3000,
|
|
279
|
+
});
|
|
280
|
+
const cronBeginMarker = `# BEGIN ${BRAND_NAME.toUpperCase()} CRONS`;
|
|
281
|
+
const cronEndMarker = `# END ${BRAND_NAME.toUpperCase()} CRONS`;
|
|
282
|
+
const beginIdx = crontabOutput.indexOf(cronBeginMarker);
|
|
283
|
+
const endIdx = crontabOutput.indexOf(cronEndMarker);
|
|
284
|
+
if (beginIdx === -1 || endIdx === -1) {
|
|
285
|
+
checks.crontab = `no ${BRAND_NAME} crons registered — run the installer to register them`;
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
const block = crontabOutput.slice(beginIdx, endIdx);
|
|
289
|
+
const entries = block
|
|
290
|
+
.split("\n")
|
|
291
|
+
.filter((line) => line.trim() && !line.startsWith("#"));
|
|
292
|
+
if (entries.length === 0) {
|
|
293
|
+
checks.crontab = `${BRAND_NAME} cron block present but empty`;
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
const names = entries.map((line) => {
|
|
297
|
+
const commentMatch = line.match(/#\s*(.+)$/);
|
|
298
|
+
if (commentMatch)
|
|
299
|
+
return commentMatch[1].trim();
|
|
300
|
+
const scriptMatch = line.match(/([^/\s]+)\.js/);
|
|
301
|
+
if (scriptMatch)
|
|
302
|
+
return scriptMatch[1];
|
|
303
|
+
return line.trim().split(/\s+/).slice(-1)[0];
|
|
304
|
+
});
|
|
305
|
+
checks.crontab = `${entries.length} registered: ${names.join(", ")}`;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
checks.crontab = "no crontab configured for this user";
|
|
311
|
+
}
|
|
312
|
+
// VNC display server (this brand's rfbPort — Task 924)
|
|
313
|
+
checks.vnc = (await checkPort(BRAND_CONFIG.rfbPort)) ? "running" : "not running";
|
|
314
|
+
// Chrome CDP (this brand's cdpPort — Task 924); personal-assistant
|
|
315
|
+
// browser tools depend on this.
|
|
316
|
+
const chromeUp = await checkPort(BRAND_CONFIG.cdpPort);
|
|
317
|
+
checks.chrome = chromeUp
|
|
318
|
+
? "running"
|
|
319
|
+
: `not running — personal-assistant browser tools degraded (CDP port ${BRAND_CONFIG.cdpPort} not listening)`;
|
|
320
|
+
// WiFi — current connection state (Task 429)
|
|
321
|
+
try {
|
|
322
|
+
const wifiScan = execFileSync("nmcli", ["-t", "-f", "ACTIVE,SSID,SIGNAL", "device", "wifi", "list"], { encoding: "utf-8", timeout: 5000 });
|
|
323
|
+
const activeLine = wifiScan.split("\n").find((l) => l.startsWith("yes:"));
|
|
324
|
+
if (activeLine) {
|
|
325
|
+
const fields = activeLine.split(/(?<!\\):/).map((f) => f.replace(/\\:/g, ":"));
|
|
326
|
+
checks.wifi = `connected to ${fields[1]} (${fields[2]}%)`;
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
checks.wifi = "not connected";
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
checks.wifi = "not available";
|
|
334
|
+
}
|
|
335
|
+
// Specialist agents — read AGENTS.md registry, verify files exist
|
|
336
|
+
try {
|
|
337
|
+
const accountDir = getAccountDir();
|
|
338
|
+
const agentsMdPath = join(accountDir, "agents/admin/AGENTS.md");
|
|
339
|
+
const specialistsAgentsDir = join(accountDir, "specialists/agents");
|
|
340
|
+
if (!existsSync(agentsMdPath)) {
|
|
341
|
+
checks.specialists = "not configured (AGENTS.md missing)";
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
const content = readFileSync(agentsMdPath, "utf-8");
|
|
345
|
+
const entries = content.match(/^- \*\*specialists:([^*]+)\*\*/gm);
|
|
346
|
+
if (!entries || entries.length === 0) {
|
|
347
|
+
checks.specialists = "not configured (AGENTS.md empty)";
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
const lines = [];
|
|
351
|
+
for (const entry of entries) {
|
|
352
|
+
const nameMatch = entry.match(/specialists:([^*]+)/);
|
|
353
|
+
if (!nameMatch)
|
|
354
|
+
continue;
|
|
355
|
+
const name = nameMatch[1];
|
|
356
|
+
const fileExists = existsSync(join(specialistsAgentsDir, `${name}.md`));
|
|
357
|
+
let status = fileExists ? "ok" : "missing file";
|
|
358
|
+
if (name === "personal-assistant" && !chromeUp) {
|
|
359
|
+
status = "degraded — chrome not running";
|
|
360
|
+
}
|
|
361
|
+
lines.push(` ${name}: ${status}`);
|
|
362
|
+
}
|
|
363
|
+
checks.specialists = `${entries.length} registered\n${lines.join("\n")}`;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
checks.specialists = "error reading specialist registry";
|
|
369
|
+
}
|
|
370
|
+
// OS hostname — the device's actual network name, used for URL construction
|
|
371
|
+
try {
|
|
372
|
+
checks.hostname = osHostname();
|
|
373
|
+
}
|
|
374
|
+
catch (err) {
|
|
375
|
+
console.error("[system-status] os.hostname() failed, falling back to brand.hostname", err);
|
|
376
|
+
checks.hostname = "unknown (os.hostname failed)";
|
|
377
|
+
}
|
|
378
|
+
// Running port — PLATFORM_PORT is what the parent process passes to MCP subprocesses;
|
|
379
|
+
// PORT is the main-process variable (available in dev); 19200 is the install default.
|
|
380
|
+
checks.port = process.env.PLATFORM_PORT ?? process.env.PORT ?? "19200";
|
|
381
|
+
console.error(`[system-status] port=${checks.port} hostname=${checks.hostname}`);
|
|
382
|
+
// Local URL — pre-computed for agent URL construction (avoids hostname/port assembly errors)
|
|
383
|
+
checks.localUrl = `http://${checks.hostname}.local:${checks.port}`;
|
|
384
|
+
// Brand identity — read build-time manifest stamped by the bundler
|
|
385
|
+
try {
|
|
386
|
+
const brandPath = resolve(PLATFORM_ROOT, "config/brand.json");
|
|
387
|
+
if (!existsSync(brandPath)) {
|
|
388
|
+
checks.brand = "not configured (dev environment)";
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
|
|
392
|
+
checks.brand = `${brand.productName ?? "unknown"} (hostname: ${brand.hostname ?? "unknown"})`;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
checks.brand = `error reading brand config: ${err instanceof Error ? err.message : String(err)}`;
|
|
397
|
+
}
|
|
398
|
+
// configDir — the brand-specific config directory name (e.g. ".maxy", ".realagent").
|
|
399
|
+
// Resolved from brand.json at module load. Exposed here so skills can reference
|
|
400
|
+
// the actual value instead of deriving it from the brand name via LLM inference.
|
|
401
|
+
checks.configDir = BRAND_CONFIG.configDir;
|
|
402
|
+
const formatted = Object.entries(checks)
|
|
403
|
+
.map(([service, status]) => `${service}: ${status}`)
|
|
404
|
+
.join("\n");
|
|
405
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
406
|
+
});
|
|
407
|
+
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 () => {
|
|
408
|
+
const TAG = "[admin:public-hostname]";
|
|
409
|
+
try {
|
|
410
|
+
const result = resolvePublicHostname(CONFIG_DIR);
|
|
411
|
+
if (result.hostname !== null) {
|
|
412
|
+
console.error(`${TAG} resolved accountId=${ACCOUNT_ID} hostname=${result.hostname} source=${result.source}`);
|
|
413
|
+
const body = `hostname: ${result.hostname}\n` +
|
|
414
|
+
`isApex: ${result.isApex}\n` +
|
|
415
|
+
`source: ${result.source}\n` +
|
|
416
|
+
`usage: paste \`https://${result.hostname}<path-slug>\` to the operator — the <path-slug> comes from publish-site.`;
|
|
417
|
+
return { content: [{ type: "text", text: body }] };
|
|
418
|
+
}
|
|
419
|
+
console.error(`${TAG} empty accountId=${ACCOUNT_ID} reason=${result.reason} files-checked=cloudflared-config.yml,alias-domains.json`);
|
|
420
|
+
return {
|
|
421
|
+
content: [{
|
|
422
|
+
type: "text",
|
|
423
|
+
text: `hostname: (none)\nreason: ${result.reason}\n` +
|
|
424
|
+
`No Cloudflare tunnel is configured for this account. Run the cloudflare setup-tunnel skill before publishing externally.`,
|
|
425
|
+
}],
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
catch (err) {
|
|
429
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
430
|
+
console.error(`${TAG} error accountId=${ACCOUNT_ID} message="${errMsg.replace(/"/g, "'")}"`);
|
|
431
|
+
return {
|
|
432
|
+
content: [{ type: "text", text: `error resolving public hostname: ${errMsg}` }],
|
|
433
|
+
isError: true,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
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 () => {
|
|
438
|
+
const TAG = "[remote-auth-status]";
|
|
439
|
+
const platformPort = parseInt(PLATFORM_PORT, 10);
|
|
440
|
+
const setupUrl = `http://${osHostname()}.local:${platformPort}/__remote-auth/setup`;
|
|
441
|
+
try {
|
|
442
|
+
const res = await fetch(`http://127.0.0.1:${platformPort}/api/remote-auth/status`, {
|
|
443
|
+
signal: AbortSignal.timeout(5000),
|
|
444
|
+
});
|
|
445
|
+
const body = await res.json();
|
|
446
|
+
const configured = body.configured === true;
|
|
447
|
+
console.error(`${TAG} configured=${configured} port=${platformPort}`);
|
|
448
|
+
// Shape the response for both the agent (deciding what to say) and
|
|
449
|
+
// the user (seeing the device-URL button when they need to act).
|
|
450
|
+
// Configured → plain status, no URL. Unconfigured → include the
|
|
451
|
+
// fenced block so the agent can surface the button verbatim.
|
|
452
|
+
const text = configured
|
|
453
|
+
? `Remote access password is configured.`
|
|
454
|
+
: `Remote access password is not configured. The admin interface will refuse to expose over the tunnel until one is set.\n\n` +
|
|
455
|
+
`${deviceUrlBlock({ url: setupUrl, intent: "Set remote access password", hostname: osHostname() })}\n\n` +
|
|
456
|
+
`Ask the operator to set a password — either by clicking the button above (opens the setup page on the device) or by telling you one you can pass to \`remote-auth-set-password\`.`;
|
|
457
|
+
return {
|
|
458
|
+
content: [{ type: "text", text }],
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
catch (err) {
|
|
462
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
463
|
+
console.error(`${TAG} failed to check remote-auth status: ${errMsg}`);
|
|
464
|
+
return {
|
|
465
|
+
content: [{
|
|
466
|
+
type: "text",
|
|
467
|
+
text: `Cannot verify remote authentication — the web server at http://127.0.0.1:${platformPort} is not reachable: ${errMsg}. Ensure the ${BRAND_NAME} platform is running.`,
|
|
468
|
+
}],
|
|
469
|
+
isError: true,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
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 () => {
|
|
474
|
+
try {
|
|
475
|
+
const brandPath = resolve(PLATFORM_ROOT, "config", "brand.json");
|
|
476
|
+
if (!existsSync(brandPath)) {
|
|
477
|
+
return { content: [{ type: "text", text: "No brand.json found — platform not properly installed." }], isError: true };
|
|
478
|
+
}
|
|
479
|
+
const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
|
|
480
|
+
return { content: [{ type: "text", text: JSON.stringify(brand, null, 2) }] };
|
|
481
|
+
}
|
|
482
|
+
catch (err) {
|
|
483
|
+
return {
|
|
484
|
+
content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
|
|
485
|
+
isError: true,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
/* ── Plugin selector options (deterministic) ────────────────────── */
|
|
490
|
+
const PLUGIN_DISPLAY = {
|
|
491
|
+
admin: { value: "admin", label: "admin", description: "Platform management — system status, account settings, logs, session control." },
|
|
492
|
+
memory: { value: "memory", label: "memory", description: "Graph memory — search, write, reindex, and ingest knowledge." },
|
|
493
|
+
docs: { value: "docs", label: "maxy-guide", description: "User guide and platform documentation — loaded on demand." },
|
|
494
|
+
cloudflare: { value: "cloudflare", label: "cloudflare", description: "Cloudflare Tunnel — expose your assistant publicly via a custom domain." },
|
|
495
|
+
anthropic: { value: "anthropic", label: "anthropic", description: "Claude connection — API key acquisition and configuration." },
|
|
496
|
+
scheduling: { value: "scheduling", label: "scheduling", description: "Calendar and scheduling — events, appointments, recurring triggers." },
|
|
497
|
+
email: { value: "email", label: "email", description: "Agent email account — setup, read, send, search." },
|
|
498
|
+
contacts: { value: "contacts", label: "contacts", description: "CRM contact management — create, lookup, update, list." },
|
|
499
|
+
tasks: { value: "tasks", label: "tasks", description: "Graph-backed task lifecycle — create, update, list, relate, complete." },
|
|
500
|
+
workflows: { value: "workflows", label: "workflows", description: "Persistent named workflows — reusable instruction sets." },
|
|
501
|
+
projects: { value: "projects", label: "projects", description: "Structured project execution — phased sprints, investigations, reviews, retrospectives." },
|
|
502
|
+
"business-assistant": { value: "business-assistant", label: "business-assistant", description: "Customer enquiries, scheduling, quoting, invoicing, daily briefings. Enhances: personal-assistant" },
|
|
503
|
+
sales: { value: "sales", label: "sales", description: "Buying signal detection, closing techniques, objection handling. Enhances: personal-assistant" },
|
|
504
|
+
"deep-research": { value: "deep-research", label: "deep-research", description: "Structured multi-source research — query decomposition, source evaluation, citation formatting. Enhances: research-assistant" },
|
|
505
|
+
telegram: { value: "telegram", label: "telegram", description: "Telegram bot setup — BotFather, admin and public channels. Enhances: personal-assistant" },
|
|
506
|
+
whatsapp: { value: "whatsapp", label: "whatsapp", description: "WhatsApp messaging, pairing, and conversation browsing. Enhances: personal-assistant" },
|
|
507
|
+
waitlist: { value: "waitlist", label: "waitlist", description: "Waitlist lifecycle — extract sign-ups, review, schedule automation." },
|
|
508
|
+
replicate: { value: "replicate", label: "replicate", description: "Image generation — photorealistic, design, and fast draft images via Replicate. Enhances: content-producer, research-assistant" },
|
|
509
|
+
};
|
|
510
|
+
server.tool("onboarding-plugin-options", "Return the fully-assembled multi-select options array for onboarding Step 1. Classification (core/recommended/available) is derived from brand.json — the agent renders the result directly via render-component without further transformation.", {}, async () => {
|
|
511
|
+
try {
|
|
512
|
+
const brandPath = resolve(PLATFORM_ROOT, "config", "brand.json");
|
|
513
|
+
if (!existsSync(brandPath)) {
|
|
514
|
+
return { content: [{ type: "text", text: "No brand.json found at " + brandPath + " — platform not properly installed." }], isError: true };
|
|
515
|
+
}
|
|
516
|
+
const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
|
|
517
|
+
const plugins = brand?.plugins;
|
|
518
|
+
if (!plugins)
|
|
519
|
+
return { content: [{ type: "text", text: "brand.json has no plugins configuration." }], isError: true };
|
|
520
|
+
const coreSet = new Set(plugins.core ?? []);
|
|
521
|
+
const defaultSet = new Set(plugins.defaultEnabled ?? []);
|
|
522
|
+
const availableSet = new Set(plugins.available ?? []);
|
|
523
|
+
const options = [];
|
|
524
|
+
for (const name of plugins.core ?? []) {
|
|
525
|
+
const meta = PLUGIN_DISPLAY[name] ?? { value: name, label: name, description: "" };
|
|
526
|
+
options.push({ ...meta, badge: "Core", group: "Core — always active", locked: true, defaultSelected: true });
|
|
527
|
+
}
|
|
528
|
+
for (const name of plugins.defaultEnabled ?? []) {
|
|
529
|
+
if (coreSet.has(name))
|
|
530
|
+
continue;
|
|
531
|
+
const meta = PLUGIN_DISPLAY[name] ?? { value: name, label: name, description: "" };
|
|
532
|
+
options.push({ ...meta, badge: "Recommended", group: "Maxy", defaultSelected: true });
|
|
533
|
+
}
|
|
534
|
+
for (const name of plugins.available ?? []) {
|
|
535
|
+
if (coreSet.has(name) || defaultSet.has(name))
|
|
536
|
+
continue;
|
|
537
|
+
const meta = PLUGIN_DISPLAY[name] ?? { value: name, label: name, description: "" };
|
|
538
|
+
options.push({ ...meta, group: "Maxy" });
|
|
539
|
+
}
|
|
540
|
+
// Task 976: signal that the onboarding skill will append three Anthropic
|
|
541
|
+
// vertical entries from the claude-for-financial-services and
|
|
542
|
+
// knowledge-work-plugins marketplaces. The count is fixed because the
|
|
543
|
+
// skill prose always appends the same three (kyc-screener,
|
|
544
|
+
// meeting-prep-agent, pdf-viewer). Absence of this line post-install
|
|
545
|
+
// = the group was not surfaced (release blocker per task spec).
|
|
546
|
+
console.error(`[plugin-onboarding] group=anthropic-verticals presented=3`);
|
|
547
|
+
return { content: [{ type: "text", text: JSON.stringify(options, null, 2) }] };
|
|
548
|
+
}
|
|
549
|
+
catch (err) {
|
|
550
|
+
return { content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
eagerTool(server, "account-manage", "Read the account configuration (tier, domains, settings).", {}, async () => {
|
|
554
|
+
try {
|
|
555
|
+
const config = await readAccountConfig();
|
|
556
|
+
return {
|
|
557
|
+
content: [{ type: "text", text: JSON.stringify(config, null, 2) }],
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
catch (err) {
|
|
561
|
+
return {
|
|
562
|
+
content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
|
|
563
|
+
isError: true,
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
eagerTool(server, "account-update", "Update a user-configurable setting in account.json. Valid fields: outputStyle (default|explanatory), thinkingView (default|expanded|collapsed), effort (low|medium|high|max|auto), adminModel (any Anthropic model ID), publicModel (any Anthropic model ID), defaultAgent (slug of an existing public agent, or empty string to clear). Changes take effect on the next session.", {
|
|
568
|
+
field: z.enum(["outputStyle", "thinkingView", "effort", "adminModel", "publicModel", "defaultAgent"]),
|
|
569
|
+
value: z.string(),
|
|
570
|
+
}, async ({ field, value }) => {
|
|
571
|
+
const VALID = {
|
|
572
|
+
outputStyle: ["default", "explanatory"],
|
|
573
|
+
thinkingView: ["default", "expanded", "collapsed"],
|
|
574
|
+
effort: ["low", "medium", "high", "max", "auto"],
|
|
575
|
+
};
|
|
576
|
+
// defaultAgent: non-empty slug must reference an existing agent; empty string clears the field
|
|
577
|
+
if (field === "defaultAgent") {
|
|
578
|
+
const slug = value.trim();
|
|
579
|
+
if (slug) {
|
|
580
|
+
const agentConfigPath = join(getAccountDir(), "agents", slug, "config.json");
|
|
581
|
+
if (!existsSync(agentConfigPath)) {
|
|
582
|
+
return {
|
|
583
|
+
content: [{ type: "text", text: `Agent '${slug}' not found — no config.json in agents/${slug}/` }],
|
|
584
|
+
isError: true,
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
// empty slug is valid — handled below as a delete operation
|
|
589
|
+
}
|
|
590
|
+
// Model fields accept any non-empty string (model IDs change frequently)
|
|
591
|
+
const FREE_FORM_FIELDS = ["adminModel", "publicModel"];
|
|
592
|
+
if (FREE_FORM_FIELDS.includes(field)) {
|
|
593
|
+
if (!value.trim()) {
|
|
594
|
+
return {
|
|
595
|
+
content: [{ type: "text", text: `Invalid value for ${field}. Must be a non-empty model ID.` }],
|
|
596
|
+
isError: true,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
else if (field !== "defaultAgent" && !VALID[field].includes(value)) {
|
|
601
|
+
console.error(`[admin:account-update] rejected field=${field} value="${value}" valid=[${VALID[field].join(", ")}]`);
|
|
602
|
+
return {
|
|
603
|
+
content: [{ type: "text", text: `Invalid value "${value}" for ${field}. Valid values: ${VALID[field].join(", ")}` }],
|
|
604
|
+
isError: true,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
try {
|
|
608
|
+
const configPath = join(getAccountDir(), "account.json");
|
|
609
|
+
const config = await readAccountConfig();
|
|
610
|
+
const oldDefault = field === "defaultAgent" ? config.defaultAgent ?? "(none)" : undefined;
|
|
611
|
+
if (field === "defaultAgent" && !value.trim()) {
|
|
612
|
+
delete config.defaultAgent;
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
config[field] = value;
|
|
616
|
+
}
|
|
617
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
618
|
+
console.error(`[admin:account-update] wrote field=${field} value="${value}" path=${configPath}`);
|
|
619
|
+
if (field === "defaultAgent") {
|
|
620
|
+
if (!value.trim()) {
|
|
621
|
+
return {
|
|
622
|
+
content: [{ type: "text", text: `Default agent cleared (was "${oldDefault}"). Visitors to the root URL will see the first available agent, or a "no agents" message if none exist. Takes effect on next page load.` }],
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
return {
|
|
626
|
+
content: [{ type: "text", text: `Default agent changed from "${oldDefault}" to "${value}". Takes effect on next page load.` }],
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
return {
|
|
630
|
+
content: [{ type: "text", text: `Updated ${field} to "${value}". Takes effect on next session.` }],
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
catch (err) {
|
|
634
|
+
return {
|
|
635
|
+
content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
|
|
636
|
+
isError: true,
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
// Plugin enable/disable: deterministic write to account.json.enabledPlugins.
|
|
641
|
+
// The pre-tool-use hook denies the agent's direct Edit on account.json (Task 831),
|
|
642
|
+
// so the agent can no longer toggle enablement by hand-editing the file. This
|
|
643
|
+
// tool is the legitimate path: validates the plugin name, refuses core plugins,
|
|
644
|
+
// confirms the plugin directory exists, and atomically updates the array.
|
|
645
|
+
// Entitlement-bearing fields (tier, purchasedPlugins) are NOT writable here.
|
|
646
|
+
server.tool("plugin-toggle-enabled", "Enable or disable a plugin in this account by adding/removing its name from account.json's enabledPlugins array. Validates the plugin exists under platform/plugins/, refuses to disable core plugins (admin, memory, docs, cloudflare, anthropic), and writes atomically. Takes effect on next session start. Does NOT change purchasedPlugins or tier — those derive from the signed entitlement payload.", {
|
|
647
|
+
pluginName: z.string().describe("Plugin slug (lowercase a-z0-9-)."),
|
|
648
|
+
action: z.enum(["enable", "disable"]).describe("enable adds to enabledPlugins; disable removes."),
|
|
649
|
+
}, async ({ pluginName, action }) => {
|
|
650
|
+
const TAG = "[admin:plugin-toggle-enabled]";
|
|
651
|
+
if (!VALID_PLUGIN_NAME.test(pluginName)) {
|
|
652
|
+
return { content: [{ type: "text", text: `${TAG} Invalid plugin name "${pluginName}". Must match ${VALID_PLUGIN_NAME}.` }], isError: true };
|
|
653
|
+
}
|
|
654
|
+
if (CORE_PLUGINS.includes(pluginName)) {
|
|
655
|
+
return { content: [{ type: "text", text: `${TAG} "${pluginName}" is a core plugin and cannot be toggled.` }], isError: true };
|
|
656
|
+
}
|
|
657
|
+
const pluginDir = resolve(PLUGINS_DIR, pluginName);
|
|
658
|
+
if (!existsSync(pluginDir) || !existsSync(join(pluginDir, "PLUGIN.md"))) {
|
|
659
|
+
return { content: [{ type: "text", text: `${TAG} Plugin "${pluginName}" not installed at ${pluginDir} (no PLUGIN.md). Install via premium-deliver or platform release.` }], isError: true };
|
|
660
|
+
}
|
|
661
|
+
try {
|
|
662
|
+
const configPath = join(getAccountDir(), "account.json");
|
|
663
|
+
const config = await readAccountConfig();
|
|
664
|
+
const current = Array.isArray(config.enabledPlugins) ? config.enabledPlugins : [];
|
|
665
|
+
let next;
|
|
666
|
+
if (action === "enable") {
|
|
667
|
+
if (current.includes(pluginName)) {
|
|
668
|
+
return { content: [{ type: "text", text: `${TAG} "${pluginName}" is already enabled.` }] };
|
|
669
|
+
}
|
|
670
|
+
next = [...current, pluginName];
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
if (!current.includes(pluginName)) {
|
|
674
|
+
return { content: [{ type: "text", text: `${TAG} "${pluginName}" is not enabled — nothing to disable.` }] };
|
|
675
|
+
}
|
|
676
|
+
next = current.filter((n) => n !== pluginName);
|
|
677
|
+
}
|
|
678
|
+
config.enabledPlugins = next;
|
|
679
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
680
|
+
console.error(`${TAG} ${action}d plugin=${pluginName} path=${configPath}`);
|
|
681
|
+
return { content: [{ type: "text", text: `Plugin "${pluginName}" ${action}d. Takes effect on next session.` }] };
|
|
682
|
+
}
|
|
683
|
+
catch (err) {
|
|
684
|
+
return { content: [{ type: "text", text: `${TAG} Failed: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
// ===================================================================
|
|
688
|
+
// Admin user management tools
|
|
689
|
+
// ===================================================================
|
|
690
|
+
eagerTool(server, "admin-add", "Add a new admin user to this account. Creates a device-level user entry (users.json) and adds them to this account's admins list (account.json). PIN must be at least 4 digits. If no PIN is provided, a unique 4-digit PIN is generated. Returns the userId and PIN to share with the new admin.\n\nIMPORTANT — retry behaviour: if the user already stated a specific PIN earlier in the conversation and a first admin-add call failed (e.g. tier cap reached, PIN collision), every retry MUST re-pass that PIN as the `pin` parameter. Omitting `pin` on the retry auto-generates a different 4-digit PIN, silently substituting what the user asked for.", {
|
|
691
|
+
name: z.string().describe("Display name for the new admin (stored on the AdminUser node in Neo4j)."),
|
|
692
|
+
pin: z.string().optional().describe("Optional PIN (minimum 4 digits). If omitted, a unique 4-digit PIN is generated."),
|
|
693
|
+
}, async ({ name, pin: rawPin }) => {
|
|
694
|
+
const TAG = "[admin]";
|
|
695
|
+
if (!name.trim()) {
|
|
696
|
+
return { content: [{ type: "text", text: `${TAG} Name is required.` }], isError: true };
|
|
697
|
+
}
|
|
698
|
+
let users;
|
|
699
|
+
try {
|
|
700
|
+
users = readUsersJson();
|
|
701
|
+
}
|
|
702
|
+
catch (err) {
|
|
703
|
+
return { content: [{ type: "text", text: `${TAG} ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
704
|
+
}
|
|
705
|
+
// Enforce per-tier admin limit. Effective tier comes from the signed
|
|
706
|
+
// entitlement payload (Task 831) — editing account.json.tier directly
|
|
707
|
+
// has no effect; the verifier uses the signed value and emits a
|
|
708
|
+
// [entitlement] tampered: line if disk and signed diverge.
|
|
709
|
+
try {
|
|
710
|
+
const config = await readAccountConfig();
|
|
711
|
+
const currentAdmins = (config.admins ?? []);
|
|
712
|
+
const entitlement = await currentEntitlement();
|
|
713
|
+
const tier = entitlement.tier;
|
|
714
|
+
const maxAdmins = MAX_ADMINS_BY_TIER[tier] ?? MAX_ADMINS_DEFAULT;
|
|
715
|
+
if (currentAdmins.length >= maxAdmins) {
|
|
716
|
+
return { content: [{ type: "text", text: `${TAG} Admin limit reached (${maxAdmins} for ${tier || "this"} tier). Remove an existing admin before adding a new one.` }], isError: true };
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
catch (err) {
|
|
720
|
+
console.error(`${TAG} failed to check admin limit: ${err instanceof Error ? err.message : String(err)}`);
|
|
721
|
+
// Fail open would be unsafe — fail closed
|
|
722
|
+
return { content: [{ type: "text", text: `${TAG} Unable to verify admin limit — cannot add admin.` }], isError: true };
|
|
723
|
+
}
|
|
724
|
+
// Resolve the calling user's identity from the session environment
|
|
725
|
+
const callerUserId = process.env.USER_ID;
|
|
726
|
+
// PIN: use provided or generate. Constraint: minimum 4 digits, no upper bound.
|
|
727
|
+
let plaintextPin;
|
|
728
|
+
if (rawPin) {
|
|
729
|
+
if (rawPin.length < 4) {
|
|
730
|
+
return { content: [{ type: "text", text: `${TAG} PIN must be at least 4 digits.` }], isError: true };
|
|
731
|
+
}
|
|
732
|
+
const hashed = hashPin(rawPin);
|
|
733
|
+
if (users.some(u => u.pin === hashed)) {
|
|
734
|
+
return { content: [{ type: "text", text: `${TAG} That PIN is already in use. Choose a different PIN.` }], isError: true };
|
|
735
|
+
}
|
|
736
|
+
plaintextPin = rawPin;
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
try {
|
|
740
|
+
plaintextPin = generateUniquePin(users);
|
|
741
|
+
}
|
|
742
|
+
catch (err) {
|
|
743
|
+
return { content: [{ type: "text", text: `${TAG} ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
const pinHash = hashPin(plaintextPin);
|
|
747
|
+
const userId = crypto.randomUUID();
|
|
748
|
+
// Three-store admin auth invariant (Task 850 → Task 904): users.json
|
|
749
|
+
// (device-level PIN auth) and account.json admins[] (account-level role)
|
|
750
|
+
// route through the shared `admins-write` helper so the dual write has a
|
|
751
|
+
// single chokepoint. Per-leg `[admins-write]` and legacy `[admin-auth-store]`
|
|
752
|
+
// log lines fire so existing dashboards continue to grep. Neo4j AdminUser
|
|
753
|
+
// (display + graph identity) writes below remain inline because they have
|
|
754
|
+
// no dual-file invariant.
|
|
755
|
+
const userIdShort = userId.slice(0, 8);
|
|
756
|
+
const authResult = writeAdminEntry({
|
|
757
|
+
userId,
|
|
758
|
+
pin: pinHash,
|
|
759
|
+
role: "admin",
|
|
760
|
+
usersFile: USERS_FILE,
|
|
761
|
+
accountDir: getAccountDir(),
|
|
762
|
+
caller: "admin-add",
|
|
763
|
+
});
|
|
764
|
+
console.error(`[admin-auth-store] action=add userId=${userIdShort} result=${authResult.usersJsonResult} store=users${authResult.usersError ? ` error=${authResult.usersError}` : ""}`);
|
|
765
|
+
if (authResult.usersJsonResult !== "ok") {
|
|
766
|
+
return { content: [{ type: "text", text: `${TAG} Failed to write users.json: ${authResult.usersError ?? "unknown error"}` }], isError: true };
|
|
767
|
+
}
|
|
768
|
+
console.error(`[admin-auth-store] action=add userId=${userIdShort} result=${authResult.accountJsonResult} store=account${authResult.accountError ? ` error=${authResult.accountError}` : ""}`);
|
|
769
|
+
if (authResult.accountJsonResult === "fail") {
|
|
770
|
+
return { content: [{ type: "text", text: `${TAG} users.json updated; account.json write FAILED — manual reconciliation needed: ${authResult.accountError ?? "unknown error"}` }], isError: true };
|
|
771
|
+
}
|
|
772
|
+
// 3. Write to Neo4j (graph-level): AdminUser + Person + OWNS atomically,
|
|
773
|
+
// plus ADMIN_OF edge to the LocalBusiness for this account.
|
|
774
|
+
// Task 830 — deterministic identity creation: never delegate node
|
|
775
|
+
// creation to the LLM. Person reuse rule mirrors writeAdminUserAndPerson
|
|
776
|
+
// in platform/ui/app/lib/neo4j-store.ts (case-insensitive exact match
|
|
777
|
+
// on givenName + familyName; partial-name ambiguity does NOT match —
|
|
778
|
+
// rationalisation is a separate agent-mediated concern).
|
|
779
|
+
// Task 850 — Neo4j-leg failure now returns is_error: true with the
|
|
780
|
+
// [admin-auth-store] line; previously it set a soft warning string and
|
|
781
|
+
// returned success, which is what hid the Adam Mackay incident from
|
|
782
|
+
// the recovery agent. The user is still functional via users.json +
|
|
783
|
+
// account.json, but the graph state is divergent and the operator
|
|
784
|
+
// must know.
|
|
785
|
+
let personReused = false;
|
|
786
|
+
try {
|
|
787
|
+
const session = getSession();
|
|
788
|
+
try {
|
|
789
|
+
const createdAt = new Date().toISOString();
|
|
790
|
+
const trimmedName = name.trim();
|
|
791
|
+
const firstSpace = trimmedName.search(/\s/);
|
|
792
|
+
const givenName = firstSpace === -1 ? trimmedName : trimmedName.slice(0, firstSpace).trim();
|
|
793
|
+
const familyName = firstSpace === -1 ? null : (trimmedName.slice(firstSpace + 1).trim() || null);
|
|
794
|
+
// Task 897: stamp `accountId` on every AdminUser at MERGE time, both
|
|
795
|
+
// ON CREATE and ON MATCH (the latter via COALESCE so a pre-existing
|
|
796
|
+
// value isn't overwritten if it differs — which would itself be a
|
|
797
|
+
// graph-invariant violation worth surfacing). Pre-897 the missing
|
|
798
|
+
// accountId fingerprint produced :AdminUser nodes that migration 004
|
|
799
|
+
// pruned silently, costing the admin pin during onboarding.
|
|
800
|
+
const result = await session.run(`MERGE (au:AdminUser {userId: $userId})
|
|
801
|
+
ON CREATE SET au.accountId = $accountId,
|
|
802
|
+
au.name = $name,
|
|
803
|
+
au.createdAt = $createdAt
|
|
804
|
+
ON MATCH SET au.accountId = COALESCE(au.accountId, $accountId),
|
|
805
|
+
au.name = $name,
|
|
806
|
+
au.updatedAt = $createdAt
|
|
807
|
+
WITH au
|
|
808
|
+
MATCH (b:LocalBusiness {accountId: $accountId})
|
|
809
|
+
MERGE (au)-[r:ADMIN_OF]->(b)
|
|
810
|
+
ON CREATE SET r.role = 'admin', r.grantedAt = $createdAt
|
|
811
|
+
WITH au
|
|
812
|
+
OPTIONAL MATCH (existingPerson:Person {accountId: $accountId})
|
|
813
|
+
WHERE toLower(existingPerson.givenName) = toLower($givenName)
|
|
814
|
+
AND coalesce(toLower(existingPerson.familyName), '') = coalesce(toLower($familyName), '')
|
|
815
|
+
WITH au, existingPerson
|
|
816
|
+
CALL {
|
|
817
|
+
WITH au, existingPerson
|
|
818
|
+
WITH au, existingPerson WHERE existingPerson IS NOT NULL
|
|
819
|
+
MERGE (au)-[:OWNS]->(existingPerson)
|
|
820
|
+
RETURN true AS reused
|
|
821
|
+
UNION
|
|
822
|
+
WITH au, existingPerson
|
|
823
|
+
WITH au WHERE existingPerson IS NULL
|
|
824
|
+
CREATE (newPerson:Person {
|
|
825
|
+
accountId: $accountId,
|
|
826
|
+
givenName: $givenName,
|
|
827
|
+
familyName: $familyName,
|
|
828
|
+
role: 'admin-personal',
|
|
829
|
+
scope: 'admin',
|
|
830
|
+
createdAt: $createdAt
|
|
831
|
+
})
|
|
832
|
+
MERGE (au)-[:OWNS]->(newPerson)
|
|
833
|
+
RETURN false AS reused
|
|
834
|
+
}
|
|
835
|
+
RETURN reused`, { userId, name: trimmedName, createdAt, accountId: ACCOUNT_ID, givenName, familyName });
|
|
836
|
+
if (result.records.length > 0) {
|
|
837
|
+
personReused = result.records[0].get("reused");
|
|
838
|
+
}
|
|
839
|
+
// Task 897 — post-write assertion mirroring Task 785 in neo4j-store.ts.
|
|
840
|
+
// MATCH back the AdminUser we just wrote and verify every required
|
|
841
|
+
// field landed. Loud-fail the tool call if accountId or name is null
|
|
842
|
+
// so a Cypher regression that drops the field is grep-detectable.
|
|
843
|
+
const verify = await session.run(`MATCH (au:AdminUser {userId: $userId})
|
|
844
|
+
RETURN coalesce(au.accountId, '') AS accountId,
|
|
845
|
+
coalesce(au.name, '') AS name`, { userId });
|
|
846
|
+
const verifiedAccountId = verify.records[0]?.get("accountId") || "";
|
|
847
|
+
const verifiedName = verify.records[0]?.get("name") || "";
|
|
848
|
+
if (!verifiedAccountId || !verifiedName) {
|
|
849
|
+
throw new Error(`post-write assertion failed: AdminUser userId=${userIdShort} accountId=${verifiedAccountId || "(null)"} name=${verifiedName || "(null)"} — required fields missing after MERGE`);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
finally {
|
|
853
|
+
await session.close();
|
|
854
|
+
}
|
|
855
|
+
console.error(`[admin] admin-add success userId=${userIdShort} accountId=${ACCOUNT_ID} name=${name.trim()} required-fields=ok personReused=${personReused}`);
|
|
856
|
+
}
|
|
857
|
+
catch (err) {
|
|
858
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
859
|
+
console.error(`[admin-auth-store] action=add userId=${userIdShort} result=fail store=neo4j error=${errMsg}`);
|
|
860
|
+
return {
|
|
861
|
+
content: [{
|
|
862
|
+
type: "text",
|
|
863
|
+
text: `${TAG} users.json + account.json updated for userId ${userId} (PIN: ${plaintextPin}); Neo4j sync FAILED — manual reconciliation needed: ${errMsg}`,
|
|
864
|
+
}],
|
|
865
|
+
isError: true,
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
console.error(`${TAG} [admin-identity] adminuser-bound userId=${userIdShort} name=${name.trim()} personReused=${personReused}`);
|
|
869
|
+
console.error(`${TAG} admin added: userId=${userId} userName=${name.trim()} accountId=${ACCOUNT_ID} role=admin addedBy=${callerUserId ?? "unknown"}`);
|
|
870
|
+
return {
|
|
871
|
+
content: [{
|
|
872
|
+
type: "text",
|
|
873
|
+
text: `Admin added successfully.\n\n- **Name:** ${name.trim()}\n- **userId:** ${userId}\n- **PIN:** ${plaintextPin}\n- **Role:** admin\n\nShare the PIN with ${name.trim()} so they can log in.`,
|
|
874
|
+
}],
|
|
875
|
+
};
|
|
876
|
+
});
|
|
877
|
+
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.", {
|
|
878
|
+
userId: z.string().describe("The userId of the admin to remove (use admin-list to find userIds)"),
|
|
879
|
+
}, async ({ userId }) => {
|
|
880
|
+
const TAG = "[admin]";
|
|
881
|
+
const callerUserId = process.env.USER_ID;
|
|
882
|
+
// Read account.json and validate
|
|
883
|
+
let config;
|
|
884
|
+
let admins;
|
|
885
|
+
try {
|
|
886
|
+
config = await readAccountConfig();
|
|
887
|
+
admins = (config.admins ?? []);
|
|
888
|
+
}
|
|
889
|
+
catch (err) {
|
|
890
|
+
return { content: [{ type: "text", text: `${TAG} Failed to read account config: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
891
|
+
}
|
|
892
|
+
const targetIndex = admins.findIndex(a => a.userId === userId);
|
|
893
|
+
if (targetIndex === -1) {
|
|
894
|
+
return { content: [{ type: "text", text: `${TAG} User ${userId} is not an admin of this account.` }], isError: true };
|
|
895
|
+
}
|
|
896
|
+
if (admins.length <= 1) {
|
|
897
|
+
return { content: [{ type: "text", text: `${TAG} Cannot remove the last admin. At least one admin must remain on the account.` }], isError: true };
|
|
898
|
+
}
|
|
899
|
+
// Resolve the admin's name from Neo4j (canonical) for the confirmation
|
|
900
|
+
// message. Best-effort — fall back to userId if the graph is unreachable.
|
|
901
|
+
let removedName = userId;
|
|
902
|
+
try {
|
|
903
|
+
const session = getSession();
|
|
904
|
+
try {
|
|
905
|
+
const result = await session.run(`MATCH (au:AdminUser {userId: $userId}) RETURN au.name AS name LIMIT 1`, { userId });
|
|
906
|
+
if (result.records.length > 0) {
|
|
907
|
+
const name = result.records[0].get("name");
|
|
908
|
+
if (name && name.trim())
|
|
909
|
+
removedName = name.trim();
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
finally {
|
|
913
|
+
await session.close();
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
catch { /* name lookup is best-effort */ }
|
|
917
|
+
// 1. Remove from account.json — single-file write through the shared
|
|
918
|
+
// chokepoint helper. users.json is intentionally untouched (the user may
|
|
919
|
+
// admin other accounts on this device); the helper logs `usersJsonResult=skip`.
|
|
920
|
+
const removeResult = removeAdminFromAccount({
|
|
921
|
+
userId,
|
|
922
|
+
accountDir: getAccountDir(),
|
|
923
|
+
caller: "admin-remove",
|
|
924
|
+
});
|
|
925
|
+
if (removeResult.accountJsonResult === "fail") {
|
|
926
|
+
return { content: [{ type: "text", text: `${TAG} Failed to write account.json: ${removeResult.accountError ?? "unknown error"}` }], isError: true };
|
|
927
|
+
}
|
|
928
|
+
// 2. Delete ADMIN_OF relationship in Neo4j — partial failure is a warning
|
|
929
|
+
let neo4jWarning = "";
|
|
930
|
+
try {
|
|
931
|
+
const session = getSession();
|
|
932
|
+
try {
|
|
933
|
+
await session.run(`MATCH (au:AdminUser {userId: $userId})-[r:ADMIN_OF]->(b:LocalBusiness {accountId: $accountId})
|
|
934
|
+
DELETE r`, { userId, accountId: ACCOUNT_ID });
|
|
935
|
+
}
|
|
936
|
+
finally {
|
|
937
|
+
await session.close();
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
catch (err) {
|
|
941
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
942
|
+
console.error(`${TAG} Neo4j sync failed during admin-remove: userId=${userId} error=${errMsg}`);
|
|
943
|
+
neo4jWarning = ` Note: Neo4j relationship deletion failed (${errMsg}) — will be reconciled on next seed.`;
|
|
944
|
+
}
|
|
945
|
+
console.error(`${TAG} admin removed: userId=${userId} accountId=${ACCOUNT_ID} removedBy=${callerUserId ?? "unknown"}`);
|
|
946
|
+
return {
|
|
947
|
+
content: [{
|
|
948
|
+
type: "text",
|
|
949
|
+
text: `Removed ${removedName} from this account's admin list. Their PIN still works for any other accounts they administer.${neo4jWarning}`,
|
|
950
|
+
}],
|
|
951
|
+
};
|
|
952
|
+
});
|
|
953
|
+
eagerTool(server, "admin-list", "List all admins for this account with their names and roles.", {}, async () => {
|
|
954
|
+
const TAG = "[admin]";
|
|
955
|
+
let admins;
|
|
956
|
+
try {
|
|
957
|
+
const config = await readAccountConfig();
|
|
958
|
+
admins = (config.admins ?? []);
|
|
959
|
+
}
|
|
960
|
+
catch (err) {
|
|
961
|
+
return { content: [{ type: "text", text: `${TAG} Failed to read account config: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
962
|
+
}
|
|
963
|
+
if (admins.length === 0) {
|
|
964
|
+
return { content: [{ type: "text", text: `${TAG} No admins configured for this account.` }] };
|
|
965
|
+
}
|
|
966
|
+
// Enrich with names from Neo4j AdminUser (canonical, Task 829). Best
|
|
967
|
+
// effort — render "(unknown)" for any userId without a graph entry.
|
|
968
|
+
const userMap = new Map();
|
|
969
|
+
try {
|
|
970
|
+
const session = getSession();
|
|
971
|
+
try {
|
|
972
|
+
const result = await session.run(`UNWIND $userIds AS uid
|
|
973
|
+
MATCH (au:AdminUser {userId: uid})
|
|
974
|
+
RETURN au.userId AS userId, au.name AS name`, { userIds: admins.map(a => a.userId) });
|
|
975
|
+
for (const record of result.records) {
|
|
976
|
+
const uid = record.get("userId");
|
|
977
|
+
const name = record.get("name");
|
|
978
|
+
if (name && name.trim())
|
|
979
|
+
userMap.set(uid, name.trim());
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
finally {
|
|
983
|
+
await session.close();
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
catch { /* name lookup is best-effort — userIds shown when graph is unreachable */ }
|
|
987
|
+
const lines = admins.map(a => {
|
|
988
|
+
const name = userMap.get(a.userId) ?? "(unknown)";
|
|
989
|
+
return `- **${name}** — role: ${a.role}, userId: ${a.userId}`;
|
|
990
|
+
});
|
|
991
|
+
return {
|
|
992
|
+
content: [{ type: "text", text: `Admins for this account:\n\n${lines.join("\n")}` }],
|
|
993
|
+
};
|
|
994
|
+
});
|
|
995
|
+
eagerTool(server, "admin-update-pin", "Update an existing admin user's PIN. Defaults to the calling admin if no userId is given. PIN must be at least 4 digits and unique across all users on the device. PINs are device-level: updating another admin's PIN does not require shared account membership — any admin on the device can rotate any other admin's PIN, matching the existing trust model used by admin-remove.", {
|
|
996
|
+
userId: z.string().optional().describe("The userId of the admin whose PIN to update. Defaults to the caller (the admin invoking this tool)."),
|
|
997
|
+
newPin: z.string().describe("The new PIN. Minimum 4 digits, no upper bound."),
|
|
998
|
+
}, async ({ userId: targetUserId, newPin }) => {
|
|
999
|
+
const TAG = "[admin-update-pin]";
|
|
1000
|
+
const callerUserId = process.env.USER_ID;
|
|
1001
|
+
const userId = targetUserId ?? callerUserId;
|
|
1002
|
+
const userIdLabel = userId ? userId.slice(0, 8) : "unknown";
|
|
1003
|
+
if (!userId) {
|
|
1004
|
+
console.error(`${TAG} userId=${userIdLabel} result=user-not-found reason=no-caller-context`);
|
|
1005
|
+
return { content: [{ type: "text", text: `${TAG} No userId supplied and no caller context — cannot update.` }], isError: true };
|
|
1006
|
+
}
|
|
1007
|
+
if (newPin.length < 4) {
|
|
1008
|
+
console.error(`${TAG} userId=${userIdLabel} result=too-short`);
|
|
1009
|
+
return { content: [{ type: "text", text: `${TAG} PIN must be at least 4 digits.` }], isError: true };
|
|
1010
|
+
}
|
|
1011
|
+
let users;
|
|
1012
|
+
try {
|
|
1013
|
+
users = readUsersJson();
|
|
1014
|
+
}
|
|
1015
|
+
catch (err) {
|
|
1016
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1017
|
+
console.error(`${TAG} userId=${userIdLabel} result=user-not-found reason=users-json-read-failed`);
|
|
1018
|
+
console.error(`[admin-auth-store] action=update-pin userId=${userIdLabel} result=fail store=users error=${errMsg}`);
|
|
1019
|
+
return { content: [{ type: "text", text: `${TAG} ${errMsg}` }], isError: true };
|
|
1020
|
+
}
|
|
1021
|
+
const targetIndex = users.findIndex(u => u.userId === userId);
|
|
1022
|
+
if (targetIndex === -1) {
|
|
1023
|
+
console.error(`${TAG} userId=${userIdLabel} result=user-not-found`);
|
|
1024
|
+
console.error(`[admin-auth-store] action=update-pin userId=${userIdLabel} result=fail store=users error=user-not-found`);
|
|
1025
|
+
return { content: [{ type: "text", text: `${TAG} User ${userId} not found in users.json.` }], isError: true };
|
|
1026
|
+
}
|
|
1027
|
+
const newHash = hashPin(newPin);
|
|
1028
|
+
const collidesWithOther = users.some((u, i) => i !== targetIndex && u.pin === newHash);
|
|
1029
|
+
if (collidesWithOther) {
|
|
1030
|
+
console.error(`${TAG} userId=${userIdLabel} result=collision`);
|
|
1031
|
+
console.error(`[admin-auth-store] action=update-pin userId=${userIdLabel} result=fail store=users error=pin-collision`);
|
|
1032
|
+
return { content: [{ type: "text", text: `${TAG} That PIN is already in use by another user. Choose a different PIN.` }], isError: true };
|
|
1033
|
+
}
|
|
1034
|
+
users[targetIndex].pin = newHash;
|
|
1035
|
+
try {
|
|
1036
|
+
writeUsersJson(users);
|
|
1037
|
+
}
|
|
1038
|
+
catch (err) {
|
|
1039
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1040
|
+
console.error(`[admin-auth-store] action=update-pin userId=${userIdLabel} result=fail store=users error=${errMsg}`);
|
|
1041
|
+
return { content: [{ type: "text", text: `${TAG} Failed to write users.json: ${errMsg}` }], isError: true };
|
|
1042
|
+
}
|
|
1043
|
+
console.error(`${TAG} userId=${userIdLabel} result=ok`);
|
|
1044
|
+
console.error(`[admin-auth-store] action=update-pin userId=${userIdLabel} result=ok store=users`);
|
|
1045
|
+
const self = userId === callerUserId;
|
|
1046
|
+
return {
|
|
1047
|
+
content: [{ type: "text", text: `PIN updated${self ? "" : ` for userId ${userId}`}. The new PIN takes effect on the next login.` }],
|
|
1048
|
+
};
|
|
1049
|
+
});
|
|
1050
|
+
server.tool("agent-image", "Upload, update, or remove an agent's image. action=set: copy the file at filePath to the agent's assets directory and update config.json with image URL and shape. action=remove: delete the image file and clear the image fields from config.json. imageShape: 'circle' for avatars/icons, 'rounded' for logos.", {
|
|
1051
|
+
agentSlug: z.string().describe("Agent slug (e.g. 'coaching', 'sales')"),
|
|
1052
|
+
action: z.enum(["set", "remove"]),
|
|
1053
|
+
filePath: z.string().optional().describe("Absolute path to the image file (required for action=set)"),
|
|
1054
|
+
imageShape: z.enum(["circle", "rounded"]).optional().describe("Image shape: circle for avatars, rounded for logos (required for action=set)"),
|
|
1055
|
+
}, async ({ agentSlug, action, filePath, imageShape }) => {
|
|
1056
|
+
const VALID_IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"];
|
|
1057
|
+
try {
|
|
1058
|
+
const accountDir = getAccountDir();
|
|
1059
|
+
const agentDir = resolve(accountDir, "agents", agentSlug);
|
|
1060
|
+
const configPath = resolve(agentDir, "config.json");
|
|
1061
|
+
if (!existsSync(configPath)) {
|
|
1062
|
+
return {
|
|
1063
|
+
content: [{ type: "text", text: `Agent '${agentSlug}' not found — no config.json at agents/${agentSlug}/` }],
|
|
1064
|
+
isError: true,
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
if (action === "set") {
|
|
1068
|
+
if (!filePath) {
|
|
1069
|
+
return {
|
|
1070
|
+
content: [{ type: "text", text: "filePath is required for action=set" }],
|
|
1071
|
+
isError: true,
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
if (!imageShape) {
|
|
1075
|
+
return {
|
|
1076
|
+
content: [{ type: "text", text: "imageShape is required for action=set — use 'circle' for avatars/icons or 'rounded' for logos" }],
|
|
1077
|
+
isError: true,
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
if (!existsSync(filePath)) {
|
|
1081
|
+
return {
|
|
1082
|
+
content: [{ type: "text", text: `File not found: ${filePath}` }],
|
|
1083
|
+
isError: true,
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
// Validate image extension
|
|
1087
|
+
const ext = filePath.toLowerCase().split(".").pop();
|
|
1088
|
+
if (!ext || !VALID_IMAGE_EXTENSIONS.includes(`.${ext}`)) {
|
|
1089
|
+
return {
|
|
1090
|
+
content: [{ type: "text", text: `Not a valid image file. Supported formats: ${VALID_IMAGE_EXTENSIONS.join(", ")}` }],
|
|
1091
|
+
isError: true,
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
// Copy file to agent assets directory
|
|
1095
|
+
const assetsDir = resolve(agentDir, "assets");
|
|
1096
|
+
const { mkdirSync: mkdirSyncFs, copyFileSync, writeFileSync: writeFileSyncFs, renameSync } = await import("node:fs");
|
|
1097
|
+
mkdirSyncFs(assetsDir, { recursive: true });
|
|
1098
|
+
const filename = `agent-image.${ext}`;
|
|
1099
|
+
const destPath = resolve(assetsDir, filename);
|
|
1100
|
+
copyFileSync(filePath, destPath);
|
|
1101
|
+
// Atomic config update: read → modify → write temp → rename
|
|
1102
|
+
const configRaw = await readFile(configPath, "utf-8");
|
|
1103
|
+
let config;
|
|
1104
|
+
try {
|
|
1105
|
+
config = JSON.parse(configRaw);
|
|
1106
|
+
}
|
|
1107
|
+
catch {
|
|
1108
|
+
config = {};
|
|
1109
|
+
}
|
|
1110
|
+
config.image = `/agent-assets/${agentSlug}/${filename}`;
|
|
1111
|
+
config.imageShape = imageShape;
|
|
1112
|
+
const tmpPath = configPath + ".tmp";
|
|
1113
|
+
writeFileSyncFs(tmpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1114
|
+
renameSync(tmpPath, configPath);
|
|
1115
|
+
console.error(`[agent-config] ${agentSlug}: image=${config.image} imageShape=${imageShape} (set)`);
|
|
1116
|
+
return {
|
|
1117
|
+
content: [{ type: "text", text: `Image set for agent '${agentSlug}':\n File: ${destPath}\n URL: ${config.image}\n Shape: ${imageShape}\n\nThe image is now accessible at ${config.image} and will appear in the public chat header.` }],
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
// action === "remove"
|
|
1121
|
+
const configRaw = await readFile(configPath, "utf-8");
|
|
1122
|
+
let config;
|
|
1123
|
+
try {
|
|
1124
|
+
config = JSON.parse(configRaw);
|
|
1125
|
+
}
|
|
1126
|
+
catch {
|
|
1127
|
+
config = {};
|
|
1128
|
+
}
|
|
1129
|
+
// Delete the image file if it exists
|
|
1130
|
+
if (typeof config.image === "string") {
|
|
1131
|
+
const filename = config.image.split("/").pop();
|
|
1132
|
+
if (filename) {
|
|
1133
|
+
const imgPath = resolve(agentDir, "assets", filename);
|
|
1134
|
+
if (existsSync(imgPath)) {
|
|
1135
|
+
const { unlinkSync } = await import("node:fs");
|
|
1136
|
+
unlinkSync(imgPath);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
// Remove image fields from config
|
|
1141
|
+
delete config.image;
|
|
1142
|
+
delete config.imageShape;
|
|
1143
|
+
const { writeFileSync: writeFileSyncRm, renameSync: renameSyncRm } = await import("node:fs");
|
|
1144
|
+
const tmpPath = configPath + ".tmp";
|
|
1145
|
+
writeFileSyncRm(tmpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1146
|
+
renameSyncRm(tmpPath, configPath);
|
|
1147
|
+
console.error(`[agent-config] ${agentSlug}: image removed`);
|
|
1148
|
+
return {
|
|
1149
|
+
content: [{ type: "text", text: `Image removed for agent '${agentSlug}'. The public chat header will fall back to the account brand logo.` }],
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
catch (err) {
|
|
1153
|
+
console.error(`[agent-config] agent-image failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1154
|
+
return {
|
|
1155
|
+
content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
|
|
1156
|
+
isError: true,
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
});
|
|
1160
|
+
// ---------------------------------------------------------------------------
|
|
1161
|
+
// Agent config tools — deterministic reads for agent configuration
|
|
1162
|
+
// ---------------------------------------------------------------------------
|
|
1163
|
+
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.", {
|
|
1164
|
+
slug: z.string().describe("Agent slug (directory name under agents/)"),
|
|
1165
|
+
}, async ({ slug }) => {
|
|
1166
|
+
const TAG = "[admin:agent-config-read]";
|
|
1167
|
+
// Path traversal guard
|
|
1168
|
+
if (slug.includes("/") || slug.includes("\\") || slug.includes("..")) {
|
|
1169
|
+
console.error(`${TAG} rejected slug="${slug}" — contains path separator or traversal sequence`);
|
|
1170
|
+
return {
|
|
1171
|
+
content: [{ type: "text", text: `Invalid agent slug: "${slug}" — slug must not contain path separators or traversal sequences` }],
|
|
1172
|
+
isError: true,
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
try {
|
|
1176
|
+
const accountDir = getAccountDir();
|
|
1177
|
+
const configPath = resolve(accountDir, "agents", slug, "config.json");
|
|
1178
|
+
if (!existsSync(configPath)) {
|
|
1179
|
+
console.error(`${TAG} agent not found: slug="${slug}"`);
|
|
1180
|
+
return {
|
|
1181
|
+
content: [{ type: "text", text: `Agent "${slug}" not found — no config.json at agents/${slug}/` }],
|
|
1182
|
+
isError: true,
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
1186
|
+
JSON.parse(raw); // validate JSON before returning
|
|
1187
|
+
return {
|
|
1188
|
+
content: [{ type: "text", text: raw }],
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
catch (err) {
|
|
1192
|
+
console.error(`${TAG} failed for slug="${slug}": ${err instanceof Error ? err.message : String(err)}`);
|
|
1193
|
+
return {
|
|
1194
|
+
content: [{ type: "text", text: `Failed to read config for agent "${slug}": ${err instanceof Error ? err.message : String(err)}` }],
|
|
1195
|
+
isError: true,
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
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 () => {
|
|
1200
|
+
const TAG = "[admin:agent-list]";
|
|
1201
|
+
try {
|
|
1202
|
+
const accountDir = getAccountDir();
|
|
1203
|
+
const agentsDir = resolve(accountDir, "agents");
|
|
1204
|
+
if (!existsSync(agentsDir)) {
|
|
1205
|
+
return {
|
|
1206
|
+
content: [{ type: "text", text: JSON.stringify({ agents: [], skipped: [] }, null, 2) }],
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
// Best-effort defaultAgent lookup — don't fail the whole list if account.json is unreadable
|
|
1210
|
+
let defaultAgent;
|
|
1211
|
+
try {
|
|
1212
|
+
const accountConfig = await readAccountConfig();
|
|
1213
|
+
defaultAgent = accountConfig.defaultAgent;
|
|
1214
|
+
}
|
|
1215
|
+
catch (err) {
|
|
1216
|
+
console.error(`${TAG} failed to read account config for defaultAgent lookup: ${err instanceof Error ? err.message : String(err)}`);
|
|
1217
|
+
}
|
|
1218
|
+
const agents = [];
|
|
1219
|
+
const skipped = [];
|
|
1220
|
+
const entries = readdirSync(agentsDir, { withFileTypes: true });
|
|
1221
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
1222
|
+
if (!entry.isDirectory())
|
|
1223
|
+
continue;
|
|
1224
|
+
if (entry.name === "admin")
|
|
1225
|
+
continue;
|
|
1226
|
+
const configPath = resolve(agentsDir, entry.name, "config.json");
|
|
1227
|
+
if (!existsSync(configPath))
|
|
1228
|
+
continue;
|
|
1229
|
+
try {
|
|
1230
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
1231
|
+
agents.push({
|
|
1232
|
+
slug: entry.name,
|
|
1233
|
+
displayName: config.displayName ?? entry.name,
|
|
1234
|
+
model: config.model,
|
|
1235
|
+
plugins: config.plugins,
|
|
1236
|
+
status: config.status,
|
|
1237
|
+
liveMemory: config.liveMemory === true || config.liveMemory === "true",
|
|
1238
|
+
isDefault: entry.name === defaultAgent,
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
catch (err) {
|
|
1242
|
+
console.error(`${TAG} failed to parse config.json for agent "${entry.name}" — skipping: ${err instanceof Error ? err.message : String(err)}`);
|
|
1243
|
+
skipped.push(entry.name);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
return {
|
|
1247
|
+
content: [{ type: "text", text: JSON.stringify({ agents, skipped }, null, 2) }],
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
catch (err) {
|
|
1251
|
+
console.error(`${TAG} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1252
|
+
return {
|
|
1253
|
+
content: [{ type: "text", text: `Failed to list agents: ${err instanceof Error ? err.message : String(err)}` }],
|
|
1254
|
+
isError: true,
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
});
|
|
1258
|
+
eagerTool(server, "logs-read", "Read recent logs. Stream logs (type=agent-stream/error/session/public) are per-session — pass `sessionKey` (preferred) or the legacy `conversationId` alias to retrieve a single session's log from first [spawn] to final [process-exit]. type=agent-stream: per-session tool-use/tool-result archive (every `[tool-use]` and `[tool-result]` pair with full input + output JSON, plus raw Claude stream-json, agent events, and MCP server stderr via tee). USE THIS when investigating what an agent ACTUALLY did with its tools — server.log only carries `[persist] tool-call persisted` markers, not bodies. (`type=system` is a backwards-compatible alias for the same archive.) type=session: SSE events sent to client. type=error: Claude subprocess stderr (raw — NODE_DEBUG HTTP/NET/UNDICI traces land in agent-stream via the stream tee, not here). type=heartbeat: platform event dispatcher (check-due-events cron). type=public: public agent diagnostic log. type=server: platform server log. type=mcp: MCP server stderr (per-plugin raw). type=vnc: VNC browser viewer lifecycle.", {
|
|
1259
|
+
type: z.enum(["agent-stream", "system", "session", "error", "heartbeat", "public", "server", "mcp", "vnc"]).optional(),
|
|
1260
|
+
lines: z.number().optional(),
|
|
1261
|
+
sessionKey: z.string().optional(),
|
|
1262
|
+
conversationId: z.string().optional(),
|
|
1263
|
+
}, async ({ type, lines = 50, sessionKey, conversationId }) => {
|
|
1264
|
+
try {
|
|
1265
|
+
const LOG_DIR = resolve(getAccountDir(), "logs");
|
|
1266
|
+
// Task 1006 — sessionKey is the single identifier on disk.
|
|
1267
|
+
// `conversationId` is retained as a legacy alias that maps to the
|
|
1268
|
+
// sessionKey-named file; Task 1007 will collapse the alias in the UI.
|
|
1269
|
+
const idForResolve = sessionKey ?? conversationId;
|
|
1270
|
+
if (idForResolve) {
|
|
1271
|
+
if (!existsSync(LOG_DIR)) {
|
|
1272
|
+
return { content: [{ type: "text", text: `Log directory does not exist: ${LOG_DIR}` }] };
|
|
1273
|
+
}
|
|
1274
|
+
const prefixMap = {
|
|
1275
|
+
"agent-stream": "claude-agent-stream",
|
|
1276
|
+
system: "claude-agent-stream", // Task 850 — backwards-compatible alias for agent-stream
|
|
1277
|
+
error: "claude-agent-stderr",
|
|
1278
|
+
session: "sse-events",
|
|
1279
|
+
public: "public-agent-stream",
|
|
1280
|
+
};
|
|
1281
|
+
const resolvedType = type ?? "agent-stream";
|
|
1282
|
+
const prefix = prefixMap[resolvedType];
|
|
1283
|
+
if (!prefix) {
|
|
1284
|
+
return {
|
|
1285
|
+
content: [{ type: "text", text: `type=${resolvedType} is not per-session. Valid per-session types: agent-stream, error, session, public. For platform-scoped types (server, vnc, heartbeat, mcp) omit the id.` }],
|
|
1286
|
+
isError: true,
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
const fileName = `${prefix}-${idForResolve}.log`;
|
|
1290
|
+
const filePath = resolve(LOG_DIR, fileName);
|
|
1291
|
+
if (existsSync(filePath)) {
|
|
1292
|
+
const result = execFileSync("tail", ["-n", String(lines), filePath], { timeout: 5000 }).toString();
|
|
1293
|
+
return { content: [{ type: "text", text: `# ${fileName}\n\n${result}` }] };
|
|
1294
|
+
}
|
|
1295
|
+
// Emit missing-on-resolve so the writer-side existence contract is
|
|
1296
|
+
// the authoritative signal. One such line on any device is a P0.
|
|
1297
|
+
try {
|
|
1298
|
+
const ts = new Date().toISOString();
|
|
1299
|
+
appendFileSync(resolve(CONFIG_DIR, "logs", "server.log"), `${ts} [log-tee] missing-on-resolve sessionKey=${idForResolve.slice(0, 8)} surface=mcp-logs-read reason="file-not-found-in-LOG_DIR"\n`);
|
|
1300
|
+
}
|
|
1301
|
+
catch {
|
|
1302
|
+
// best-effort
|
|
1303
|
+
}
|
|
1304
|
+
return {
|
|
1305
|
+
content: [{
|
|
1306
|
+
type: "text",
|
|
1307
|
+
text: `No log file found for id=${idForResolve} type=${resolvedType}. tried=[${filePath}] reason=file-not-found`,
|
|
1308
|
+
}],
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
if (!existsSync(LOG_DIR)) {
|
|
1312
|
+
return { content: [{ type: "text", text: `Log directory does not exist: ${LOG_DIR}` }] };
|
|
1313
|
+
}
|
|
1314
|
+
// --- Session-key filtered mode: grep across log files ---
|
|
1315
|
+
if (sessionKey) {
|
|
1316
|
+
const prefixes = {
|
|
1317
|
+
"agent-stream": "claude-agent-stream-",
|
|
1318
|
+
system: "claude-agent-stream-", // Task 850 — backwards-compatible alias
|
|
1319
|
+
error: "claude-agent-stderr-",
|
|
1320
|
+
session: "sse-events-",
|
|
1321
|
+
public: "public-agent-stream-",
|
|
1322
|
+
mcp: "mcp-",
|
|
1323
|
+
};
|
|
1324
|
+
// When a type is explicitly provided, search only that type's files.
|
|
1325
|
+
// When type is omitted, search all prefix-based log types (excludes heartbeat — no session context).
|
|
1326
|
+
// server and vnc are single-file (configDir-scoped), so they have no prefix search;
|
|
1327
|
+
// their matches are produced by dedicated blocks below.
|
|
1328
|
+
const searchPrefixes = type && type !== "heartbeat" && type !== "server" && type !== "vnc" && prefixes[type]
|
|
1329
|
+
? { [type]: prefixes[type] }
|
|
1330
|
+
: (type === "server" || type === "vnc") ? {} : prefixes;
|
|
1331
|
+
const allFiles = readdirSync(LOG_DIR).filter(f => f.endsWith(".log"));
|
|
1332
|
+
const sections = [];
|
|
1333
|
+
// Search account-scoped prefix-based log files
|
|
1334
|
+
for (const [logType, prefix] of Object.entries(searchPrefixes)) {
|
|
1335
|
+
const typeFiles = allFiles
|
|
1336
|
+
.filter(f => f.startsWith(prefix))
|
|
1337
|
+
.sort(); // chronological by date suffix
|
|
1338
|
+
for (const file of typeFiles) {
|
|
1339
|
+
const filePath = resolve(LOG_DIR, file);
|
|
1340
|
+
try {
|
|
1341
|
+
const result = execFileSync("grep", ["-F", sessionKey, filePath], { timeout: 5000 }).toString().trim();
|
|
1342
|
+
if (result) {
|
|
1343
|
+
sections.push(`## ${file} (${logType})\n${result}`);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
catch (grepErr) {
|
|
1347
|
+
// grep exits 1 when no lines match — that's not an error
|
|
1348
|
+
const exitCode = grepErr.status;
|
|
1349
|
+
if (exitCode !== 1)
|
|
1350
|
+
throw grepErr;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
// Search server.log (platform-scoped, in configDir — not account-scoped)
|
|
1355
|
+
const includeServer = !type || type === "server";
|
|
1356
|
+
if (includeServer) {
|
|
1357
|
+
const serverLog = resolve(CONFIG_DIR, "logs", "server.log");
|
|
1358
|
+
if (existsSync(serverLog)) {
|
|
1359
|
+
try {
|
|
1360
|
+
const result = execFileSync("grep", ["-F", sessionKey, serverLog], { timeout: 5000 }).toString().trim();
|
|
1361
|
+
if (result) {
|
|
1362
|
+
sections.push(`## server.log (server)\n${result}`);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
catch (grepErr) {
|
|
1366
|
+
const exitCode = grepErr.status;
|
|
1367
|
+
if (exitCode !== 1)
|
|
1368
|
+
throw grepErr;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
// Search vnc-boot.log (platform-scoped, in configDir) — the
|
|
1373
|
+
// consolidated VNC browser viewer lifecycle log.
|
|
1374
|
+
const includeVnc = !type || type === "vnc";
|
|
1375
|
+
if (includeVnc) {
|
|
1376
|
+
const vncLog = resolve(CONFIG_DIR, "logs", "vnc-boot.log");
|
|
1377
|
+
if (existsSync(vncLog)) {
|
|
1378
|
+
try {
|
|
1379
|
+
const result = execFileSync("grep", ["-F", sessionKey, vncLog], { timeout: 5000 }).toString().trim();
|
|
1380
|
+
if (result) {
|
|
1381
|
+
sections.push(`## vnc-boot.log (vnc)\n${result}`);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
catch (grepErr) {
|
|
1385
|
+
const exitCode = grepErr.status;
|
|
1386
|
+
if (exitCode !== 1)
|
|
1387
|
+
throw grepErr;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
if (sections.length === 0) {
|
|
1392
|
+
return { content: [{ type: "text", text: `No log lines found for session key "${sessionKey}".` }] };
|
|
1393
|
+
}
|
|
1394
|
+
return { content: [{ type: "text", text: `# Session timeline: ${sessionKey}\n\n${sections.join("\n\n")}` }] };
|
|
1395
|
+
}
|
|
1396
|
+
// --- Standard mode: tail most recent file of the requested type ---
|
|
1397
|
+
const resolvedType = type ?? "agent-stream";
|
|
1398
|
+
// Heartbeat log is a single fixed file, not prefix-based
|
|
1399
|
+
if (resolvedType === "heartbeat") {
|
|
1400
|
+
const logFile = resolve(LOG_DIR, "check-due-events.log");
|
|
1401
|
+
if (!existsSync(logFile)) {
|
|
1402
|
+
return { content: [{ type: "text", text: "No heartbeat log found yet. The platform cron may not have run." }] };
|
|
1403
|
+
}
|
|
1404
|
+
const result = execFileSync("tail", ["-n", String(lines), logFile], {
|
|
1405
|
+
timeout: 5000,
|
|
1406
|
+
}).toString();
|
|
1407
|
+
return { content: [{ type: "text", text: `# check-due-events.log\n\n${result}` }] };
|
|
1408
|
+
}
|
|
1409
|
+
// Server log is a single fixed file in configDir (platform-scoped, not account-scoped)
|
|
1410
|
+
if (resolvedType === "server") {
|
|
1411
|
+
const serverLog = resolve(CONFIG_DIR, "logs", "server.log");
|
|
1412
|
+
if (!existsSync(serverLog)) {
|
|
1413
|
+
return { content: [{ type: "text", text: `No server log found at ${serverLog}` }] };
|
|
1414
|
+
}
|
|
1415
|
+
const result = execFileSync("tail", ["-n", String(lines), serverLog], {
|
|
1416
|
+
timeout: 5000,
|
|
1417
|
+
}).toString();
|
|
1418
|
+
return { content: [{ type: "text", text: `# server.log\n\n${result}` }] };
|
|
1419
|
+
}
|
|
1420
|
+
// VNC log is a single platform-scoped file covering the full
|
|
1421
|
+
// browser viewer lifecycle — see tool description for signals.
|
|
1422
|
+
if (resolvedType === "vnc") {
|
|
1423
|
+
const vncLogFile = resolve(CONFIG_DIR, "logs", "vnc-boot.log");
|
|
1424
|
+
if (!existsSync(vncLogFile)) {
|
|
1425
|
+
return { content: [{ type: "text", text: `No vnc-boot log found at ${vncLogFile}` }] };
|
|
1426
|
+
}
|
|
1427
|
+
const result = execFileSync("tail", ["-n", String(lines), vncLogFile], {
|
|
1428
|
+
timeout: 5000,
|
|
1429
|
+
}).toString();
|
|
1430
|
+
return { content: [{ type: "text", text: `# vnc-boot.log\n\n${result}` }] };
|
|
1431
|
+
}
|
|
1432
|
+
// Task 850 — agent-stream and the legacy system alias both map to
|
|
1433
|
+
// claude-agent-stream-. The fall-through default also covers them.
|
|
1434
|
+
const prefix = resolvedType === "error" ? "claude-agent-stderr-"
|
|
1435
|
+
: resolvedType === "session" ? "sse-events-"
|
|
1436
|
+
: resolvedType === "public" ? "public-agent-stream-"
|
|
1437
|
+
: resolvedType === "mcp" ? "mcp-"
|
|
1438
|
+
: "claude-agent-stream-";
|
|
1439
|
+
// Find the most recently modified file with the matching prefix
|
|
1440
|
+
const match = readdirSync(LOG_DIR)
|
|
1441
|
+
.filter(f => f.startsWith(prefix) && f.endsWith(".log"))
|
|
1442
|
+
.map(f => ({ file: f, mtime: statSync(resolve(LOG_DIR, f)).mtimeMs }))
|
|
1443
|
+
.sort((a, b) => b.mtime - a.mtime)[0];
|
|
1444
|
+
if (!match) {
|
|
1445
|
+
return { content: [{ type: "text", text: `No log file found for type '${resolvedType}' in ${LOG_DIR}` }] };
|
|
1446
|
+
}
|
|
1447
|
+
const logFile = resolve(LOG_DIR, match.file);
|
|
1448
|
+
const result = execFileSync("tail", ["-n", String(lines), logFile], {
|
|
1449
|
+
timeout: 5000,
|
|
1450
|
+
}).toString();
|
|
1451
|
+
return { content: [{ type: "text", text: `# ${match.file}\n\n${result}` }] };
|
|
1452
|
+
}
|
|
1453
|
+
catch (err) {
|
|
1454
|
+
return { content: [{ type: "text", text: `Failed to read logs: ${err instanceof Error ? err.message : String(err)}` }] };
|
|
1455
|
+
}
|
|
1456
|
+
});
|
|
1457
|
+
// Task 918 — operator-authored skills are written to disk as a plugin so the
|
|
1458
|
+
// existing plugin-manifest loader picks them up (parsePluginFrontmatter +
|
|
1459
|
+
// assemblePublicPluginContent + loadEmbeddedPlugins all read from
|
|
1460
|
+
// <PLATFORM_ROOT>/plugins/<dir>/). Canonical = <accountDir>/plugins/<plugin>
|
|
1461
|
+
// survives installer rmSync (data/ excluded); the mirror under
|
|
1462
|
+
// <PLATFORM_ROOT>/plugins/<plugin> is rehydrated at admin session start by
|
|
1463
|
+
// autoDeliverUserPlugins. The agent supplies pluginName/skillName/body —
|
|
1464
|
+
// path is computed by this tool from ACCOUNT_ID. Symmetric write counterpart
|
|
1465
|
+
// to plugin-read (Task 916).
|
|
1466
|
+
eagerTool(server, "store-skill", "Save an operator-authored skill on disk as part of an admin-managed plugin. " +
|
|
1467
|
+
"The skill becomes immediately discoverable by the admin agent (and the public agent if publicEmbed=true). " +
|
|
1468
|
+
"Path is computed internally from the active account; the agent supplies content + names only. " +
|
|
1469
|
+
"Re-running for the same skillName overwrites in place (drops orphan reference files).", {
|
|
1470
|
+
pluginName: z.string().describe("Plugin directory name in lowercase kebab-case (e.g. 'beacons-skills'). Must not collide with a shipped plugin name."),
|
|
1471
|
+
skillName: z.string().describe("Skill directory name in lowercase kebab-case (e.g. 'lead-capture')."),
|
|
1472
|
+
description: z.string().describe("One-sentence skill description used in SKILL.md frontmatter and as the trigger hint for the agent."),
|
|
1473
|
+
publicEmbed: z.boolean().describe("True to embed this skill in the public agent's system prompt; false for admin-only."),
|
|
1474
|
+
body: z.string().describe("SKILL.md body content WITHOUT frontmatter. Frontmatter (name, description, publicEmbed) is composed by the tool."),
|
|
1475
|
+
references: z.array(z.object({
|
|
1476
|
+
filename: z.string().describe("Reference filename matching ^[a-z0-9-]+\\.md$ (no path separators)."),
|
|
1477
|
+
content: z.string().describe("Reference file body content."),
|
|
1478
|
+
})).default([]).describe("Optional reference files written under skills/<skillName>/references/."),
|
|
1479
|
+
}, async ({ pluginName, skillName, description, publicEmbed, body, references }) => {
|
|
1480
|
+
const KEBAB_RE = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
|
|
1481
|
+
const REF_RE = /^[a-z0-9-]+\.md$/;
|
|
1482
|
+
if (!KEBAB_RE.test(pluginName)) {
|
|
1483
|
+
console.error(`[store-skill] ERROR pluginName=${pluginName} reason=invalid-kebab path=-`);
|
|
1484
|
+
return { content: [{ type: "text", text: `pluginName must be lowercase kebab-case (1-64 chars, no leading/trailing hyphen): "${pluginName}"` }], isError: true };
|
|
1485
|
+
}
|
|
1486
|
+
if (!KEBAB_RE.test(skillName)) {
|
|
1487
|
+
console.error(`[store-skill] ERROR pluginName=${pluginName} skillName=${skillName} reason=invalid-kebab path=-`);
|
|
1488
|
+
return { content: [{ type: "text", text: `skillName must be lowercase kebab-case (1-64 chars, no leading/trailing hyphen): "${skillName}"` }], isError: true };
|
|
1489
|
+
}
|
|
1490
|
+
for (const ref of references) {
|
|
1491
|
+
if (!REF_RE.test(ref.filename)) {
|
|
1492
|
+
console.error(`[store-skill] ERROR pluginName=${pluginName} skillName=${skillName} reason=invalid-ref-filename ref=${ref.filename} path=-`);
|
|
1493
|
+
return { content: [{ type: "text", text: `reference filename must match ^[a-z0-9-]+\\.md$: "${ref.filename}"` }], isError: true };
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
const accountDir = getAccountDir();
|
|
1497
|
+
const canonicalPluginDir = resolve(accountDir, "plugins", pluginName);
|
|
1498
|
+
const mirrorPluginDir = resolve(PLATFORM_ROOT, "plugins", pluginName);
|
|
1499
|
+
const canonicalPluginMd = resolve(canonicalPluginDir, "PLUGIN.md");
|
|
1500
|
+
const mirrorPluginMd = resolve(mirrorPluginDir, "PLUGIN.md");
|
|
1501
|
+
// Collision: mirror dir exists from a shipped plugin (no canonical mirror).
|
|
1502
|
+
// Refuse rather than silently shadow.
|
|
1503
|
+
if (existsSync(mirrorPluginMd) && !existsSync(canonicalPluginMd)) {
|
|
1504
|
+
console.error(`[store-skill] ERROR pluginName=${pluginName} skillName=${skillName} reason=collision-with-shipped path=${mirrorPluginDir}`);
|
|
1505
|
+
return { content: [{ type: "text", text: `pluginName "${pluginName}" collides with a shipped plugin. Choose a different pluginName.` }], isError: true };
|
|
1506
|
+
}
|
|
1507
|
+
const skillDir = resolve(canonicalPluginDir, "skills", skillName);
|
|
1508
|
+
const action = existsSync(resolve(skillDir, "SKILL.md")) ? "overwrite" : "create";
|
|
1509
|
+
try {
|
|
1510
|
+
mkdirSync(canonicalPluginDir, { recursive: true });
|
|
1511
|
+
// Compose PLUGIN.md only on first write. embed=["admin","public"] +
|
|
1512
|
+
// optional=false matches assemblePublicPluginContent / loadEmbeddedPlugins
|
|
1513
|
+
// expectations. metadata is single-line JSON (parsePluginFrontmatter at
|
|
1514
|
+
// plugin-manifest.ts:67 expects single-line) — multi-line silently falls
|
|
1515
|
+
// back to platform={}.
|
|
1516
|
+
if (!existsSync(canonicalPluginMd)) {
|
|
1517
|
+
const pluginMd = `---
|
|
1518
|
+
name: ${pluginName}
|
|
1519
|
+
description: "Operator-authored plugin"
|
|
1520
|
+
tools: []
|
|
1521
|
+
hidden: []
|
|
1522
|
+
requires: []
|
|
1523
|
+
metadata: {"platform":{"embed":["admin","public"],"optional":false}}
|
|
1524
|
+
---
|
|
1525
|
+
|
|
1526
|
+
# ${pluginName}
|
|
1527
|
+
|
|
1528
|
+
Operator-authored plugin. Skills under \`skills/\` were created via the admin skill-builder.
|
|
1529
|
+
`;
|
|
1530
|
+
writeFileSync(canonicalPluginMd, pluginMd);
|
|
1531
|
+
}
|
|
1532
|
+
// Reset skill dir before write so removed reference files don't linger.
|
|
1533
|
+
if (existsSync(skillDir)) {
|
|
1534
|
+
rmSync(skillDir, { recursive: true, force: true });
|
|
1535
|
+
}
|
|
1536
|
+
mkdirSync(skillDir, { recursive: true });
|
|
1537
|
+
// Normalise description — strip newlines and tabs to keep YAML
|
|
1538
|
+
// frontmatter on a single line. parsePluginFrontmatter uses a
|
|
1539
|
+
// single-line regex; an embedded newline (or `\n---\n`) would
|
|
1540
|
+
// otherwise corrupt the frontmatter parse and silently drop body
|
|
1541
|
+
// content from the agent prompt.
|
|
1542
|
+
const singleLineDescription = description.replace(/[\r\n\t]+/g, " ").trim();
|
|
1543
|
+
const escapedDescription = singleLineDescription.replace(/"/g, '\\"');
|
|
1544
|
+
const skillMd = `---
|
|
1545
|
+
name: ${skillName}
|
|
1546
|
+
description: "${escapedDescription}"
|
|
1547
|
+
publicEmbed: ${publicEmbed}
|
|
1548
|
+
---
|
|
1549
|
+
|
|
1550
|
+
${body}
|
|
1551
|
+
`;
|
|
1552
|
+
writeFileSync(resolve(skillDir, "SKILL.md"), skillMd);
|
|
1553
|
+
let refCount = 0;
|
|
1554
|
+
if (references.length > 0) {
|
|
1555
|
+
const refsDir = resolve(skillDir, "references");
|
|
1556
|
+
mkdirSync(refsDir, { recursive: true });
|
|
1557
|
+
for (const ref of references) {
|
|
1558
|
+
writeFileSync(resolve(refsDir, ref.filename), ref.content);
|
|
1559
|
+
refCount++;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
// Write the `.user-mirror` marker to CANONICAL first — cpSync will
|
|
1563
|
+
// carry it into the platform-side mirror as part of the same recursive
|
|
1564
|
+
// copy. This eliminates the post-cpSync writeFileSync race: if cpSync
|
|
1565
|
+
// succeeds, the marker is already in target; if cpSync fails partway,
|
|
1566
|
+
// canonical's marker is intact and the next autoDeliverUserPlugins
|
|
1567
|
+
// call retries the copy. autoDeliverUserPlugins relies on the marker
|
|
1568
|
+
// arriving with the source tree — never writes its own marker.
|
|
1569
|
+
writeFileSync(resolve(canonicalPluginDir, ".user-mirror"), `pluginName=${pluginName}\nsource=${canonicalPluginDir}\n`);
|
|
1570
|
+
// Mirror to <PLATFORM_ROOT>/plugins/<pluginName>/ so the change is
|
|
1571
|
+
// visible to the loader without a session restart.
|
|
1572
|
+
mkdirSync(mirrorPluginDir, { recursive: true });
|
|
1573
|
+
cpSync(canonicalPluginDir, mirrorPluginDir, { recursive: true, force: true });
|
|
1574
|
+
const bodyBytes = Buffer.byteLength(body, "utf-8");
|
|
1575
|
+
console.log(`[store-skill] write pluginName=${pluginName} skillName=${skillName} publicEmbed=${publicEmbed} path=${canonicalPluginDir} bodyBytes=${bodyBytes} referenceCount=${refCount} action=${action}`);
|
|
1576
|
+
return {
|
|
1577
|
+
content: [{
|
|
1578
|
+
type: "text",
|
|
1579
|
+
text: `Skill "${skillName}" saved to plugin "${pluginName}" (${action}). publicEmbed=${publicEmbed}, references=${refCount}. Active immediately for the admin agent${publicEmbed ? "; surfaces in the public agent on next session start" : ""}.`,
|
|
1580
|
+
}],
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
catch (err) {
|
|
1584
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1585
|
+
console.error(`[store-skill] ERROR pluginName=${pluginName} skillName=${skillName} reason=${msg.slice(0, 120)} path=${canonicalPluginDir}`);
|
|
1586
|
+
return { content: [{ type: "text", text: `Failed to write skill: ${msg}` }], isError: true };
|
|
1587
|
+
}
|
|
1588
|
+
});
|
|
1589
|
+
eagerTool(server, "plugin-read", "Read a plugin definition (PLUGIN.md) or one of its reference files.", {
|
|
1590
|
+
pluginName: z.string().describe("Name of the plugin directory (e.g. 'sales', 'business-assistant')"),
|
|
1591
|
+
file: z.string().optional().describe("Specific file to read (e.g. 'references/pricing.md'). Defaults to PLUGIN.md."),
|
|
1592
|
+
}, async ({ pluginName, file }) => {
|
|
1593
|
+
try {
|
|
1594
|
+
const resolvedFile = file ?? "PLUGIN.md";
|
|
1595
|
+
const pluginPath = resolve(PLATFORM_ROOT, "plugins", pluginName, resolvedFile);
|
|
1596
|
+
if (!existsSync(pluginPath)) {
|
|
1597
|
+
// Task 964/1000 — when the agent's pluginName/file combination misses
|
|
1598
|
+
// a skill that DOES live under `plugins/*/skills/<slug>/SKILL.md`,
|
|
1599
|
+
// surface the corrected pluginName AND the canonical file path so
|
|
1600
|
+
// the next call is deterministic. Both wrong-`file` and wrong-`pluginName`
|
|
1601
|
+
// shapes feed through `computePluginReadHint`.
|
|
1602
|
+
const hint = computePluginReadHint(PLATFORM_ROOT, pluginName, resolvedFile);
|
|
1603
|
+
const hintText = hint ? ` Did you mean pluginName="${hint.pluginName}", file="${hint.file}"?` : "";
|
|
1604
|
+
console.log(`[plugin-read] ${pluginName}/${resolvedFile} not-found hint=${hint ? hint.pluginName : "none"}`);
|
|
1605
|
+
return {
|
|
1606
|
+
content: [{ type: "text", text: `Plugin file not found: ${pluginPath}.${hintText}` }],
|
|
1607
|
+
isError: true,
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
console.log(`[plugin-read] ${pluginName}/${resolvedFile}`);
|
|
1611
|
+
const rawContent = await readFile(pluginPath, "utf-8");
|
|
1612
|
+
// Brand-substitute every plugin file before it reaches the agent —
|
|
1613
|
+
// PLUGIN.md, skills/<name>/SKILL.md, references/*.md all flow through
|
|
1614
|
+
// here. Missing brand.json hard-throws (Tasks 787/788 doctrine).
|
|
1615
|
+
const content = substituteBrandPlaceholders(rawContent, pluginPath);
|
|
1616
|
+
// Append file inventory on default calls (PLUGIN.md) so agents can discover
|
|
1617
|
+
// skill and reference paths without guessing
|
|
1618
|
+
if (resolvedFile === "PLUGIN.md") {
|
|
1619
|
+
const pluginRoot = resolve(PLATFORM_ROOT, "plugins", pluginName);
|
|
1620
|
+
const skills = [];
|
|
1621
|
+
const references = [];
|
|
1622
|
+
const scanDir = (base, prefix, target) => {
|
|
1623
|
+
const scanPath = resolve(pluginRoot, base);
|
|
1624
|
+
if (!existsSync(scanPath))
|
|
1625
|
+
return;
|
|
1626
|
+
try {
|
|
1627
|
+
const walk = (current, rel) => {
|
|
1628
|
+
for (const entry of readdirSync(current)) {
|
|
1629
|
+
const full = resolve(current, entry);
|
|
1630
|
+
try {
|
|
1631
|
+
const stat = statSync(full);
|
|
1632
|
+
if (stat.isDirectory()) {
|
|
1633
|
+
walk(full, `${rel}${entry}/`);
|
|
1634
|
+
}
|
|
1635
|
+
else if (entry.endsWith(".md")) {
|
|
1636
|
+
target.push(`${prefix}${rel}${entry}`);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
catch { /* skip unreadable entries */ }
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
walk(scanPath, "");
|
|
1643
|
+
}
|
|
1644
|
+
catch { /* skip unreadable directories */ }
|
|
1645
|
+
};
|
|
1646
|
+
scanDir("skills", "skills/", skills);
|
|
1647
|
+
scanDir("references", "references/", references);
|
|
1648
|
+
let inventory = "";
|
|
1649
|
+
if (skills.length > 0)
|
|
1650
|
+
inventory += `\nSkills: ${skills.join(", ")}`;
|
|
1651
|
+
if (references.length > 0)
|
|
1652
|
+
inventory += `\nReferences: ${references.join(", ")}`;
|
|
1653
|
+
return { content: [{ type: "text", text: content + inventory }] };
|
|
1654
|
+
}
|
|
1655
|
+
return { content: [{ type: "text", text: content }] };
|
|
1656
|
+
}
|
|
1657
|
+
catch (err) {
|
|
1658
|
+
return {
|
|
1659
|
+
content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
|
|
1660
|
+
isError: true,
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
// Task 964 — agent-side `Glob` fallthrough when "set up cloudflare" maps a
|
|
1665
|
+
// SKILL.md to the wrong plugin: skill-find walks
|
|
1666
|
+
// `${PLATFORM_ROOT}/plugins/*/skills/<skillName>/SKILL.md` once and returns
|
|
1667
|
+
// the owner. With one tool call the agent learns the correct plugin name.
|
|
1668
|
+
//
|
|
1669
|
+
// Task 1015 — `skill-load` (below) is the canonical single-call surface that
|
|
1670
|
+
// most agents want (resolve + read body in one shot). `skill-find` is kept
|
|
1671
|
+
// for callers that need only the path (e.g. tooling that lists owners
|
|
1672
|
+
// without reading the body).
|
|
1673
|
+
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.", {
|
|
1674
|
+
skillName: z.string().regex(/^[a-z0-9][a-z0-9-]*$/, "skillName must be lowercase kebab-case (a-z, 0-9, -)"),
|
|
1675
|
+
}, async ({ skillName }) => {
|
|
1676
|
+
const start = Date.now();
|
|
1677
|
+
const result = findSkillOwners(PLATFORM_ROOT, skillName);
|
|
1678
|
+
const ms = Date.now() - start;
|
|
1679
|
+
console.log(`[skill-find] skillName=${skillName} result=${result.status} candidates=${result.candidates.length} ms=${ms}`);
|
|
1680
|
+
if (result.status === "unique") {
|
|
1681
|
+
const [{ pluginName, file }] = result.candidates;
|
|
1682
|
+
return {
|
|
1683
|
+
content: [{
|
|
1684
|
+
type: "text",
|
|
1685
|
+
text: `pluginName="${pluginName}" file="${file}"\nTo read the body in one call, use: skill-load skillName="${skillName}"`,
|
|
1686
|
+
}],
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
if (result.status === "ambiguous") {
|
|
1690
|
+
const lines = result.candidates.map(c => ` pluginName="${c.pluginName}" file="${c.file}"`).join("\n");
|
|
1691
|
+
return {
|
|
1692
|
+
content: [{
|
|
1693
|
+
type: "text",
|
|
1694
|
+
text: `Ambiguous — skill "${skillName}" exists in ${result.candidates.length} plugins:\n${lines}`,
|
|
1695
|
+
}],
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
return {
|
|
1699
|
+
content: [{ type: "text", text: `not_found — no plugin owns a skill named "${skillName}"` }],
|
|
1700
|
+
isError: true,
|
|
1701
|
+
};
|
|
1702
|
+
});
|
|
1703
|
+
// Task 1015 — `skill-load` collapses skill resolve+read into one call.
|
|
1704
|
+
//
|
|
1705
|
+
// Why: 21 prose call sites previously said *"Load `admin/skills/plainly/SKILL.md`
|
|
1706
|
+
// via `plugin-read`"*. The LLM kept passing the path as `pluginName`, tripping
|
|
1707
|
+
// the not-found branch with the corrected-pluginName hint that Task 1000 added.
|
|
1708
|
+
// The recurring failure is recorded in the corrections ledger. One slug-arg
|
|
1709
|
+
// tool removes the path-format ambiguity at the source.
|
|
1710
|
+
//
|
|
1711
|
+
// Shape mirrors `plugin-read` (eagerTool registration, same brand-substitution
|
|
1712
|
+
// call) so SKILL.md bodies are byte-identical regardless of which tool reads
|
|
1713
|
+
// them. `plugin-read` is retained for `references/*` and `PLUGIN.md` reads —
|
|
1714
|
+
// those are plugin-scoped by nature and the path-as-pluginName failure pattern
|
|
1715
|
+
// is specific to skills (addressed by slug in prose).
|
|
1716
|
+
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.", {
|
|
1717
|
+
skillName: z.string().regex(/^[a-z0-9][a-z0-9-]*$/, "skillName must be lowercase kebab-case (a-z, 0-9, -)"),
|
|
1718
|
+
}, async ({ skillName }) => {
|
|
1719
|
+
const start = Date.now();
|
|
1720
|
+
try {
|
|
1721
|
+
const outcome = await loadSkill(PLATFORM_ROOT, skillName);
|
|
1722
|
+
if (outcome.status === "ambiguous") {
|
|
1723
|
+
const ms = Date.now() - start;
|
|
1724
|
+
console.log(`[skill-load] skillName=${skillName} result=ambiguous pluginName=none file=none bodyBytes=0 ms=${ms}`);
|
|
1725
|
+
const lines = outcome.candidates.map(c => ` pluginName="${c.pluginName}" file="${c.file}"`).join("\n");
|
|
1726
|
+
return {
|
|
1727
|
+
content: [{
|
|
1728
|
+
type: "text",
|
|
1729
|
+
text: `Ambiguous — skill "${skillName}" exists in ${outcome.candidates.length} plugins:\n${lines}\nUse plugin-read with one of the listed pluginName/file pairs.`,
|
|
1730
|
+
}],
|
|
1731
|
+
isError: true,
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
if (outcome.status === "not-found") {
|
|
1735
|
+
const ms = Date.now() - start;
|
|
1736
|
+
console.log(`[skill-load] skillName=${skillName} result=not-found pluginName=none file=none bodyBytes=0 ms=${ms}`);
|
|
1737
|
+
return {
|
|
1738
|
+
content: [{ type: "text", text: `not_found — no plugin owns a skill named "${skillName}"` }],
|
|
1739
|
+
isError: true,
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
const content = substituteBrandPlaceholders(outcome.body, outcome.absolutePath);
|
|
1743
|
+
// Task 1021 — prepend a skill-input-contract notice when the skill's
|
|
1744
|
+
// frontmatter declares `requiredInputs:`. The notice tells the agent
|
|
1745
|
+
// to route missing inputs through AskUserQuestion (per IDENTITY.md
|
|
1746
|
+
// "Skill-input resolution"); the embedded `<!-- skill-input-contract
|
|
1747
|
+
// requiredInputs=... -->` marker is the per-turn keying signal that
|
|
1748
|
+
// parseClaudeStream uses to fire [skill-input-resolution] /
|
|
1749
|
+
// [skill-input-missing] observability lines.
|
|
1750
|
+
const requiredInputs = parseRequiredInputs(outcome.body);
|
|
1751
|
+
let bodyOut = content;
|
|
1752
|
+
if (requiredInputs) {
|
|
1753
|
+
const slotLabels = requiredInputs.map(s => s.join(" | ")).join(", ");
|
|
1754
|
+
const marker = JSON.stringify(requiredInputs);
|
|
1755
|
+
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 task-get to guess the missing input.\n<!-- /skill-input-contract -->\n\n`;
|
|
1756
|
+
bodyOut = notice + content;
|
|
1757
|
+
}
|
|
1758
|
+
const ms = Date.now() - start;
|
|
1759
|
+
const reqLog = requiredInputs ? ` requiredInputs=${JSON.stringify(requiredInputs)}` : "";
|
|
1760
|
+
console.log(`[skill-load] skillName=${skillName} result=unique pluginName=${outcome.pluginName} file=${outcome.file} bodyBytes=${bodyOut.length}${reqLog} ms=${ms}`);
|
|
1761
|
+
return { content: [{ type: "text", text: bodyOut }] };
|
|
1762
|
+
}
|
|
1763
|
+
catch (err) {
|
|
1764
|
+
const ms = Date.now() - start;
|
|
1765
|
+
console.log(`[skill-load] skillName=${skillName} result=read-error pluginName=unknown file=unknown bodyBytes=0 ms=${ms}`);
|
|
1766
|
+
return {
|
|
1767
|
+
content: [{ type: "text", text: `Failed to load skill "${skillName}": ${err instanceof Error ? err.message : String(err)}` }],
|
|
1768
|
+
isError: true,
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
});
|
|
1772
|
+
// Task 940 — `"rendered"` is a sentinel, not a persistence ack. The platform
|
|
1773
|
+
// UI's stream-parser intercepts this tool_use upstream of the SDK reply
|
|
1774
|
+
// (platform/ui/app/lib/claude-agent/stream-parser.ts:362) and emits a typed
|
|
1775
|
+
// `component` event that admin-agent.ts's persistMessage flushes to Neo4j as
|
|
1776
|
+
// a sibling :Component node.
|
|
1777
|
+
//
|
|
1778
|
+
// Task 942 — render-component is also the *server commitment surface* for
|
|
1779
|
+
// PERSISTENT_COMPONENTS (action-list, document-editor, rich-content-editor,
|
|
1780
|
+
// grid-editor). The actual file write + :KnowledgeDocument projection lives
|
|
1781
|
+
// in the admin server process (stream-parser path) because the SDK delivers
|
|
1782
|
+
// `tool_use.input` to the UI in the assistant message but the MCP handler's
|
|
1783
|
+
// return reaches only the agent — there is no path back to persistMessage
|
|
1784
|
+
// from here. This handler emits a one-line observability marker per
|
|
1785
|
+
// persistence-eligible call so operators can grep for the commitment, and
|
|
1786
|
+
// returns richer JSON so the doctrine grep
|
|
1787
|
+
// `render-component.*"rendered"` resolves to a persistence-aware handler
|
|
1788
|
+
// rather than a bare stub.
|
|
1789
|
+
eagerTool(server, "render-component", "Render a pre-built UI component inline in the conversation. " +
|
|
1790
|
+
"Call this instead of describing a UI — the component handles input collection. " +
|
|
1791
|
+
"Wait for the user's response before continuing.", {
|
|
1792
|
+
name: z.string().describe("Component name from the UI suite (e.g. single-select, confirm, info-card, form, progress)"),
|
|
1793
|
+
data: z.record(z.string(), z.unknown()),
|
|
1794
|
+
}, async ({ name, data }) => {
|
|
1795
|
+
if (isPersistentComponent(name)) {
|
|
1796
|
+
// Persistence-eligible: log the commitment so the live + audit grep
|
|
1797
|
+
// (`[render-component-persist]`) can correlate the MCP call against
|
|
1798
|
+
// the downstream :KnowledgeDocument MERGE. Byte counts come from
|
|
1799
|
+
// data.html (preferred) or data.content; both being absent is a
|
|
1800
|
+
// legitimate render of an empty editor and the projection is skipped
|
|
1801
|
+
// server-side.
|
|
1802
|
+
const html = typeof data?.html === "string" ? data.html : "";
|
|
1803
|
+
const content = typeof data?.content === "string" ? data.content : "";
|
|
1804
|
+
const bytes = html.length > 0 ? html.length : content.length;
|
|
1805
|
+
const mimeType = html.length > 0 ? "text/html" : (content.length > 0 ? "text/markdown" : "");
|
|
1806
|
+
console.error(`[render-component-persist] componentName=${name} bytes=${bytes} mimeType=${mimeType || "none"}`);
|
|
1807
|
+
return {
|
|
1808
|
+
content: [{
|
|
1809
|
+
type: "text",
|
|
1810
|
+
text: JSON.stringify({ rendered: true, name, persistent: true, bytes, mimeType }),
|
|
1811
|
+
}],
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
return { content: [{ type: "text", text: "rendered" }] };
|
|
1815
|
+
});
|
|
1816
|
+
eagerTool(server, "session-reset", "Reset the current session. Compacts conversation history to memory, clears the visible conversation, " +
|
|
1817
|
+
"and starts a fresh session with a new greeting. Call when the user asks to start a new session, " +
|
|
1818
|
+
"clear the conversation, or start fresh.", {}, async () => ({ content: [{ type: "text", text: "reset" }] }));
|
|
1819
|
+
eagerTool(server, "session-resume", "Resume a previous session. Loads the selected session's message history into the chat timeline " +
|
|
1820
|
+
"and routes new messages to that conversation. Call after the user selects a session from the " +
|
|
1821
|
+
"session-list results. Pass the conversationId from the selected session.", { conversationId: z.string().uuid().describe("The conversationId of the session to resume") }, async () => ({ content: [{ type: "text", text: "resumed" }] }));
|
|
1822
|
+
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. " +
|
|
1823
|
+
"Validates strength (8+ chars, digit, special character, no spaces anywhere). " +
|
|
1824
|
+
"Protects the admin interface when exposed over the tunnel — has no effect on the public endpoint or the tunnel itself. " +
|
|
1825
|
+
"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 }) => {
|
|
1826
|
+
// Validate strength — same rules as the web server's validatePasswordStrength
|
|
1827
|
+
const checks = [
|
|
1828
|
+
{ label: "at least 8 characters", met: password.length >= 8 },
|
|
1829
|
+
{ label: "contains a number", met: /\d/.test(password) },
|
|
1830
|
+
{ label: "contains a special character", met: /[^A-Za-z0-9]/.test(password) },
|
|
1831
|
+
{ label: "no spaces", met: password.length > 0 && !/\s/.test(password) },
|
|
1832
|
+
];
|
|
1833
|
+
const failed = checks.filter((c) => !c.met);
|
|
1834
|
+
if (failed.length > 0) {
|
|
1835
|
+
return {
|
|
1836
|
+
content: [{ type: "text", text: `Password requirements not met: ${failed.map((c) => c.label).join(", ")}` }],
|
|
1837
|
+
isError: true,
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
try {
|
|
1841
|
+
const { scrypt, randomBytes } = await import("node:crypto");
|
|
1842
|
+
const salt = randomBytes(32);
|
|
1843
|
+
const hash = await new Promise((res, rej) => {
|
|
1844
|
+
scrypt(password, salt, 64, { N: 16384, r: 8, p: 1 }, (err, key) => {
|
|
1845
|
+
if (err)
|
|
1846
|
+
rej(err);
|
|
1847
|
+
else
|
|
1848
|
+
res(key);
|
|
1849
|
+
});
|
|
1850
|
+
});
|
|
1851
|
+
const stored = `${salt.toString("hex")}:${hash.toString("hex")}`;
|
|
1852
|
+
const pwPath = REMOTE_PASSWORD_FILE;
|
|
1853
|
+
await writeFile(pwPath, stored, { mode: 0o600 });
|
|
1854
|
+
return { content: [{ type: "text", text: "Remote access password set." }] };
|
|
1855
|
+
}
|
|
1856
|
+
catch (err) {
|
|
1857
|
+
return {
|
|
1858
|
+
content: [{ type: "text", text: `Failed to set password: ${err instanceof Error ? err.message : String(err)}` }],
|
|
1859
|
+
isError: true,
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
});
|
|
1863
|
+
eagerTool(server, "api-key-store", "Validate and store an Anthropic API key. Key must start with sk-ant- and be at least 90 characters. " +
|
|
1864
|
+
"Writes to the platform's brand-specific config directory. Takes effect immediately for the public agent.", { apiKey: z.string() }, async ({ apiKey }) => {
|
|
1865
|
+
try {
|
|
1866
|
+
writeKey(apiKey);
|
|
1867
|
+
return { content: [{ type: "text", text: `API key stored at ${keyFilePath()}.` }] };
|
|
1868
|
+
}
|
|
1869
|
+
catch (err) {
|
|
1870
|
+
return {
|
|
1871
|
+
content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }],
|
|
1872
|
+
isError: true,
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
});
|
|
1876
|
+
eagerTool(server, "api-key-verify", "Verify the stored Anthropic API key works by making a minimal probe call. " +
|
|
1877
|
+
"Returns one of: valid (key works, credits available), billing (key valid but credit balance too low — " +
|
|
1878
|
+
"user must add credits at console.anthropic.com/settings/billing), auth_error (key rejected — invalid or revoked), " +
|
|
1879
|
+
"missing (no key file found), error (could not verify — network issue or Anthropic outage). " +
|
|
1880
|
+
"Call this on greeting, during system checks, or after storing a new key.", {}, async () => {
|
|
1881
|
+
const result = await validateKey();
|
|
1882
|
+
return {
|
|
1883
|
+
content: [{
|
|
1884
|
+
type: "text",
|
|
1885
|
+
text: `Status: ${result.status}\n${result.message}`,
|
|
1886
|
+
}],
|
|
1887
|
+
};
|
|
1888
|
+
});
|
|
1889
|
+
// ===================================================================
|
|
1890
|
+
// Anthropic setup — deterministic state machine
|
|
1891
|
+
// ===================================================================
|
|
1892
|
+
/*
|
|
1893
|
+
* anthropic-setup state machine (action-relay pattern)
|
|
1894
|
+
*
|
|
1895
|
+
* Call 1: anthropic-setup({})
|
|
1896
|
+
* ├─ no public endpoint → { status: "not_needed" } ← Phase 0 gate
|
|
1897
|
+
* ├─ key stored + valid? → { status: "complete" }
|
|
1898
|
+
* ├─ key stored + billing → { status: "awaiting_credits" }
|
|
1899
|
+
* ├─ key stored + error → { status: "error" }
|
|
1900
|
+
* └─ no key → { status: "awaiting_signin", action }
|
|
1901
|
+
*
|
|
1902
|
+
* Call 2: anthropic-setup({ consoleResult: "..." })
|
|
1903
|
+
* ├─ no_session → { status: "awaiting_signin", action }
|
|
1904
|
+
* ├─ no_org → { status: "error" }
|
|
1905
|
+
* ├─ no_credits → { status: "awaiting_credits", action }
|
|
1906
|
+
* ├─ success → writeKey → validateKey → { status: "complete" }
|
|
1907
|
+
* └─ *_failed → { status: "error" }
|
|
1908
|
+
*/
|
|
1909
|
+
const CONSOLE_BASE = "https://platform.claude.com";
|
|
1910
|
+
/**
|
|
1911
|
+
* JavaScript expression for browser_evaluate that runs the complete
|
|
1912
|
+
* Console API pipeline in one call: check session → find API org →
|
|
1913
|
+
* check credits → create key. Returns a structured JSON result.
|
|
1914
|
+
*/
|
|
1915
|
+
function consoleEvaluateExpression(keyName) {
|
|
1916
|
+
return `async () => {
|
|
1917
|
+
try {
|
|
1918
|
+
const orgsRes = await fetch("/api/organizations", { credentials: "include" });
|
|
1919
|
+
if (orgsRes.status === 401 || orgsRes.status === 403) {
|
|
1920
|
+
return JSON.stringify({ status: "no_session" });
|
|
1921
|
+
}
|
|
1922
|
+
if (!orgsRes.ok) {
|
|
1923
|
+
return JSON.stringify({ status: "fetch_error", error: "organizations: " + orgsRes.status });
|
|
1924
|
+
}
|
|
1925
|
+
const orgs = await orgsRes.json();
|
|
1926
|
+
const apiOrg = orgs.find(o => o.capabilities && o.capabilities.includes("api"));
|
|
1927
|
+
if (!apiOrg) {
|
|
1928
|
+
return JSON.stringify({ status: "no_org" });
|
|
1929
|
+
}
|
|
1930
|
+
const creditsRes = await fetch("/api/organizations/" + apiOrg.uuid + "/prepaid/credits", { credentials: "include" });
|
|
1931
|
+
if (!creditsRes.ok) {
|
|
1932
|
+
return JSON.stringify({ status: "fetch_error", error: "credits: " + creditsRes.status });
|
|
1933
|
+
}
|
|
1934
|
+
const credits = await creditsRes.json();
|
|
1935
|
+
if (typeof credits.amount === "number" && credits.amount <= 0) {
|
|
1936
|
+
return JSON.stringify({ status: "no_credits", orgId: apiOrg.uuid, orgName: apiOrg.name, creditUrl: "${CONSOLE_BASE}/settings/billing" });
|
|
1937
|
+
}
|
|
1938
|
+
const createRes = await fetch("/api/console/organizations/" + apiOrg.uuid + "/workspaces/default/api_keys", {
|
|
1939
|
+
method: "POST",
|
|
1940
|
+
credentials: "include",
|
|
1941
|
+
headers: { "content-type": "application/json" },
|
|
1942
|
+
body: JSON.stringify({ name: ${JSON.stringify(keyName)} })
|
|
1943
|
+
});
|
|
1944
|
+
if (!createRes.ok) {
|
|
1945
|
+
return JSON.stringify({ status: "create_failed", error: "key creation: " + createRes.status });
|
|
1946
|
+
}
|
|
1947
|
+
const keyData = await createRes.json();
|
|
1948
|
+
if (!keyData.raw_key) {
|
|
1949
|
+
return JSON.stringify({ status: "create_failed", error: "no raw_key in response" });
|
|
1950
|
+
}
|
|
1951
|
+
return JSON.stringify({
|
|
1952
|
+
status: "success",
|
|
1953
|
+
key: keyData.raw_key,
|
|
1954
|
+
keyId: keyData.id,
|
|
1955
|
+
keyHint: keyData.partial_key_hint,
|
|
1956
|
+
orgName: apiOrg.name,
|
|
1957
|
+
credits: credits.amount
|
|
1958
|
+
});
|
|
1959
|
+
} catch (err) {
|
|
1960
|
+
return JSON.stringify({ status: "fetch_error", error: String(err) });
|
|
1961
|
+
}
|
|
1962
|
+
}`;
|
|
1963
|
+
}
|
|
1964
|
+
function anthropicResult(r) {
|
|
1965
|
+
return {
|
|
1966
|
+
content: [{ type: "text", text: JSON.stringify(r, null, 2) }],
|
|
1967
|
+
...(r.status === "error" ? { isError: true } : {}),
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Check whether a public-facing hostname is configured. The Anthropic API key
|
|
1972
|
+
* powers the public agent only — admin runs on Claude OAuth — so without a
|
|
1973
|
+
* public endpoint there is nothing for the key to power.
|
|
1974
|
+
*
|
|
1975
|
+
* Mirrors the runtime `isPublicHost(host)` predicate in
|
|
1976
|
+
* `platform/ui/server/index.ts`: a host is public iff it starts with `public.`
|
|
1977
|
+
* OR is listed in `~/{configDir}/alias-domains.json`. Step 7's form submit
|
|
1978
|
+
* handler at `platform/ui/server/routes/admin/cloudflare.ts` writes secondary
|
|
1979
|
+
* hostnames (apex + custom public labels) to `alias-domains.json` and skips
|
|
1980
|
+
* `public.*` because the prefix itself is the signal — so the two predicates
|
|
1981
|
+
* together cover every shape the form can produce.
|
|
1982
|
+
*
|
|
1983
|
+
* Returns true if any configured hostname satisfies either predicate.
|
|
1984
|
+
*/
|
|
1985
|
+
function hasPublicEndpointConfigured() {
|
|
1986
|
+
const aliasPath = join(CONFIG_DIR, "alias-domains.json");
|
|
1987
|
+
if (existsSync(aliasPath)) {
|
|
1988
|
+
try {
|
|
1989
|
+
const parsed = JSON.parse(readFileSync(aliasPath, "utf-8"));
|
|
1990
|
+
if (Array.isArray(parsed)) {
|
|
1991
|
+
const entries = parsed.filter((h) => typeof h === "string" && h.length > 0);
|
|
1992
|
+
if (entries.length > 0) {
|
|
1993
|
+
return { has: true, reason: `alias-domains.json: ${entries.join(", ")}` };
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
catch {
|
|
1998
|
+
// Malformed file — treat as empty; matches the server-side reader's tolerance.
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
const cfConfigPath = join(CONFIG_DIR, "cloudflared", "config.yml");
|
|
2002
|
+
if (existsSync(cfConfigPath)) {
|
|
2003
|
+
try {
|
|
2004
|
+
const yaml = readFileSync(cfConfigPath, "utf-8");
|
|
2005
|
+
const hostnames = [...yaml.matchAll(/^\s*-\s*hostname:\s*(\S+)/gm)].map((m) => m[1]);
|
|
2006
|
+
const publicPrefixed = hostnames.find((h) => h.startsWith("public."));
|
|
2007
|
+
if (publicPrefixed) {
|
|
2008
|
+
return { has: true, reason: `cloudflared/config.yml ingress: ${publicPrefixed}` };
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
catch {
|
|
2012
|
+
// Unreadable — treat as no public host.
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
return {
|
|
2016
|
+
has: false,
|
|
2017
|
+
reason: "no entries in alias-domains.json and no public.* hostname in cloudflared/config.yml",
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
eagerTool(server, "anthropic-setup", "Deterministic state machine for Anthropic API key acquisition. " +
|
|
2021
|
+
"Checks current state, advances as far as possible, and returns a structured JSON result. " +
|
|
2022
|
+
"On first call (no consoleResult): first verifies a public-facing endpoint is configured " +
|
|
2023
|
+
"(the key only powers the public agent); if not, returns status 'not_needed'. Otherwise " +
|
|
2024
|
+
"checks if a valid key is already stored. If not, returns status 'awaiting_signin' with " +
|
|
2025
|
+
"a browser_evaluate action — the agent must open the browser to platform.claude.com, run " +
|
|
2026
|
+
"the action's function via browser_evaluate, and pass the string result back as " +
|
|
2027
|
+
"consoleResult on the next call. " +
|
|
2028
|
+
"On second call (with consoleResult): parses the Console API result, stores the key, " +
|
|
2029
|
+
"verifies it, and returns status 'complete'. " +
|
|
2030
|
+
"Statuses: complete (key stored and verified), not_needed (no public endpoint configured — " +
|
|
2031
|
+
"skip the step), awaiting_signin (user must sign in to Console), awaiting_credits (signed in " +
|
|
2032
|
+
"but no credits — user must add credits), error (fatal — relay message).", {
|
|
2033
|
+
consoleResult: z.string().optional().describe("JSON string result from running the browser_evaluate action returned by a prior call. " +
|
|
2034
|
+
"Omit on the first call."),
|
|
2035
|
+
}, async ({ consoleResult }) => {
|
|
2036
|
+
const log = (msg) => console.error(`[anthropic-setup] ${msg}`);
|
|
2037
|
+
// ── Phase 0: gate on public endpoint existence ─────────────
|
|
2038
|
+
// The API key only powers the public-facing agent. If no public hostname
|
|
2039
|
+
// is configured (operator skipped step 7, or completed it without a
|
|
2040
|
+
// public/apex hostname) the key has no consumer — short-circuit before
|
|
2041
|
+
// any Console interaction so we never create keys the operator cannot use.
|
|
2042
|
+
if (!consoleResult) {
|
|
2043
|
+
const publicCheck = hasPublicEndpointConfigured();
|
|
2044
|
+
if (!publicCheck.has) {
|
|
2045
|
+
log(`gate: no public endpoint (${publicCheck.reason}) — returning not_needed`);
|
|
2046
|
+
return anthropicResult({
|
|
2047
|
+
status: "not_needed",
|
|
2048
|
+
message: "No public-facing endpoint is configured, so an Anthropic API key is not needed. " +
|
|
2049
|
+
"The key powers the public agent only — admin runs on your Claude OAuth session. " +
|
|
2050
|
+
"When you set up a public hostname via Cloudflare (or apex domain), come back and " +
|
|
2051
|
+
"ask to set up the API key then.",
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
2054
|
+
log(`gate: public endpoint present (${publicCheck.reason})`);
|
|
2055
|
+
}
|
|
2056
|
+
// ── Phase 1: check stored key ──────────────────────────────
|
|
2057
|
+
if (!consoleResult) {
|
|
2058
|
+
log("checking stored key");
|
|
2059
|
+
if (hasKey()) {
|
|
2060
|
+
const verification = await validateKey();
|
|
2061
|
+
log(`stored key verification: ${verification.status}`);
|
|
2062
|
+
switch (verification.status) {
|
|
2063
|
+
case "valid":
|
|
2064
|
+
log("key valid, returning complete");
|
|
2065
|
+
return anthropicResult({
|
|
2066
|
+
status: "complete",
|
|
2067
|
+
message: `API key is stored and working. Key file: ${keyFilePath()}.`,
|
|
2068
|
+
});
|
|
2069
|
+
case "billing":
|
|
2070
|
+
log("key valid but billing issue");
|
|
2071
|
+
return anthropicResult({
|
|
2072
|
+
status: "awaiting_credits",
|
|
2073
|
+
message: "API key is stored and valid, but your account has insufficient credits. " +
|
|
2074
|
+
`Add credits at ${CONSOLE_BASE}/settings/billing, then call this tool again.`,
|
|
2075
|
+
data: { url: `${CONSOLE_BASE}/settings/billing` },
|
|
2076
|
+
});
|
|
2077
|
+
case "auth_error":
|
|
2078
|
+
// Deterministic transition: the state machine knows the key file
|
|
2079
|
+
// path and the only recovery is to delete and restart sign-in.
|
|
2080
|
+
// Auto-reset here so the agent never has to rediscover the path
|
|
2081
|
+
// via filesystem probes.
|
|
2082
|
+
log(`auth_error → auto-resetting: deleting revoked key at ${keyFilePath()}`);
|
|
2083
|
+
try {
|
|
2084
|
+
deleteKey();
|
|
2085
|
+
}
|
|
2086
|
+
catch (err) {
|
|
2087
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2088
|
+
log(`auto-reset failed: ${msg}`);
|
|
2089
|
+
return anthropicResult({
|
|
2090
|
+
status: "error",
|
|
2091
|
+
message: `The stored API key was rejected and auto-reset failed: ${msg}. ` +
|
|
2092
|
+
`Manual intervention needed — remove ${keyFilePath()} and call this tool again.`,
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
// Fall through to the awaiting_signin path below.
|
|
2096
|
+
break;
|
|
2097
|
+
case "missing":
|
|
2098
|
+
// hasKey() returned true but readKey() returned null — shouldn't happen
|
|
2099
|
+
log("hasKey true but validate returned missing — proceeding to setup");
|
|
2100
|
+
break;
|
|
2101
|
+
case "error":
|
|
2102
|
+
log(`verification failed: ${verification.message}`);
|
|
2103
|
+
return anthropicResult({
|
|
2104
|
+
status: "error",
|
|
2105
|
+
message: `Could not verify the stored key: ${verification.message}. ` +
|
|
2106
|
+
"This may be a network issue or Anthropic outage. Try again later.",
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
else {
|
|
2111
|
+
log("no key stored");
|
|
2112
|
+
}
|
|
2113
|
+
// No valid key — return action for Console API calls
|
|
2114
|
+
const expression = consoleEvaluateExpression(BRAND_NAME);
|
|
2115
|
+
log("returning awaiting_signin with browser_evaluate action");
|
|
2116
|
+
return anthropicResult({
|
|
2117
|
+
status: "awaiting_signin",
|
|
2118
|
+
message: "No valid API key stored. Open the browser to the Anthropic Console " +
|
|
2119
|
+
`(${CONSOLE_BASE}) and sign in. This is a separate service from Claude — ` +
|
|
2120
|
+
"you may need to sign in again even if you used the same credentials earlier. " +
|
|
2121
|
+
"Once signed in, run the browser_evaluate action below, then call this tool " +
|
|
2122
|
+
"again with the result as consoleResult.",
|
|
2123
|
+
data: {
|
|
2124
|
+
url: CONSOLE_BASE,
|
|
2125
|
+
action: {
|
|
2126
|
+
tool: "browser_evaluate",
|
|
2127
|
+
params: { function: expression },
|
|
2128
|
+
},
|
|
2129
|
+
},
|
|
2130
|
+
});
|
|
2131
|
+
}
|
|
2132
|
+
// ── Phase 2: process Console API result ────────────────────
|
|
2133
|
+
log("processing consoleResult");
|
|
2134
|
+
let parsed;
|
|
2135
|
+
try {
|
|
2136
|
+
parsed = JSON.parse(consoleResult);
|
|
2137
|
+
}
|
|
2138
|
+
catch {
|
|
2139
|
+
log(`consoleResult parse failed: ${consoleResult.slice(0, 200)}`);
|
|
2140
|
+
return anthropicResult({
|
|
2141
|
+
status: "error",
|
|
2142
|
+
message: "Failed to parse the browser result. The Console session may have " +
|
|
2143
|
+
"been lost or the browser encountered an error. Please try again — " +
|
|
2144
|
+
"open the browser to platform.claude.com, sign in, and re-run the action.",
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2147
|
+
log(`console result status: ${parsed.status}`);
|
|
2148
|
+
switch (parsed.status) {
|
|
2149
|
+
case "no_session": {
|
|
2150
|
+
log("no Console session — user needs to sign in");
|
|
2151
|
+
const expression = consoleEvaluateExpression(BRAND_NAME);
|
|
2152
|
+
return anthropicResult({
|
|
2153
|
+
status: "awaiting_signin",
|
|
2154
|
+
message: "Not signed in to the Anthropic Console. Please sign in at " +
|
|
2155
|
+
`${CONSOLE_BASE} and then run the browser_evaluate action below.`,
|
|
2156
|
+
data: {
|
|
2157
|
+
url: CONSOLE_BASE,
|
|
2158
|
+
action: {
|
|
2159
|
+
tool: "browser_evaluate",
|
|
2160
|
+
params: { function: expression },
|
|
2161
|
+
},
|
|
2162
|
+
},
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
2165
|
+
case "no_org":
|
|
2166
|
+
log("no API organization found");
|
|
2167
|
+
return anthropicResult({
|
|
2168
|
+
status: "error",
|
|
2169
|
+
message: "No API organization found in your Anthropic account. " +
|
|
2170
|
+
"You may be signed in to a Claude consumer account instead of the API Console. " +
|
|
2171
|
+
`Sign in at ${CONSOLE_BASE} with an account that has API access.`,
|
|
2172
|
+
});
|
|
2173
|
+
case "no_credits": {
|
|
2174
|
+
log(`no credits — org: ${parsed.orgName ?? "unknown"}`);
|
|
2175
|
+
const expression = consoleEvaluateExpression(BRAND_NAME);
|
|
2176
|
+
return anthropicResult({
|
|
2177
|
+
status: "awaiting_credits",
|
|
2178
|
+
message: `Your API organization (${parsed.orgName ?? "unknown"}) has no credits. ` +
|
|
2179
|
+
`Add credits at ${parsed.creditUrl ?? CONSOLE_BASE + "/settings/billing"}, ` +
|
|
2180
|
+
"then run the browser_evaluate action below.",
|
|
2181
|
+
data: {
|
|
2182
|
+
url: parsed.creditUrl ?? `${CONSOLE_BASE}/settings/billing`,
|
|
2183
|
+
action: {
|
|
2184
|
+
tool: "browser_evaluate",
|
|
2185
|
+
params: { function: expression },
|
|
2186
|
+
},
|
|
2187
|
+
},
|
|
2188
|
+
});
|
|
2189
|
+
}
|
|
2190
|
+
case "create_failed":
|
|
2191
|
+
log(`key creation failed: ${parsed.error ?? "unknown"}`);
|
|
2192
|
+
return anthropicResult({
|
|
2193
|
+
status: "error",
|
|
2194
|
+
message: `Failed to create API key: ${parsed.error ?? "unknown error"}. ` +
|
|
2195
|
+
"This may be a permissions issue or a temporary Console error. Try again.",
|
|
2196
|
+
});
|
|
2197
|
+
case "fetch_error":
|
|
2198
|
+
log(`fetch error: ${parsed.error ?? "unknown"}`);
|
|
2199
|
+
return anthropicResult({
|
|
2200
|
+
status: "error",
|
|
2201
|
+
message: `Console API error: ${parsed.error ?? "unknown"}. ` +
|
|
2202
|
+
"The Console may be temporarily unavailable. Try again.",
|
|
2203
|
+
});
|
|
2204
|
+
case "success": {
|
|
2205
|
+
if (!parsed.key) {
|
|
2206
|
+
log("success status but no key in result");
|
|
2207
|
+
return anthropicResult({
|
|
2208
|
+
status: "error",
|
|
2209
|
+
message: "Key creation succeeded but the key was not returned. " +
|
|
2210
|
+
"This is unexpected. Please try again.",
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
log(`key created: ${parsed.keyId ?? "unknown"} (hint: ${parsed.keyHint ?? "n/a"}) — org: ${parsed.orgName ?? "unknown"}`);
|
|
2214
|
+
// Store the key — persist AFTER confirmation (sprint learning: Task 201)
|
|
2215
|
+
try {
|
|
2216
|
+
writeKey(parsed.key);
|
|
2217
|
+
log("key stored");
|
|
2218
|
+
}
|
|
2219
|
+
catch (err) {
|
|
2220
|
+
log(`key storage failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2221
|
+
return anthropicResult({
|
|
2222
|
+
status: "error",
|
|
2223
|
+
message: `Key was created in the Console but could not be stored locally: ` +
|
|
2224
|
+
`${err instanceof Error ? err.message : String(err)}`,
|
|
2225
|
+
});
|
|
2226
|
+
}
|
|
2227
|
+
// Verify the stored key
|
|
2228
|
+
const verification = await validateKey();
|
|
2229
|
+
log(`post-store verification: ${verification.status}`);
|
|
2230
|
+
switch (verification.status) {
|
|
2231
|
+
case "valid":
|
|
2232
|
+
log("key stored and verified");
|
|
2233
|
+
return anthropicResult({
|
|
2234
|
+
status: "complete",
|
|
2235
|
+
message: `API key created, stored at ${keyFilePath()}, and verified. ` +
|
|
2236
|
+
`Organization: ${parsed.orgName ?? "unknown"}. ` +
|
|
2237
|
+
`Credits: $${((parsed.credits ?? 0) / 100).toFixed(2)}.`,
|
|
2238
|
+
});
|
|
2239
|
+
case "billing":
|
|
2240
|
+
log("key stored but billing issue on verification");
|
|
2241
|
+
return anthropicResult({
|
|
2242
|
+
status: "awaiting_credits",
|
|
2243
|
+
message: "API key created and stored, but your account has insufficient credits. " +
|
|
2244
|
+
`Add credits at ${CONSOLE_BASE}/settings/billing, then verify the key with api-key-verify.`,
|
|
2245
|
+
data: { url: `${CONSOLE_BASE}/settings/billing` },
|
|
2246
|
+
});
|
|
2247
|
+
default:
|
|
2248
|
+
log(`unexpected verification status after store: ${verification.status} — ${verification.message}`);
|
|
2249
|
+
return anthropicResult({
|
|
2250
|
+
status: "error",
|
|
2251
|
+
message: `Key was stored but verification returned: ${verification.status}. ` +
|
|
2252
|
+
`${verification.message}. Try running api-key-verify manually.`,
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
default:
|
|
2257
|
+
log(`unknown console result status: ${parsed.status}`);
|
|
2258
|
+
return anthropicResult({
|
|
2259
|
+
status: "error",
|
|
2260
|
+
message: `Unexpected result from Console: status "${parsed.status}". ` +
|
|
2261
|
+
"Please try again.",
|
|
2262
|
+
});
|
|
2263
|
+
}
|
|
2264
|
+
});
|
|
2265
|
+
// ===================================================================
|
|
2266
|
+
// Onboarding tools
|
|
2267
|
+
// ===================================================================
|
|
2268
|
+
server.tool("onboarding-get", "Read the current onboarding state from the graph. Returns which setup steps " +
|
|
2269
|
+
"have been completed and their timestamps. On first call for an account, " +
|
|
2270
|
+
"migrates any existing onboardingStep from account.json to the graph and " +
|
|
2271
|
+
"removes it from the file. If Neo4j is unreachable, returns an error — " +
|
|
2272
|
+
"the agent should skip onboarding for this session and retry next time.", {}, async () => {
|
|
2273
|
+
try {
|
|
2274
|
+
const state = await getOnboardingState(getSession, ACCOUNT_ID, getAccountDir());
|
|
2275
|
+
return {
|
|
2276
|
+
content: [{
|
|
2277
|
+
type: "text",
|
|
2278
|
+
text: JSON.stringify(state, null, 2),
|
|
2279
|
+
}],
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
catch (err) {
|
|
2283
|
+
return {
|
|
2284
|
+
content: [{
|
|
2285
|
+
type: "text",
|
|
2286
|
+
text: `Failed to read onboarding state: ${err instanceof Error ? err.message : String(err)}`,
|
|
2287
|
+
}],
|
|
2288
|
+
isError: true,
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
});
|
|
2292
|
+
server.tool("onboarding-complete-step", "Mark an onboarding step as completed. Sets a completion timestamp on the step " +
|
|
2293
|
+
"and updates currentStep to the highest completed step. Idempotent — completing " +
|
|
2294
|
+
"an already-completed step preserves its original timestamp. Step must be 1–9. " +
|
|
2295
|
+
"When step is 1, optionally pass acceptedAnthropicVerticals + declinedAnthropicVerticals " +
|
|
2296
|
+
"(both required together) to emit the paired counter for the [plugin-onboarding] " +
|
|
2297
|
+
"group=anthropic-verticals prefix; the skill derives both arrays from the closed set " +
|
|
2298
|
+
"{kyc-screener, meeting-prep-agent, pdf-viewer} and the user's submission.", {
|
|
2299
|
+
step: z.number().int().min(1).max(9).describe("The onboarding step number (1–9) to mark as completed"),
|
|
2300
|
+
acceptedAnthropicVerticals: z.array(z.string()).optional().describe("Step 1 only: plugin slugs the user accepted from the Anthropic verticals group. Pass together with declinedAnthropicVerticals."),
|
|
2301
|
+
declinedAnthropicVerticals: z.array(z.string()).optional().describe("Step 1 only: plugin slugs the user declined from the Anthropic verticals group. Pass together with acceptedAnthropicVerticals."),
|
|
2302
|
+
}, async ({ step, acceptedAnthropicVerticals, declinedAnthropicVerticals }) => {
|
|
2303
|
+
try {
|
|
2304
|
+
const state = await completeOnboardingStep(getSession, ACCOUNT_ID, step);
|
|
2305
|
+
// Task 977: closed set lives in skill prose, not server validation — a fourth vertical updates the skill.
|
|
2306
|
+
if (step === 1 && acceptedAnthropicVerticals !== undefined && declinedAnthropicVerticals !== undefined) {
|
|
2307
|
+
console.error(`[plugin-onboarding] group=anthropic-verticals accepted=${acceptedAnthropicVerticals.length} declined=${declinedAnthropicVerticals.length}`);
|
|
2308
|
+
}
|
|
2309
|
+
return {
|
|
2310
|
+
content: [{
|
|
2311
|
+
type: "text",
|
|
2312
|
+
text: JSON.stringify(state, null, 2),
|
|
2313
|
+
}],
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
catch (err) {
|
|
2317
|
+
return {
|
|
2318
|
+
content: [{
|
|
2319
|
+
type: "text",
|
|
2320
|
+
text: `Failed to complete onboarding step: ${err instanceof Error ? err.message : String(err)}`,
|
|
2321
|
+
}],
|
|
2322
|
+
isError: true,
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
});
|
|
2326
|
+
// Task 704: pin the operator's persona (personal/business-owner) at step 9
|
|
2327
|
+
// BEFORE any graph write happens. The agent calls this immediately on the
|
|
2328
|
+
// user's single-select submission. The tool's job is twofold:
|
|
2329
|
+
// 1. Emit a deterministic [onboarding-step9-mode] log line for the
|
|
2330
|
+
// diagnostic-grep path on the Pi (matches [onboarding-step-complete]
|
|
2331
|
+
// format — same accountId-prefix slicing, same surrounding code).
|
|
2332
|
+
// 2. Return the next-action prose so the agent's branching is anchored to
|
|
2333
|
+
// the tool surface, not free-text inference.
|
|
2334
|
+
// No persistence — the graph state (LocalBusiness exists vs Person with
|
|
2335
|
+
// role=admin-personal exists) remains the source of truth for gate
|
|
2336
|
+
// satisfaction. Re-calling this tool with a different mode on the same
|
|
2337
|
+
// session is allowed; the most recent log line wins for diagnostic purposes.
|
|
2338
|
+
server.tool("onboarding-step9-mode", "Step 9 onboarding fork: record the operator's persona (personal or " +
|
|
2339
|
+
"business-owner) before any graph write. Logs the choice for diagnostic " +
|
|
2340
|
+
"grep and returns the deterministic next-action prose for the agent.", {
|
|
2341
|
+
mode: z
|
|
2342
|
+
.enum(["personal", "business-owner"])
|
|
2343
|
+
.describe("The operator's persona: 'personal' for an individual using Maxy for personal operations (including someone with an employer that is NOT being registered here), or 'business-owner' for someone setting up Maxy as the operations agent for their own company."),
|
|
2344
|
+
}, async ({ mode }) => {
|
|
2345
|
+
console.log(`[onboarding-step9-mode] accountId=${ACCOUNT_ID.slice(0, 8)}… mode=${mode}`);
|
|
2346
|
+
const nextAction = mode === "business-owner"
|
|
2347
|
+
? "Invoke the `business-profile` skill to create AdminUser + LocalBusiness and capture the business identity. Mark step 9 complete only after both nodes exist in the graph."
|
|
2348
|
+
: "Ask the user for their email (one sentence). Use their name from `admin-identity` in the system prompt; split into givenName + familyName. Call `memory-write` to create the AdminUser node, then call `memory-write` to create a Person node with `role: \"admin-personal\"`, the givenName, familyName, email, and an OWNS edge from the AdminUser. Then call `onboarding-complete-step` with step 9. Do not invoke the `business-profile` skill — personal mode does not register a LocalBusiness.";
|
|
2349
|
+
return {
|
|
2350
|
+
content: [{
|
|
2351
|
+
type: "text",
|
|
2352
|
+
text: JSON.stringify({ mode, nextAction }, null, 2),
|
|
2353
|
+
}],
|
|
2354
|
+
};
|
|
2355
|
+
});
|
|
2356
|
+
// ===================================================================
|
|
2357
|
+
// Utility tools
|
|
2358
|
+
// ===================================================================
|
|
2359
|
+
eagerTool(server, "qr-generate", "Generate a QR code from text or a URL. Returns the QR code as a data URI (PNG) or saves to a file path.", {
|
|
2360
|
+
data: z.string().describe("The text or URL to encode in the QR code"),
|
|
2361
|
+
outputPath: z
|
|
2362
|
+
.string()
|
|
2363
|
+
.optional()
|
|
2364
|
+
.describe("File path to save the PNG. If omitted, returns a data URI."),
|
|
2365
|
+
width: z.number().optional().describe("Width in pixels (default 300)"),
|
|
2366
|
+
}, async ({ data, outputPath, width }) => {
|
|
2367
|
+
try {
|
|
2368
|
+
const options = { width: width ?? 300, margin: 2 };
|
|
2369
|
+
if (outputPath) {
|
|
2370
|
+
await QRCode.toFile(outputPath, data, options);
|
|
2371
|
+
return {
|
|
2372
|
+
content: [
|
|
2373
|
+
{
|
|
2374
|
+
type: "text",
|
|
2375
|
+
text: `QR code saved to ${outputPath}`,
|
|
2376
|
+
},
|
|
2377
|
+
],
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
const dataUri = await QRCode.toDataURL(data, options);
|
|
2381
|
+
return {
|
|
2382
|
+
content: [
|
|
2383
|
+
{
|
|
2384
|
+
type: "text",
|
|
2385
|
+
text: `QR code generated for: ${data}`,
|
|
2386
|
+
},
|
|
2387
|
+
{
|
|
2388
|
+
type: "image",
|
|
2389
|
+
data: dataUri.replace(/^data:image\/png;base64,/, ""),
|
|
2390
|
+
mimeType: "image/png",
|
|
2391
|
+
},
|
|
2392
|
+
],
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
catch (err) {
|
|
2396
|
+
return {
|
|
2397
|
+
content: [
|
|
2398
|
+
{
|
|
2399
|
+
type: "text",
|
|
2400
|
+
text: `QR generation failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
2401
|
+
},
|
|
2402
|
+
],
|
|
2403
|
+
isError: true,
|
|
2404
|
+
};
|
|
2405
|
+
}
|
|
2406
|
+
});
|
|
2407
|
+
// ===================================================================
|
|
2408
|
+
// WiFi management (Task 429)
|
|
2409
|
+
// ===================================================================
|
|
2410
|
+
eagerTool(server, "wifi", "Manage WiFi connections on this device. Actions: " +
|
|
2411
|
+
"scan (list visible networks with signal strength and security), " +
|
|
2412
|
+
"connect (join a network by SSID and password — warn user first if signal is below 30%), " +
|
|
2413
|
+
"status (current WiFi connection details), " +
|
|
2414
|
+
"saved (list remembered networks), " +
|
|
2415
|
+
"forget (remove a saved network — if the user is forgetting their currently active WiFi " +
|
|
2416
|
+
"connection, warn them that they may lose connectivity before calling this action).", {
|
|
2417
|
+
action: z.enum(["scan", "connect", "status", "saved", "forget"]).describe("The WiFi operation to perform"),
|
|
2418
|
+
ssid: z.string().optional().describe("Network name — required for connect and forget actions"),
|
|
2419
|
+
password: z.string().optional().describe("Network password — required for connect action (WPA/WPA2/WPA3 PSK)"),
|
|
2420
|
+
}, async ({ action, ssid, password }) => {
|
|
2421
|
+
/** Parse nmcli terse output into rows of colon-separated fields.
|
|
2422
|
+
* nmcli -t escapes literal colons in values as \: — split on
|
|
2423
|
+
* unescaped colons only, then unescape the fields. */
|
|
2424
|
+
const parseNmcliLines = (output) => output.split("\n").filter(Boolean).map((l) => l.split(/(?<!\\):/).map((f) => f.replace(/\\:/g, ":")));
|
|
2425
|
+
const logPrefix = "[wifi]";
|
|
2426
|
+
try {
|
|
2427
|
+
switch (action) {
|
|
2428
|
+
// ── scan ──────────────────────────────────────────────────
|
|
2429
|
+
case "scan": {
|
|
2430
|
+
const raw = execFileSync("nmcli", ["-t", "-f", "SSID,SIGNAL,SECURITY", "device", "wifi", "list", "--rescan", "yes"], { encoding: "utf-8", timeout: 15000 });
|
|
2431
|
+
const lines = parseNmcliLines(raw);
|
|
2432
|
+
// Filter hidden networks (empty SSID) and deduplicate by SSID
|
|
2433
|
+
const seen = new Set();
|
|
2434
|
+
const networks = [];
|
|
2435
|
+
for (const [name, sig, sec] of lines) {
|
|
2436
|
+
if (!name || seen.has(name))
|
|
2437
|
+
continue;
|
|
2438
|
+
seen.add(name);
|
|
2439
|
+
networks.push({ ssid: name, signal: parseInt(sig, 10) || 0, security: sec || "open" });
|
|
2440
|
+
}
|
|
2441
|
+
// Sort by signal strength descending
|
|
2442
|
+
networks.sort((a, b) => b.signal - a.signal);
|
|
2443
|
+
console.error(`${logPrefix} action="scan" result="ok" networks=${networks.length}`);
|
|
2444
|
+
if (networks.length === 0) {
|
|
2445
|
+
return {
|
|
2446
|
+
content: [{ type: "text", text: "No WiFi networks found. Make sure your router is powered on and within range." }],
|
|
2447
|
+
};
|
|
2448
|
+
}
|
|
2449
|
+
const formatted = networks
|
|
2450
|
+
.map((n) => `${n.ssid} (${n.signal}% ${n.security})`)
|
|
2451
|
+
.join("\n");
|
|
2452
|
+
return {
|
|
2453
|
+
content: [{ type: "text", text: `# WiFi Networks\n\n${formatted}` }],
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
// ── connect ──────────────────────────────────────────────
|
|
2457
|
+
case "connect": {
|
|
2458
|
+
if (!ssid) {
|
|
2459
|
+
return {
|
|
2460
|
+
content: [{ type: "text", text: "SSID is required for connect action." }],
|
|
2461
|
+
isError: true,
|
|
2462
|
+
};
|
|
2463
|
+
}
|
|
2464
|
+
if (!password) {
|
|
2465
|
+
return {
|
|
2466
|
+
content: [{ type: "text", text: "Password is required for connect action." }],
|
|
2467
|
+
isError: true,
|
|
2468
|
+
};
|
|
2469
|
+
}
|
|
2470
|
+
// Check signal strength before connecting (cherry-pick 1)
|
|
2471
|
+
try {
|
|
2472
|
+
const scanRaw = execFileSync("nmcli", ["-t", "-f", "SSID,SIGNAL", "device", "wifi", "list"], { encoding: "utf-8", timeout: 10000 });
|
|
2473
|
+
const scanLines = parseNmcliLines(scanRaw);
|
|
2474
|
+
const match = scanLines.find(([name]) => name === ssid);
|
|
2475
|
+
const signal = match ? parseInt(match[1], 10) || 0 : 0;
|
|
2476
|
+
if (signal > 0 && signal < 30) {
|
|
2477
|
+
// Include warning in result but proceed with connection
|
|
2478
|
+
console.error(`${logPrefix} action="connect" ssid="${ssid}" signal=${signal} warning="weak_signal"`);
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
catch {
|
|
2482
|
+
// Scan check is best-effort — proceed with connect regardless
|
|
2483
|
+
}
|
|
2484
|
+
execFileSync("nmcli", ["device", "wifi", "connect", ssid, "password", password, "--wait", "30"], { encoding: "utf-8", timeout: 35000 });
|
|
2485
|
+
console.error(`${logPrefix} action="connect" ssid="${ssid}" result="connecting" password=[REDACTED]`);
|
|
2486
|
+
// Post-connect verification: confirm IP assignment on wlan0 (cherry-pick 2)
|
|
2487
|
+
let ip = "";
|
|
2488
|
+
try {
|
|
2489
|
+
const deviceInfo = execFileSync("nmcli", ["-t", "-f", "IP4.ADDRESS", "device", "show", "wlan0"], { encoding: "utf-8", timeout: 5000 });
|
|
2490
|
+
const ipLine = deviceInfo.split("\n").find((l) => l.startsWith("IP4.ADDRESS"));
|
|
2491
|
+
if (ipLine) {
|
|
2492
|
+
// Format: IP4.ADDRESS[1]:192.168.1.100/24
|
|
2493
|
+
ip = ipLine.split(":").slice(1).join(":").split("/")[0].trim();
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
catch {
|
|
2497
|
+
// IP check failed — connection may still be establishing
|
|
2498
|
+
}
|
|
2499
|
+
if (ip) {
|
|
2500
|
+
console.error(`${logPrefix} action="connect" ssid="${ssid}" result="connected" ip="${ip}"`);
|
|
2501
|
+
// Check if signal was weak and include warning
|
|
2502
|
+
let warning = "";
|
|
2503
|
+
try {
|
|
2504
|
+
const scanRaw = execFileSync("nmcli", ["-t", "-f", "SSID,SIGNAL", "device", "wifi", "list"], { encoding: "utf-8", timeout: 5000 });
|
|
2505
|
+
const match = parseNmcliLines(scanRaw).find(([name]) => name === ssid);
|
|
2506
|
+
const signal = match ? parseInt(match[1], 10) || 0 : 0;
|
|
2507
|
+
if (signal > 0 && signal < 30) {
|
|
2508
|
+
warning = `\n\nWarning: Signal strength is weak (${signal}%). Connection may be unreliable.`;
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
catch {
|
|
2512
|
+
// Signal check is best-effort
|
|
2513
|
+
}
|
|
2514
|
+
return {
|
|
2515
|
+
content: [{
|
|
2516
|
+
type: "text",
|
|
2517
|
+
text: `Connected to ${ssid}. IP address: ${ip}. You can safely unplug ethernet.${warning}`,
|
|
2518
|
+
}],
|
|
2519
|
+
};
|
|
2520
|
+
}
|
|
2521
|
+
else {
|
|
2522
|
+
console.error(`${logPrefix} action="connect" ssid="${ssid}" result="connected_no_ip"`);
|
|
2523
|
+
return {
|
|
2524
|
+
content: [{
|
|
2525
|
+
type: "text",
|
|
2526
|
+
text: `Connected to ${ssid} but no IP address was assigned. Stay on ethernet. The network may require additional configuration — check with your network administrator.`,
|
|
2527
|
+
}],
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
// ── status ───────────────────────────────────────────────
|
|
2532
|
+
case "status": {
|
|
2533
|
+
// Check if wlan0 exists
|
|
2534
|
+
let deviceStatus;
|
|
2535
|
+
try {
|
|
2536
|
+
deviceStatus = execFileSync("nmcli", ["-t", "-f", "DEVICE,TYPE,STATE", "device", "status"], { encoding: "utf-8", timeout: 5000 });
|
|
2537
|
+
}
|
|
2538
|
+
catch (err) {
|
|
2539
|
+
console.error(`${logPrefix} action="status" result="failed" error="${err instanceof Error ? err.message : String(err)}"`);
|
|
2540
|
+
return {
|
|
2541
|
+
content: [{ type: "text", text: `WiFi status check failed: ${err instanceof Error ? err.message : String(err)}` }],
|
|
2542
|
+
isError: true,
|
|
2543
|
+
};
|
|
2544
|
+
}
|
|
2545
|
+
const wifiDevice = parseNmcliLines(deviceStatus).find(([, type]) => type === "wifi");
|
|
2546
|
+
if (!wifiDevice) {
|
|
2547
|
+
console.error(`${logPrefix} action="status" result="no_hardware"`);
|
|
2548
|
+
return {
|
|
2549
|
+
content: [{ type: "text", text: "No WiFi hardware detected on this device." }],
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2552
|
+
if (wifiDevice[2] !== "connected") {
|
|
2553
|
+
console.error(`${logPrefix} action="status" result="not_connected" state="${wifiDevice[2]}"`);
|
|
2554
|
+
return {
|
|
2555
|
+
content: [{ type: "text", text: `WiFi is not connected. Device state: ${wifiDevice[2]}.` }],
|
|
2556
|
+
};
|
|
2557
|
+
}
|
|
2558
|
+
// Get detailed connection info
|
|
2559
|
+
const details = execFileSync("nmcli", ["-t", "-f", "GENERAL.CONNECTION,IP4.ADDRESS,IP4.GATEWAY,GENERAL.HWADDR", "device", "show", "wlan0"], { encoding: "utf-8", timeout: 5000 });
|
|
2560
|
+
const detailLines = parseNmcliLines(details);
|
|
2561
|
+
const conn = detailLines.find(([k]) => k === "GENERAL.CONNECTION")?.[1] ?? "unknown";
|
|
2562
|
+
const ipAddr = detailLines.find(([k]) => k.startsWith("IP4.ADDRESS"))?.[1] ?? "none";
|
|
2563
|
+
const gateway = detailLines.find(([k]) => k.startsWith("IP4.GATEWAY"))?.[1] ?? "none";
|
|
2564
|
+
// Get signal strength
|
|
2565
|
+
let signal = "unknown";
|
|
2566
|
+
try {
|
|
2567
|
+
const scanRaw = execFileSync("nmcli", ["-t", "-f", "SSID,SIGNAL", "device", "wifi", "list"], { encoding: "utf-8", timeout: 5000 });
|
|
2568
|
+
const match = parseNmcliLines(scanRaw).find(([name]) => name === conn);
|
|
2569
|
+
if (match)
|
|
2570
|
+
signal = `${match[1]}%`;
|
|
2571
|
+
}
|
|
2572
|
+
catch {
|
|
2573
|
+
// Signal lookup is best-effort
|
|
2574
|
+
}
|
|
2575
|
+
console.error(`${logPrefix} action="status" result="connected" ssid="${conn}"`);
|
|
2576
|
+
return {
|
|
2577
|
+
content: [{
|
|
2578
|
+
type: "text",
|
|
2579
|
+
text: `WiFi: connected\nSSID: ${conn}\nIP: ${ipAddr}\nGateway: ${gateway}\nSignal: ${signal}`,
|
|
2580
|
+
}],
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
// ── saved ────────────────────────────────────────────────
|
|
2584
|
+
case "saved": {
|
|
2585
|
+
const raw = execFileSync("nmcli", ["-t", "-f", "NAME,TYPE", "connection", "show"], { encoding: "utf-8", timeout: 5000 });
|
|
2586
|
+
const wifiConns = parseNmcliLines(raw).filter(([, type]) => type === "802-11-wireless");
|
|
2587
|
+
console.error(`${logPrefix} action="saved" count=${wifiConns.length}`);
|
|
2588
|
+
if (wifiConns.length === 0) {
|
|
2589
|
+
return {
|
|
2590
|
+
content: [{ type: "text", text: "No saved WiFi networks." }],
|
|
2591
|
+
};
|
|
2592
|
+
}
|
|
2593
|
+
const formatted = wifiConns.map(([name]) => name).join("\n");
|
|
2594
|
+
return {
|
|
2595
|
+
content: [{ type: "text", text: `# Saved WiFi Networks\n\n${formatted}` }],
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
// ── forget ───────────────────────────────────────────────
|
|
2599
|
+
case "forget": {
|
|
2600
|
+
if (!ssid) {
|
|
2601
|
+
return {
|
|
2602
|
+
content: [{ type: "text", text: "SSID is required for forget action." }],
|
|
2603
|
+
isError: true,
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
execFileSync("nmcli", ["connection", "delete", ssid], { encoding: "utf-8", timeout: 10000 });
|
|
2607
|
+
console.error(`${logPrefix} action="forget" ssid="${ssid}" result="ok"`);
|
|
2608
|
+
return {
|
|
2609
|
+
content: [{
|
|
2610
|
+
type: "text",
|
|
2611
|
+
text: `Forgotten network: ${ssid}. It will no longer auto-connect.`,
|
|
2612
|
+
}],
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
default:
|
|
2616
|
+
return {
|
|
2617
|
+
content: [{ type: "text", text: `Unknown WiFi action: ${action}` }],
|
|
2618
|
+
isError: true,
|
|
2619
|
+
};
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
catch (err) {
|
|
2623
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2624
|
+
console.error(`${logPrefix} action="${action}" ssid="${ssid ?? ""}" result="failed" error="${errMsg}"`);
|
|
2625
|
+
// Parse common nmcli error patterns into user-friendly messages
|
|
2626
|
+
if (errMsg.includes("NetworkManager is not running")) {
|
|
2627
|
+
return {
|
|
2628
|
+
content: [{ type: "text", text: "NetworkManager is not running. Start it with: sudo systemctl start NetworkManager" }],
|
|
2629
|
+
isError: true,
|
|
2630
|
+
};
|
|
2631
|
+
}
|
|
2632
|
+
if (errMsg.includes("No network with SSID") || errMsg.includes("not found")) {
|
|
2633
|
+
return {
|
|
2634
|
+
content: [{ type: "text", text: `Network '${ssid}' not found — it may be out of range or the name may be incorrect.` }],
|
|
2635
|
+
isError: true,
|
|
2636
|
+
};
|
|
2637
|
+
}
|
|
2638
|
+
if (errMsg.includes("Secrets were required") || errMsg.includes("802-11-wireless-security.psk")) {
|
|
2639
|
+
return {
|
|
2640
|
+
content: [{ type: "text", text: `Authentication failed for '${ssid}' — the password may be incorrect.` }],
|
|
2641
|
+
isError: true,
|
|
2642
|
+
};
|
|
2643
|
+
}
|
|
2644
|
+
if (errMsg.includes("no wifi device found") || errMsg.includes("Wi-Fi device not found")) {
|
|
2645
|
+
return {
|
|
2646
|
+
content: [{ type: "text", text: "No WiFi hardware detected on this device." }],
|
|
2647
|
+
isError: true,
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
if (errMsg.includes("ENOENT")) {
|
|
2651
|
+
return {
|
|
2652
|
+
content: [{ type: "text", text: "nmcli not found — NetworkManager may not be installed on this device." }],
|
|
2653
|
+
isError: true,
|
|
2654
|
+
};
|
|
2655
|
+
}
|
|
2656
|
+
if (errMsg.includes("ETIMEDOUT") || errMsg.includes("timed out") || errMsg.includes("Timeout")) {
|
|
2657
|
+
return {
|
|
2658
|
+
content: [{ type: "text", text: `WiFi operation timed out. The adapter may be busy — try again.` }],
|
|
2659
|
+
isError: true,
|
|
2660
|
+
};
|
|
2661
|
+
}
|
|
2662
|
+
return {
|
|
2663
|
+
content: [{ type: "text", text: `WiFi ${action} failed: ${errMsg}` }],
|
|
2664
|
+
isError: true,
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
});
|
|
2668
|
+
// ===================================================================
|
|
2669
|
+
// Premium plugin tools
|
|
2670
|
+
// ===================================================================
|
|
2671
|
+
server.tool("premium-list", "List available premium plugins and their delivery status. Returns structured data " +
|
|
2672
|
+
"for each premium plugin in the staging area: name, type (standalone/bundle), description, " +
|
|
2673
|
+
"purchase status, and delivery status (per sub-plugin for bundles).", {}, async () => {
|
|
2674
|
+
try {
|
|
2675
|
+
if (!existsSync(STAGING_ROOT)) {
|
|
2676
|
+
return {
|
|
2677
|
+
content: [{ type: "text", text: "No premium plugins available. The staging directory does not exist — premium plugins become available after the next platform update." }],
|
|
2678
|
+
};
|
|
2679
|
+
}
|
|
2680
|
+
const config = await readAccountConfig();
|
|
2681
|
+
// Effective purchasedPlugins from signed entitlement (Task 831).
|
|
2682
|
+
const entitlement = await currentEntitlement();
|
|
2683
|
+
const purchased = entitlement.purchasedPlugins;
|
|
2684
|
+
const enabled = Array.isArray(config.enabledPlugins) ? config.enabledPlugins : [];
|
|
2685
|
+
const entries = readdirSync(STAGING_ROOT, { withFileTypes: true })
|
|
2686
|
+
.filter(e => e.isDirectory())
|
|
2687
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
2688
|
+
if (entries.length === 0) {
|
|
2689
|
+
return {
|
|
2690
|
+
content: [{ type: "text", text: "No premium plugins found in the staging area." }],
|
|
2691
|
+
};
|
|
2692
|
+
}
|
|
2693
|
+
const plugins = [];
|
|
2694
|
+
for (const entry of entries) {
|
|
2695
|
+
const stagingDir = resolve(STAGING_ROOT, entry.name);
|
|
2696
|
+
const bundlePath = join(stagingDir, "BUNDLE.md");
|
|
2697
|
+
const pluginPath = join(stagingDir, "PLUGIN.md");
|
|
2698
|
+
if (existsSync(bundlePath)) {
|
|
2699
|
+
// Bundle
|
|
2700
|
+
const fm = parseFrontmatter(readFileSync(bundlePath, "utf-8"));
|
|
2701
|
+
const subPlugins = (Array.isArray(fm.plugins) ? fm.plugins : [])
|
|
2702
|
+
.filter(name => VALID_PLUGIN_NAME.test(name));
|
|
2703
|
+
const subStatus = subPlugins.map(name => ({
|
|
2704
|
+
name,
|
|
2705
|
+
delivered: existsSync(join(PLUGINS_DIR, name, "PLUGIN.md")),
|
|
2706
|
+
enabled: enabled.includes(name),
|
|
2707
|
+
}));
|
|
2708
|
+
plugins.push({
|
|
2709
|
+
name: entry.name,
|
|
2710
|
+
type: "bundle",
|
|
2711
|
+
description: typeof fm.description === "string" ? fm.description : "",
|
|
2712
|
+
purchased: purchased.includes(entry.name),
|
|
2713
|
+
subPlugins: subStatus,
|
|
2714
|
+
});
|
|
2715
|
+
}
|
|
2716
|
+
else if (existsSync(pluginPath)) {
|
|
2717
|
+
// Standalone
|
|
2718
|
+
const fm = parseFrontmatter(readFileSync(pluginPath, "utf-8"));
|
|
2719
|
+
plugins.push({
|
|
2720
|
+
name: entry.name,
|
|
2721
|
+
type: "standalone",
|
|
2722
|
+
description: typeof fm.description === "string" ? fm.description : "",
|
|
2723
|
+
purchased: purchased.includes(entry.name),
|
|
2724
|
+
delivered: existsSync(join(PLUGINS_DIR, entry.name, "PLUGIN.md")),
|
|
2725
|
+
enabled: enabled.includes(entry.name),
|
|
2726
|
+
});
|
|
2727
|
+
}
|
|
2728
|
+
// Skip directories that have neither BUNDLE.md nor PLUGIN.md
|
|
2729
|
+
}
|
|
2730
|
+
return {
|
|
2731
|
+
content: [{ type: "text", text: JSON.stringify(plugins, null, 2) }],
|
|
2732
|
+
};
|
|
2733
|
+
}
|
|
2734
|
+
catch (err) {
|
|
2735
|
+
return {
|
|
2736
|
+
content: [{ type: "text", text: `Failed to list premium plugins: ${err instanceof Error ? err.message : String(err)}` }],
|
|
2737
|
+
isError: true,
|
|
2738
|
+
};
|
|
2739
|
+
}
|
|
2740
|
+
});
|
|
2741
|
+
server.tool("premium-deliver", "Deliver a purchased premium plugin. Copies plugin files from the staging area to the active " +
|
|
2742
|
+
"plugin directory, verifies each copy, updates enabledPlugins in account.json, and scans for " +
|
|
2743
|
+
"agent templates. Handles both standalone plugins and bundles (auto-detected). Idempotent — " +
|
|
2744
|
+
"already-delivered sub-plugins are skipped. Returns a structured result with per-sub-plugin " +
|
|
2745
|
+
"status and available templates.", {
|
|
2746
|
+
pluginName: z.string().describe("Name of the premium plugin to deliver (e.g. 'real-agency', 'teaching')"),
|
|
2747
|
+
}, async ({ pluginName }) => {
|
|
2748
|
+
const TAG = "[premium-deliver]";
|
|
2749
|
+
// --- Validate plugin name ---
|
|
2750
|
+
if (!pluginName || !VALID_PLUGIN_NAME.test(pluginName)) {
|
|
2751
|
+
return {
|
|
2752
|
+
content: [{ type: "text", text: `${TAG} Invalid plugin name "${pluginName}". Must match [a-z0-9-].` }],
|
|
2753
|
+
isError: true,
|
|
2754
|
+
};
|
|
2755
|
+
}
|
|
2756
|
+
// --- Guard against clobbering core plugins ---
|
|
2757
|
+
if (CORE_PLUGINS.includes(pluginName)) {
|
|
2758
|
+
return {
|
|
2759
|
+
content: [{ type: "text", text: `${TAG} "${pluginName}" is a core plugin and cannot be delivered as a premium plugin.` }],
|
|
2760
|
+
isError: true,
|
|
2761
|
+
};
|
|
2762
|
+
}
|
|
2763
|
+
// --- Check staging directory ---
|
|
2764
|
+
if (!existsSync(STAGING_ROOT)) {
|
|
2765
|
+
return {
|
|
2766
|
+
content: [{ type: "text", text: `${TAG} Premium plugins staging directory does not exist. Premium plugins become available after the next platform update.` }],
|
|
2767
|
+
isError: true,
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
const stagingDir = resolve(STAGING_ROOT, pluginName);
|
|
2771
|
+
if (!existsSync(stagingDir)) {
|
|
2772
|
+
return {
|
|
2773
|
+
content: [{ type: "text", text: `${TAG} No premium plugin named "${pluginName}" found in the staging area.` }],
|
|
2774
|
+
isError: true,
|
|
2775
|
+
};
|
|
2776
|
+
}
|
|
2777
|
+
// --- Read account config ---
|
|
2778
|
+
let config;
|
|
2779
|
+
try {
|
|
2780
|
+
config = await readAccountConfig();
|
|
2781
|
+
}
|
|
2782
|
+
catch (err) {
|
|
2783
|
+
return {
|
|
2784
|
+
content: [{ type: "text", text: `${TAG} Failed to read account config: ${err instanceof Error ? err.message : String(err)}` }],
|
|
2785
|
+
isError: true,
|
|
2786
|
+
};
|
|
2787
|
+
}
|
|
2788
|
+
// Effective purchasedPlugins from signed entitlement (Task 831). The
|
|
2789
|
+
// raw account.json value is ignored on commercial installs; verifier
|
|
2790
|
+
// returns the signed list (or [] on anonymous-fallback).
|
|
2791
|
+
const entitlement = await resolveEntitlement(ENTITLEMENT_BRAND, {
|
|
2792
|
+
accountId: typeof config.accountId === "string" ? config.accountId : "",
|
|
2793
|
+
customerEmail: typeof config.customerEmail === "string" ? config.customerEmail : undefined,
|
|
2794
|
+
tier: typeof config.tier === "string" ? config.tier : undefined,
|
|
2795
|
+
purchasedPlugins: Array.isArray(config.purchasedPlugins) ? config.purchasedPlugins : undefined,
|
|
2796
|
+
});
|
|
2797
|
+
const purchased = entitlement.purchasedPlugins;
|
|
2798
|
+
// --- Check purchase status ---
|
|
2799
|
+
if (!purchased.includes(pluginName)) {
|
|
2800
|
+
return {
|
|
2801
|
+
content: [{ type: "text", text: `${TAG} "${pluginName}" is not in purchasedPlugins. Record the purchase first.` }],
|
|
2802
|
+
isError: true,
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
// --- Detect type: bundle vs standalone ---
|
|
2806
|
+
const bundlePath = join(stagingDir, "BUNDLE.md");
|
|
2807
|
+
const pluginMdPath = join(stagingDir, "PLUGIN.md");
|
|
2808
|
+
const isBundle = existsSync(bundlePath);
|
|
2809
|
+
const isStandalone = !isBundle && existsSync(pluginMdPath);
|
|
2810
|
+
if (!isBundle && !isStandalone) {
|
|
2811
|
+
return {
|
|
2812
|
+
content: [{ type: "text", text: `${TAG} Staging directory "${pluginName}" contains neither BUNDLE.md nor PLUGIN.md. Invalid premium plugin.` }],
|
|
2813
|
+
isError: true,
|
|
2814
|
+
};
|
|
2815
|
+
}
|
|
2816
|
+
const items = [];
|
|
2817
|
+
if (isBundle) {
|
|
2818
|
+
const fm = parseFrontmatter(readFileSync(bundlePath, "utf-8"));
|
|
2819
|
+
const subPluginNames = Array.isArray(fm.plugins) ? fm.plugins : [];
|
|
2820
|
+
if (subPluginNames.length === 0) {
|
|
2821
|
+
return {
|
|
2822
|
+
content: [{ type: "text", text: `${TAG} BUNDLE.md for "${pluginName}" has an empty or missing plugins array.` }],
|
|
2823
|
+
isError: true,
|
|
2824
|
+
};
|
|
2825
|
+
}
|
|
2826
|
+
for (const sub of subPluginNames) {
|
|
2827
|
+
if (!VALID_PLUGIN_NAME.test(sub)) {
|
|
2828
|
+
return {
|
|
2829
|
+
content: [{ type: "text", text: `${TAG} Sub-plugin name "${sub}" in BUNDLE.md contains invalid characters. Aborting delivery.` }],
|
|
2830
|
+
isError: true,
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
if (CORE_PLUGINS.includes(sub)) {
|
|
2834
|
+
return {
|
|
2835
|
+
content: [{ type: "text", text: `${TAG} Sub-plugin "${sub}" collides with a core plugin name. Aborting delivery.` }],
|
|
2836
|
+
isError: true,
|
|
2837
|
+
};
|
|
2838
|
+
}
|
|
2839
|
+
items.push({ name: sub, sourcePath: resolve(stagingDir, "plugins", sub) });
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
else {
|
|
2843
|
+
items.push({ name: pluginName, sourcePath: stagingDir });
|
|
2844
|
+
}
|
|
2845
|
+
const results = [];
|
|
2846
|
+
const newlyEnabled = [];
|
|
2847
|
+
for (const item of items) {
|
|
2848
|
+
const destPath = resolve(PLUGINS_DIR, item.name);
|
|
2849
|
+
// Already delivered — skip (preserves user's enable/disable state)
|
|
2850
|
+
if (existsSync(join(destPath, "PLUGIN.md"))) {
|
|
2851
|
+
console.log(`${TAG} ${item.name} already delivered — skipping`);
|
|
2852
|
+
results.push({ name: item.name, status: "skipped" });
|
|
2853
|
+
continue;
|
|
2854
|
+
}
|
|
2855
|
+
// Validate source
|
|
2856
|
+
if (!existsSync(join(item.sourcePath, "PLUGIN.md"))) {
|
|
2857
|
+
const msg = `Source PLUGIN.md missing at ${item.sourcePath}`;
|
|
2858
|
+
console.error(`${TAG} FAILED ${item.name}: ${msg}`);
|
|
2859
|
+
results.push({ name: item.name, status: "failed", error: msg });
|
|
2860
|
+
continue;
|
|
2861
|
+
}
|
|
2862
|
+
// Copy
|
|
2863
|
+
try {
|
|
2864
|
+
cpSync(item.sourcePath, destPath, { recursive: true });
|
|
2865
|
+
console.log(`${TAG} copied ${item.name} from staging`);
|
|
2866
|
+
}
|
|
2867
|
+
catch (err) {
|
|
2868
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2869
|
+
console.error(`${TAG} FAILED to copy ${item.name}: ${msg}`);
|
|
2870
|
+
results.push({ name: item.name, status: "failed", error: msg });
|
|
2871
|
+
continue;
|
|
2872
|
+
}
|
|
2873
|
+
// Verify
|
|
2874
|
+
const verifyPath = join(destPath, "PLUGIN.md");
|
|
2875
|
+
if (!existsSync(verifyPath)) {
|
|
2876
|
+
console.error(`${TAG} FAILED to verify ${item.name}: PLUGIN.md missing after copy`);
|
|
2877
|
+
results.push({ name: item.name, status: "failed", error: "PLUGIN.md missing after copy" });
|
|
2878
|
+
continue;
|
|
2879
|
+
}
|
|
2880
|
+
// Count skills
|
|
2881
|
+
const skillsDir = join(destPath, "skills");
|
|
2882
|
+
let skillCount = 0;
|
|
2883
|
+
if (existsSync(skillsDir)) {
|
|
2884
|
+
skillCount = readdirSync(skillsDir, { withFileTypes: true })
|
|
2885
|
+
.filter(e => e.isDirectory()).length;
|
|
2886
|
+
}
|
|
2887
|
+
console.log(`${TAG} verified ${item.name} (${skillCount} skills)`);
|
|
2888
|
+
results.push({ name: item.name, status: "delivered", skills: skillCount });
|
|
2889
|
+
newlyEnabled.push(item.name);
|
|
2890
|
+
}
|
|
2891
|
+
// --- Update enabledPlugins (only add newly delivered, deduplicate) ---
|
|
2892
|
+
if (newlyEnabled.length > 0) {
|
|
2893
|
+
try {
|
|
2894
|
+
// Re-read config to reduce race window
|
|
2895
|
+
const freshConfig = await readAccountConfig();
|
|
2896
|
+
const freshEnabled = Array.isArray(freshConfig.enabledPlugins)
|
|
2897
|
+
? freshConfig.enabledPlugins
|
|
2898
|
+
: [];
|
|
2899
|
+
const merged = [...new Set([...freshEnabled, ...newlyEnabled])];
|
|
2900
|
+
freshConfig.enabledPlugins = merged;
|
|
2901
|
+
const configPath = join(getAccountDir(), "account.json");
|
|
2902
|
+
await writeFile(configPath, JSON.stringify(freshConfig, null, 2) + "\n", "utf-8");
|
|
2903
|
+
console.log(`${TAG} updated enabledPlugins: added ${newlyEnabled.join(", ")}`);
|
|
2904
|
+
}
|
|
2905
|
+
catch (err) {
|
|
2906
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2907
|
+
console.error(`${TAG} FAILED to update account.json: ${msg}`);
|
|
2908
|
+
return {
|
|
2909
|
+
content: [{
|
|
2910
|
+
type: "text",
|
|
2911
|
+
text: `${TAG} Plugins were copied but account.json update failed: ${msg}\n\nDelivery results:\n${JSON.stringify(results, null, 2)}`,
|
|
2912
|
+
}],
|
|
2913
|
+
isError: true,
|
|
2914
|
+
};
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
const templates = [];
|
|
2918
|
+
// For bundles, templates live in the staging agents/ dir; for standalone, in the delivered plugin
|
|
2919
|
+
const agentsSearchDirs = [];
|
|
2920
|
+
if (isBundle) {
|
|
2921
|
+
const bundleAgentsDir = join(stagingDir, "agents");
|
|
2922
|
+
if (existsSync(bundleAgentsDir))
|
|
2923
|
+
agentsSearchDirs.push(bundleAgentsDir);
|
|
2924
|
+
}
|
|
2925
|
+
else {
|
|
2926
|
+
const pluginAgentsDir = join(PLUGINS_DIR, pluginName, "agents");
|
|
2927
|
+
if (existsSync(pluginAgentsDir))
|
|
2928
|
+
agentsSearchDirs.push(pluginAgentsDir);
|
|
2929
|
+
}
|
|
2930
|
+
for (const agentsDir of agentsSearchDirs) {
|
|
2931
|
+
const subdirs = readdirSync(agentsDir, { withFileTypes: true })
|
|
2932
|
+
.filter(e => e.isDirectory());
|
|
2933
|
+
for (const sub of subdirs) {
|
|
2934
|
+
const templateJsonPath = join(agentsDir, sub.name, "template.json");
|
|
2935
|
+
if (!existsSync(templateJsonPath))
|
|
2936
|
+
continue;
|
|
2937
|
+
try {
|
|
2938
|
+
const templateData = JSON.parse(readFileSync(templateJsonPath, "utf-8"));
|
|
2939
|
+
const identityExists = existsSync(join(agentsDir, sub.name, "IDENTITY.md"));
|
|
2940
|
+
const soulExists = existsSync(join(agentsDir, sub.name, "SOUL.md"));
|
|
2941
|
+
if (!identityExists || !soulExists) {
|
|
2942
|
+
console.log(`${TAG} template ${sub.name} incomplete — missing ${!identityExists ? "IDENTITY.md" : "SOUL.md"}, skipping`);
|
|
2943
|
+
continue;
|
|
2944
|
+
}
|
|
2945
|
+
templates.push({
|
|
2946
|
+
directory: join(agentsDir, sub.name),
|
|
2947
|
+
displayName: templateData.displayName ?? sub.name,
|
|
2948
|
+
description: templateData.description ?? "",
|
|
2949
|
+
suggestedSlug: templateData.suggestedSlug,
|
|
2950
|
+
});
|
|
2951
|
+
}
|
|
2952
|
+
catch {
|
|
2953
|
+
console.log(`${TAG} template ${sub.name} has invalid template.json, skipping`);
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
// --- Build structured result ---
|
|
2958
|
+
const delivered = results.filter(r => r.status === "delivered");
|
|
2959
|
+
const skipped = results.filter(r => r.status === "skipped");
|
|
2960
|
+
const failed = results.filter(r => r.status === "failed");
|
|
2961
|
+
const totalSkills = delivered.reduce((sum, r) => sum + (r.skills ?? 0), 0);
|
|
2962
|
+
const output = {
|
|
2963
|
+
pluginName,
|
|
2964
|
+
type: isBundle ? "bundle" : "standalone",
|
|
2965
|
+
delivered: delivered.map(r => ({ name: r.name, skills: r.skills })),
|
|
2966
|
+
skipped: skipped.map(r => r.name),
|
|
2967
|
+
failed: failed.map(r => ({ name: r.name, error: r.error })),
|
|
2968
|
+
totalDelivered: delivered.length,
|
|
2969
|
+
totalSkipped: skipped.length,
|
|
2970
|
+
totalFailed: failed.length,
|
|
2971
|
+
totalSkills,
|
|
2972
|
+
templates: templates.map(t => ({
|
|
2973
|
+
displayName: t.displayName,
|
|
2974
|
+
description: t.description,
|
|
2975
|
+
suggestedSlug: t.suggestedSlug,
|
|
2976
|
+
directory: t.directory,
|
|
2977
|
+
})),
|
|
2978
|
+
};
|
|
2979
|
+
return {
|
|
2980
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
|
|
2981
|
+
};
|
|
2982
|
+
});
|
|
2983
|
+
// ---------------------------------------------------------------------------
|
|
2984
|
+
// file-attach — copy a generated file into the attachment store for download
|
|
2985
|
+
// ---------------------------------------------------------------------------
|
|
2986
|
+
eagerTool(server, "file-attach", "Attach a file from the account directory for delivery to the user. " +
|
|
2987
|
+
"Copies the file into the attachment store and returns metadata for rendering " +
|
|
2988
|
+
"a download component. The file must be within the account directory tree. " +
|
|
2989
|
+
"After calling this tool, call render-component with name 'file-attachment' " +
|
|
2990
|
+
"and pass the returned metadata as data.", {
|
|
2991
|
+
filePath: z.string().describe("Absolute path to the file to attach"),
|
|
2992
|
+
}, async ({ filePath }) => {
|
|
2993
|
+
const TAG = "[file-attach]";
|
|
2994
|
+
if (!filePath || !filePath.trim()) {
|
|
2995
|
+
return {
|
|
2996
|
+
content: [{ type: "text", text: `${TAG} filePath is required` }],
|
|
2997
|
+
isError: true,
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
3000
|
+
const accountDir = getAccountDir();
|
|
3001
|
+
// --- Path validation: must be within the account directory ---
|
|
3002
|
+
let resolvedPath;
|
|
3003
|
+
try {
|
|
3004
|
+
const { realpathSync: rp } = await import("node:fs");
|
|
3005
|
+
resolvedPath = rp(filePath);
|
|
3006
|
+
const accountResolved = rp(accountDir);
|
|
3007
|
+
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
3008
|
+
const sanitised = filePath.replace(accountDir, "<account>/");
|
|
3009
|
+
console.error(`${TAG} REJECTED path=${sanitised} reason=outside_account_directory`);
|
|
3010
|
+
return {
|
|
3011
|
+
content: [{ type: "text", text: `${TAG} Access denied: file is outside the account directory.` }],
|
|
3012
|
+
isError: true,
|
|
3013
|
+
};
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
catch (err) {
|
|
3017
|
+
const code = err.code;
|
|
3018
|
+
if (code === "ENOENT") {
|
|
3019
|
+
console.error(`${TAG} ENOENT path=${filePath}`);
|
|
3020
|
+
return {
|
|
3021
|
+
content: [{ type: "text", text: `${TAG} File not found: ${filePath}` }],
|
|
3022
|
+
isError: true,
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
console.error(`${TAG} path validation error: ${err instanceof Error ? err.message : String(err)}`);
|
|
3026
|
+
return {
|
|
3027
|
+
content: [{ type: "text", text: `${TAG} Failed to validate file path: ${err instanceof Error ? err.message : String(err)}` }],
|
|
3028
|
+
isError: true,
|
|
3029
|
+
};
|
|
3030
|
+
}
|
|
3031
|
+
// --- Store in attachment store ---
|
|
3032
|
+
const targetUrl = `http://127.0.0.1:${PLATFORM_PORT}/api/admin/file-attach`;
|
|
3033
|
+
try {
|
|
3034
|
+
const res = await fetch(targetUrl, {
|
|
3035
|
+
method: "POST",
|
|
3036
|
+
headers: { "Content-Type": "application/json" },
|
|
3037
|
+
body: JSON.stringify({ filePath: resolvedPath, accountId: ACCOUNT_ID }),
|
|
3038
|
+
});
|
|
3039
|
+
const result = await res.json();
|
|
3040
|
+
if (!res.ok || result.error) {
|
|
3041
|
+
const msg = result.error || `HTTP ${res.status}`;
|
|
3042
|
+
console.error(`${TAG} store failed url=${targetUrl} status=${res.status} error=${msg}`);
|
|
3043
|
+
return {
|
|
3044
|
+
content: [{ type: "text", text: `${TAG} Failed to attach file: ${msg} (url=${targetUrl})` }],
|
|
3045
|
+
isError: true,
|
|
3046
|
+
};
|
|
3047
|
+
}
|
|
3048
|
+
console.error(`${TAG} attached path=${resolvedPath} size=${result.sizeBytes} mime=${result.mimeType} id=${result.attachmentId}`);
|
|
3049
|
+
return {
|
|
3050
|
+
content: [{
|
|
3051
|
+
type: "text",
|
|
3052
|
+
text: JSON.stringify({
|
|
3053
|
+
attachmentId: result.attachmentId,
|
|
3054
|
+
filename: result.filename,
|
|
3055
|
+
sizeBytes: result.sizeBytes,
|
|
3056
|
+
mimeType: result.mimeType,
|
|
3057
|
+
}),
|
|
3058
|
+
}],
|
|
3059
|
+
};
|
|
3060
|
+
}
|
|
3061
|
+
catch (err) {
|
|
3062
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3063
|
+
console.error(`${TAG} fetch failed url=${targetUrl} error=${errMsg}`);
|
|
3064
|
+
return {
|
|
3065
|
+
content: [{ type: "text", text: `${TAG} Failed to attach file: could not reach platform server at ${targetUrl} (${errMsg})` }],
|
|
3066
|
+
isError: true,
|
|
3067
|
+
};
|
|
3068
|
+
}
|
|
3069
|
+
});
|
|
3070
|
+
// ===================================================================
|
|
3071
|
+
// Action approval tools (EU AI Act Article 14 — human oversight)
|
|
3072
|
+
//
|
|
3073
|
+
// Hook queues require-review actions to {accountDir}/pending-actions/.
|
|
3074
|
+
// These tools let the admin list, approve, reject, or edit pending actions.
|
|
3075
|
+
// On approval, the target plugin MCP server is spawned directly (dispatchStep
|
|
3076
|
+
// pattern) to execute the original tool call.
|
|
3077
|
+
// ===================================================================
|
|
3078
|
+
let _pendingDir;
|
|
3079
|
+
function getPendingDir() {
|
|
3080
|
+
if (!_pendingDir)
|
|
3081
|
+
_pendingDir = resolve(getAccountDir(), "pending-actions");
|
|
3082
|
+
return _pendingDir;
|
|
3083
|
+
}
|
|
3084
|
+
function readPendingActions() {
|
|
3085
|
+
const pendingDir = getPendingDir();
|
|
3086
|
+
if (!existsSync(pendingDir))
|
|
3087
|
+
return [];
|
|
3088
|
+
const files = readdirSync(pendingDir)
|
|
3089
|
+
.filter(f => f.endsWith(".json") && !f.startsWith("."));
|
|
3090
|
+
const actions = [];
|
|
3091
|
+
for (const file of files) {
|
|
3092
|
+
try {
|
|
3093
|
+
const raw = readFileSync(resolve(pendingDir, file), "utf-8");
|
|
3094
|
+
const parsed = JSON.parse(raw);
|
|
3095
|
+
if (parsed.state === "pending") {
|
|
3096
|
+
actions.push(parsed);
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
catch (err) {
|
|
3100
|
+
console.error(`[approval] Failed to read pending action ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
return actions.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
3104
|
+
}
|
|
3105
|
+
function readPendingAction(actionId) {
|
|
3106
|
+
const filePath = resolve(getPendingDir(), `${actionId}.json`);
|
|
3107
|
+
if (!existsSync(filePath))
|
|
3108
|
+
return null;
|
|
3109
|
+
try {
|
|
3110
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
3111
|
+
}
|
|
3112
|
+
catch {
|
|
3113
|
+
return null;
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
function resolvePendingAction(actionId, state, editedInput) {
|
|
3117
|
+
const filePath = resolve(getPendingDir(), `${actionId}.json`);
|
|
3118
|
+
if (!existsSync(filePath))
|
|
3119
|
+
return false;
|
|
3120
|
+
try {
|
|
3121
|
+
const action = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
3122
|
+
if (action.state !== "pending")
|
|
3123
|
+
return false;
|
|
3124
|
+
action.state = state;
|
|
3125
|
+
action.resolvedAt = new Date().toISOString();
|
|
3126
|
+
if (editedInput)
|
|
3127
|
+
action.editedInput = editedInput;
|
|
3128
|
+
writeFileSync(filePath, JSON.stringify(action, null, 2), "utf-8");
|
|
3129
|
+
return true;
|
|
3130
|
+
}
|
|
3131
|
+
catch {
|
|
3132
|
+
return false;
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
function cleanupPendingAction(actionId) {
|
|
3136
|
+
const pendingDir = getPendingDir();
|
|
3137
|
+
const filePath = resolve(pendingDir, `${actionId}.json`);
|
|
3138
|
+
try {
|
|
3139
|
+
if (existsSync(filePath)) {
|
|
3140
|
+
const archiveDir = resolve(pendingDir, "resolved");
|
|
3141
|
+
if (!existsSync(archiveDir)) {
|
|
3142
|
+
mkdirSync(archiveDir, { recursive: true });
|
|
3143
|
+
}
|
|
3144
|
+
renameSync(filePath, resolve(archiveDir, `${actionId}.json`));
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
catch (err) {
|
|
3148
|
+
console.error(`[approval] Failed to archive pending action ${actionId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
async function persistApprovalToolCall(opts) {
|
|
3152
|
+
// Write doctrine (Task 673): a ToolCall without its owning Conversation is
|
|
3153
|
+
// noise — every admin tool call belongs to a chat. When conversationId is
|
|
3154
|
+
// missing or its Conversation cannot be matched, we log and skip the
|
|
3155
|
+
// persist rather than create an orphan ToolCall node. The legacy
|
|
3156
|
+
// OPTIONAL-MATCH + FOREACH pattern created orphans silently.
|
|
3157
|
+
if (!opts.conversationId) {
|
|
3158
|
+
console.error(`[approval] ToolCall skipped (no conversationId): tool=${opts.toolName} state=${opts.approvalState}`);
|
|
3159
|
+
return;
|
|
3160
|
+
}
|
|
3161
|
+
const session = getSession();
|
|
3162
|
+
try {
|
|
3163
|
+
const optionalFields = [
|
|
3164
|
+
opts.originalInput != null ? ", originalInput: $originalInput" : "",
|
|
3165
|
+
].join("");
|
|
3166
|
+
const res = await session.run(`MATCH (c:Conversation {conversationId: $conversationId})
|
|
3167
|
+
CREATE (c)-[:HAS_TOOL_CALL]->(tc:ToolCall {
|
|
3168
|
+
callId: $callId,
|
|
3169
|
+
toolName: $toolName,
|
|
3170
|
+
pluginName: $pluginName,
|
|
3171
|
+
input: $input,
|
|
3172
|
+
output: $output,
|
|
3173
|
+
isError: $isError,
|
|
3174
|
+
agentType: 'admin',
|
|
3175
|
+
accountId: $accountId,
|
|
3176
|
+
approvalState: $approvalState,
|
|
3177
|
+
conversationId: $conversationId,
|
|
3178
|
+
createdBySource: 'admin-approval',
|
|
3179
|
+
startedAt: datetime($startedAt),
|
|
3180
|
+
completedAt: datetime($completedAt)
|
|
3181
|
+
${optionalFields}
|
|
3182
|
+
})`, {
|
|
3183
|
+
callId: randomUUID(),
|
|
3184
|
+
toolName: opts.toolName,
|
|
3185
|
+
pluginName: opts.pluginName,
|
|
3186
|
+
input: opts.input.slice(0, 300),
|
|
3187
|
+
output: opts.output.slice(0, 300),
|
|
3188
|
+
isError: opts.isError,
|
|
3189
|
+
accountId: ACCOUNT_ID,
|
|
3190
|
+
approvalState: opts.approvalState,
|
|
3191
|
+
conversationId: opts.conversationId,
|
|
3192
|
+
startedAt: new Date().toISOString(),
|
|
3193
|
+
completedAt: new Date().toISOString(),
|
|
3194
|
+
...(opts.originalInput != null ? { originalInput: opts.originalInput.slice(0, 300) } : {}),
|
|
3195
|
+
});
|
|
3196
|
+
if (res.summary.counters.updates().nodesCreated === 0) {
|
|
3197
|
+
console.error(`[approval] ToolCall skipped (conversation ${opts.conversationId} not found): tool=${opts.toolName}`);
|
|
3198
|
+
return;
|
|
3199
|
+
}
|
|
3200
|
+
console.error(`[approval] ToolCall persisted: ${opts.toolName} state=${opts.approvalState}`);
|
|
3201
|
+
}
|
|
3202
|
+
catch (err) {
|
|
3203
|
+
console.error(`[approval] ToolCall persist failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3204
|
+
}
|
|
3205
|
+
finally {
|
|
3206
|
+
await session.close();
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
/**
|
|
3210
|
+
* Spawn a plugin MCP server and execute a tool call directly.
|
|
3211
|
+
* Same pattern as workflow-execute.ts dispatchStep.
|
|
3212
|
+
*/
|
|
3213
|
+
async function dispatchApprovedAction(plugin, tool, args, timeoutMs = 30_000) {
|
|
3214
|
+
const serverPath = resolve(PLATFORM_ROOT, "plugins", plugin, "mcp", "dist", "index.js");
|
|
3215
|
+
// Path traversal check
|
|
3216
|
+
const pluginsRoot = resolve(PLATFORM_ROOT, "plugins");
|
|
3217
|
+
if (!serverPath.startsWith(pluginsRoot + "/")) {
|
|
3218
|
+
throw new Error(`Invalid plugin name: "${plugin}" — path traversal blocked`);
|
|
3219
|
+
}
|
|
3220
|
+
if (!existsSync(serverPath)) {
|
|
3221
|
+
throw new Error(`Plugin MCP server not found: ${serverPath}`);
|
|
3222
|
+
}
|
|
3223
|
+
const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
|
|
3224
|
+
const { StdioClientTransport } = await import("@modelcontextprotocol/sdk/client/stdio.js");
|
|
3225
|
+
const env = {};
|
|
3226
|
+
for (const [k, v] of Object.entries(process.env)) {
|
|
3227
|
+
if (v !== undefined)
|
|
3228
|
+
env[k] = v;
|
|
3229
|
+
}
|
|
3230
|
+
env.PLATFORM_ROOT = PLATFORM_ROOT;
|
|
3231
|
+
env.ACCOUNT_ID = ACCOUNT_ID;
|
|
3232
|
+
const transport = new StdioClientTransport({
|
|
3233
|
+
command: process.execPath,
|
|
3234
|
+
args: [serverPath],
|
|
3235
|
+
env,
|
|
3236
|
+
stderr: "pipe",
|
|
3237
|
+
});
|
|
3238
|
+
let childStderr = "";
|
|
3239
|
+
const stderrStream = transport.stderr;
|
|
3240
|
+
if (stderrStream) {
|
|
3241
|
+
stderrStream.on("data", (chunk) => {
|
|
3242
|
+
if (childStderr.length < 4096) {
|
|
3243
|
+
childStderr += chunk.toString("utf-8").slice(0, 4096 - childStderr.length);
|
|
3244
|
+
}
|
|
3245
|
+
});
|
|
3246
|
+
}
|
|
3247
|
+
const client = new Client({ name: "approval-executor", version: "1.0.0" });
|
|
3248
|
+
let timedOut = false;
|
|
3249
|
+
const timeout = setTimeout(() => {
|
|
3250
|
+
timedOut = true;
|
|
3251
|
+
client.close().catch(() => { });
|
|
3252
|
+
}, timeoutMs);
|
|
3253
|
+
try {
|
|
3254
|
+
await client.connect(transport);
|
|
3255
|
+
const result = await client.callTool({ name: tool, arguments: args });
|
|
3256
|
+
const contentArr = Array.isArray(result.content) ? result.content : [];
|
|
3257
|
+
const text = contentArr
|
|
3258
|
+
.filter((c) => c.type === "text")
|
|
3259
|
+
.map((c) => c.text)
|
|
3260
|
+
.join("\n");
|
|
3261
|
+
return { text, isError: result.isError === true };
|
|
3262
|
+
}
|
|
3263
|
+
catch (err) {
|
|
3264
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3265
|
+
const stderrSuffix = childStderr ? `\nChild stderr: ${childStderr.trim()}` : "";
|
|
3266
|
+
if (timedOut) {
|
|
3267
|
+
throw new Error(`Tool "${tool}" on plugin "${plugin}" timed out after ${timeoutMs}ms${stderrSuffix}`);
|
|
3268
|
+
}
|
|
3269
|
+
throw new Error(`Tool "${tool}" on plugin "${plugin}" failed: ${msg}${stderrSuffix}`);
|
|
3270
|
+
}
|
|
3271
|
+
finally {
|
|
3272
|
+
clearTimeout(timeout);
|
|
3273
|
+
await client.close().catch(() => { });
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
eagerTool(server, "action-pending", "List actions that are queued for human approval. Returns each pending action's ID, " +
|
|
3277
|
+
"tool name, input summary, and when it was queued. Use this to review what the agent " +
|
|
3278
|
+
"wanted to do before approving or rejecting.", {}, async () => {
|
|
3279
|
+
const actions = readPendingActions();
|
|
3280
|
+
if (actions.length === 0) {
|
|
3281
|
+
return {
|
|
3282
|
+
content: [{ type: "text", text: "No pending actions. All actions are either approved, rejected, or auto-executed." }],
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
3285
|
+
const lines = actions.map((a) => {
|
|
3286
|
+
const inputSummary = JSON.stringify(a.hookPayload?.tool_input ?? {}).slice(0, 200);
|
|
3287
|
+
return `- **${a.actionId}** | ${a.toolName} | queued ${a.createdAt}\n Input: ${inputSummary}`;
|
|
3288
|
+
});
|
|
3289
|
+
return {
|
|
3290
|
+
content: [{ type: "text", text: `## Pending Actions (${actions.length})\n\n${lines.join("\n\n")}` }],
|
|
3291
|
+
};
|
|
3292
|
+
});
|
|
3293
|
+
eagerTool(server, "action-approve", "Approve a pending action and execute it immediately. The original tool call is " +
|
|
3294
|
+
"executed via the target plugin's MCP server. The result is returned and an audit " +
|
|
3295
|
+
"record is written to Neo4j with approvalState: approved.", {
|
|
3296
|
+
actionId: z.string().describe("The action ID to approve (from action-pending)"),
|
|
3297
|
+
conversationId: z.string().optional().describe("Current conversation ID for audit trail linking"),
|
|
3298
|
+
}, async ({ actionId, conversationId }) => {
|
|
3299
|
+
const action = readPendingAction(actionId);
|
|
3300
|
+
if (!action) {
|
|
3301
|
+
return {
|
|
3302
|
+
content: [{ type: "text", text: `Action ${actionId} not found. It may have already been resolved or the ID is incorrect.` }],
|
|
3303
|
+
isError: true,
|
|
3304
|
+
};
|
|
3305
|
+
}
|
|
3306
|
+
if (action.state !== "pending") {
|
|
3307
|
+
return {
|
|
3308
|
+
content: [{ type: "text", text: `Action ${actionId} is already ${action.state}. Cannot approve a resolved action.` }],
|
|
3309
|
+
isError: true,
|
|
3310
|
+
};
|
|
3311
|
+
}
|
|
3312
|
+
if (!action.pluginName) {
|
|
3313
|
+
return {
|
|
3314
|
+
content: [{ type: "text", text: `Action ${actionId} has no plugin name — cannot determine which MCP server to execute.` }],
|
|
3315
|
+
isError: true,
|
|
3316
|
+
};
|
|
3317
|
+
}
|
|
3318
|
+
// Mark as approved before execution
|
|
3319
|
+
if (!resolvePendingAction(actionId, "approved")) {
|
|
3320
|
+
return {
|
|
3321
|
+
content: [{ type: "text", text: `Failed to update action ${actionId} state. The file may have been modified concurrently.` }],
|
|
3322
|
+
isError: true,
|
|
3323
|
+
};
|
|
3324
|
+
}
|
|
3325
|
+
const toolInput = action.hookPayload?.tool_input ?? {};
|
|
3326
|
+
console.error(`[approval] Executing approved action: ${action.toolName} plugin=${action.pluginName} actionId=${actionId}`);
|
|
3327
|
+
try {
|
|
3328
|
+
const result = await dispatchApprovedAction(action.pluginName, action.toolName, toolInput);
|
|
3329
|
+
// Persist audit record
|
|
3330
|
+
await persistApprovalToolCall({
|
|
3331
|
+
toolName: action.toolName,
|
|
3332
|
+
pluginName: action.pluginName,
|
|
3333
|
+
input: JSON.stringify(toolInput),
|
|
3334
|
+
output: result.text,
|
|
3335
|
+
isError: result.isError,
|
|
3336
|
+
approvalState: "approved",
|
|
3337
|
+
conversationId,
|
|
3338
|
+
});
|
|
3339
|
+
cleanupPendingAction(actionId);
|
|
3340
|
+
return {
|
|
3341
|
+
content: [{ type: "text", text: `Action approved and executed.\n\nTool: ${action.toolName}\nResult:\n${result.text}` }],
|
|
3342
|
+
isError: result.isError,
|
|
3343
|
+
};
|
|
3344
|
+
}
|
|
3345
|
+
catch (err) {
|
|
3346
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3347
|
+
console.error(`[approval] Execution failed for ${actionId}: ${errMsg}`);
|
|
3348
|
+
await persistApprovalToolCall({
|
|
3349
|
+
toolName: action.toolName,
|
|
3350
|
+
pluginName: action.pluginName,
|
|
3351
|
+
input: JSON.stringify(toolInput),
|
|
3352
|
+
output: errMsg,
|
|
3353
|
+
isError: true,
|
|
3354
|
+
approvalState: "approved",
|
|
3355
|
+
conversationId,
|
|
3356
|
+
});
|
|
3357
|
+
return {
|
|
3358
|
+
content: [{ type: "text", text: `Action approved but execution failed: ${errMsg}` }],
|
|
3359
|
+
isError: true,
|
|
3360
|
+
};
|
|
3361
|
+
}
|
|
3362
|
+
});
|
|
3363
|
+
eagerTool(server, "action-reject", "Reject a pending action. The action is not executed. An audit record is written " +
|
|
3364
|
+
"to Neo4j with approvalState: rejected.", {
|
|
3365
|
+
actionId: z.string().describe("The action ID to reject (from action-pending)"),
|
|
3366
|
+
reason: z.string().optional().describe("Optional reason for rejection"),
|
|
3367
|
+
conversationId: z.string().optional().describe("Current conversation ID for audit trail linking"),
|
|
3368
|
+
}, async ({ actionId, reason, conversationId }) => {
|
|
3369
|
+
const action = readPendingAction(actionId);
|
|
3370
|
+
if (!action) {
|
|
3371
|
+
return {
|
|
3372
|
+
content: [{ type: "text", text: `Action ${actionId} not found. It may have already been resolved or the ID is incorrect.` }],
|
|
3373
|
+
isError: true,
|
|
3374
|
+
};
|
|
3375
|
+
}
|
|
3376
|
+
if (action.state !== "pending") {
|
|
3377
|
+
return {
|
|
3378
|
+
content: [{ type: "text", text: `Action ${actionId} is already ${action.state}. Cannot reject a resolved action.` }],
|
|
3379
|
+
isError: true,
|
|
3380
|
+
};
|
|
3381
|
+
}
|
|
3382
|
+
if (!resolvePendingAction(actionId, "rejected")) {
|
|
3383
|
+
return {
|
|
3384
|
+
content: [{ type: "text", text: `Failed to update action ${actionId} state.` }],
|
|
3385
|
+
isError: true,
|
|
3386
|
+
};
|
|
3387
|
+
}
|
|
3388
|
+
const reasonSuffix = reason ? ` Reason: ${reason}` : "";
|
|
3389
|
+
console.error(`[approval] Action rejected: ${action.toolName} actionId=${actionId}${reasonSuffix}`);
|
|
3390
|
+
await persistApprovalToolCall({
|
|
3391
|
+
toolName: action.toolName,
|
|
3392
|
+
pluginName: action.pluginName ?? "unknown",
|
|
3393
|
+
input: JSON.stringify(action.hookPayload?.tool_input ?? {}),
|
|
3394
|
+
output: `Rejected by admin.${reasonSuffix}`,
|
|
3395
|
+
isError: false,
|
|
3396
|
+
approvalState: "rejected",
|
|
3397
|
+
conversationId,
|
|
3398
|
+
});
|
|
3399
|
+
cleanupPendingAction(actionId);
|
|
3400
|
+
return {
|
|
3401
|
+
content: [{ type: "text", text: `Action rejected. ${action.toolName} will not be executed.${reasonSuffix}` }],
|
|
3402
|
+
};
|
|
3403
|
+
});
|
|
3404
|
+
eagerTool(server, "action-edit", "Edit a pending action's input and then execute the modified version. The original " +
|
|
3405
|
+
"input is preserved in the audit trail. Use this when the admin wants to modify the " +
|
|
3406
|
+
"action (e.g., change the email subject) before approving.", {
|
|
3407
|
+
actionId: z.string().describe("The action ID to edit (from action-pending)"),
|
|
3408
|
+
editedInput: z.string().describe("The modified tool input as a JSON string"),
|
|
3409
|
+
conversationId: z.string().optional().describe("Current conversation ID for audit trail linking"),
|
|
3410
|
+
}, async ({ actionId, editedInput, conversationId }) => {
|
|
3411
|
+
const action = readPendingAction(actionId);
|
|
3412
|
+
if (!action) {
|
|
3413
|
+
return {
|
|
3414
|
+
content: [{ type: "text", text: `Action ${actionId} not found.` }],
|
|
3415
|
+
isError: true,
|
|
3416
|
+
};
|
|
3417
|
+
}
|
|
3418
|
+
if (action.state !== "pending") {
|
|
3419
|
+
return {
|
|
3420
|
+
content: [{ type: "text", text: `Action ${actionId} is already ${action.state}. Cannot edit a resolved action.` }],
|
|
3421
|
+
isError: true,
|
|
3422
|
+
};
|
|
3423
|
+
}
|
|
3424
|
+
if (!action.pluginName) {
|
|
3425
|
+
return {
|
|
3426
|
+
content: [{ type: "text", text: `Action ${actionId} has no plugin name — cannot determine which MCP server to execute.` }],
|
|
3427
|
+
isError: true,
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
let parsedInput;
|
|
3431
|
+
try {
|
|
3432
|
+
parsedInput = JSON.parse(editedInput);
|
|
3433
|
+
}
|
|
3434
|
+
catch {
|
|
3435
|
+
return {
|
|
3436
|
+
content: [{ type: "text", text: `Invalid JSON in editedInput. Please provide valid JSON for the tool input.` }],
|
|
3437
|
+
isError: true,
|
|
3438
|
+
};
|
|
3439
|
+
}
|
|
3440
|
+
const originalInput = action.hookPayload?.tool_input ?? {};
|
|
3441
|
+
// Mark as approved with the edited input
|
|
3442
|
+
if (!resolvePendingAction(actionId, "approved", parsedInput)) {
|
|
3443
|
+
return {
|
|
3444
|
+
content: [{ type: "text", text: `Failed to update action ${actionId} state.` }],
|
|
3445
|
+
isError: true,
|
|
3446
|
+
};
|
|
3447
|
+
}
|
|
3448
|
+
console.error(`[approval] Executing edited action: ${action.toolName} plugin=${action.pluginName} actionId=${actionId}`);
|
|
3449
|
+
try {
|
|
3450
|
+
const result = await dispatchApprovedAction(action.pluginName, action.toolName, parsedInput);
|
|
3451
|
+
await persistApprovalToolCall({
|
|
3452
|
+
toolName: action.toolName,
|
|
3453
|
+
pluginName: action.pluginName,
|
|
3454
|
+
input: JSON.stringify(parsedInput),
|
|
3455
|
+
output: result.text,
|
|
3456
|
+
isError: result.isError,
|
|
3457
|
+
approvalState: "approved",
|
|
3458
|
+
originalInput: JSON.stringify(originalInput),
|
|
3459
|
+
conversationId,
|
|
3460
|
+
});
|
|
3461
|
+
cleanupPendingAction(actionId);
|
|
3462
|
+
return {
|
|
3463
|
+
content: [{ type: "text", text: `Action edited, approved, and executed.\n\nTool: ${action.toolName}\nResult:\n${result.text}` }],
|
|
3464
|
+
isError: result.isError,
|
|
3465
|
+
};
|
|
3466
|
+
}
|
|
3467
|
+
catch (err) {
|
|
3468
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3469
|
+
console.error(`[approval] Edited execution failed for ${actionId}: ${errMsg}`);
|
|
3470
|
+
await persistApprovalToolCall({
|
|
3471
|
+
toolName: action.toolName,
|
|
3472
|
+
pluginName: action.pluginName,
|
|
3473
|
+
input: JSON.stringify(parsedInput),
|
|
3474
|
+
output: errMsg,
|
|
3475
|
+
isError: true,
|
|
3476
|
+
approvalState: "approved",
|
|
3477
|
+
originalInput: JSON.stringify(originalInput),
|
|
3478
|
+
conversationId,
|
|
3479
|
+
});
|
|
3480
|
+
return {
|
|
3481
|
+
content: [{ type: "text", text: `Action edited and approved but execution failed: ${errMsg}` }],
|
|
3482
|
+
isError: true,
|
|
3483
|
+
};
|
|
3484
|
+
}
|
|
3485
|
+
});
|
|
3486
|
+
// Cleanup on exit (SIGTERM for systemd service stops on Pi)
|
|
3487
|
+
const cleanup = async () => {
|
|
3488
|
+
await closeDriver();
|
|
3489
|
+
process.exit(0);
|
|
3490
|
+
};
|
|
3491
|
+
process.on("SIGINT", cleanup);
|
|
3492
|
+
process.on("SIGTERM", cleanup);
|
|
3493
|
+
const transport = new StdioServerTransport();
|
|
3494
|
+
await server.connect(transport);
|
|
3495
|
+
//# sourceMappingURL=index.js.map
|