@rubytech/create-siteoffice-code 0.1.256
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__/brand-fonts.test.js +102 -0
- package/dist/__tests__/brew-install.test.js +151 -0
- package/dist/__tests__/brew-resolve.test.js +138 -0
- package/dist/__tests__/bundler-rewrite-platform-lib.test.js +68 -0
- package/dist/__tests__/cdp-port-no-silent-fallback.test.js +53 -0
- package/dist/__tests__/claude-ptys-slice.test.js +25 -0
- package/dist/__tests__/cloudflared-slice.test.js +19 -0
- package/dist/__tests__/entitlement-flag.test.js +43 -0
- package/dist/__tests__/init-logging.test.js +85 -0
- package/dist/__tests__/installer-settings-permissions.test.js +112 -0
- package/dist/__tests__/installer-specialist-registration.test.js +116 -0
- package/dist/__tests__/launchd-plist.test.js +195 -0
- package/dist/__tests__/macos-darwin-branch.test.js +85 -0
- package/dist/__tests__/macos-version.test.js +96 -0
- package/dist/__tests__/onboarding-state-readback.test.js +61 -0
- package/dist/__tests__/peer-brand-detect.test.js +103 -0
- package/dist/__tests__/platform-detect.test.js +50 -0
- package/dist/__tests__/platform-port-stamp.test.js +28 -0
- package/dist/__tests__/plugin-install.test.js +123 -0
- package/dist/__tests__/port-canonicalisation.test.js +200 -0
- package/dist/__tests__/preflight-port-classifier.test.js +330 -0
- package/dist/__tests__/premium-bundle-gate.test.js +59 -0
- package/dist/__tests__/premium-mcp-discover.test.js +127 -0
- package/dist/__tests__/rss-sampler-installer.test.js +27 -0
- package/dist/__tests__/samba-provision.test.js +226 -0
- package/dist/__tests__/snap-chromium.test.js +115 -0
- package/dist/__tests__/tier-flag.test.js +53 -0
- package/dist/apt-resolve.js +73 -0
- package/dist/brew-install.js +180 -0
- package/dist/brew-resolve.js +68 -0
- package/dist/bundler-rewrite-platform-lib.js +29 -0
- package/dist/index.js +4048 -0
- package/dist/init-logging.js +28 -0
- package/dist/launchd-plist.js +68 -0
- package/dist/lib/plugin-install.js +110 -0
- package/dist/lib/premium-mcp-discover.js +41 -0
- package/dist/macos-version.js +53 -0
- package/dist/onboarding-readback.js +27 -0
- package/dist/peer-brand-detect.js +39 -0
- package/dist/permissions-seed.js +76 -0
- package/dist/pinned-binaries.js +12 -0
- package/dist/platform-detect.js +36 -0
- package/dist/port-resolution.js +208 -0
- package/dist/preflight-port-classifier.js +222 -0
- package/dist/samba-provision.js +218 -0
- package/dist/snap-chromium.js +88 -0
- package/dist/specialist-registration.js +78 -0
- package/dist/tier-flag.js +85 -0
- package/dist/uninstall.js +947 -0
- package/package.json +35 -0
- package/payload/platform/.docs/search-surface-contract.md +58 -0
- package/payload/platform/config/brand-registry.json +28 -0
- package/payload/platform/config/brand.json +82 -0
- package/payload/platform/docs/superpowers/plans/2026-06-02-task-610-follower-202-retry.md +372 -0
- package/payload/platform/docs/superpowers/plans/2026-06-04-public-agent-knowledge-delivery.md +230 -0
- package/payload/platform/docs/superpowers/specs/2026-06-02-task-610-follower-202-retry-design.md +116 -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/admin-conversation-purge/dist/index.d.ts +54 -0
- package/payload/platform/lib/admin-conversation-purge/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/admin-conversation-purge/dist/index.js +84 -0
- package/payload/platform/lib/admin-conversation-purge/dist/index.js.map +1 -0
- package/payload/platform/lib/admin-conversation-purge/src/index.ts +120 -0
- package/payload/platform/lib/admin-conversation-purge/tsconfig.json +8 -0
- package/payload/platform/lib/admins-write/dist/index.d.ts +86 -0
- package/payload/platform/lib/admins-write/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/admins-write/dist/index.js +245 -0
- package/payload/platform/lib/admins-write/dist/index.js.map +1 -0
- package/payload/platform/lib/admins-write/src/index.ts +305 -0
- package/payload/platform/lib/admins-write/tsconfig.json +8 -0
- package/payload/platform/lib/aeo-llms-txt-writer/dist/index.d.ts +33 -0
- package/payload/platform/lib/aeo-llms-txt-writer/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/aeo-llms-txt-writer/dist/index.js +119 -0
- package/payload/platform/lib/aeo-llms-txt-writer/dist/index.js.map +1 -0
- package/payload/platform/lib/aeo-llms-txt-writer/src/index.ts +172 -0
- package/payload/platform/lib/aeo-llms-txt-writer/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/embed-client/dist/index.d.ts +4 -0
- package/payload/platform/lib/embed-client/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/embed-client/dist/index.js +53 -0
- package/payload/platform/lib/embed-client/dist/index.js.map +1 -0
- package/payload/platform/lib/embed-client/src/index.ts +53 -0
- package/payload/platform/lib/embed-client/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 +845 -0
- package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts +76 -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 +218 -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 +1074 -0
- package/payload/platform/lib/graph-mcp/src/schema-cache.ts +243 -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/boosts.d.ts +39 -0
- package/payload/platform/lib/graph-search/dist/boosts.d.ts.map +1 -0
- package/payload/platform/lib/graph-search/dist/boosts.js +57 -0
- package/payload/platform/lib/graph-search/dist/boosts.js.map +1 -0
- package/payload/platform/lib/graph-search/dist/dedup.d.ts +29 -0
- package/payload/platform/lib/graph-search/dist/dedup.d.ts.map +1 -0
- package/payload/platform/lib/graph-search/dist/dedup.js +97 -0
- package/payload/platform/lib/graph-search/dist/dedup.js.map +1 -0
- package/payload/platform/lib/graph-search/dist/index.d.ts +355 -0
- package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/graph-search/dist/index.js +864 -0
- package/payload/platform/lib/graph-search/dist/index.js.map +1 -0
- package/payload/platform/lib/graph-search/dist/query-expansion.d.ts +37 -0
- package/payload/platform/lib/graph-search/dist/query-expansion.d.ts.map +1 -0
- package/payload/platform/lib/graph-search/dist/query-expansion.js +101 -0
- package/payload/platform/lib/graph-search/dist/query-expansion.js.map +1 -0
- package/payload/platform/lib/graph-search/dist/route.d.ts +29 -0
- package/payload/platform/lib/graph-search/dist/route.d.ts.map +1 -0
- package/payload/platform/lib/graph-search/dist/route.js +53 -0
- package/payload/platform/lib/graph-search/dist/route.js.map +1 -0
- package/payload/platform/lib/graph-search/dist/rrf-fusion.d.ts +31 -0
- package/payload/platform/lib/graph-search/dist/rrf-fusion.d.ts.map +1 -0
- package/payload/platform/lib/graph-search/dist/rrf-fusion.js +57 -0
- package/payload/platform/lib/graph-search/dist/rrf-fusion.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__/boosts.test.ts +59 -0
- package/payload/platform/lib/graph-search/src/__tests__/brochure-threshold.test.ts +136 -0
- package/payload/platform/lib/graph-search/src/__tests__/dedup.test.ts +83 -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 +357 -0
- package/payload/platform/lib/graph-search/src/__tests__/hybrid.test.ts +355 -0
- package/payload/platform/lib/graph-search/src/__tests__/route.test.ts +62 -0
- package/payload/platform/lib/graph-search/src/__tests__/rrf-fusion.test.ts +39 -0
- package/payload/platform/lib/graph-search/src/__tests__/vector-index-coverage.test.ts +198 -0
- package/payload/platform/lib/graph-search/src/__tests__/vector-threshold.test.ts +170 -0
- package/payload/platform/lib/graph-search/src/boosts.ts +61 -0
- package/payload/platform/lib/graph-search/src/dedup.ts +108 -0
- package/payload/platform/lib/graph-search/src/index.ts +1162 -0
- package/payload/platform/lib/graph-search/src/route.ts +70 -0
- package/payload/platform/lib/graph-search/src/rrf-fusion.ts +62 -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-style/dist/index.d.ts +80 -0
- package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/graph-style/dist/index.js +285 -0
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -0
- package/payload/platform/lib/graph-style/src/__tests__/parity.test.ts +114 -0
- package/payload/platform/lib/graph-style/src/index.ts +299 -0
- package/payload/platform/lib/graph-style/tsconfig.json +9 -0
- package/payload/platform/lib/graph-style/vitest.config.ts +9 -0
- package/payload/platform/lib/graph-trash/dist/index.d.ts +106 -0
- package/payload/platform/lib/graph-trash/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/graph-trash/dist/index.js +340 -0
- package/payload/platform/lib/graph-trash/dist/index.js.map +1 -0
- package/payload/platform/lib/graph-trash/package.json +7 -0
- package/payload/platform/lib/graph-trash/src/index.ts +493 -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 +263 -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 +30 -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 +88 -0
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js.map +1 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts +151 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/index.js +377 -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 +279 -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 +148 -0
- package/payload/platform/lib/graph-write/src/index.ts +491 -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/package.json +7 -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/package.json +7 -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 +116 -0
- package/payload/platform/lib/oauth-llm/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/oauth-llm/dist/index.js +386 -0
- package/payload/platform/lib/oauth-llm/dist/index.js.map +1 -0
- package/payload/platform/lib/obsidian-parser/dist/index.d.ts +98 -0
- package/payload/platform/lib/obsidian-parser/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/obsidian-parser/dist/index.js +480 -0
- package/payload/platform/lib/obsidian-parser/dist/index.js.map +1 -0
- package/payload/platform/lib/obsidian-parser/src/index.ts +572 -0
- package/payload/platform/lib/obsidian-parser/tsconfig.json +8 -0
- package/payload/platform/lib/persistent-components/dist/index.d.ts +20 -0
- package/payload/platform/lib/persistent-components/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/persistent-components/dist/index.js +31 -0
- package/payload/platform/lib/persistent-components/dist/index.js.map +1 -0
- package/payload/platform/lib/persistent-components/src/index.ts +27 -0
- package/payload/platform/lib/persistent-components/tsconfig.json +8 -0
- package/payload/platform/lib/require-port-env/dist/index.d.ts +31 -0
- package/payload/platform/lib/require-port-env/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/require-port-env/dist/index.js +52 -0
- package/payload/platform/lib/require-port-env/dist/index.js.map +1 -0
- package/payload/platform/lib/require-port-env/src/index.ts +56 -0
- package/payload/platform/lib/require-port-env/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/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 +158 -0
- package/payload/platform/neo4j/schema.cypher +1899 -0
- package/payload/platform/package-lock.json +4060 -0
- package/payload/platform/package.json +26 -0
- package/payload/platform/plugins/.claude-plugin/marketplace.json +158 -0
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/admin/PLUGIN.md +163 -0
- package/payload/platform/plugins/admin/hooks/__tests__/archive-ingest-surface-gate.test.sh +191 -0
- package/payload/platform/plugins/admin/hooks/__tests__/askuserquestion-investigate-gate.test.sh +176 -0
- package/payload/platform/plugins/admin/hooks/__tests__/hook-emit-stale-lock-ttl.test.sh +102 -0
- package/payload/platform/plugins/admin/hooks/__tests__/hook-emit.test.sh +75 -0
- package/payload/platform/plugins/admin/hooks/__tests__/pin-identity-gate.test.sh +96 -0
- package/payload/platform/plugins/admin/hooks/__tests__/post-tool-use-agent.test.sh +173 -0
- package/payload/platform/plugins/admin/hooks/__tests__/prompt-optimiser-directive.test.sh +70 -0
- package/payload/platform/plugins/admin/hooks/admin-authoring-observer.sh +155 -0
- package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +224 -0
- package/payload/platform/plugins/admin/hooks/askuserquestion-investigate-gate.sh +257 -0
- package/payload/platform/plugins/admin/hooks/lib/hook-emit.sh +143 -0
- package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +16 -0
- package/payload/platform/plugins/admin/hooks/mcp-tool-missing.sh +94 -0
- package/payload/platform/plugins/admin/hooks/pin-identity-gate.sh +136 -0
- package/payload/platform/plugins/admin/hooks/post-tool-use-agent.sh +155 -0
- package/payload/platform/plugins/admin/hooks/prompt-optimiser-directive.sh +52 -0
- package/payload/platform/plugins/admin/hooks/webfetch-preflight.mjs +363 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.js +79 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.js +34 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.js +130 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.js.map +1 -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/__tests__/skill-no-prescribed-role.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.js +50 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.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 +3120 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/catalogue-census.d.ts +14 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/catalogue-census.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/catalogue-census.js +50 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/catalogue-census.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 +44 -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 +187 -0
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.d.ts +6 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.js +32 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts +28 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js +68 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/publish-site.d.ts +34 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/publish-site.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/publish-site.js +176 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/publish-site.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/publish-site-routing.md +44 -0
- package/payload/platform/plugins/admin/skills/a4-print-documents/SKILL.md +241 -0
- package/payload/platform/plugins/admin/skills/access-manager/SKILL.md +30 -0
- package/payload/platform/plugins/admin/skills/access-manager/references/operations.md +154 -0
- package/payload/platform/plugins/admin/skills/admin-user-management/SKILL.md +49 -0
- package/payload/platform/plugins/admin/skills/business-profile/SKILL.md +53 -0
- package/payload/platform/plugins/admin/skills/capabilities-here/SKILL.md +31 -0
- package/payload/platform/plugins/admin/skills/datetime/SKILL.md +149 -0
- package/payload/platform/plugins/admin/skills/deck-pages/SKILL.md +404 -0
- package/payload/platform/plugins/admin/skills/file-presentation/SKILL.md +47 -0
- package/payload/platform/plugins/admin/skills/insight/SKILL.md +24 -0
- package/payload/platform/plugins/admin/skills/investigate/SKILL.md +318 -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/platform-architecture/SKILL.md +3943 -0
- package/payload/platform/plugins/admin/skills/plugin-management/SKILL.md +99 -0
- package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +178 -0
- package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +256 -0
- package/payload/platform/plugins/admin/skills/publish-site/SKILL.md +42 -0
- package/payload/platform/plugins/admin/skills/qr-code/SKILL.md +36 -0
- package/payload/platform/plugins/admin/skills/qr-code/references/data-formats.md +113 -0
- package/payload/platform/plugins/admin/skills/session-management/SKILL.md +62 -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 +45 -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 +193 -0
- package/payload/platform/plugins/admin/skills/superpowers-sprint/SKILL.md +361 -0
- package/payload/platform/plugins/admin/skills/task/SKILL.md +314 -0
- package/payload/platform/plugins/admin/skills/unzip-attachment/SKILL.md +84 -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 +52 -0
- package/payload/platform/plugins/admin/skills/upgrade/SKILL.md +34 -0
- package/payload/platform/plugins/aeo/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/aeo/PLUGIN.md +80 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.d.ts +2 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.js +121 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.d.ts +2 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.js +129 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.js +189 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.d.ts +27 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.js +274 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.js +38 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.d.ts +48 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.js +254 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.d.ts +25 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.js +78 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.d.ts +18 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.js +56 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.d.ts +9 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.js +11 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/package.json +22 -0
- package/payload/platform/plugins/aeo/mcp/vitest.config.ts +9 -0
- package/payload/platform/plugins/aeo/skills/structured-answer/SKILL.md +53 -0
- package/payload/platform/plugins/browser/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/browser/PLUGIN.md +114 -0
- package/payload/platform/plugins/browser/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/browser/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/index.js +165 -0
- package/payload/platform/plugins/browser/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.d.ts +98 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.d.ts.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.js +455 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.js.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.d.ts +44 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.d.ts.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.js +89 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.js.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.d.ts +153 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.d.ts.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.js +401 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.js.map +1 -0
- package/payload/platform/plugins/browser/mcp/package.json +19 -0
- package/payload/platform/plugins/business-assistant/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/business-assistant/PLUGIN.md +62 -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/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/cloudflare/PLUGIN.md +61 -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 +24 -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 +551 -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/api.md +166 -0
- package/payload/platform/plugins/cloudflare/references/d1-data-capture.md +157 -0
- package/payload/platform/plugins/cloudflare/references/dashboard-guide.md +173 -0
- package/payload/platform/plugins/cloudflare/references/hosting-sites.md +66 -0
- package/payload/platform/plugins/cloudflare/references/manual-setup.md +633 -0
- package/payload/platform/plugins/cloudflare/references/reset-guide.md +119 -0
- package/payload/platform/plugins/cloudflare/references/serving-published-sites.md +73 -0
- package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +72 -0
- package/payload/platform/plugins/contacts/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/contacts/PLUGIN.md +62 -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 +467 -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 +52 -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 +181 -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 +122 -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/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/deep-research/PLUGIN.md +19 -0
- package/payload/platform/plugins/deep-research/recipes/README.md +36 -0
- package/payload/platform/plugins/deep-research/skills/academic-verify/SKILL.md +75 -0
- package/payload/platform/plugins/deep-research/skills/book-mirror/SKILL.md +68 -0
- package/payload/platform/plugins/deep-research/skills/data-research/SKILL.md +108 -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/deep-research/skills/strategic-reading/SKILL.md +69 -0
- package/payload/platform/plugins/docs/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/docs/PLUGIN.md +58 -0
- package/payload/platform/plugins/docs/references/access-control.md +84 -0
- package/payload/platform/plugins/docs/references/admin-identity-gate.md +81 -0
- package/payload/platform/plugins/docs/references/admin-session.md +177 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +266 -0
- package/payload/platform/plugins/docs/references/aeo.md +87 -0
- package/payload/platform/plugins/docs/references/attachments.md +44 -0
- package/payload/platform/plugins/docs/references/cloudflare.md +102 -0
- package/payload/platform/plugins/docs/references/contacts-guide.md +94 -0
- package/payload/platform/plugins/docs/references/deployment.md +303 -0
- package/payload/platform/plugins/docs/references/getting-started.md +82 -0
- package/payload/platform/plugins/docs/references/graph.md +163 -0
- package/payload/platform/plugins/docs/references/internals.md +539 -0
- package/payload/platform/plugins/docs/references/investigate-and-task-skills.md +9 -0
- package/payload/platform/plugins/docs/references/linkedin-extension.md +49 -0
- package/payload/platform/plugins/docs/references/memory-guide.md +163 -0
- package/payload/platform/plugins/docs/references/neo4j.md +63 -0
- package/payload/platform/plugins/docs/references/outlook-guide.md +69 -0
- package/payload/platform/plugins/docs/references/platform.md +193 -0
- package/payload/platform/plugins/docs/references/plugins-guide.md +188 -0
- package/payload/platform/plugins/docs/references/projects-guide.md +94 -0
- package/payload/platform/plugins/docs/references/samba.md +80 -0
- package/payload/platform/plugins/docs/references/session-retrospective.md +14 -0
- package/payload/platform/plugins/docs/references/settings.md +82 -0
- package/payload/platform/plugins/docs/references/slides.md +31 -0
- package/payload/platform/plugins/docs/references/telegram-guide.md +58 -0
- package/payload/platform/plugins/docs/references/troubleshooting.md +289 -0
- package/payload/platform/plugins/docs/references/visitor-graph.md +83 -0
- package/payload/platform/plugins/docs/references/voice-mirror-guide.md +64 -0
- package/payload/platform/plugins/docs/superpowers/plans/2026-06-01-memory-edge.md +589 -0
- package/payload/platform/plugins/email/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/email/PLUGIN.md +98 -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 +381 -0
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/attachment-archive.d.ts +39 -0
- package/payload/platform/plugins/email/mcp/dist/lib/attachment-archive.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/attachment-archive.js +0 -0
- package/payload/platform/plugins/email/mcp/dist/lib/attachment-archive.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.d.ts +19 -0
- package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.js +64 -0
- package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/claude-bridge.d.ts +17 -0
- package/payload/platform/plugins/email/mcp/dist/lib/claude-bridge.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/claude-bridge.js +186 -0
- package/payload/platform/plugins/email/mcp/dist/lib/claude-bridge.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.d.ts +30 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.js +305 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-lookup.d.ts +19 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-lookup.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-lookup.js +51 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-lookup.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts +89 -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 +245 -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 +29 -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 +18 -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 +266 -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 +770 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/ingest-batch.d.ts +23 -0
- package/payload/platform/plugins/email/mcp/dist/lib/ingest-batch.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/ingest-batch.js +15 -0
- package/payload/platform/plugins/email/mcp/dist/lib/ingest-batch.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 +60 -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 +675 -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 +25 -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 +78 -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 +817 -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 +30 -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 +215 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.js +56 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.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-classify.d.ts +6 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js +89 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch.d.ts +15 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch.js +54 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.d.ts +24 -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 +293 -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-ingest.d.ts +20 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.js +191 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.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 +84 -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 +11 -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 +74 -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 +67 -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 +11 -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 +37 -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 +162 -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 +22 -0
- package/payload/platform/plugins/email/references/email-reference.md +144 -0
- package/payload/platform/plugins/email/skills/email-composition/SKILL.md +184 -0
- package/payload/platform/plugins/email/skills/email-ingest/SKILL.md +87 -0
- package/payload/platform/plugins/graph/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/graph/PLUGIN.md +37 -0
- package/payload/platform/plugins/graph-viewer/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/graph-viewer/PLUGIN.md +56 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/index.d.ts +7 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/index.js +70 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/lib/neo4j.js +43 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/render/draw.d.ts +28 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/render/draw.d.ts.map +1 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/render/draw.js +73 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/render/draw.js.map +1 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/render/layout.d.ts +40 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/render/layout.d.ts.map +1 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/render/layout.js +117 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/render/layout.js.map +1 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.d.ts +45 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.d.ts.map +1 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js +198 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js.map +1 -0
- package/payload/platform/plugins/graph-viewer/mcp/package.json +25 -0
- package/payload/platform/plugins/graph-viewer/mcp/vitest.config.ts +8 -0
- package/payload/platform/plugins/graph-viewer/skills/render-graph/SKILL.md +38 -0
- package/payload/platform/plugins/linkedin-extension/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/linkedin-extension/PLUGIN.md +58 -0
- package/payload/platform/plugins/linkedin-extension/extension/README.md +44 -0
- package/payload/platform/plugins/linkedin-extension/extension/__tests__/fixtures/profile.html +34 -0
- package/payload/platform/plugins/linkedin-extension/extension/__tests__/fixtures/thread.html +36 -0
- package/payload/platform/plugins/linkedin-extension/extension/assets/pill.css +52 -0
- package/payload/platform/plugins/linkedin-extension/extension/background/sw.js +60 -0
- package/payload/platform/plugins/linkedin-extension/extension/content/extractors.js +127 -0
- package/payload/platform/plugins/linkedin-extension/extension/content/profile.js +82 -0
- package/payload/platform/plugins/linkedin-extension/extension/content/thread.js +84 -0
- package/payload/platform/plugins/linkedin-extension/extension/manifest.json +32 -0
- package/payload/platform/plugins/linkedin-extension/extension/options/options.html +33 -0
- package/payload/platform/plugins/linkedin-extension/extension/options/options.js +30 -0
- package/payload/platform/plugins/linkedin-extension/skills/linkedin-extension/SKILL.md +91 -0
- package/payload/platform/plugins/linkedin-import/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/linkedin-import/PLUGIN.md +27 -0
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md +119 -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 +94 -0
- package/payload/platform/plugins/memory/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/memory/PLUGIN.md +291 -0
- package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +616 -0
- package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +106 -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 +2522 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.js +68 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.js +90 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.js +41 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.js +90 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-label.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-label.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-label.test.js +30 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-label.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.js +25 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.js +62 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-origin-gate.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-origin-gate.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-origin-gate.test.js +50 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-origin-gate.test.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 +154 -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 +226 -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__/neo4j-password-path.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.js +57 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-backlinks.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-backlinks.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-backlinks.test.js +24 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-backlinks.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-prune-revisions.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-prune-revisions.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-prune-revisions.test.js +51 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-prune-revisions.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-tag-normalisation.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-tag-normalisation.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-tag-normalisation.test.js +51 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-tag-normalisation.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relative-date.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relative-date.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relative-date.test.js +68 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relative-date.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/resolve-active-vertical.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/resolve-active-vertical.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/resolve-active-vertical.test.js +116 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/resolve-active-vertical.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js +67 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.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 +217 -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 +640 -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/__tests__/timeline-extractor.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.js +111 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js +79 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.js +27 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.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/compiled-truth-revision.d.ts +91 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.js +39 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts +60 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js +169 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.d.ts +3 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.js +61 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts +7 -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 +36 -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 +38 -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-normalisers/x-dm.d.ts +3 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/x-dm.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/x-dm.js +101 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/x-dm.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 +16 -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 +43 -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 +60 -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/conversation-pipeline/turn-text-cache-path.d.ts +8 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/turn-text-cache-path.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/turn-text-cache-path.js +27 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/turn-text-cache-path.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/document-sectioniser.d.ts +10 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.js +47 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.d.ts +44 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.js +14 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-backlinks.d.ts +15 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-backlinks.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-backlinks.js +37 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-backlinks.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-citation-audit.d.ts +3 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-citation-audit.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-citation-audit.js +16 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-citation-audit.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-dead-edges.d.ts +19 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-dead-edges.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-dead-edges.js +39 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-dead-edges.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-orphans.d.ts +19 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-orphans.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-orphans.js +42 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-orphans.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-prune-revisions.d.ts +21 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-prune-revisions.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-prune-revisions.js +27 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-prune-revisions.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-stale-truth.d.ts +18 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-stale-truth.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-stale-truth.js +60 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-stale-truth.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-tag-normalisation.d.ts +25 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-tag-normalisation.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-tag-normalisation.js +90 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-tag-normalisation.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts +2 -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 +6 -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 +41 -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 +113 -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 +76 -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 +148 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.d.ts +41 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.js +69 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.d.ts +18 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.js +31 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-origin-gate.d.ts +18 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-origin-gate.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-origin-gate.js +35 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-origin-gate.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.d.ts +119 -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 +208 -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 +248 -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 +824 -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/log-ingest.d.ts +65 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js +182 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts +19 -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 +76 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/relative-date.d.ts +13 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/relative-date.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/relative-date.js +191 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/relative-date.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/resolve-active-vertical.d.ts +33 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/resolve-active-vertical.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/resolve-active-vertical.js +44 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/resolve-active-vertical.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts +141 -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 +516 -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 +92 -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 +243 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/section-types.d.ts +127 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/section-types.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/section-types.js +56 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/section-types.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.d.ts +19 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.js +179 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts +35 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.js +28 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts +29 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js +142 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.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__/_helpers/emit-capture.d.ts +22 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/_helpers/emit-capture.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/_helpers/emit-capture.js +44 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/_helpers/emit-capture.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.js +20 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights-emit.test.js +68 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts +7 -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 +298 -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-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection-emit.test.js +48 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection-emit.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-archive-preference-embed.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.js +67 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-memory-expunge-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-memory-expunge-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-memory-expunge-emit.test.js +53 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-memory-expunge-emit.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 +75 -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__/embeddings-cap.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.js +34 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/graph-prune-denylist-add-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/graph-prune-denylist-add-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/graph-prune-denylist-add-emit.test.js +40 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/graph-prune-denylist-add-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/graph-prune-denylist-remove-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/graph-prune-denylist-remove-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/graph-prune-denylist-remove-emit.test.js +39 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/graph-prune-denylist-remove-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/log-ingest.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/log-ingest.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/log-ingest.test.js +241 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/log-ingest.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.js +61 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-emit.test.js +88 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-emit.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 +106 -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-classify-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-classify-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-classify-emit.test.js +58 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-classify-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.js +129 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js +64 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.js +141 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edge.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edge.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edge.test.js +164 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edge.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edit-attachment-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edit-attachment-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edit-attachment-emit.test.js +70 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edit-attachment-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-admin-conversation.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-admin-conversation.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-admin-conversation.test.js +168 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-admin-conversation.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-emit.test.js +48 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.js +86 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.js +95 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-chat-body-server-sliced.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-chat-body-server-sliced.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-chat-body-server-sliced.test.js +124 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-chat-body-server-sliced.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-emit.test.js +74 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-extract-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-extract-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-extract-emit.test.js +47 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-extract-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-inline-rewrite.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-inline-rewrite.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-inline-rewrite.test.js +66 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-inline-rewrite.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.js +104 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-web-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-web-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-web-emit.test.js +65 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-web-emit.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 +175 -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__/memory-lookup-by-name.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-lookup-by-name.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-lookup-by-name.test.js +135 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-lookup-by-name.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-emit.test.js +43 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.js +87 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-rename-attachment-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-rename-attachment-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-rename-attachment-emit.test.js +65 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-rename-attachment-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-report-tools.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-report-tools.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-report-tools.test.js +115 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-report-tools.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-restore-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-restore-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-restore-emit.test.js +49 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-restore-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js +128 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.js +34 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.js +125 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js +79 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.js +329 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-compiled-truth-rejection.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-compiled-truth-rejection.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-compiled-truth-rejection.test.js +39 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-compiled-truth-rejection.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js +81 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-timeline-learned-at.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-timeline-learned-at.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-timeline-learned-at.test.js +30 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-timeline-learned-at.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-delete-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-delete-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-delete-emit.test.js +62 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-delete-emit.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-emit.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-emit.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-emit.test.js +76 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-emit.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 +88 -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 +149 -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 +114 -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 +415 -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 +124 -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-archive-list-chunks.d.ts +43 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.js +96 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.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 +15 -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 +32 -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 +31 -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/image-fetch.d.ts +15 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/image-fetch.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/image-fetch.js +64 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/image-fetch.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.d.ts +6 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.js +73 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +202 -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 +1084 -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-brain-capture-recent.d.ts +35 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.js +80 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.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 +62 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts +41 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js +73 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts +65 -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 +151 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.d.ts +48 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.js +197 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edge.d.ts +21 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edge.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edge.js +51 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edge.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 +95 -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 +50 -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 +104 -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 +37 -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 +97 -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 +32 -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 +116 -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 +194 -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 +1273 -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-lookup-by-name.d.ts +16 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.js +56 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.d.ts +28 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.js +46 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.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 +127 -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 +67 -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-report-list.d.ts +28 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-list.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-list.js +64 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-list.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-read-latest.d.ts +24 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-read-latest.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-read-latest.js +49 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-read-latest.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-write.d.ts +34 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-write.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-write.js +99 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-write.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 +47 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.d.ts +29 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.js +67 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts +7 -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 +184 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-signals-recent.d.ts +35 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-signals-recent.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-signals-recent.js +73 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-signals-recent.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts +29 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js +22 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.d.ts +15 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.js +45 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts +16 -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 +194 -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 +376 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.d.ts +127 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.js +477 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.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 +35 -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 +46 -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 +423 -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 +395 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts +30 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js +130 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/package.json +23 -0
- package/payload/platform/plugins/memory/mcp/scripts/backfill-typed-edges.ts +72 -0
- package/payload/platform/plugins/memory/mcp/scripts/boot-smoke.sh +69 -0
- package/payload/platform/plugins/memory/mcp/scripts/generate-edge-docs.ts +75 -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 +48 -0
- package/payload/platform/plugins/memory/references/graph-primitives.md +394 -0
- package/payload/platform/plugins/memory/references/schema-base.md +334 -0
- package/payload/platform/plugins/memory/references/schema-construction.md +72 -0
- package/payload/platform/plugins/memory/references/schema-creator.md +35 -0
- package/payload/platform/plugins/memory/references/schema-estate-agent.md +110 -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/references/transcript-formats/circleback.md +49 -0
- package/payload/platform/plugins/memory/references/transcript-formats/granola.md +50 -0
- package/payload/platform/plugins/memory/references/transcript-formats/otter.md +50 -0
- package/payload/platform/plugins/memory/skills/archive-crawler/SKILL.md +67 -0
- package/payload/platform/plugins/memory/skills/challenge/SKILL.md +52 -0
- package/payload/platform/plugins/memory/skills/concept-synthesis/SKILL.md +79 -0
- package/payload/platform/plugins/memory/skills/connect/SKILL.md +56 -0
- package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +193 -0
- package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +207 -0
- package/payload/platform/plugins/memory/skills/conversation-archive-mcp/SKILL.md +170 -0
- package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +113 -0
- package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +331 -0
- package/payload/platform/plugins/memory/skills/emerge/SKILL.md +87 -0
- package/payload/platform/plugins/notion-import/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/notion-import/PLUGIN.md +27 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/SKILL.md +114 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/references/attachments.md +55 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/references/databases.md +83 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/references/page-tree.md +61 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/references/workspace-export.md +41 -0
- package/payload/platform/plugins/obsidian-import/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/obsidian-import/PLUGIN.md +39 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/SKILL.md +92 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/attachments.md +80 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/daily-notes.md +31 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/vault-structure.md +46 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/wikilinks.md +70 -0
- package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/outlook/PLUGIN.md +74 -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 +183 -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/.claude-plugin/plugin.json +8 -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/prompt-optimiser/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/prompt-optimiser/PLUGIN.md +14 -0
- package/payload/platform/plugins/prompt-optimiser/skills/prompt-optimiser/SKILL.md +318 -0
- package/payload/platform/plugins/replicate/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/replicate/PLUGIN.md +49 -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 +125 -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 +201 -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/.claude-plugin/plugin.json +8 -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 +73 -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/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/scheduling/PLUGIN.md +153 -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 +440 -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-graph-ingest.d.ts +80 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.js +410 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.d.ts +63 -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 +471 -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 +24 -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 +572 -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-archive-ics.d.ts +20 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-archive-ics.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-archive-ics.js +63 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-archive-ics.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 +77 -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/scheduling/skills/briefing/SKILL.md +86 -0
- package/payload/platform/plugins/scheduling/skills/daily-prep/SKILL.md +61 -0
- package/payload/platform/plugins/slides/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/slides/LICENSE +21 -0
- package/payload/platform/plugins/slides/PLUGIN.md +18 -0
- package/payload/platform/plugins/slides/PROVENANCE.md +40 -0
- package/payload/platform/plugins/slides/commands/add-slide.md +29 -0
- package/payload/platform/plugins/slides/commands/slides-claus.md +39 -0
- package/payload/platform/plugins/slides/commands/slides-new-component.md +39 -0
- package/payload/platform/plugins/slides/commands/slides-outline.md +43 -0
- package/payload/platform/plugins/slides/commands/slides-review.md +52 -0
- package/payload/platform/plugins/slides/commands/slides-theme.md +64 -0
- package/payload/platform/plugins/slides/commands/slides.md +59 -0
- package/payload/platform/plugins/slides/skills/deck-system/REFERENCE.md +581 -0
- package/payload/platform/plugins/slides/skills/deck-system/SKILL.md +607 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-board.md +426 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-claus.md +185 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-mbb.md +450 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-product-launch.md +579 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-sales.md +464 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-sequoia.md +489 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING.md +273 -0
- package/payload/platform/plugins/slides/skills/deck-system/deck-craft.html +1371 -0
- package/payload/platform/plugins/slides/skills/deck-system/deck-solid.html +1667 -0
- package/payload/platform/plugins/slides/skills/deck-system/deck.html +1359 -0
- package/payload/platform/plugins/substack-import/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/substack-import/PLUGIN.md +34 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/SKILL.md +183 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/archive-shape.md +68 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/attachments.md +72 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/engagement.md +61 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/posts.md +80 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/subscribers.md +74 -0
- package/payload/platform/plugins/telegram/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/telegram/PLUGIN.md +53 -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/url-get/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/url-get/PLUGIN.md +91 -0
- package/payload/platform/plugins/url-get/mcp/dist/index.d.ts +9 -0
- package/payload/platform/plugins/url-get/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/url-get/mcp/dist/index.js +53 -0
- package/payload/platform/plugins/url-get/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/url-get/mcp/dist/lib/summarise.d.ts +8 -0
- package/payload/platform/plugins/url-get/mcp/dist/lib/summarise.d.ts.map +1 -0
- package/payload/platform/plugins/url-get/mcp/dist/lib/summarise.js +83 -0
- package/payload/platform/plugins/url-get/mcp/dist/lib/summarise.js.map +1 -0
- package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.d.ts +21 -0
- package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.d.ts.map +1 -0
- package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.js +133 -0
- package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.js.map +1 -0
- package/payload/platform/plugins/url-get/mcp/package.json +22 -0
- package/payload/platform/plugins/whatsapp/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/whatsapp/PLUGIN.md +108 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/boot-without-env.test.d.ts +2 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/boot-without-env.test.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/boot-without-env.test.js +58 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/boot-without-env.test.js.map +1 -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 +489 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/package.json +21 -0
- package/payload/platform/plugins/whatsapp/mcp/vitest.config.ts +9 -0
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +256 -0
- package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +82 -0
- package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +93 -0
- package/payload/platform/plugins/work/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/work/.mcp.json +13 -0
- package/payload/platform/plugins/work/PLUGIN.md +124 -0
- package/payload/platform/plugins/work/mcp/dist/cli/project-create-cli.d.ts +2 -0
- package/payload/platform/plugins/work/mcp/dist/cli/project-create-cli.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/cli/project-create-cli.js +78 -0
- package/payload/platform/plugins/work/mcp/dist/cli/project-create-cli.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/work/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/index.js +547 -0
- package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.d.ts +7 -0
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.js +24 -0
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/work/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/lib/neo4j.js +40 -0
- package/payload/platform/plugins/work/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-complete.d.ts +17 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-complete.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-complete.js +76 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-complete.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.d.ts +29 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.js +235 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-get.d.ts +40 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-get.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-get.js +125 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-get.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-list.d.ts +26 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-list.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-list.js +81 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-list.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-update.d.ts +19 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-update.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-update.js +102 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-update.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.d.ts +20 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.js +37 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-name.d.ts +12 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-name.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-name.js +28 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-name.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-complete.d.ts +16 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-complete.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-complete.js +33 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-complete.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts +63 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js +141 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-get.d.ts +19 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-get.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-get.js +51 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-get.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-list.d.ts +18 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-list.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-list.js +66 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-list.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-ready.d.ts +21 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-ready.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-ready.js +54 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-ready.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-relate.d.ts +12 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-relate.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-relate.js +59 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-relate.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-update.d.ts +32 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-update.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-update.js +112 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-update.js.map +1 -0
- package/payload/platform/plugins/work/mcp/package.json +20 -0
- package/payload/platform/plugins/work/skills/execute-task/SKILL.md +101 -0
- package/payload/platform/plugins/workflows/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/workflows/.mcp.json +12 -0
- package/payload/platform/plugins/workflows/PLUGIN.md +160 -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 +440 -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 +66 -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 +187 -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 +25 -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 +56 -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 +33 -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 +130 -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 +51 -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 +382 -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 +24 -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 +48 -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 +150 -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 +53 -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 +93 -0
- package/payload/platform/plugins/x-import/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/x-import/PLUGIN.md +33 -0
- package/payload/platform/plugins/x-import/references/archive-shape.md +124 -0
- package/payload/platform/plugins/x-import/references/transcript-shape.md +121 -0
- package/payload/platform/plugins/x-import/skills/x-import/SKILL.md +123 -0
- package/payload/platform/scripts/__tests__/agents-md-bootstrap.test.sh +111 -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/__tests__/resume-tunnel.test.sh +251 -0
- package/payload/platform/scripts/admin-conversation-recover.mjs +386 -0
- package/payload/platform/scripts/admin-persist-audit.ts +211 -0
- package/payload/platform/scripts/check-architecture-skill-no-drift.mjs +114 -0
- package/payload/platform/scripts/check-canonical-tool-names.mjs +110 -0
- package/payload/platform/scripts/check-cypher-int-params.mjs +277 -0
- package/payload/platform/scripts/check-no-conversation-id-leaks.mjs +165 -0
- package/payload/platform/scripts/check-no-direct-clipboard.mjs +54 -0
- package/payload/platform/scripts/check-no-esm-require.mjs +138 -0
- package/payload/platform/scripts/check-no-legacy-spawn-route.mjs +37 -0
- package/payload/platform/scripts/check-no-task-id-leaks.mjs +110 -0
- package/payload/platform/scripts/check-plugin-references.mjs +256 -0
- package/payload/platform/scripts/check-plugin-tools-mcp-consistency.mjs +136 -0
- package/payload/platform/scripts/check-roles-doc-completeness.mjs +96 -0
- package/payload/platform/scripts/check-skill-frontmatter.mjs +139 -0
- package/payload/platform/scripts/check-skill-load-coverage.mjs +100 -0
- package/payload/platform/scripts/check-specialist-tool-surface.mjs +323 -0
- package/payload/platform/scripts/conversation-id-allowlist.txt +142 -0
- package/payload/platform/scripts/dedupe-userprofile-ghosts.sh +388 -0
- package/payload/platform/scripts/generate-canonical-tool-names.mjs +63 -0
- package/payload/platform/scripts/generate-entitlement-fixture.mjs +152 -0
- package/payload/platform/scripts/identity-forbidden-token-check.mjs +88 -0
- package/payload/platform/scripts/installer-device-verify.sh +249 -0
- package/payload/platform/scripts/lib/agents-md-bootstrap.sh +49 -0
- package/payload/platform/scripts/lib/canonical-tool-names.mjs +88 -0
- package/payload/platform/scripts/lib/read-brand-json.sh +69 -0
- package/payload/platform/scripts/lib/resolve-account-dir.sh +184 -0
- package/payload/platform/scripts/log-adherence-check.sh +125 -0
- package/payload/platform/scripts/logs-read-jsonl.test.sh +307 -0
- package/payload/platform/scripts/logs-read.sh +980 -0
- package/payload/platform/scripts/logs-read.test.sh +159 -0
- package/payload/platform/scripts/redact-install-logs.sh +87 -0
- package/payload/platform/scripts/resume-tunnel.sh +148 -0
- package/payload/platform/scripts/rss-sampler.sh +58 -0
- package/payload/platform/scripts/seed-neo4j.sh +193 -0
- package/payload/platform/scripts/setup-account.sh +373 -0
- package/payload/platform/scripts/smoke-boot-services.sh +413 -0
- package/payload/platform/scripts/test-laptop-vnc-boot.sh +88 -0
- package/payload/platform/scripts/verify-skill-tool-surface.sh +389 -0
- package/payload/platform/scripts/vnc.sh +476 -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/admin-identity-audit.d.ts +39 -0
- package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.js +133 -0
- package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts +23 -0
- package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js +29 -0
- package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/auth-snapshot.d.ts +4 -0
- package/payload/platform/services/claude-session-manager/dist/auth-snapshot.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/auth-snapshot.js +50 -0
- package/payload/platform/services/claude-session-manager/dist/auth-snapshot.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/brand-foreign-filter.d.ts +30 -0
- package/payload/platform/services/claude-session-manager/dist/brand-foreign-filter.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/brand-foreign-filter.js +86 -0
- package/payload/platform/services/claude-session-manager/dist/brand-foreign-filter.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.d.ts +7 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +205 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.js +23 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/config.d.ts +55 -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 +234 -0
- package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +150 -0
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +881 -0
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/hooks-discovery.d.ts +7 -0
- package/payload/platform/services/claude-session-manager/dist/hooks-discovery.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/hooks-discovery.js +90 -0
- package/payload/platform/services/claude-session-manager/dist/hooks-discovery.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +40 -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 +1761 -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 +675 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.d.ts +43 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.js +155 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-observer.d.ts +30 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-observer.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-observer.js +204 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-observer.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +41 -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 +105 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts +81 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js +499 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/mcp-tools-probe.d.ts +38 -0
- package/payload/platform/services/claude-session-manager/dist/mcp-tools-probe.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/mcp-tools-probe.js +131 -0
- package/payload/platform/services/claude-session-manager/dist/mcp-tools-probe.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/permission-mode-tail.d.ts +20 -0
- package/payload/platform/services/claude-session-manager/dist/permission-mode-tail.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/permission-mode-tail.js +71 -0
- package/payload/platform/services/claude-session-manager/dist/permission-mode-tail.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts +40 -0
- package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-census.js +125 -0
- package/payload/platform/services/claude-session-manager/dist/pty-census.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +641 -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 +2077 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts +40 -0
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +123 -0
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/public-tool-audit.d.ts +33 -0
- package/payload/platform/services/claude-session-manager/dist/public-tool-audit.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/public-tool-audit.js +149 -0
- package/payload/platform/services/claude-session-manager/dist/public-tool-audit.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +157 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +556 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-life.d.ts +4 -0
- package/payload/platform/services/claude-session-manager/dist/rc-life.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-life.js +16 -0
- package/payload/platform/services/claude-session-manager/dist/rc-life.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.d.ts +80 -0
- package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.js +306 -0
- package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/reaper.d.ts +28 -0
- package/payload/platform/services/claude-session-manager/dist/reaper.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/reaper.js +118 -0
- package/payload/platform/services/claude-session-manager/dist/reaper.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +91 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +232 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/session-store.d.ts +49 -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 +52 -0
- package/payload/platform/services/claude-session-manager/dist/session-store.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/spawn-rate-limiter.d.ts +28 -0
- package/payload/platform/services/claude-session-manager/dist/spawn-rate-limiter.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/spawn-rate-limiter.js +77 -0
- package/payload/platform/services/claude-session-manager/dist/spawn-rate-limiter.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts +60 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.js +203 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +151 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +445 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts +164 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.js +296 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-spawn.d.ts +83 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-spawn.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-spawn.js +345 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-spawn.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/tool-surface.d.ts +69 -0
- package/payload/platform/services/claude-session-manager/dist/tool-surface.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/tool-surface.js +373 -0
- package/payload/platform/services/claude-session-manager/dist/tool-surface.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/types.d.ts +41 -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 +79 -0
- package/payload/platform/services/claude-session-manager/dist/url-capture.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/user-title-store.d.ts +39 -0
- package/payload/platform/services/claude-session-manager/dist/user-title-store.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/user-title-store.js +138 -0
- package/payload/platform/services/claude-session-manager/dist/user-title-store.js.map +1 -0
- package/payload/platform/services/claude-session-manager/package.json +22 -0
- package/payload/platform/services/claude-session-manager/scripts/stash-cleanup.sh +410 -0
- package/payload/platform/services/claude-session-manager/vitest.config.ts +14 -0
- package/payload/platform/templates/account.json +12 -0
- package/payload/platform/templates/agents/admin/AGENTS.md +23 -0
- package/payload/platform/templates/agents/admin/IDENTITY.md +69 -0
- package/payload/platform/templates/agents/admin/LEARNINGS.md +3 -0
- package/payload/platform/templates/agents/admin/SOUL.md +1 -0
- package/payload/platform/templates/agents/public/IDENTITY.md +19 -0
- package/payload/platform/templates/specialists/.claude-plugin/plugin.json +4 -0
- package/payload/platform/templates/specialists/agents/archive-ingest-operator.md +45 -0
- package/payload/platform/templates/specialists/agents/citation-auditor.md +37 -0
- package/payload/platform/templates/specialists/agents/coding-assistant.md +73 -0
- package/payload/platform/templates/specialists/agents/compiled-truth-rewriter.md +42 -0
- package/payload/platform/templates/specialists/agents/content-producer.md +65 -0
- package/payload/platform/templates/specialists/agents/database-operator.md +29 -0
- package/payload/platform/templates/specialists/agents/librarian.md +57 -0
- package/payload/platform/templates/specialists/agents/personal-assistant.md +63 -0
- package/payload/platform/templates/specialists/agents/project-manager.md +63 -0
- package/payload/platform/templates/specialists/agents/public-session-reviewer.md +40 -0
- package/payload/platform/templates/specialists/agents/research-assistant.md +73 -0
- package/payload/platform/templates/specialists/agents/typed-edge-classifier.md +37 -0
- package/payload/platform/templates/systemd/edge.service.template +38 -0
- package/payload/platform/tsconfig.base.json +18 -0
- package/payload/premium-plugins/.claude-plugin/marketplace.json +23 -0
- package/payload/premium-plugins/teaching/.claude-plugin/plugin.json +8 -0
- package/payload/premium-plugins/teaching/PLUGIN.md +58 -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/venture-studio/.claude-plugin/plugin.json +8 -0
- package/payload/premium-plugins/venture-studio/PLUGIN.md +119 -0
- package/payload/premium-plugins/venture-studio/bin/scaffold.sh +116 -0
- package/payload/premium-plugins/venture-studio/skills/brand-pack/SKILL.md +256 -0
- package/payload/premium-plugins/venture-studio/skills/brand-pack/references/color-psychology.md +118 -0
- package/payload/premium-plugins/venture-studio/skills/investor-data-room/SKILL.md +376 -0
- package/payload/premium-plugins/venture-studio/skills/investor-data-room/references/business-plan-template.md +64 -0
- package/payload/premium-plugins/venture-studio/skills/investor-data-room/references/compliance-research-checklist.md +53 -0
- package/payload/premium-plugins/venture-studio/skills/investor-data-room/references/data-room-structure.md +88 -0
- package/payload/premium-plugins/venture-studio/skills/investor-data-room/references/deck-blueprint-template.md +39 -0
- package/payload/premium-plugins/venture-studio/skills/investor-data-room/references/design-tokens-application.md +79 -0
- package/payload/premium-plugins/venture-studio/skills/investor-data-room/references/html-pdf-pipeline.md +236 -0
- package/payload/premium-plugins/venture-studio/skills/investor-data-room/references/internal-workings-scrub.md +33 -0
- package/payload/premium-plugins/venture-studio/skills/investor-data-room/references/termsheet-template.md +88 -0
- package/payload/premium-plugins/venture-studio/skills/investor-data-room/templates/prospectus/index.html +1565 -0
- package/payload/premium-plugins/venture-studio/skills/investor-data-room/templates/prospectus/render-pdf.mjs +91 -0
- package/payload/premium-plugins/venture-studio/skills/investor-data-room/templates/prospectus/term_sheet.html +715 -0
- package/payload/premium-plugins/venture-studio/skills/office-hours/SKILL.md +587 -0
- package/payload/premium-plugins/venture-studio/skills/prototype-host/SKILL.md +179 -0
- package/payload/premium-plugins/venture-studio/skills/prototype-host/references/cloudflared-ingress-edit.md +81 -0
- package/payload/premium-plugins/venture-studio/skills/prototype-host/references/scaffold-frameworks.md +60 -0
- package/payload/premium-plugins/venture-studio/skills/prototype-host/references/systemd-user-service.md +104 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/SKILL.md +336 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/aarrr-metrics.md +275 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/assumption-testing.md +93 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/boolean-search.md +308 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/build-measure-learn.md +262 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/business-model-canvas.md +171 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/commitment-signals.md +246 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/design-thinking.md +183 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/earlyvangelist.md +190 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/first-principles.md +58 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/fishbone.md +114 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/five-whys.md +43 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/ice-scoring.md +237 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/innovation-accounting.md +290 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/jtbd.md +105 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/landing-page.md +361 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/market-type.md +167 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/mom-test.md +193 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/mvp-types.md +200 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/og-images.md +239 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/pareto.md +103 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/persona-development.md +291 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/pivot-types.md +225 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/positioning-statement.md +179 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/prd.md +363 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/pre-mortem.md +74 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/problem-validation.md +253 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/product-market-fit.md +256 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/research-synthesis.md +276 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/three-engines-of-growth.md +248 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/validation-tests.md +89 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/value-proposition-canvas.md +121 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/win-loss-analysis.md +242 -0
- package/payload/premium-plugins/venture-studio/skills/zero-to-prototype/references/workflow-mapping.md +271 -0
- package/payload/premium-plugins/writer-craft/.claude-plugin/plugin.json +17 -0
- package/payload/premium-plugins/writer-craft/PLUGIN.md +134 -0
- package/payload/premium-plugins/writer-craft/agents/writer-craft--manuscript-reviewer.md +96 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/dist/index.d.ts +51 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/dist/index.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/dist/index.js +196 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/dist/index.js.map +1 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/package.json +7 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.d.ts +3 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.js +58 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/index.d.ts +3 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/index.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js +362 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/neo4j.d.ts +14 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/neo4j.js +47 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.d.ts +86 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.js +110 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts +82 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js +560 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.d.ts +19 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.js +123 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.d.ts +32 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.js +82 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts +47 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js +160 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.d.ts +17 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.js +98 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/package-lock.json +1327 -0
- package/payload/premium-plugins/writer-craft/mcp/package.json +19 -0
- package/payload/premium-plugins/writer-craft/mcp/scripts/smoke.mjs +351 -0
- package/payload/premium-plugins/writer-craft/mcp/src/index.ts +409 -0
- package/payload/premium-plugins/writer-craft/mcp/src/lib/neo4j.ts +56 -0
- package/payload/premium-plugins/writer-craft/mcp/src/lib/voice-corpus.ts +115 -0
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-distil-profile.ts +767 -0
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-ingest-session-text.ts +152 -0
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-record-feedback.ts +128 -0
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-retrieve-conditioning.ts +234 -0
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-tag-content.ts +139 -0
- package/payload/premium-plugins/writer-craft/mcp/tsconfig.json +8 -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/premium-plugins/writer-craft/skills/voice-mirror/SKILL.md +228 -0
- package/payload/server/adminuser-self-heal-YC47O34W.js +46 -0
- package/payload/server/chunk-76HRO7NX.js +5684 -0
- package/payload/server/chunk-HYQNUVGO.js +38 -0
- package/payload/server/maxy-edge.js +814 -0
- package/payload/server/package.json +11 -0
- package/payload/server/public/assets/AdminShell-T-YknnBn.js +1 -0
- package/payload/server/public/assets/AdminShell-qc_xy7Az.css +1 -0
- package/payload/server/public/assets/Checkbox-DmDxpqVv.js +1 -0
- package/payload/server/public/assets/_baseFor-Cs8Y-rGh.js +1 -0
- package/payload/server/public/assets/admin-COUV-jgt.js +1 -0
- package/payload/server/public/assets/admin-CWMpccrR.css +1 -0
- package/payload/server/public/assets/arc-B2CweJq3.js +1 -0
- package/payload/server/public/assets/architecture-YZFGNWBL-Dnn6Hc65.js +1 -0
- package/payload/server/public/assets/architectureDiagram-Q4EWVU46-DP2o-MFV.js +36 -0
- package/payload/server/public/assets/array-iHZP4KWJ.js +1 -0
- package/payload/server/public/assets/blockDiagram-DXYQGD6D-DO4mcYDJ.js +132 -0
- package/payload/server/public/assets/c4Diagram-AHTNJAMY-Sy1giHbj.js +10 -0
- package/payload/server/public/assets/channel-CEpR_0rE.js +1 -0
- package/payload/server/public/assets/chunk-2KRD3SAO-CKsCYCsN.js +1 -0
- package/payload/server/public/assets/chunk-336JU56O-C0-P-aUF.js +2 -0
- package/payload/server/public/assets/chunk-426QAEUC-DFjEt3Zb.js +1 -0
- package/payload/server/public/assets/chunk-4BX2VUAB-B8bqAmBa.js +1 -0
- package/payload/server/public/assets/chunk-4TB4RGXK-D1k0VSlW.js +206 -0
- package/payload/server/public/assets/chunk-55IACEB6-B-p_QNqz.js +1 -0
- package/payload/server/public/assets/chunk-5FUZZQ4R-D6U6tV_j.js +62 -0
- package/payload/server/public/assets/chunk-5PVQY5BW-CYK76xfs.js +2 -0
- package/payload/server/public/assets/chunk-67CJDMHE-BC9js-lf.js +1 -0
- package/payload/server/public/assets/chunk-7N4EOEYR-4j2OqKkv.js +1 -0
- package/payload/server/public/assets/chunk-AA7GKIK3-Coen-fXN.js +1 -0
- package/payload/server/public/assets/chunk-BSJP7CBP-CAiOBvec.js +1 -0
- package/payload/server/public/assets/chunk-CIAEETIT-AJzzpZVb.js +1 -0
- package/payload/server/public/assets/chunk-DD-I1_y5.js +1 -0
- package/payload/server/public/assets/chunk-EDXVE4YY-BL4BKozX.js +1 -0
- package/payload/server/public/assets/chunk-ENJZ2VHE-mhAFG8UD.js +10 -0
- package/payload/server/public/assets/chunk-FMBD7UC4-H231gZA_.js +15 -0
- package/payload/server/public/assets/chunk-FOC6F5B3-Cl3ZZjYG.js +1 -0
- package/payload/server/public/assets/chunk-ICPOFSXX-DOEzvzJa.js +122 -0
- package/payload/server/public/assets/chunk-K5T4RW27-C_ipbUDD.js +94 -0
- package/payload/server/public/assets/chunk-KGLVRYIC-CTsDNSCU.js +1 -0
- package/payload/server/public/assets/chunk-LIHQZDEY-DvSXhkGf.js +1 -0
- package/payload/server/public/assets/chunk-ORNJ4GCN-p574NOI7.js +1 -0
- package/payload/server/public/assets/chunk-OYMX7WX6-BlEgFM6U.js +231 -0
- package/payload/server/public/assets/chunk-QZHKN3VN-DpF06ZZQ.js +1 -0
- package/payload/server/public/assets/chunk-U2HBQHQK-B2bDK0jv.js +70 -0
- package/payload/server/public/assets/chunk-X2U36JSP-D69BxKFw.js +1 -0
- package/payload/server/public/assets/chunk-XPW4576I-Dm-PcyUi.js +32 -0
- package/payload/server/public/assets/chunk-YZCP3GAM-Be8RnXgx.js +1 -0
- package/payload/server/public/assets/chunk-ZZ45TVLE-Ck8PCTa4.js +1 -0
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-CYbXvKLI.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DEyHzRhq.js +1 -0
- package/payload/server/public/assets/clone-y8gexbBy.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-CmkW2Eaj.js +1 -0
- package/payload/server/public/assets/cytoscape.esm-BR2GOQ8_.js +321 -0
- package/payload/server/public/assets/dagre-Dqp-ns8F.js +1 -0
- package/payload/server/public/assets/dagre-KV5264BT-ZgWWXPLc.js +4 -0
- package/payload/server/public/assets/data-gy6QH9c1.js +1 -0
- package/payload/server/public/assets/defaultLocale-B9aLeOTg.js +1 -0
- package/payload/server/public/assets/diagram-5BDNPKRD-CTX5-ScM.js +10 -0
- package/payload/server/public/assets/diagram-G4DWMVQ6-BovIsO6H.js +24 -0
- package/payload/server/public/assets/diagram-MMDJMWI5-DcETsQy-.js +43 -0
- package/payload/server/public/assets/diagram-TYMM5635-yyq6peoZ.js +24 -0
- package/payload/server/public/assets/dist-DB-VPj_8.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-CiNToftB.js +85 -0
- package/payload/server/public/assets/flatten-BtFI066E.js +1 -0
- package/payload/server/public/assets/flowDiagram-DWJPFMVM-Xnl3SpIM.js +162 -0
- package/payload/server/public/assets/ganttDiagram-T4ZO3ILL-C1iyWe0f.js +292 -0
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-CNs-LD5i.js +1 -0
- package/payload/server/public/assets/gitGraphDiagram-UUTBAWPF-D97pbMQb.js +106 -0
- package/payload/server/public/assets/graph-labels-cZu4pK16.js +1 -0
- package/payload/server/public/assets/graph-qz5tFKqU.js +51 -0
- package/payload/server/public/assets/graphlib-Lq8ijgON.js +1 -0
- package/payload/server/public/assets/info-OMHHGYJF-DsTNigSS.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-C_OarRTA.js +2 -0
- package/payload/server/public/assets/init-BNFRgqHM.js +1 -0
- package/payload/server/public/assets/inter-cyrillic-400-normal-HOLc17fK.woff +0 -0
- package/payload/server/public/assets/inter-cyrillic-400-normal-obahsSVq.woff2 +0 -0
- package/payload/server/public/assets/inter-cyrillic-500-normal-BasfLYem.woff2 +0 -0
- package/payload/server/public/assets/inter-cyrillic-500-normal-CxZf_p3X.woff +0 -0
- package/payload/server/public/assets/inter-cyrillic-ext-400-normal-BQZuk6qB.woff2 +0 -0
- package/payload/server/public/assets/inter-cyrillic-ext-400-normal-DQukG94-.woff +0 -0
- package/payload/server/public/assets/inter-cyrillic-ext-500-normal-B0yAr1jD.woff2 +0 -0
- package/payload/server/public/assets/inter-cyrillic-ext-500-normal-BmqWE9Dz.woff +0 -0
- package/payload/server/public/assets/inter-greek-400-normal-B4URO6DV.woff2 +0 -0
- package/payload/server/public/assets/inter-greek-400-normal-q2sYcFCs.woff +0 -0
- package/payload/server/public/assets/inter-greek-500-normal-BIZE56-Y.woff2 +0 -0
- package/payload/server/public/assets/inter-greek-500-normal-Xzm54t5V.woff +0 -0
- package/payload/server/public/assets/inter-greek-ext-400-normal-DGGRlc-M.woff2 +0 -0
- package/payload/server/public/assets/inter-greek-ext-400-normal-KugGGMne.woff +0 -0
- package/payload/server/public/assets/inter-greek-ext-500-normal-2j5mBUwD.woff +0 -0
- package/payload/server/public/assets/inter-greek-ext-500-normal-C4iEst2y.woff2 +0 -0
- package/payload/server/public/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
- package/payload/server/public/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
- package/payload/server/public/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
- package/payload/server/public/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
- package/payload/server/public/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
- package/payload/server/public/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
- package/payload/server/public/assets/inter-latin-ext-500-normal-BxGbmqWO.woff +0 -0
- package/payload/server/public/assets/inter-latin-ext-500-normal-CV4jyFjo.woff2 +0 -0
- package/payload/server/public/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
- package/payload/server/public/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
- package/payload/server/public/assets/inter-vietnamese-500-normal-DOriooB6.woff2 +0 -0
- package/payload/server/public/assets/inter-vietnamese-500-normal-mJboJaSs.woff +0 -0
- package/payload/server/public/assets/isEmpty-D6QovjYR.js +1 -0
- package/payload/server/public/assets/ishikawaDiagram-UXIWVN3A-B8XBdjJn.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-CZYbiOaQ.js +139 -0
- package/payload/server/public/assets/kanban-definition-6JOO6SKY-B1PybFoh.js +89 -0
- package/payload/server/public/assets/katex-B-EfS3nw.js +257 -0
- package/payload/server/public/assets/line-D-tw3hHp.js +1 -0
- package/payload/server/public/assets/linear-BHhXD3cd.js +1 -0
- package/payload/server/public/assets/mermaid-parser.core-C9RAnysF.js +4 -0
- package/payload/server/public/assets/mermaid.core-B532LT1r.js +11 -0
- package/payload/server/public/assets/mindmap-definition-QFDTVHPH-DGlgeeTV.js +96 -0
- package/payload/server/public/assets/newsreader-latin-300-normal-AOSWdb_s.woff +0 -0
- package/payload/server/public/assets/newsreader-latin-300-normal-FGBQ0wlI.woff2 +0 -0
- package/payload/server/public/assets/newsreader-latin-400-normal-BFBkh4jY.woff2 +0 -0
- package/payload/server/public/assets/newsreader-latin-400-normal-gRTjlS2D.woff +0 -0
- package/payload/server/public/assets/newsreader-latin-500-normal-B66TYsaK.woff2 +0 -0
- package/payload/server/public/assets/newsreader-latin-500-normal-DFwuUcdu.woff +0 -0
- package/payload/server/public/assets/newsreader-latin-ext-300-normal-CFtw49Zd.woff +0 -0
- package/payload/server/public/assets/newsreader-latin-ext-300-normal-DRMzurxT.woff2 +0 -0
- package/payload/server/public/assets/newsreader-latin-ext-400-normal-DYA1XoQK.woff +0 -0
- package/payload/server/public/assets/newsreader-latin-ext-400-normal-svq1FPys.woff2 +0 -0
- package/payload/server/public/assets/newsreader-latin-ext-500-normal-BNHmvKvI.woff2 +0 -0
- package/payload/server/public/assets/newsreader-latin-ext-500-normal-CZruMFou.woff +0 -0
- package/payload/server/public/assets/newsreader-vietnamese-300-normal-CsrIkm-V.woff +0 -0
- package/payload/server/public/assets/newsreader-vietnamese-300-normal-D3VHEe81.woff2 +0 -0
- package/payload/server/public/assets/newsreader-vietnamese-400-normal-BekUZro8.woff +0 -0
- package/payload/server/public/assets/newsreader-vietnamese-400-normal-DdKr49mV.woff2 +0 -0
- package/payload/server/public/assets/newsreader-vietnamese-500-normal-BEAbKU8A.woff +0 -0
- package/payload/server/public/assets/newsreader-vietnamese-500-normal-CL6a8tp2.woff2 +0 -0
- package/payload/server/public/assets/ordinal-Bl-aM5b9.js +1 -0
- package/payload/server/public/assets/packet-4T2RLAQJ-DGES22b-.js +1 -0
- package/payload/server/public/assets/path-DmWWdwp7.js +1 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-ChKeDbzt.js +1 -0
- package/payload/server/public/assets/pieDiagram-DEJITSTG-DV9FIWko.js +30 -0
- package/payload/server/public/assets/public-Bu2_Xi0a.js +35 -0
- package/payload/server/public/assets/public-vnj7OhQj.css +1 -0
- package/payload/server/public/assets/quadrantDiagram-34T5L4WZ-Betwya4l.js +7 -0
- package/payload/server/public/assets/radar-PYXPWWZC-FGG5Fs7N.js +1 -0
- package/payload/server/public/assets/reduce-BD4xUd2c.js +1 -0
- package/payload/server/public/assets/requirementDiagram-MS252O5E-Cq3vODdg.js +84 -0
- package/payload/server/public/assets/rough.esm-Ci7Kjt46.js +1 -0
- package/payload/server/public/assets/sankeyDiagram-XADWPNL6-x8krXWcS.js +10 -0
- package/payload/server/public/assets/sequenceDiagram-FGHM5R23-i-_uH-Yl.js +157 -0
- package/payload/server/public/assets/src-C1jfwBq0.js +1 -0
- package/payload/server/public/assets/stateDiagram-FHFEXIEX-il4KqSgI.js +1 -0
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B6zNJ6Tv.js +1 -0
- package/payload/server/public/assets/timeline-definition-GMOUNBTQ-DATdZkA5.js +120 -0
- package/payload/server/public/assets/treeView-SZITEDCU-VAQQdbtf.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-DKchO3zI.js +1 -0
- package/payload/server/public/assets/useSelectionMode-A5KItZ2T.js +13 -0
- package/payload/server/public/assets/useSelectionMode-C-Ojh7W9.css +1 -0
- package/payload/server/public/assets/vennDiagram-DHZGUBPP-BJh9tJTt.js +34 -0
- package/payload/server/public/assets/wardley-RL74JXVD-CBGtx0bS.js +1 -0
- package/payload/server/public/assets/wardleyDiagram-NUSXRM2D-EMN1Hdfg.js +20 -0
- package/payload/server/public/assets/xychartDiagram-5P7HB3ND-DbUWXa7T.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/consent.css +97 -0
- package/payload/server/public/consent.js +259 -0
- package/payload/server/public/data.html +19 -0
- package/payload/server/public/favicon.ico +0 -0
- package/payload/server/public/graph.html +20 -0
- package/payload/server/public/index.html +21 -0
- package/payload/server/public/privacy.html +129 -0
- package/payload/server/public/public.html +19 -0
- package/payload/server/public/robots.txt +5 -0
- package/payload/server/public/v.js +244 -0
- package/payload/server/public/vnc-popout.html +63 -0
- package/payload/server/server-init.cjs +197 -0
- package/payload/server/server.js +21128 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4048 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync, cpSync, readFileSync, rmSync, readdirSync, appendFileSync, openSync, closeSync, chmodSync, statSync, realpathSync } from "node:fs";
|
|
4
|
+
import { registerSpecialistAgentsAt, SpecialistSymlinkCollision } from "./specialist-registration.js";
|
|
5
|
+
import { seedBypassPermissionsSettings, assertBypassPermissionsSeed } from "./permissions-seed.js";
|
|
6
|
+
import { resolve, join, dirname } from "node:path";
|
|
7
|
+
import { randomBytes } from "node:crypto";
|
|
8
|
+
import { resolveInstallPortFromFs, buildMaxyUnitFile, buildClaudeSessionManagerUnitFile, buildClaudePtysSliceUnitFile, buildCloudflaredSliceUnitFile } from "./port-resolution.js";
|
|
9
|
+
import { validateTierFlag, validateEntitlementBase64, applyTierToAccountConfig, entitlementPath } from "./tier-flag.js";
|
|
10
|
+
import { parseOsRelease, isUbuntuLike as isUbuntuLikePure, parseAptCacheCandidate, decideAptResolution, } from "./apt-resolve.js";
|
|
11
|
+
import { findPeerBrandOnDefaultNeo4jPort } from "./peer-brand-detect.js";
|
|
12
|
+
import { runInitLogging } from "./init-logging.js";
|
|
13
|
+
import { requireSupportedPlatform } from "./platform-detect.js";
|
|
14
|
+
import { renderPlist } from "./launchd-plist.js";
|
|
15
|
+
import { installAllBrewPackages } from "./brew-install.js";
|
|
16
|
+
import { parseSwVers, isSupportedMacosVersion } from "./macos-version.js";
|
|
17
|
+
import { decideChromiumAction, isSnapConfinedPath } from "./snap-chromium.js";
|
|
18
|
+
import { classifyPortHolder } from "./preflight-port-classifier.js";
|
|
19
|
+
import { parsePluginList, computeInstallActions, parseExternalPlugins, } from "./lib/plugin-install.js";
|
|
20
|
+
import { findPremiumMcpDirs } from "./lib/premium-mcp-discover.js";
|
|
21
|
+
import { pickLanInterface, mergeSmbConf, formatSambaMarker, } from "./samba-provision.js";
|
|
22
|
+
import { networkInterfaces, userInfo } from "node:os";
|
|
23
|
+
const PAYLOAD_DIR = resolve(import.meta.dirname, "../payload");
|
|
24
|
+
// Brand manifest — read from payload to derive all brand-specific installation values.
|
|
25
|
+
// The bundler stamps brand.json into the payload at build time.
|
|
26
|
+
const BRAND_PATH = join(PAYLOAD_DIR, "platform", "config", "brand.json");
|
|
27
|
+
if (!existsSync(BRAND_PATH)) {
|
|
28
|
+
console.error("Setup failed: brand.json not found in payload (package may be corrupted)");
|
|
29
|
+
console.error("FATAL: brand.json not found in payload. Package may be corrupted.");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
let BRAND;
|
|
33
|
+
try {
|
|
34
|
+
BRAND = JSON.parse(readFileSync(BRAND_PATH, "utf-8"));
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
console.error(`Setup failed: failed to parse brand.json: ${err.message}`);
|
|
38
|
+
console.error(`FATAL: Failed to parse brand.json: ${err.message}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const INSTALL_DIR = resolve(process.env.HOME ?? "/root", BRAND.installDir);
|
|
42
|
+
const PERSIST_DIR = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
43
|
+
const LOG_DIR = join(PERSIST_DIR, "logs");
|
|
44
|
+
const LOG_FILE = join(LOG_DIR, `install-${new Date().toISOString().replace(/[:.]/g, "-")}.log`);
|
|
45
|
+
// every `claude plugin` shellout must read/write the per-brand
|
|
46
|
+
// CLAUDE_CONFIG_DIR that the systemd unit at port-resolution.ts:229,286 uses
|
|
47
|
+
// at runtime. Without this override, the installer writes marketplaces and
|
|
48
|
+
// enabledPlugins to $HOME/.claude/settings.json (user-default), and the
|
|
49
|
+
// PTY spawned by the session-manager sees an empty config dir — plugins
|
|
50
|
+
// the operator turned on are invisible to the agent.
|
|
51
|
+
const CLAUDE_CONFIG_DIR = join(PERSIST_DIR, ".claude");
|
|
52
|
+
function claudePluginEnv() {
|
|
53
|
+
return { ...process.env, CLAUDE_CONFIG_DIR };
|
|
54
|
+
}
|
|
55
|
+
/** Known brand hostnames in the Maxy ecosystem. Each brand ships a main unit
|
|
56
|
+
* (`<hostname>.service`) and a per-brand edge unit
|
|
57
|
+
* (`<hostname>-edge.service`). Peer-brand detection matches only these
|
|
58
|
+
* filenames — stale units,
|
|
59
|
+
* gnome-keyring disable markers, and unrelated user services are not peer
|
|
60
|
+
* evidence. When a third brand is added under `brands/`, append its hostname
|
|
61
|
+
* here AND in the matching constant in `uninstall.ts` (intentional duplication
|
|
62
|
+
* per `uninstall.ts:` "Shell helpers (duplicated from index.ts ...)" policy). */
|
|
63
|
+
const KNOWN_BRAND_HOSTNAMES = ["maxy", "maxy-code", "realagent", "realagent-code", "maxy-2", "maxy-3", "maxy-4"];
|
|
64
|
+
// The device's actual hostname — may differ from BRAND.hostname if the user customized it.
|
|
65
|
+
// Updated by installSystemDeps() after hostname setup; used for user-facing URLs.
|
|
66
|
+
let DEVICE_HOSTNAME = BRAND.hostname;
|
|
67
|
+
// absolute path to the non-snap Chromium binary chosen during
|
|
68
|
+
// installSystemDeps(). Defaults to /usr/bin/chromium so non-Linux installs
|
|
69
|
+
// (which never call ensureNonSnapChromium) and the systemd unit's
|
|
70
|
+
// PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH still see a sensible value. On Linux,
|
|
71
|
+
// installSystemDeps() always overwrites this — either /usr/bin/chromium (Pi
|
|
72
|
+
// Bookworm: real .deb) or /usr/bin/google-chrome-stable (Ubuntu Noble laptop:
|
|
73
|
+
// snap-confined chromium replaced). Read by writeChromiumBinaryPathFile()
|
|
74
|
+
// and threaded into buildMaxyUnitFile() so the chromium-binary.path config
|
|
75
|
+
// file, the systemd unit's PLAYWRIGHT env var, and vnc.sh's runtime resolver
|
|
76
|
+
// all agree on one absolute path.
|
|
77
|
+
let RESOLVED_CHROMIUM_BIN = "/usr/bin/chromium";
|
|
78
|
+
// npm flags tuned for Raspberry Pi — reduce parallelism, increase patience
|
|
79
|
+
const NPM_NET_FLAGS = [
|
|
80
|
+
"--fetch-retries=5",
|
|
81
|
+
"--fetch-retry-mintimeout=30000",
|
|
82
|
+
"--fetch-retry-maxtimeout=120000",
|
|
83
|
+
"--maxsockets=3",
|
|
84
|
+
];
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Logging — timestamped to console AND persistent log file
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
function initLogging() {
|
|
89
|
+
runInitLogging({
|
|
90
|
+
logDir: LOG_DIR,
|
|
91
|
+
logFile: LOG_FILE,
|
|
92
|
+
persistDir: PERSIST_DIR,
|
|
93
|
+
headerLines: [
|
|
94
|
+
"",
|
|
95
|
+
"=".repeat(64),
|
|
96
|
+
` ${BRAND.productName} Install Log — ${new Date().toISOString()}`,
|
|
97
|
+
` Node ${process.version} | ${process.platform} ${process.arch}`,
|
|
98
|
+
"=".repeat(64),
|
|
99
|
+
"",
|
|
100
|
+
],
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function logFile(msg) {
|
|
104
|
+
try {
|
|
105
|
+
appendFileSync(LOG_FILE, `[${new Date().toISOString()}] ${msg}\n`);
|
|
106
|
+
}
|
|
107
|
+
catch { /* fs full */ }
|
|
108
|
+
}
|
|
109
|
+
// Mirror all console output into the log file
|
|
110
|
+
const _log = console.log;
|
|
111
|
+
const _err = console.error;
|
|
112
|
+
console.log = (...args) => { _log(...args); logFile(args.map(String).join(" ")); };
|
|
113
|
+
console.error = (...args) => { _err(...args); logFile(`[ERROR] ${args.map(String).join(" ")}`); };
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Diagnostics — system state snapshot for post-mortem analysis
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
function logDiagnostics(label) {
|
|
118
|
+
logFile(`\n--- Diagnostics: ${label} ---`);
|
|
119
|
+
if (!isLinux()) {
|
|
120
|
+
logFile(" (not Linux — limited diagnostics)");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const run = (cmd, args) => {
|
|
124
|
+
const r = spawnSync(cmd, args, { encoding: "utf-8", stdio: "pipe", timeout: 10_000 });
|
|
125
|
+
return (r.stdout || r.stderr || "").trim();
|
|
126
|
+
};
|
|
127
|
+
logFile(`Disk:\n${run("df", ["-h", "/", "/tmp"])}`);
|
|
128
|
+
logFile(`Memory:\n${run("free", ["-h"])}`);
|
|
129
|
+
logFile(`Uptime: ${run("uptime", [])}`);
|
|
130
|
+
for (const host of ["registry.npmjs.org", "github.com"]) {
|
|
131
|
+
const dns = spawnSync("host", ["-W", "5", host], { encoding: "utf-8", stdio: "pipe", timeout: 10_000 });
|
|
132
|
+
logFile(`DNS ${host}: ${dns.status === 0 ? "OK" : "FAIL"} — ${(dns.stdout || dns.stderr || "").trim().split("\n")[0]}`);
|
|
133
|
+
}
|
|
134
|
+
const curl = spawnSync("curl", [
|
|
135
|
+
"-sf", "-o", "/dev/null",
|
|
136
|
+
"-w", "HTTP %{http_code} in %{time_total}s (dns: %{time_namelookup}s, connect: %{time_connect}s)",
|
|
137
|
+
"--connect-timeout", "10",
|
|
138
|
+
"https://registry.npmjs.org/",
|
|
139
|
+
], { encoding: "utf-8", stdio: "pipe", timeout: 15_000 });
|
|
140
|
+
logFile(`Registry HTTP: ${curl.stdout?.trim() || `FAIL — ${curl.stderr?.trim()}`}`);
|
|
141
|
+
logFile(`--- End Diagnostics ---\n`);
|
|
142
|
+
}
|
|
143
|
+
/** Append the most recent npm debug log into our install log for post-mortem. */
|
|
144
|
+
function captureNpmDebugLog() {
|
|
145
|
+
const logsDir = resolve(process.env.HOME ?? "/root", ".npm/_logs");
|
|
146
|
+
if (!existsSync(logsDir))
|
|
147
|
+
return;
|
|
148
|
+
try {
|
|
149
|
+
const files = readdirSync(logsDir).filter(f => f.endsWith("-debug-0.log")).sort().reverse();
|
|
150
|
+
if (files[0]) {
|
|
151
|
+
const content = readFileSync(join(logsDir, files[0]), "utf-8");
|
|
152
|
+
logFile(`\n--- npm debug log: ${files[0]} (last 200 lines) ---`);
|
|
153
|
+
const lines = content.split("\n");
|
|
154
|
+
logFile(lines.slice(Math.max(0, lines.length - 200)).join("\n"));
|
|
155
|
+
logFile(`--- end npm debug log ---\n`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch { /* ignore */ }
|
|
159
|
+
}
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Helpers
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
function log(step, total, message) {
|
|
164
|
+
console.log(`[${step}/${total}] ${message}`);
|
|
165
|
+
}
|
|
166
|
+
function shell(command, args, options) {
|
|
167
|
+
const cmd = options?.sudo ? "sudo" : command;
|
|
168
|
+
const cmdArgs = options?.sudo ? [command, ...args] : args;
|
|
169
|
+
const start = Date.now();
|
|
170
|
+
// Redaction: callers handling secrets pass redact: true so the
|
|
171
|
+
// wrapper records the command name only, not the secret-bearing args. The
|
|
172
|
+
// child process still receives the real args via spawnSync below; only the
|
|
173
|
+
// install log line is sanitised. The grep-able audit shape stays:
|
|
174
|
+
// > sudo neo4j-admin dbms set-initial-password [REDACTED]
|
|
175
|
+
const loggedArgs = options?.redact
|
|
176
|
+
? `${cmdArgs.slice(0, options?.sudo ? 4 : 3).join(" ")} [REDACTED]`
|
|
177
|
+
: cmdArgs.join(" ");
|
|
178
|
+
logFile(`> ${cmd} ${loggedArgs}${options?.cwd ? ` [cwd: ${options.cwd}]` : ""}`);
|
|
179
|
+
const result = spawnSync(cmd, cmdArgs, {
|
|
180
|
+
stdio: "inherit",
|
|
181
|
+
timeout: options?.timeout ?? 300_000,
|
|
182
|
+
cwd: options?.cwd,
|
|
183
|
+
env: options?.env,
|
|
184
|
+
});
|
|
185
|
+
const dur = ((Date.now() - start) / 1000).toFixed(1);
|
|
186
|
+
// bestEffort: tear-down ops on units/state that may or may not
|
|
187
|
+
// exist (stop/disable a system service we may never have started, reset-failed
|
|
188
|
+
// a freshly-created unit) log the non-zero exit but do not throw. Reserved
|
|
189
|
+
// for the "may not exist" pattern only — never use for ops that must succeed.
|
|
190
|
+
if (result.signal) {
|
|
191
|
+
logFile(` KILLED (${result.signal}) after ${dur}s`);
|
|
192
|
+
if (options?.bestEffort)
|
|
193
|
+
return;
|
|
194
|
+
throw new Error(`Command killed (${result.signal}) after ${dur}s: ${cmd} ${cmdArgs.join(" ")}`);
|
|
195
|
+
}
|
|
196
|
+
if (result.status !== 0) {
|
|
197
|
+
logFile(` ${options?.bestEffort ? "best-effort non-zero" : "FAILED"} (exit ${result.status}) after ${dur}s`);
|
|
198
|
+
if (options?.bestEffort)
|
|
199
|
+
return;
|
|
200
|
+
throw new Error(`Command failed (exit ${result.status}) after ${dur}s: ${cmd} ${cmdArgs.join(" ")}`);
|
|
201
|
+
}
|
|
202
|
+
logFile(` OK in ${dur}s`);
|
|
203
|
+
}
|
|
204
|
+
/** Wait until DNS resolves for a host. Returns true if recovered. */
|
|
205
|
+
function waitForDns(host, maxSec = 60) {
|
|
206
|
+
for (let elapsed = 0; elapsed < maxSec; elapsed += 5) {
|
|
207
|
+
const r = spawnSync("host", ["-W", "3", host], { stdio: "pipe", timeout: 5_000 });
|
|
208
|
+
if (r.status === 0) {
|
|
209
|
+
logFile(` DNS ${host}: recovered after ${elapsed}s`);
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
console.log(` DNS ${host} not resolving — waiting (${elapsed}s/${maxSec}s)...`);
|
|
213
|
+
spawnSync("sleep", ["5"]);
|
|
214
|
+
}
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
/** Retry a shell command with exponential backoff. Runs network diagnostics between attempts. */
|
|
218
|
+
function shellRetry(command, args, options, maxAttempts = 3, backoffSec = 20) {
|
|
219
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
220
|
+
try {
|
|
221
|
+
shell(command, args, options);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
226
|
+
logFile(` Attempt ${attempt}/${maxAttempts} error: ${msg}`);
|
|
227
|
+
if (attempt === maxAttempts) {
|
|
228
|
+
console.error(` All ${maxAttempts} attempts failed.`);
|
|
229
|
+
logDiagnostics(`final failure — ${command}`);
|
|
230
|
+
if (command === "npm")
|
|
231
|
+
captureNpmDebugLog();
|
|
232
|
+
throw err;
|
|
233
|
+
}
|
|
234
|
+
const wait = backoffSec * attempt;
|
|
235
|
+
console.log(` Attempt ${attempt}/${maxAttempts} failed. Retrying in ${wait}s...`);
|
|
236
|
+
// On network errors, diagnose and wait for DNS recovery before retrying
|
|
237
|
+
if (/ETIMEDOUT|EAI_AGAIN|ECONNRESET|ENOTFOUND|ENETUNREACH/.test(msg)) {
|
|
238
|
+
console.log(" Network error — running diagnostics...");
|
|
239
|
+
logDiagnostics(`network failure — attempt ${attempt}`);
|
|
240
|
+
waitForDns("registry.npmjs.org");
|
|
241
|
+
}
|
|
242
|
+
if (command === "npm")
|
|
243
|
+
captureNpmDebugLog();
|
|
244
|
+
spawnSync("sleep", [String(wait)]);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function commandExists(cmd) {
|
|
249
|
+
try {
|
|
250
|
+
execFileSync("which", [cmd], { stdio: "pipe" });
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function nodeVersion() {
|
|
258
|
+
try {
|
|
259
|
+
const v = execFileSync("node", ["-v"], { encoding: "utf-8" }).trim();
|
|
260
|
+
return parseInt(v.replace("v", "").split(".")[0], 10);
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
return 0;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function isLinux() {
|
|
267
|
+
return process.platform === "linux";
|
|
268
|
+
}
|
|
269
|
+
function isArm64() {
|
|
270
|
+
return process.arch === "arm64";
|
|
271
|
+
}
|
|
272
|
+
/** Check whether non-interactive sudo is available (passwordless or cached credentials). */
|
|
273
|
+
function canSudo() {
|
|
274
|
+
const result = spawnSync("sudo", ["-n", "true"], { stdio: "pipe", timeout: 5_000 });
|
|
275
|
+
return result.status === 0;
|
|
276
|
+
}
|
|
277
|
+
// verified-not-asserted apt-dep reconciliation.
|
|
278
|
+
// resolve package-name aliases before probing dpkg, so the
|
|
279
|
+
// post-install check no longer false-negatives when apt resolves a virtual
|
|
280
|
+
// name (e.g. Noble's `chromium` → `chromium-browser`).
|
|
281
|
+
// UBUNTU_ALIASES, parseOsRelease, isUbuntuLike, and decideAptResolution moved
|
|
282
|
+
// to./apt-resolve.ts. The pure logic lives there so a unit test
|
|
283
|
+
// can hit every branch without spawning real apt/dpkg or reading
|
|
284
|
+
// /etc/os-release. The thin wrappers below feed real spawn + fs results into
|
|
285
|
+
// that pure decision.
|
|
286
|
+
function readOsRelease() {
|
|
287
|
+
try {
|
|
288
|
+
return parseOsRelease(readFileSync("/etc/os-release", "utf-8"));
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
return {};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/** Summarise `apt-cache policy` output for diagnostics — one token per package. */
|
|
295
|
+
function aptCachePolicySummary(pkg) {
|
|
296
|
+
const r = spawnSync("apt-cache", ["policy", pkg], { stdio: "pipe", encoding: "utf-8", timeout: 5_000 });
|
|
297
|
+
if (r.status !== 0)
|
|
298
|
+
return "policy-spawn-failed";
|
|
299
|
+
const cand = parseAptCacheCandidate(r.stdout ?? "");
|
|
300
|
+
if (cand === null)
|
|
301
|
+
return "no-candidate-line";
|
|
302
|
+
return cand === "(none)" ? "candidate-none" : `candidate=${cand}`;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* map an apt-level package name to the concrete name dpkg will
|
|
306
|
+
* record after install. Resolution order:
|
|
307
|
+
* 1. `dpkg -s pkg` exits 0 → name is concrete and installed; return pkg.
|
|
308
|
+
* 2. `apt-cache policy pkg` reports a real Candidate → concrete-but-missing;
|
|
309
|
+
* return pkg (apt-get install + post-check will use the same name).
|
|
310
|
+
* 3. `apt-cache policy pkg` reports `Candidate: (none)` and `pkg` is in
|
|
311
|
+
* UBUNTU_ALIASES on an Ubuntu-like host → return the alias. Log the
|
|
312
|
+
* resolution so the install log answers "what did apt resolve this to?".
|
|
313
|
+
* 4. Otherwise return pkg unchanged — the post-check will throw loudly,
|
|
314
|
+
* preserving the fail-loud contract for genuinely missing packages.
|
|
315
|
+
*/
|
|
316
|
+
function resolveAptName(pkg) {
|
|
317
|
+
const dpkg = spawnSync("dpkg", ["-s", pkg], { stdio: "pipe", timeout: 5_000 });
|
|
318
|
+
const dpkgInstalled = dpkg.status === 0;
|
|
319
|
+
// Short-circuit: when dpkg already records the name as installed, skip the
|
|
320
|
+
// apt-cache + os-release work — `decideAptResolution` returns pkg unchanged
|
|
321
|
+
// anyway and the extra spawn would burn ~10 ms per already-installed
|
|
322
|
+
// package across every `pkgsMissing` pass.
|
|
323
|
+
if (dpkgInstalled)
|
|
324
|
+
return pkg;
|
|
325
|
+
const policy = spawnSync("apt-cache", ["policy", pkg], {
|
|
326
|
+
stdio: "pipe", encoding: "utf-8", timeout: 5_000,
|
|
327
|
+
});
|
|
328
|
+
const aptCandidate = policy.status === 0
|
|
329
|
+
? parseAptCacheCandidate(policy.stdout ?? "")
|
|
330
|
+
: null;
|
|
331
|
+
const os = readOsRelease();
|
|
332
|
+
const decision = decideAptResolution({
|
|
333
|
+
pkg,
|
|
334
|
+
dpkgInstalled,
|
|
335
|
+
aptCandidate,
|
|
336
|
+
ubuntuLike: isUbuntuLikePure(os),
|
|
337
|
+
distro: `${os.ID ?? "unknown"}-${os.VERSION_CODENAME ?? "unknown"}`,
|
|
338
|
+
});
|
|
339
|
+
if (decision.log)
|
|
340
|
+
logFile(decision.log);
|
|
341
|
+
return decision.resolved;
|
|
342
|
+
}
|
|
343
|
+
/** Probe runtime binary presence on PATH (independent of dpkg-recorded state). */
|
|
344
|
+
function commandVPath(pkg) {
|
|
345
|
+
const r = spawnSync("which", [pkg], { stdio: "pipe", encoding: "utf-8", timeout: 5_000 });
|
|
346
|
+
return r.status === 0 ? (r.stdout ?? "").trim() || "missing" : "missing";
|
|
347
|
+
}
|
|
348
|
+
/** Probe snap-recorded state for a name (snap lives outside dpkg). */
|
|
349
|
+
function snapStatus(pkg) {
|
|
350
|
+
const r = spawnSync("snap", ["list", pkg], { stdio: "pipe", encoding: "utf-8", timeout: 5_000 });
|
|
351
|
+
if (r.status !== 0)
|
|
352
|
+
return "none";
|
|
353
|
+
const line = (r.stdout ?? "").split("\n").find((l) => l.startsWith(pkg));
|
|
354
|
+
return line ? line.split(/\s+/).slice(0, 2).join(" ") : "none";
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Returns the subset of `pkgs` that are not currently installed, after
|
|
358
|
+
* alias resolution. Uses `dpkg -s <resolved>` per package (exit 0 = installed,
|
|
359
|
+
* any non-zero = missing) so we never parse dpkg's prose output for control
|
|
360
|
+
* flow (feedback_no_stdout_parsing_for_control_flow.md). The operator-facing
|
|
361
|
+
* name stays the original `pkg` — the resolution is logged but not renamed
|
|
362
|
+
* in the returned list, so diagnostics match what the installer declared.
|
|
363
|
+
*/
|
|
364
|
+
function pkgsMissing(pkgs) {
|
|
365
|
+
return pkgs.filter((p) => {
|
|
366
|
+
const resolved = resolveAptName(p);
|
|
367
|
+
const r = spawnSync("dpkg", ["-s", resolved], { stdio: "pipe", timeout: 5_000 });
|
|
368
|
+
return r.status !== 0;
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Install a logical group of apt packages, then verify each one landed in
|
|
373
|
+
* dpkg's installed state. The post-install `dpkg -s` probe catches the
|
|
374
|
+
* partial-failure class where `apt-get install` returns 0 but a package did
|
|
375
|
+
* not actually install — silent regressions that would otherwise only
|
|
376
|
+
* surface at runtime when the binary is missing.
|
|
377
|
+
*
|
|
378
|
+
* Resolves each package through `resolveAptName` before both the install
|
|
379
|
+
* command and the post-check, so alias packages pass through
|
|
380
|
+
* cleanly on distros where the apt-level name differs from the dpkg-recorded
|
|
381
|
+
* name (e.g. Noble's chromium → chromium-browser). Resolution is computed
|
|
382
|
+
* once per package and reused across install, post-check, and error
|
|
383
|
+
* diagnostics — three spawnSync chains per package, not nine on failure.
|
|
384
|
+
*/
|
|
385
|
+
function installAptGroup(label, pkgs) {
|
|
386
|
+
const pairs = pkgs.map((original) => ({ original, resolved: resolveAptName(original) }));
|
|
387
|
+
logFile(` apt install (${label}): ${pairs.map((x) => x.resolved).join(" ")}`);
|
|
388
|
+
console.log(" [privileged] apt-get install");
|
|
389
|
+
shell("apt-get", ["install", "-y", ...pairs.map((x) => x.resolved)], { sudo: true });
|
|
390
|
+
const stillMissing = pairs.filter(({ resolved }) => {
|
|
391
|
+
const r = spawnSync("dpkg", ["-s", resolved], { stdio: "pipe", timeout: 5_000 });
|
|
392
|
+
return r.status !== 0;
|
|
393
|
+
});
|
|
394
|
+
if (stillMissing.length > 0) {
|
|
395
|
+
const diag = stillMissing.map(({ original, resolved }) => `${original} (resolved-name=${resolved}, apt-cache-policy=${aptCachePolicySummary(original)}, command-v=${commandVPath(original)}, snap-status=${snapStatus(resolved)})`).join("; ");
|
|
396
|
+
throw new Error(`apt-get install (${label}) returned 0 but packages are still not installed per dpkg -s: ${diag}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// ---------------------------------------------------------------------------
|
|
400
|
+
// Installation steps
|
|
401
|
+
// ---------------------------------------------------------------------------
|
|
402
|
+
const TOTAL = "11";
|
|
403
|
+
/**
|
|
404
|
+
* set macOS hostname via scutil. Three sequential `sudo scutil
|
|
405
|
+
* --set` calls (HostName, LocalHostName, ComputerName) — `hostnamectl` is
|
|
406
|
+
* Linux-only and `--hostname <h>` silently no-ops on darwin. All-or-nothing
|
|
407
|
+
* rollback within the 3-call batch: if any call fails, we restore the
|
|
408
|
+
* pre-batch values for the keys we already changed and re-throw. Full
|
|
409
|
+
* system-state rollback (avahi, /etc/hosts) is out of scope per the brief.
|
|
410
|
+
*/
|
|
411
|
+
function setMacosHostnameViaScutil(hostname) {
|
|
412
|
+
const keys = ["HostName", "LocalHostName", "ComputerName"];
|
|
413
|
+
// Capture pre-batch values for rollback. Empty string is a legitimate
|
|
414
|
+
// scutil --get value (the key was never set) — preserve that as "" so
|
|
415
|
+
// rollback writes empty back rather than failing.
|
|
416
|
+
const previous = {};
|
|
417
|
+
for (const k of keys) {
|
|
418
|
+
const r = spawnSync("scutil", ["--get", k], { encoding: "utf-8", stdio: "pipe", timeout: 5_000 });
|
|
419
|
+
previous[k] = (r.stdout ?? "").trim();
|
|
420
|
+
}
|
|
421
|
+
const applied = [];
|
|
422
|
+
for (const k of keys) {
|
|
423
|
+
const r = spawnSync("sudo", ["scutil", "--set", k, hostname], { encoding: "utf-8", stdio: "inherit", timeout: 30_000 });
|
|
424
|
+
if (r.status !== 0) {
|
|
425
|
+
// Roll back any keys we already changed within this batch.
|
|
426
|
+
for (const done of applied) {
|
|
427
|
+
spawnSync("sudo", ["scutil", "--set", done, previous[done] ?? ""], { stdio: "inherit", timeout: 30_000 });
|
|
428
|
+
}
|
|
429
|
+
const stderr = r.stderr ? `: ${r.stderr.toString().trim()}` : "";
|
|
430
|
+
console.error(` [scutil] ${k} failed${stderr}`);
|
|
431
|
+
throw new Error(`scutil --set ${k} ${hostname} exited ${r.status}`);
|
|
432
|
+
}
|
|
433
|
+
applied.push(k);
|
|
434
|
+
}
|
|
435
|
+
console.log(` [scutil] HostName=${hostname} LocalHostName=${hostname} ComputerName=${hostname} set ok`);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* detect snap-confined Chromium on Linux and install Google Chrome
|
|
439
|
+
* stable as the non-snap replacement. Runs after `installAptGroup("VNC stack")`
|
|
440
|
+
* inside `installSystemDeps`. Resolution rules live in `snap-chromium.ts`
|
|
441
|
+
* (pure decision); this wrapper does the spawnSync + apt-repo writes + post-
|
|
442
|
+
* install gate. Skipped on darwin (Maxy uses Playwright-managed Chromium per
|
|
443
|
+
* brew-install.ts) — RESOLVED_CHROMIUM_BIN keeps its `/usr/bin/chromium`
|
|
444
|
+
* default which is unused on darwin (no systemd unit).
|
|
445
|
+
*
|
|
446
|
+
* Detection: `command -v chromium` + `realpath`; an extra probe for
|
|
447
|
+
* `google-chrome-stable` covers re-run installs where a previous run already
|
|
448
|
+
* landed Chrome. Snap detection is the literal `snap` segment in the realpath
|
|
449
|
+
* (see isSnapConfinedPath in snap-chromium.ts) — covers the three real-world
|
|
450
|
+
* shapes (`/snap/bin/chromium`, `/snap/<rev>/usr/...`, `/usr/bin/snap` which
|
|
451
|
+
* is the snap launcher binary that `readlink -f` terminates at on Noble).
|
|
452
|
+
*
|
|
453
|
+
* Replacement: Google's signed apt repo (cryptographic verification via
|
|
454
|
+
* `signed-by=` GPG key) — the canonical pinned-deterministic source for
|
|
455
|
+
* Chrome stable. Pinning a specific Chrome version would require an out-of-
|
|
456
|
+
* band SHA-bump cadence and contradicts the apt-repo trust model.
|
|
457
|
+
*
|
|
458
|
+
* Post-install gate: spawn the resolved binary headless against a throwaway
|
|
459
|
+
* profile dir under persistDir, assert exit 0. The AppArmor denial that
|
|
460
|
+
* triggered the original denial was on SingletonLock writes which `--headless=new` still
|
|
461
|
+
* attempts, so the headless probe fires the same EACCES path that production
|
|
462
|
+
* VNC headed launches do — closing the post-fix-sibling-audit-skipped gap.
|
|
463
|
+
*/
|
|
464
|
+
function ensureNonSnapChromium() {
|
|
465
|
+
if (process.platform !== "linux") {
|
|
466
|
+
logFile(` ensureNonSnapChromium skipped: platform=${process.platform}`);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const which = (cmd) => {
|
|
470
|
+
const r = spawnSync("command", ["-v", cmd], { stdio: "pipe", encoding: "utf-8", shell: "/bin/bash", timeout: 5_000 });
|
|
471
|
+
if (r.status !== 0)
|
|
472
|
+
return null;
|
|
473
|
+
const out = (r.stdout ?? "").trim();
|
|
474
|
+
return out || null;
|
|
475
|
+
};
|
|
476
|
+
const realpath = (path) => {
|
|
477
|
+
if (!path)
|
|
478
|
+
return null;
|
|
479
|
+
try {
|
|
480
|
+
return realpathSync(path);
|
|
481
|
+
}
|
|
482
|
+
catch {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
const whichChromium = which("chromium");
|
|
487
|
+
const whichGoogleChrome = which("google-chrome-stable");
|
|
488
|
+
const decision = decideChromiumAction({
|
|
489
|
+
platform: "linux",
|
|
490
|
+
whichChromium,
|
|
491
|
+
realpathChromium: realpath(whichChromium),
|
|
492
|
+
whichGoogleChrome,
|
|
493
|
+
realpathGoogleChrome: realpath(whichGoogleChrome),
|
|
494
|
+
});
|
|
495
|
+
logFile(` [snap-chromium] decision: ${decision.action} reason="${decision.reason}"`);
|
|
496
|
+
if (decision.action === "fail") {
|
|
497
|
+
throw new Error(`ensureNonSnapChromium: ${decision.reason}. apt install of \`chromium\` ran in installAptGroup(VNC stack) above; if its post-check passed but no chromium binary is on PATH, the system PATH is misconfigured.`);
|
|
498
|
+
}
|
|
499
|
+
if (decision.action === "install-google-chrome") {
|
|
500
|
+
console.log(" Detected snap-confined Chromium — installing Google Chrome stable...");
|
|
501
|
+
logFile(` [snap-chromium] installing google-chrome-stable from Google's signed apt repo`);
|
|
502
|
+
// Fetch + dearmor the signing key, write to /etc/apt/trusted.gpg.d/. Pipe
|
|
503
|
+
// composition runs through bash -c so the curl|gpg pipeline is one
|
|
504
|
+
// privileged command rather than two separate sudo escalations.
|
|
505
|
+
console.log(" [privileged] curl + gpg --dearmor (Google Chrome signing key)");
|
|
506
|
+
shell("bash", ["-c",
|
|
507
|
+
"set -euo pipefail; " +
|
|
508
|
+
"curl -fsSL https://dl.google.com/linux/linux_signing_key.pub | " +
|
|
509
|
+
"gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/google-chrome.gpg",
|
|
510
|
+
], { sudo: true });
|
|
511
|
+
// Add the apt source list with `signed-by=` scoping so the key only
|
|
512
|
+
// verifies google-chrome-* packages, not arbitrary repo overrides. arch
|
|
513
|
+
// pinned to amd64 — Google does not ship arm64 Chrome for Linux.
|
|
514
|
+
console.log(" [privileged] tee /etc/apt/sources.list.d/google-chrome.list");
|
|
515
|
+
shell("bash", ["-c",
|
|
516
|
+
"echo 'deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/google-chrome.gpg] " +
|
|
517
|
+
"http://dl.google.com/linux/chrome/deb/ stable main' " +
|
|
518
|
+
"> /etc/apt/sources.list.d/google-chrome.list",
|
|
519
|
+
], { sudo: true });
|
|
520
|
+
console.log(" [privileged] apt-get update");
|
|
521
|
+
shell("apt-get", ["update"], { sudo: true });
|
|
522
|
+
installAptGroup("Google Chrome stable", ["google-chrome-stable"]);
|
|
523
|
+
// Re-resolve after install to capture the now-installed absolute path.
|
|
524
|
+
const postInstallWhich = which("google-chrome-stable");
|
|
525
|
+
if (!postInstallWhich) {
|
|
526
|
+
throw new Error("ensureNonSnapChromium: apt install of google-chrome-stable returned 0 and dpkg -s passed, but `command -v google-chrome-stable` is empty — PATH is broken.");
|
|
527
|
+
}
|
|
528
|
+
RESOLVED_CHROMIUM_BIN = postInstallWhich;
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
// action === "use" — decision.resolvedPath is the existing non-snap binary
|
|
532
|
+
// (chromium or google-chrome-stable already installed).
|
|
533
|
+
if (!decision.resolvedPath) {
|
|
534
|
+
throw new Error(`ensureNonSnapChromium: action=use returned without resolvedPath — bug in snap-chromium.ts (input: chromium=${whichChromium} google-chrome=${whichGoogleChrome})`);
|
|
535
|
+
}
|
|
536
|
+
RESOLVED_CHROMIUM_BIN = decision.resolvedPath;
|
|
537
|
+
}
|
|
538
|
+
// Defensive: never persist a snap-confined path. If realpath of the resolved
|
|
539
|
+
// binary still lands under /snap/ (e.g. apt landed a snap package by mistake
|
|
540
|
+
// on a misconfigured device), throw before writeChromiumBinaryPathFile sees
|
|
541
|
+
// it — the runtime gate in vnc.sh would refuse anyway, but failing here
|
|
542
|
+
// surfaces the contract breach with the install context still in scope.
|
|
543
|
+
const finalRealpath = realpath(RESOLVED_CHROMIUM_BIN);
|
|
544
|
+
if (isSnapConfinedPath(finalRealpath)) {
|
|
545
|
+
throw new Error(`ensureNonSnapChromium: resolved Chromium binary ${RESOLVED_CHROMIUM_BIN} realpaths to ${finalRealpath} which is under /snap/ — refusing to persist.`);
|
|
546
|
+
}
|
|
547
|
+
console.log(` Chromium binary: ${RESOLVED_CHROMIUM_BIN} (realpath=${finalRealpath ?? "?"})`);
|
|
548
|
+
logFile(` [snap-chromium] resolved bin=${RESOLVED_CHROMIUM_BIN} realpath=${finalRealpath ?? "null"}`);
|
|
549
|
+
runChromiumPostInstallGate(RESOLVED_CHROMIUM_BIN);
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* post-install gate. Spawns the resolved Chromium binary headless
|
|
553
|
+
* against a throwaway profile dir under persistDir (`~/.{brand}/chromium-
|
|
554
|
+
* gate-profile/`). The AppArmor denial that triggered the task was on
|
|
555
|
+
* SingletonLock writes which `--headless=new` still attempts, so this probe
|
|
556
|
+
* fires the same EACCES path the headed VNC stack would. Cleans up the gate
|
|
557
|
+
* profile afterward — the live profile (`chromium-profile/`) is owned by
|
|
558
|
+
* vnc.sh's start_chrome and not touched here.
|
|
559
|
+
*/
|
|
560
|
+
function runChromiumPostInstallGate(chromiumBin) {
|
|
561
|
+
const persistDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
562
|
+
const gateProfileDir = join(persistDir, "chromium-gate-profile");
|
|
563
|
+
mkdirSync(gateProfileDir, { recursive: true });
|
|
564
|
+
console.log(` Verifying ${chromiumBin} can write to ${gateProfileDir} (post-install gate)...`);
|
|
565
|
+
const r = spawnSync(chromiumBin, [
|
|
566
|
+
`--user-data-dir=${gateProfileDir}`,
|
|
567
|
+
"--headless=new",
|
|
568
|
+
"--disable-gpu",
|
|
569
|
+
"--no-sandbox",
|
|
570
|
+
"--disable-dev-shm-usage",
|
|
571
|
+
"--dump-dom",
|
|
572
|
+
"about:blank",
|
|
573
|
+
], { stdio: "pipe", encoding: "utf-8", timeout: 30_000 });
|
|
574
|
+
// Cleanup before throwing on failure so successive runs start clean.
|
|
575
|
+
try {
|
|
576
|
+
rmSync(gateProfileDir, { recursive: true, force: true });
|
|
577
|
+
}
|
|
578
|
+
catch { /* best-effort */ }
|
|
579
|
+
if (r.status !== 0) {
|
|
580
|
+
const stderr = (r.stderr ?? "").slice(-2000);
|
|
581
|
+
const eaccesHit = /Permission denied/i.test(stderr) || /EACCES/i.test(stderr);
|
|
582
|
+
const taskRef = eaccesHit
|
|
583
|
+
? "chromium-profile not writable (likely AppArmor denial on snap-confined binary). "
|
|
584
|
+
: "";
|
|
585
|
+
throw new Error(`${taskRef}Chromium post-install gate failed: ${chromiumBin} exited ${r.status} signal=${r.signal ?? "none"}. stderr:\n${stderr}`);
|
|
586
|
+
}
|
|
587
|
+
console.log(" Chromium post-install gate passed.");
|
|
588
|
+
logFile(` [snap-chromium] post-install gate ok: ${chromiumBin} exit=0`);
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* write the resolved Chromium absolute path to
|
|
592
|
+
* `<INSTALL_DIR>/platform/config/chromium-binary.path` so vnc.sh and
|
|
593
|
+
* writeChromiumWrapper read the same value. Called
|
|
594
|
+
* after deployPayload so the platform/config/ directory exists. Idempotent:
|
|
595
|
+
* re-running the installer with the same RESOLVED_CHROMIUM_BIN is a no-op
|
|
596
|
+
* write (writeFileSync overwrites in place).
|
|
597
|
+
*/
|
|
598
|
+
function writeChromiumBinaryPathFile() {
|
|
599
|
+
if (process.platform !== "linux") {
|
|
600
|
+
logFile(` writeChromiumBinaryPathFile skipped: platform=${process.platform}`);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const configDir = resolve(INSTALL_DIR, "platform/config");
|
|
604
|
+
mkdirSync(configDir, { recursive: true });
|
|
605
|
+
const target = join(configDir, "chromium-binary.path");
|
|
606
|
+
writeFileSync(target, RESOLVED_CHROMIUM_BIN + "\n", { mode: 0o644 });
|
|
607
|
+
console.log(` Wrote ${target} → ${RESOLVED_CHROMIUM_BIN}`);
|
|
608
|
+
logFile(` [snap-chromium] wrote ${target} contents=${RESOLVED_CHROMIUM_BIN}`);
|
|
609
|
+
}
|
|
610
|
+
function installSystemDeps() {
|
|
611
|
+
log("1", TOTAL, "System dependencies and network...");
|
|
612
|
+
const platform = requireSupportedPlatform(process.platform);
|
|
613
|
+
if (platform === "darwin") {
|
|
614
|
+
// darwin hostname via scutil when --hostname is supplied,
|
|
615
|
+
// otherwise preserve the existing system hostname.
|
|
616
|
+
if (HOSTNAME_FLAG) {
|
|
617
|
+
console.log(` Hostname: ${HOSTNAME_FLAG} (from --hostname flag)`);
|
|
618
|
+
logFile(` [create-maxy] darwin-hostname-mode=scutil-set value=${HOSTNAME_FLAG}`);
|
|
619
|
+
try {
|
|
620
|
+
setMacosHostnameViaScutil(HOSTNAME_FLAG);
|
|
621
|
+
DEVICE_HOSTNAME = HOSTNAME_FLAG;
|
|
622
|
+
}
|
|
623
|
+
catch (err) {
|
|
624
|
+
console.error(` WARNING: Failed to set hostname to '${HOSTNAME_FLAG}': ${err instanceof Error ? err.message : String(err)}`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
// No flag — read the live LocalHostName so the printed URL matches mDNS
|
|
629
|
+
// resolution (`<LocalHostName>.local`). The `hostname` command on darwin
|
|
630
|
+
// returns the fully-qualified `<name>.local`, which would render the
|
|
631
|
+
// completion URL with a double `.local` suffix; scutil --get returns the
|
|
632
|
+
// bare token. No sudo, no system mutation.
|
|
633
|
+
const scutilGet = spawnSync("scutil", ["--get", "LocalHostName"], { encoding: "utf-8", stdio: "pipe", timeout: 5_000 });
|
|
634
|
+
const scutilName = ((scutilGet.stdout ?? "").trim());
|
|
635
|
+
if (scutilGet.status === 0 && scutilName.length > 0) {
|
|
636
|
+
DEVICE_HOSTNAME = scutilName;
|
|
637
|
+
console.log(` Hostname: ${DEVICE_HOSTNAME} (from scutil --get LocalHostName — no --hostname flag)`);
|
|
638
|
+
logFile(` [create-maxy] darwin-hostname-mode=scutil-get value=${DEVICE_HOSTNAME}`);
|
|
639
|
+
}
|
|
640
|
+
else {
|
|
641
|
+
// scutil failed or returned empty — fall back to BRAND.hostname so the
|
|
642
|
+
// completion URL at least matches the brand's documented .local name.
|
|
643
|
+
DEVICE_HOSTNAME = BRAND.hostname;
|
|
644
|
+
console.log(` Hostname: ${DEVICE_HOSTNAME} (brand fallback — scutil --get LocalHostName empty)`);
|
|
645
|
+
logFile(` [create-maxy] darwin-hostname-mode=brand-fallback value=${DEVICE_HOSTNAME} scutil-status=${scutilGet.status}`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
// macOS has no apt analogue for the VNC/WiFi-AP stacks
|
|
649
|
+
// (kiosk display + hostapd/dnsmasq are Pi-specific's
|
|
650
|
+
// out-of-scope note). mDNS is provided by the OS, so avahi-* drop out.
|
|
651
|
+
// Translate the remaining apt names through decideBrewResolution and
|
|
652
|
+
// let installAllBrewPackages handle the install + verify pattern.
|
|
653
|
+
const DARWIN_BASE_DEPS = ["curl", "git", "unzip", "jq", "poppler", "ffmpeg", "tesseract", "ocrmypdf"];
|
|
654
|
+
installAllBrewPackages(DARWIN_BASE_DEPS, logFile);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
const BASE_DEPS = ["curl", "git", "unzip", "jq", "avahi-daemon", "avahi-utils", "poppler-utils", "ffmpeg", "bind9-dnsutils", "tesseract-ocr", "ocrmypdf"];
|
|
658
|
+
// xterm is the *preferred* terminal-emulator binary for the VNC-rendered
|
|
659
|
+
// Terminal surface
|
|
660
|
+
// delegates window creation to the session's gnome-terminal-server,
|
|
661
|
+
// opening windows on the wrong display; xterm has no IPC layer and
|
|
662
|
+
// honours DISPLAY directly). Kept in the apt list unconditionally so
|
|
663
|
+
// vnc.sh's resolve_terminal_bin has a display-safe binary on every
|
|
664
|
+
// supported distro (Ubuntu 24.04 noble/universe, Debian 12 bookworm/main
|
|
665
|
+
// verified). xdotool backs the post-spawn display-membership
|
|
666
|
+
// assertion in vnc.sh check_window_on_display, closing the silent-fail
|
|
667
|
+
// class where PID is alive but no window is mapped on the target display.
|
|
668
|
+
const VNC_DEPS = ["tigervnc-standalone-server", "python3-websockify", "novnc", "xdg-utils", "chromium", "xterm", "xdotool"];
|
|
669
|
+
// retired the ttyd/tmux admin terminal stack — upgrades run via
|
|
670
|
+
// the action runner (systemd-run --user transient units) and no longer
|
|
671
|
+
// need a shared tmux session. `tmux` was only required by the retired
|
|
672
|
+
// byte-stream terminal; removing it shrinks the apt footprint.
|
|
673
|
+
const WIFI_DEPS = ["hostapd", "dnsmasq"];
|
|
674
|
+
const ALL_APT_DEPS = [...BASE_DEPS, ...VNC_DEPS, ...WIFI_DEPS];
|
|
675
|
+
// verify the "deps are present" assumption with `dpkg -s` instead
|
|
676
|
+
// of asserting it (feedback_loud_failures.md). The previous silent-skip
|
|
677
|
+
// branch was benign added xdotool (the first new apt dep
|
|
678
|
+
// since the skip path became load-bearing on user-password-sudo devices).
|
|
679
|
+
const missing = pkgsMissing(ALL_APT_DEPS);
|
|
680
|
+
if (missing.length === 0) {
|
|
681
|
+
logFile(` all system deps present per dpkg -s (${ALL_APT_DEPS.length} packages) — skipping apt install`);
|
|
682
|
+
console.log(` All ${ALL_APT_DEPS.length} system deps already installed — skipping apt install.`);
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
const canEscalate = canSudo() || process.stdout.isTTY === true;
|
|
686
|
+
if (!canEscalate) {
|
|
687
|
+
const repair = `sudo apt-get install -y ${missing.join(" ")}`;
|
|
688
|
+
console.error(` MISSING ${missing.length} system deps per dpkg -s: ${missing.join(" ")}`);
|
|
689
|
+
console.error(` Non-interactive sudo is unavailable; cannot prompt for password from a non-TTY shell.`);
|
|
690
|
+
console.error(` Re-run this installer from an interactive shell, or repair manually:`);
|
|
691
|
+
console.error(` ${repair}`);
|
|
692
|
+
logFile(` FAIL: missing apt deps without interactive sudo: ${missing.join(",")}`);
|
|
693
|
+
throw new Error(`installSystemDeps: missing packages (${missing.join(", ")}) and sudo is unavailable non-interactively — repair with: ${repair}`);
|
|
694
|
+
}
|
|
695
|
+
console.log(` Missing apt deps (${missing.length}): ${missing.join(", ")}`);
|
|
696
|
+
console.log(` Installing via sudo apt-get — sudo may prompt for your password...`);
|
|
697
|
+
console.log(" [privileged] apt-get update");
|
|
698
|
+
shell("apt-get", ["update"], { sudo: true });
|
|
699
|
+
installAptGroup("base utilities", BASE_DEPS);
|
|
700
|
+
installAptGroup("VNC stack", VNC_DEPS);
|
|
701
|
+
installAptGroup("WiFi AP", WIFI_DEPS);
|
|
702
|
+
}
|
|
703
|
+
// replace snap-confined Chromium with Google Chrome stable on
|
|
704
|
+
// Linux laptops (Ubuntu Noble) where `/usr/bin/chromium` realpaths to the
|
|
705
|
+
// snap launcher. The snap AppArmor profile denies writes to hidden top-level
|
|
706
|
+
// paths under $HOME, so any write to `~/.{brand}/chromium-profile/SingletonLock`
|
|
707
|
+
// hits EACCES and Chromium never starts the CDP listener. Always sets
|
|
708
|
+
// RESOLVED_CHROMIUM_BIN even on Pi Bookworm (where the path is unchanged),
|
|
709
|
+
// so deployPayload's writeChromiumBinaryPathFile and installService's
|
|
710
|
+
// buildMaxyUnitFile both have a real absolute path to thread through.
|
|
711
|
+
ensureNonSnapChromium();
|
|
712
|
+
// Task 600 — provision swap so the OOM-killer has a buffer to work in
|
|
713
|
+
// instead of silently freezing the box. dphys-swapfile is the standard
|
|
714
|
+
// Raspberry Pi OS swap manager. CONF_MAXSWAP raised to 4096 so the
|
|
715
|
+
// operator can manually grow the swap file without reinstalling.
|
|
716
|
+
// Best-effort: a failure here warns but does not abort the install.
|
|
717
|
+
try {
|
|
718
|
+
console.log(" [privileged] apt-get install dphys-swapfile");
|
|
719
|
+
shell("apt-get", ["install", "-y", "dphys-swapfile"], { sudo: true, bestEffort: true });
|
|
720
|
+
const desiredSwapConf = "CONF_SWAPSIZE=2048\nCONF_MAXSWAP=4096\n";
|
|
721
|
+
const swapConfPath = "/etc/dphys-swapfile";
|
|
722
|
+
let swapCurrent = "";
|
|
723
|
+
if (existsSync(swapConfPath)) {
|
|
724
|
+
try {
|
|
725
|
+
swapCurrent = readFileSync(swapConfPath, "utf-8");
|
|
726
|
+
}
|
|
727
|
+
catch {
|
|
728
|
+
swapCurrent = "";
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
if (swapCurrent === desiredSwapConf) {
|
|
732
|
+
logFile(" swap: already configured (CONF_SWAPSIZE=2048 CONF_MAXSWAP=4096)");
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
const swapTmpPath = `/tmp/dphys-swapfile.${process.pid}`;
|
|
736
|
+
writeFileSync(swapTmpPath, desiredSwapConf);
|
|
737
|
+
console.log(" [privileged] install /etc/dphys-swapfile (CONF_SWAPSIZE=2048 CONF_MAXSWAP=4096)");
|
|
738
|
+
shell("cp", [swapTmpPath, swapConfPath], { sudo: true });
|
|
739
|
+
spawnSync("rm", ["-f", swapTmpPath]);
|
|
740
|
+
console.log(" [privileged] systemctl restart dphys-swapfile");
|
|
741
|
+
shell("systemctl", ["restart", "dphys-swapfile"], { sudo: true, bestEffort: true });
|
|
742
|
+
logFile(" swap: configured CONF_SWAPSIZE=2048 CONF_MAXSWAP=4096 and restarted dphys-swapfile");
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
catch (err) {
|
|
746
|
+
console.error(` WARNING: failed to provision swap: ${err instanceof Error ? err.message : String(err)}`);
|
|
747
|
+
}
|
|
748
|
+
// Hostname resolution — four sources, in priority order:
|
|
749
|
+
// 1. --hostname flag (unconditional — the caller is the authority)
|
|
750
|
+
// 2. OS detection on same-brand upgrade (service exists → keep whatever is currently set)
|
|
751
|
+
// 3. OS preservation on multi-brand device (another brand's service detected → keep hostname)
|
|
752
|
+
// 4. BRAND.hostname on fresh install (brand default)
|
|
753
|
+
if (HOSTNAME_FLAG) {
|
|
754
|
+
// --hostname flag: set unconditionally, no detection, no preservation logic.
|
|
755
|
+
console.log(` Hostname: ${HOSTNAME_FLAG} (from --hostname flag)`);
|
|
756
|
+
try {
|
|
757
|
+
console.log(" [privileged] hostnamectl set-hostname");
|
|
758
|
+
shell("hostnamectl", ["set-hostname", HOSTNAME_FLAG], { sudo: true });
|
|
759
|
+
console.log(" [privileged] sed -i");
|
|
760
|
+
shell("sed", ["-i", `s/127\\.0\\.1\\.1.*$/127.0.1.1\\t${HOSTNAME_FLAG}/`, "/etc/hosts"], { sudo: true });
|
|
761
|
+
try {
|
|
762
|
+
console.log(" [privileged] sed -i");
|
|
763
|
+
shell("sed", ["-i", `s/^[#]*host-name=.*/host-name=${HOSTNAME_FLAG}/`, "/etc/avahi/avahi-daemon.conf"], { sudo: true });
|
|
764
|
+
console.log(` Avahi host-name: ${HOSTNAME_FLAG} (updated avahi-daemon.conf)`);
|
|
765
|
+
}
|
|
766
|
+
catch { /* avahi-daemon.conf may not exist — non-critical */ }
|
|
767
|
+
// Restart avahi-daemon so the new hostname takes effect immediately
|
|
768
|
+
// and any stale "maxytest-2" auto-renamed records from a previous
|
|
769
|
+
// boot's hostname-conflict cycle are withdrawn. Without this,
|
|
770
|
+
// avahi-resolve -n <hostname>.local times out from the device itself
|
|
771
|
+
// because the daemon is still advertising the previous identity.
|
|
772
|
+
try {
|
|
773
|
+
console.log(" [privileged] systemctl restart avahi-daemon");
|
|
774
|
+
shell("systemctl", ["restart", "avahi-daemon"], { sudo: true });
|
|
775
|
+
}
|
|
776
|
+
catch (err) {
|
|
777
|
+
console.error(` WARNING: avahi-daemon restart failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
catch (err) {
|
|
781
|
+
console.error(` WARNING: Failed to set hostname to '${HOSTNAME_FLAG}': ${err instanceof Error ? err.message : String(err)}`);
|
|
782
|
+
}
|
|
783
|
+
DEVICE_HOSTNAME = HOSTNAME_FLAG;
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
// No flag — fall back to detection (upgrade) or brand default (fresh install).
|
|
787
|
+
// Check for this brand's service (same-brand upgrade) and any other brand's service
|
|
788
|
+
// (multi-brand device). On a multi-brand device, hostname is a device-level concern
|
|
789
|
+
// set by the user or the first installer — subsequent brands must not overwrite it.
|
|
790
|
+
const systemdUserDir = resolve(process.env.HOME ?? "/root", ".config/systemd/user");
|
|
791
|
+
const serviceExists = existsSync(join(systemdUserDir, BRAND.serviceName));
|
|
792
|
+
// narrow peer detection to KNOWN_BRAND_HOSTNAMES. The previous
|
|
793
|
+
// predicate ("any *.service that isn't ours") matched stray user units
|
|
794
|
+
// such as a `gnome-keyring-daemon.service -> /dev/null` disable marker,
|
|
795
|
+
// silently flipping single-brand fresh installs into the "preserve hostname"
|
|
796
|
+
// branch. Mirrors the `peerBrandPresent` allowlist in `uninstall.ts`.
|
|
797
|
+
let otherBrandService = false;
|
|
798
|
+
if (!serviceExists) {
|
|
799
|
+
const peerUnits = KNOWN_BRAND_HOSTNAMES
|
|
800
|
+
.filter((h) => h !== BRAND.hostname)
|
|
801
|
+
.flatMap((h) => [`${h}.service`, `${h}-edge.service`]);
|
|
802
|
+
try {
|
|
803
|
+
const files = new Set(readdirSync(systemdUserDir));
|
|
804
|
+
otherBrandService = peerUnits.some((unit) => files.has(unit));
|
|
805
|
+
}
|
|
806
|
+
catch { /* directory may not exist on a fresh device — not an error */ }
|
|
807
|
+
}
|
|
808
|
+
// "raspberrypi" is the Pi factory default — it means "never configured,"
|
|
809
|
+
// not "the admin chose this hostname." Treat it the same as a fresh install.
|
|
810
|
+
const FACTORY_HOSTNAMES = ["raspberrypi", "localhost"];
|
|
811
|
+
let hostnameSetAttempted = false;
|
|
812
|
+
try {
|
|
813
|
+
const currentHostname = execFileSync("hostname", [], { encoding: "utf-8" }).trim();
|
|
814
|
+
const isFactory = FACTORY_HOSTNAMES.includes(currentHostname);
|
|
815
|
+
if (currentHostname === BRAND.hostname) {
|
|
816
|
+
console.log(` Hostname: ${currentHostname} (detected from OS)`);
|
|
817
|
+
}
|
|
818
|
+
else if (serviceExists && !isFactory) {
|
|
819
|
+
// Upgrade: admin-chosen hostname — preserve it.
|
|
820
|
+
console.log(` Hostname: ${currentHostname} (detected from OS — differs from brand '${BRAND.hostname}')`);
|
|
821
|
+
}
|
|
822
|
+
else if (otherBrandService && !isFactory) {
|
|
823
|
+
// Multi-brand: another brand set the hostname — preserve it.
|
|
824
|
+
console.log(` Hostname preserved: ${currentHostname} (another brand service detected)`);
|
|
825
|
+
}
|
|
826
|
+
else {
|
|
827
|
+
// Fresh install, OR a factory default that was never changed.
|
|
828
|
+
const reason = isFactory && serviceExists
|
|
829
|
+
? `factory default '${currentHostname}' was never changed`
|
|
830
|
+
: `brand default — fresh install, was '${currentHostname}'`;
|
|
831
|
+
console.log(` Hostname: ${BRAND.hostname} (${reason})`);
|
|
832
|
+
hostnameSetAttempted = true;
|
|
833
|
+
try {
|
|
834
|
+
console.log(" [privileged] hostnamectl set-hostname");
|
|
835
|
+
shell("hostnamectl", ["set-hostname", BRAND.hostname], { sudo: true });
|
|
836
|
+
console.log(" [privileged] sed -i");
|
|
837
|
+
shell("sed", ["-i", `s/127\\.0\\.1\\.1.*$/127.0.1.1\\t${BRAND.hostname}/`, "/etc/hosts"], { sudo: true });
|
|
838
|
+
try {
|
|
839
|
+
console.log(" [privileged] sed -i");
|
|
840
|
+
shell("sed", ["-i", `s/^[#]*host-name=.*/host-name=${BRAND.hostname}/`, "/etc/avahi/avahi-daemon.conf"], { sudo: true });
|
|
841
|
+
console.log(` Avahi host-name: ${BRAND.hostname} (updated avahi-daemon.conf)`);
|
|
842
|
+
}
|
|
843
|
+
catch { /* avahi-daemon.conf may not exist — non-critical */ }
|
|
844
|
+
}
|
|
845
|
+
catch (err) {
|
|
846
|
+
console.error(` WARNING: Could not set hostname to '${BRAND.hostname}': ${err instanceof Error ? err.message : String(err)}`);
|
|
847
|
+
console.error(` After install, run: sudo hostnamectl set-hostname ${BRAND.hostname}`);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
catch (err) {
|
|
852
|
+
console.error(` WARNING: Could not detect hostname: ${err instanceof Error ? err.message : String(err)}`);
|
|
853
|
+
}
|
|
854
|
+
// Read the actual hostname after any changes — used for user-facing URLs.
|
|
855
|
+
try {
|
|
856
|
+
DEVICE_HOSTNAME = execFileSync("hostname", [], { encoding: "utf-8" }).trim();
|
|
857
|
+
}
|
|
858
|
+
catch { /* fallback to brand — set at declaration */ }
|
|
859
|
+
// If we attempted to set the hostname but it didn't take, use the brand
|
|
860
|
+
// hostname for the completion URL. The user needs the URL that will work
|
|
861
|
+
// after a reboot or manual hostname fix — not the stale factory default.
|
|
862
|
+
if (hostnameSetAttempted && DEVICE_HOSTNAME !== BRAND.hostname) {
|
|
863
|
+
console.error(` WARNING: Hostname is still '${DEVICE_HOSTNAME}' — using '${BRAND.hostname}' for the completion URL.`);
|
|
864
|
+
DEVICE_HOSTNAME = BRAND.hostname;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
// Avahi service file for LAN discovery
|
|
868
|
+
const avahiService = `<?xml version="1.0" standalone='no'?>
|
|
869
|
+
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
|
|
870
|
+
<service-group>
|
|
871
|
+
<name replace-wildcards="yes">${BRAND.productName} on %h</name>
|
|
872
|
+
<service>
|
|
873
|
+
<type>_http._tcp</type>
|
|
874
|
+
<port>${PORT}</port>
|
|
875
|
+
<txt-record>role=${BRAND.hostname}</txt-record>
|
|
876
|
+
</service>
|
|
877
|
+
</service-group>`;
|
|
878
|
+
const avahiTmpPath = `/tmp/${BRAND.hostname}-avahi.service`;
|
|
879
|
+
const avahiDestPath = `/etc/avahi/services/${BRAND.hostname}.service`;
|
|
880
|
+
try {
|
|
881
|
+
writeFileSync(avahiTmpPath, avahiService);
|
|
882
|
+
console.log(" [privileged] cp");
|
|
883
|
+
shell("cp", [avahiTmpPath, avahiDestPath], { sudo: true });
|
|
884
|
+
console.log(" [privileged] systemctl enable");
|
|
885
|
+
shell("systemctl", ["enable", "avahi-daemon"], { sudo: true });
|
|
886
|
+
console.log(" [privileged] systemctl restart");
|
|
887
|
+
shell("systemctl", ["restart", "avahi-daemon"], { sudo: true });
|
|
888
|
+
}
|
|
889
|
+
catch { /* not critical */ }
|
|
890
|
+
// Hostname collision detection: check if another device already claims this hostname.
|
|
891
|
+
// Avahi appends -2 on collision, which causes the agent to construct URLs to the wrong device.
|
|
892
|
+
try {
|
|
893
|
+
const resolveResult = spawnSync("avahi-resolve", ["-n", `${BRAND.hostname}.local`], {
|
|
894
|
+
encoding: "utf-8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"],
|
|
895
|
+
});
|
|
896
|
+
if (resolveResult.status === 0 && resolveResult.stdout.trim()) {
|
|
897
|
+
const resolvedIp = resolveResult.stdout.trim().split(/\s+/)[1];
|
|
898
|
+
// Get this device's IP addresses to compare
|
|
899
|
+
const hostnameResult = spawnSync("hostname", ["-I"], { encoding: "utf-8", timeout: 3000 });
|
|
900
|
+
const localIps = hostnameResult.stdout?.trim().split(/\s+/) ?? [];
|
|
901
|
+
if (resolvedIp && !localIps.includes(resolvedIp)) {
|
|
902
|
+
console.log(` WARNING: ${BRAND.hostname}.local already resolves to ${resolvedIp} — this device may get ${BRAND.hostname}-2.local`);
|
|
903
|
+
logFile(` WARNING: hostname collision detected — ${BRAND.hostname}.local resolves to ${resolvedIp}, local IPs: ${localIps.join(", ")}`);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
catch { /* non-critical — avahi-resolve may not be ready yet */ }
|
|
908
|
+
// Disable WiFi power management — the Pi's WiFi driver aggressively sleeps
|
|
909
|
+
// the radio between transmissions, causing dropped DNS/mDNS packets and
|
|
910
|
+
// intermittent connection resets on LAN.
|
|
911
|
+
try {
|
|
912
|
+
const nmConfDir = "/etc/NetworkManager/conf.d";
|
|
913
|
+
const nmConfFile = join(nmConfDir, `${BRAND.hostname}-no-powersave.conf`);
|
|
914
|
+
if (existsSync("/usr/bin/nmcli") && !existsSync(nmConfFile)) {
|
|
915
|
+
console.log(" Disabling WiFi power save...");
|
|
916
|
+
writeFileSync(`/tmp/${BRAND.hostname}-no-powersave.conf`, "[connection]\nwifi.powersave = 2\n");
|
|
917
|
+
console.log(" [privileged] cp");
|
|
918
|
+
shell("cp", [`/tmp/${BRAND.hostname}-no-powersave.conf`, nmConfFile], { sudo: true });
|
|
919
|
+
spawnSync("sudo", ["systemctl", "restart", "NetworkManager"], { stdio: "pipe" });
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
catch { /* not critical — wired connections unaffected */ }
|
|
923
|
+
console.log(` Device reachable at http://${DEVICE_HOSTNAME}.local:${PORT}`);
|
|
924
|
+
}
|
|
925
|
+
function installNodejs() {
|
|
926
|
+
if (commandExists("node") && nodeVersion() >= 20) {
|
|
927
|
+
log("2", TOTAL, `Node.js v${nodeVersion()} already installed.`);
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
log("2", TOTAL, "Installing Node.js...");
|
|
931
|
+
const platform = requireSupportedPlatform(process.platform);
|
|
932
|
+
if (platform === "darwin") {
|
|
933
|
+
// Pass the apt-side `nodejs` name; decideBrewResolution maps it to
|
|
934
|
+
// `node@22` per DARWIN_ALIASES so the cellar formula matches Maxy's
|
|
935
|
+
// pinned-binaries floor.
|
|
936
|
+
installAllBrewPackages(["nodejs"], logFile);
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
spawnSync("bash", ["-c", "curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -"], { stdio: "inherit" });
|
|
940
|
+
console.log(" [privileged] apt-get install");
|
|
941
|
+
shell("apt-get", ["install", "-y", "nodejs"], { sudo: true });
|
|
942
|
+
}
|
|
943
|
+
function installClaudeCode() {
|
|
944
|
+
let needsInstall = true;
|
|
945
|
+
if (commandExists("claude")) {
|
|
946
|
+
try {
|
|
947
|
+
// `claude --version` prints "2.1.114 (Claude Code)" — extract the semver so
|
|
948
|
+
// the equality check against `npm view` (which returns bare "2.1.114") works.
|
|
949
|
+
const rawVersion = execFileSync("claude", ["--version"], { encoding: "utf-8", timeout: 10_000 }).trim();
|
|
950
|
+
const installed = rawVersion.match(/^(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/)?.[1] ?? rawVersion;
|
|
951
|
+
let latest = null;
|
|
952
|
+
try {
|
|
953
|
+
latest = execFileSync("npm", ["view", "@anthropic-ai/claude-code", "version"], { encoding: "utf-8", timeout: 30_000 }).trim();
|
|
954
|
+
}
|
|
955
|
+
catch {
|
|
956
|
+
logFile(" Could not check latest version — will attempt install anyway");
|
|
957
|
+
}
|
|
958
|
+
if (latest && installed === latest) {
|
|
959
|
+
log("3", TOTAL, `Claude Code v${installed} already up to date.`);
|
|
960
|
+
needsInstall = false;
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
log("3", TOTAL, `Upgrading Claude Code (${installed}${latest ? ` → ${latest}` : ""})...`);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
catch {
|
|
967
|
+
log("3", TOTAL, "Claude Code found but version check failed. Reinstalling...");
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
else {
|
|
971
|
+
log("3", TOTAL, "Installing Claude Code...");
|
|
972
|
+
}
|
|
973
|
+
if (needsInstall) {
|
|
974
|
+
// `npm install -g` needs write access to the global prefix, which on Linux is
|
|
975
|
+
// root-owned by default — so we run it under sudo. When sudo requires a password
|
|
976
|
+
// and the installer is running non-interactively (e.g. systemd-run --scope on
|
|
977
|
+
// upgrade), sudo fails instantly. Skip the upgrade in that case; the running
|
|
978
|
+
// installation is assumed adequate. Matches the apt-get skip in step 1.
|
|
979
|
+
if (isLinux() && !canSudo()) {
|
|
980
|
+
console.log(" Skipping Claude Code upgrade (sudo unavailable non-interactively — keeping installed version)");
|
|
981
|
+
}
|
|
982
|
+
else {
|
|
983
|
+
console.log(" This may take 15–30 minutes on Raspberry Pi...");
|
|
984
|
+
console.log(" [privileged] npm install -g @anthropic-ai/claude-code@latest");
|
|
985
|
+
shellRetry("npm", ["install", "-g", ...NPM_NET_FLAGS, "--loglevel", "verbose", "@anthropic-ai/claude-code@latest"], { sudo: true, timeout: 2_400_000 }, // 40 min — Pi downloads can take 25+ min
|
|
986
|
+
3, 30);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
console.log(" Registering Claude plugin marketplaces...");
|
|
990
|
+
// pre-create the per-brand CLAUDE_CONFIG_DIR so the first
|
|
991
|
+
// `claude plugin marketplace add` has a directory to write into.
|
|
992
|
+
mkdirSync(CLAUDE_CONFIG_DIR, { recursive: true });
|
|
993
|
+
// Task 583 — seed the brand-scoped permissions block so claude.ai/code
|
|
994
|
+
// sessions short-circuit Stage 1 of the local matcher and never fall
|
|
995
|
+
// through to the Anthropic remote auto-classifier (which routes Agent
|
|
996
|
+
// dispatch to `ask` and surfaces a prompt no unattended operator clicks).
|
|
997
|
+
const permissionsSeed = seedBypassPermissionsSettings(CLAUDE_CONFIG_DIR);
|
|
998
|
+
logFile(`[install-permissions] action=${permissionsSeed.action} path=${permissionsSeed.path}`);
|
|
999
|
+
const marketplaceList = spawnSync("claude", ["plugin", "marketplace", "list"], { stdio: "pipe", encoding: "utf-8", env: claudePluginEnv() });
|
|
1000
|
+
if (marketplaceList.stderr)
|
|
1001
|
+
process.stderr.write(marketplaceList.stderr);
|
|
1002
|
+
const listed = marketplaceList.stdout ?? "";
|
|
1003
|
+
// register three Anthropic marketplaces uniformly with
|
|
1004
|
+
// [plugin-marketplace] observability. Each slug is grep-checked against
|
|
1005
|
+
// `marketplace list` for idempotence; failures log exit + first stderr
|
|
1006
|
+
// line but do not abort the installer (one marketplace's outage must not
|
|
1007
|
+
// block the others).
|
|
1008
|
+
// `slug` is the GitHub repo path passed to `marketplace add`. `marketplaceName`
|
|
1009
|
+
// is the marketplace's declared `name` from its .claude-plugin/marketplace.json —
|
|
1010
|
+
// what `marketplace list` prints and what `<plugin>@<marketplace>` resolves on
|
|
1011
|
+
// install. For FSI the two diverge: repo `anthropics/financial-services`,
|
|
1012
|
+
// marketplace name `claude-for-financial-services`. The idempotence check
|
|
1013
|
+
// greps the declared name to avoid substring false-positives.
|
|
1014
|
+
const MARKETPLACES = [
|
|
1015
|
+
{ slug: "anthropics/claude-plugins-official", marketplaceName: "claude-plugins-official" },
|
|
1016
|
+
{ slug: "anthropics/knowledge-work-plugins", marketplaceName: "knowledge-work-plugins" },
|
|
1017
|
+
{ slug: "anthropics/financial-services", marketplaceName: "claude-for-financial-services" },
|
|
1018
|
+
];
|
|
1019
|
+
for (const { slug, marketplaceName } of MARKETPLACES) {
|
|
1020
|
+
if (listed.includes(marketplaceName)) {
|
|
1021
|
+
logFile(`[plugin-marketplace] added ${slug} idempotent=true CLAUDE_CONFIG_DIR=${CLAUDE_CONFIG_DIR}`);
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
const add = spawnSync("claude", ["plugin", "marketplace", "add", slug], { stdio: "pipe", encoding: "utf-8", timeout: 60_000, env: claudePluginEnv() });
|
|
1025
|
+
if (add.status === 0) {
|
|
1026
|
+
logFile(`[plugin-marketplace] added ${slug} idempotent=false CLAUDE_CONFIG_DIR=${CLAUDE_CONFIG_DIR}`);
|
|
1027
|
+
}
|
|
1028
|
+
else {
|
|
1029
|
+
const stderrShort = (add.stderr ?? "").split("\n")[0]?.slice(0, 200) ?? "";
|
|
1030
|
+
logFile(`[plugin-marketplace] ERROR add ${slug} exit=${add.status} stderr=${JSON.stringify(stderrShort)} CLAUDE_CONFIG_DIR=${CLAUDE_CONFIG_DIR}`);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
console.log(" Configuring git to use HTTPS for GitHub (plugin install support)...");
|
|
1034
|
+
shell("git", ["config", "--global", "url.https://github.com/.insteadOf", "git@github.com:"]);
|
|
1035
|
+
// Remove the Playwright plugin if previously installed. The programmatic MCP
|
|
1036
|
+
// server in claude-agent.ts (with --cdp-endpoint) is the single correct path.
|
|
1037
|
+
// The plugin creates a shadow server that intermittently handles tool calls
|
|
1038
|
+
// without --cdp-endpoint, causing Chrome launch failures on ARM64.
|
|
1039
|
+
const pluginList = spawnSync("claude", ["plugin", "list"], { stdio: "pipe", encoding: "utf-8", env: claudePluginEnv() });
|
|
1040
|
+
if (pluginList.stdout?.includes("playwright")) {
|
|
1041
|
+
console.log(" Removing Playwright plugin (replaced by programmatic CDP server)...");
|
|
1042
|
+
spawnSync("claude", ["plugin", "uninstall", "playwright"], { stdio: "inherit", env: claudePluginEnv() });
|
|
1043
|
+
}
|
|
1044
|
+
// Ensure @playwright/mcp and all its dependencies (including playwright-core)
|
|
1045
|
+
// are cached. Wipe any stale npx cache for it first — killed installs on Pi
|
|
1046
|
+
// leave corrupt temp dirs that block subsequent npm operations.
|
|
1047
|
+
console.log(" Pre-caching Playwright MCP server...");
|
|
1048
|
+
const npxDir = resolve(process.env.HOME ?? "/root", ".npm/_npx");
|
|
1049
|
+
const findResult = spawnSync("find", [npxDir, "-path", "*/@playwright/mcp/package.json", "-print", "-quit"], {
|
|
1050
|
+
encoding: "utf-8", stdio: "pipe",
|
|
1051
|
+
});
|
|
1052
|
+
const existingCache = findResult.stdout?.trim();
|
|
1053
|
+
if (existingCache) {
|
|
1054
|
+
// Nuke the entire npx cache entry to avoid ENOTEMPTY errors
|
|
1055
|
+
const cacheEntry = resolve(existingCache, "../../..");
|
|
1056
|
+
spawnSync("rm", ["-rf", cacheEntry], { stdio: "pipe" });
|
|
1057
|
+
}
|
|
1058
|
+
// Fresh install — npx creates a clean cache with all deps
|
|
1059
|
+
const npxResult = spawnSync("npx", ["-y", "@playwright/mcp@latest", "--version"], {
|
|
1060
|
+
stdio: "pipe",
|
|
1061
|
+
timeout: 180000,
|
|
1062
|
+
encoding: "utf-8",
|
|
1063
|
+
});
|
|
1064
|
+
// Verify playwright-core landed
|
|
1065
|
+
const verifyResult = spawnSync("find", [npxDir, "-path", "*/playwright-core/package.json", "-print", "-quit"], {
|
|
1066
|
+
encoding: "utf-8", stdio: "pipe",
|
|
1067
|
+
});
|
|
1068
|
+
if (verifyResult.stdout?.trim()) {
|
|
1069
|
+
console.log(" Playwright MCP server cached with all dependencies.");
|
|
1070
|
+
}
|
|
1071
|
+
else {
|
|
1072
|
+
console.log(` Warning: Playwright MCP cache may be incomplete (browser automation may not work).${npxResult.stderr ? ` npx stderr: ${npxResult.stderr.slice(0, 200)}` : ""}`);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
function resetNeo4jAuth(port = DEFAULT_NEO4J_PORT, dataDir = "/var/lib/neo4j") {
|
|
1076
|
+
const password = randomBytes(24).toString("base64url");
|
|
1077
|
+
const dedicated = port !== DEFAULT_NEO4J_PORT;
|
|
1078
|
+
const serviceName = dedicated ? `neo4j-${BRAND.hostname}` : "neo4j";
|
|
1079
|
+
console.log(` Resetting Neo4j auth with fresh password (${serviceName})...`);
|
|
1080
|
+
spawnSync("sudo", ["systemctl", "stop", serviceName], { stdio: "inherit" });
|
|
1081
|
+
// Clear the system database (stores auth/roles) and dbms auth config.
|
|
1082
|
+
// The neo4j user database (graph data) is preserved.
|
|
1083
|
+
// set-initial-password only works before the system DB's first start,
|
|
1084
|
+
// so we must delete it to make Neo4j treat the next start as initial.
|
|
1085
|
+
spawnSync("sudo", ["rm", "-rf",
|
|
1086
|
+
`${dataDir}/data/dbms`,
|
|
1087
|
+
`${dataDir}/data/databases/system`,
|
|
1088
|
+
`${dataDir}/data/transactions/system`,
|
|
1089
|
+
], { stdio: "inherit" });
|
|
1090
|
+
if (dedicated) {
|
|
1091
|
+
const confDir = `/etc/neo4j-${BRAND.hostname}`;
|
|
1092
|
+
// sudo env VAR=val passes the variable through sudo's env_reset
|
|
1093
|
+
spawnSync("sudo", ["env", `NEO4J_CONF=${confDir}`, "neo4j-admin", "dbms", "set-initial-password", "--", password], {
|
|
1094
|
+
stdio: "inherit",
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
else {
|
|
1098
|
+
console.log(" [privileged] neo4j-admin dbms");
|
|
1099
|
+
shell("neo4j-admin", ["dbms", "set-initial-password", "--", password], { sudo: true, redact: true });
|
|
1100
|
+
}
|
|
1101
|
+
console.log(" [privileged] systemctl start");
|
|
1102
|
+
shell("systemctl", ["start", serviceName], { sudo: true });
|
|
1103
|
+
console.log(" Waiting for Neo4j to start...");
|
|
1104
|
+
for (let i = 0; i < 15; i++) {
|
|
1105
|
+
const check = spawnSync("cypher-shell", [
|
|
1106
|
+
"-u", "neo4j", "-p", password,
|
|
1107
|
+
"-a", `bolt://localhost:${port}`,
|
|
1108
|
+
"RETURN 1",
|
|
1109
|
+
], { stdio: "pipe", timeout: 5000 });
|
|
1110
|
+
if (check.status === 0)
|
|
1111
|
+
break;
|
|
1112
|
+
spawnSync("sleep", ["2"]);
|
|
1113
|
+
}
|
|
1114
|
+
return password;
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* scrub plaintext neo4j passwords from pre-fix install-*.log files.
|
|
1118
|
+
* Calls platform/scripts/redact-install-logs.sh against the installer's LOG_DIR.
|
|
1119
|
+
* The script is idempotent; re-running on clean logs is a no-op. Failures here
|
|
1120
|
+
* are non-fatal — credential redaction is best-effort cleanup, not a blocker
|
|
1121
|
+
* for installation.
|
|
1122
|
+
*/
|
|
1123
|
+
function redactInstallLogs() {
|
|
1124
|
+
const script = resolve(INSTALL_DIR, "platform/scripts/redact-install-logs.sh");
|
|
1125
|
+
if (!existsSync(script)) {
|
|
1126
|
+
logFile("[redact-install-logs] script not found at " + script + " — skipping");
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
const r = spawnSync("bash", [script, "--dir", LOG_DIR], {
|
|
1130
|
+
stdio: "pipe",
|
|
1131
|
+
encoding: "utf-8",
|
|
1132
|
+
timeout: 30_000,
|
|
1133
|
+
});
|
|
1134
|
+
if (r.stdout)
|
|
1135
|
+
logFile(r.stdout.trim());
|
|
1136
|
+
if (r.status !== 0 && r.stderr)
|
|
1137
|
+
logFile("[redact-install-logs] WARN " + r.stderr.trim());
|
|
1138
|
+
}
|
|
1139
|
+
/** Check Neo4j has a working password. Called AFTER deploy so config is in place. */
|
|
1140
|
+
function ensureNeo4jPassword() {
|
|
1141
|
+
const passwordFile = join(INSTALL_DIR, "platform/config/.neo4j-password");
|
|
1142
|
+
const configDir = resolve(INSTALL_DIR, "platform/config");
|
|
1143
|
+
mkdirSync(configDir, { recursive: true });
|
|
1144
|
+
const persistDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
1145
|
+
const persistentPasswordFile = join(persistDir, ".neo4j-password");
|
|
1146
|
+
// Dedicated instances have their own auth database — password checks and resets
|
|
1147
|
+
// must target the dedicated port and data directory, not the shared instance.
|
|
1148
|
+
const dataDir = NEO4J_DEDICATED ? `/var/lib/neo4j-${BRAND.hostname}` : "/var/lib/neo4j";
|
|
1149
|
+
// 1. Same-brand check: if our own password file exists and works, done (upgrade path).
|
|
1150
|
+
if (existsSync(passwordFile)) {
|
|
1151
|
+
const existingPassword = readFileSync(passwordFile, "utf-8").trim();
|
|
1152
|
+
if (neo4jPasswordWorks(existingPassword, NEO4J_PORT)) {
|
|
1153
|
+
logFile(` Neo4j password: already configured (port ${NEO4J_PORT})`);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
if (NEO4J_DEDICATED) {
|
|
1157
|
+
console.log(" Stored password doesn't match dedicated Neo4j instance.");
|
|
1158
|
+
}
|
|
1159
|
+
else {
|
|
1160
|
+
console.log(" Stored password doesn't match Neo4j. Resetting auth.");
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
// 2. Fresh install or recovery: no working same-brand password. Generate and set a new one.
|
|
1164
|
+
// Brand isolation: never read another brand's.neo4j-password.
|
|
1165
|
+
if (!existsSync(passwordFile)) {
|
|
1166
|
+
console.log(" No Neo4j password file found. Setting initial password...");
|
|
1167
|
+
}
|
|
1168
|
+
// Reset auth and set a new password. Graph data is preserved —
|
|
1169
|
+
// only the auth config ({dataDir}/data/dbms/) is cleared.
|
|
1170
|
+
const hasData = existsSync(`${dataDir}/data/databases/neo4j`);
|
|
1171
|
+
if (hasData) {
|
|
1172
|
+
console.log(" Neo4j has existing data. Resetting auth only (data preserved)...");
|
|
1173
|
+
logFile(` Neo4j auth reset — clearing dbms auth in ${dataDir}, preserving databases + transactions`);
|
|
1174
|
+
}
|
|
1175
|
+
const password = resetNeo4jAuth(NEO4J_PORT, dataDir);
|
|
1176
|
+
writeFileSync(passwordFile, password, { mode: 0o600 });
|
|
1177
|
+
mkdirSync(persistDir, { recursive: true });
|
|
1178
|
+
writeFileSync(persistentPasswordFile, password, { mode: 0o600 });
|
|
1179
|
+
logFile(" Neo4j password: generated new");
|
|
1180
|
+
}
|
|
1181
|
+
/** Test a Neo4j password against the running instance on the given port. */
|
|
1182
|
+
function neo4jPasswordWorks(password, port = DEFAULT_NEO4J_PORT) {
|
|
1183
|
+
const check = spawnSync("cypher-shell", [
|
|
1184
|
+
"-u", "neo4j", "-p", password,
|
|
1185
|
+
"-a", `bolt://localhost:${port}`,
|
|
1186
|
+
"RETURN 1",
|
|
1187
|
+
], { stdio: "pipe", timeout: 10000 });
|
|
1188
|
+
return check.status === 0;
|
|
1189
|
+
}
|
|
1190
|
+
function installNeo4j() {
|
|
1191
|
+
if (commandExists("neo4j")) {
|
|
1192
|
+
log("4", TOTAL, "Neo4j already installed.");
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
log("4", TOTAL, "Installing Neo4j Community Edition 5...");
|
|
1196
|
+
const platform = requireSupportedPlatform(process.platform);
|
|
1197
|
+
if (platform === "darwin") {
|
|
1198
|
+
// Homebrew's neo4j formula declares openjdk as a runtime dependency, so
|
|
1199
|
+
// Java is pulled in transitively — no separate openjdk install step.
|
|
1200
|
+
// Process supervision (launchd LaunchAgent) is owned; this
|
|
1201
|
+
// step ends after `brew install neo4j` + initial-password seeding so the
|
|
1202
|
+
// payload deploy step finds the shared password file.
|
|
1203
|
+
installAllBrewPackages(["neo4j"], logFile);
|
|
1204
|
+
// Generate strong random password — stored in persistent location (~/{configDir}/)
|
|
1205
|
+
const password = randomBytes(24).toString("base64url");
|
|
1206
|
+
const persistDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
1207
|
+
mkdirSync(persistDir, { recursive: true });
|
|
1208
|
+
writeFileSync(join(persistDir, ".neo4j-password"), password, { mode: 0o600 });
|
|
1209
|
+
const configDir = resolve(INSTALL_DIR, "platform/config");
|
|
1210
|
+
mkdirSync(configDir, { recursive: true });
|
|
1211
|
+
writeFileSync(join(configDir, ".neo4j-password"), password, { mode: 0o600 });
|
|
1212
|
+
// Persist Neo4j data under ~/.maxy/neo4j-data/ per the task brief, so a
|
|
1213
|
+
// brew uninstall (which removes the cellar) does not destroy the graph.
|
|
1214
|
+
const neo4jDataDir = resolve(process.env.HOME ?? "/root", BRAND.configDir, "neo4j-data");
|
|
1215
|
+
mkdirSync(neo4jDataDir, { recursive: true });
|
|
1216
|
+
shell("neo4j-admin", ["dbms", "set-initial-password", "--", password], { redact: true });
|
|
1217
|
+
// Start Neo4j under brew's launchd supervision. Linux path starts the
|
|
1218
|
+
// service via systemd a few lines below; without this start the darwin
|
|
1219
|
+
// brand server binds successfully but every Cypher write fails with
|
|
1220
|
+
// "Failed to connect to server" because nothing is listening on 7687.
|
|
1221
|
+
// `brew services start` is idempotent — if already running it no-ops,
|
|
1222
|
+
// and the LaunchAgent persists across reboots.
|
|
1223
|
+
const brewStart = spawnSync("brew", ["services", "start", "neo4j"], { stdio: "inherit" });
|
|
1224
|
+
if (brewStart.status !== 0) {
|
|
1225
|
+
throw new Error(`brew services start neo4j failed (exit ${brewStart.status}) — Neo4j must be running before the brand server starts or all graph writes will fail`);
|
|
1226
|
+
}
|
|
1227
|
+
console.log(" Neo4j installed and started via Homebrew launchd. Password stored securely.");
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
// Neo4j 5.x supports Java 17 and 21. Debian Bookworm ships 17, Trixie ships 21.
|
|
1231
|
+
// apt-cache policy shows "Candidate: (none)" when no installable version exists.
|
|
1232
|
+
const policyResult = spawnSync("apt-cache", ["policy", "openjdk-17-jre-headless"], { stdio: "pipe" });
|
|
1233
|
+
const policyOutput = policyResult.stdout?.toString() ?? "";
|
|
1234
|
+
const has17 = policyResult.status === 0 && !policyOutput.includes("Candidate: (none)");
|
|
1235
|
+
const javaPackage = has17 ? "openjdk-17-jre-headless" : "openjdk-21-jre-headless";
|
|
1236
|
+
console.log(` Installing Java (${javaPackage})...`);
|
|
1237
|
+
console.log(" [privileged] apt-get install");
|
|
1238
|
+
shell("apt-get", ["install", "-y", javaPackage], { sudo: true });
|
|
1239
|
+
spawnSync("bash", ["-c", "curl -fsSL https://debian.neo4j.com/neotechnology.gpg.key | sudo gpg --yes --dearmor -o /usr/share/keyrings/neo4j.gpg 2>/dev/null"], { stdio: "inherit" });
|
|
1240
|
+
spawnSync("bash", ["-c", 'echo "deb [signed-by=/usr/share/keyrings/neo4j.gpg] https://debian.neo4j.com stable 5" | sudo tee /etc/apt/sources.list.d/neo4j.list'], { stdio: "inherit" });
|
|
1241
|
+
console.log(" [privileged] apt-get update");
|
|
1242
|
+
shell("apt-get", ["update"], { sudo: true });
|
|
1243
|
+
console.log(" [privileged] apt-get install");
|
|
1244
|
+
shell("apt-get", ["install", "-y", "neo4j"], { sudo: true });
|
|
1245
|
+
console.log(" [privileged] sed -i");
|
|
1246
|
+
shell("sed", ["-i", "s/#server.default_listen_address=0.0.0.0/server.default_listen_address=127.0.0.1/", "/etc/neo4j/neo4j.conf"], { sudo: true });
|
|
1247
|
+
// Generate strong random password — stored in persistent location (~/{configDir}/)
|
|
1248
|
+
const password = randomBytes(24).toString("base64url");
|
|
1249
|
+
const persistDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
1250
|
+
mkdirSync(persistDir, { recursive: true });
|
|
1251
|
+
writeFileSync(join(persistDir, ".neo4j-password"), password, { mode: 0o600 });
|
|
1252
|
+
// Also write to install dir (will be there when deploy step runs)
|
|
1253
|
+
const configDir = resolve(INSTALL_DIR, "platform/config");
|
|
1254
|
+
mkdirSync(configDir, { recursive: true });
|
|
1255
|
+
writeFileSync(join(configDir, ".neo4j-password"), password, { mode: 0o600 });
|
|
1256
|
+
console.log(" [privileged] neo4j-admin dbms");
|
|
1257
|
+
shell("neo4j-admin", ["dbms", "set-initial-password", "--", password], { sudo: true, redact: true });
|
|
1258
|
+
console.log(" [privileged] systemctl enable");
|
|
1259
|
+
shell("systemctl", ["enable", "neo4j"], { sudo: true });
|
|
1260
|
+
console.log(" [privileged] systemctl start");
|
|
1261
|
+
shell("systemctl", ["start", "neo4j"], { sudo: true });
|
|
1262
|
+
console.log(" Neo4j started. Password stored securely.");
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* does any peer brand on this host pin `NEO4J_URI=bolt://localhost:<default>`
|
|
1266
|
+
* in its `.env`? If yes, the apt-installed `neo4j.service` is its database and the
|
|
1267
|
+
* dedicated-unit installer must NOT stop+disable it. Returns the first matching
|
|
1268
|
+
* peer's hostname, or `null` when no peer pins the default port.
|
|
1269
|
+
*
|
|
1270
|
+
* Wraps the pure decision in `peer-brand-detect.ts` with the fs reads of
|
|
1271
|
+
* `~/.<peer>/.env`. Bias on read errors: if `.env` exists but is unreadable
|
|
1272
|
+
* (permissions, transient I/O), the wrapper treats that peer as a *potential*
|
|
1273
|
+
* dependency and short-circuits to the kept-active path. Disabling the system
|
|
1274
|
+
* unit on faulty evidence would silently kill the peer's database (the exact
|
|
1275
|
+
* failure mode the kept-active path prevents); a conservatively-skipped disable is recoverable
|
|
1276
|
+
* because the dedicated-unit bind check at the end of `setupDedicatedNeo4j`
|
|
1277
|
+
* fails loud if the system unit is actually free.
|
|
1278
|
+
*/
|
|
1279
|
+
function peerBrandUsingSystemUnit() {
|
|
1280
|
+
const home = process.env.HOME ?? "/root";
|
|
1281
|
+
const peerEnvContents = [];
|
|
1282
|
+
for (const hostname of KNOWN_BRAND_HOSTNAMES) {
|
|
1283
|
+
if (hostname === BRAND.hostname) {
|
|
1284
|
+
peerEnvContents.push([hostname, null]);
|
|
1285
|
+
continue;
|
|
1286
|
+
}
|
|
1287
|
+
const envPath = resolve(home, `.${hostname}`, ".env");
|
|
1288
|
+
if (!existsSync(envPath)) {
|
|
1289
|
+
peerEnvContents.push([hostname, null]);
|
|
1290
|
+
continue;
|
|
1291
|
+
}
|
|
1292
|
+
try {
|
|
1293
|
+
peerEnvContents.push([hostname, readFileSync(envPath, "utf-8")]);
|
|
1294
|
+
}
|
|
1295
|
+
catch (err) {
|
|
1296
|
+
console.error(` WARNING: unable to read peer brand .env at ${envPath} — treating as potential dependency to avoid data loss: ${err instanceof Error ? err.message : String(err)}`);
|
|
1297
|
+
return hostname;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
return findPeerBrandOnDefaultNeo4jPort({
|
|
1301
|
+
currentBrandHostname: BRAND.hostname,
|
|
1302
|
+
defaultNeo4jPort: DEFAULT_NEO4J_PORT,
|
|
1303
|
+
peerEnvContents,
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Create a dedicated Neo4j instance for this brand when NEO4J_DEDICATED is true.
|
|
1308
|
+
* Produces: separate config dir, data dir, log dir, systemd service, and password.
|
|
1309
|
+
* On upgrade (config already exists), skips conf creation — but always runs the
|
|
1310
|
+
* state-remediation block (stop/disable system unit, reset-failed
|
|
1311
|
+
* dedicated, start, verify) so a half-installed Pi recovers in-place without
|
|
1312
|
+
* manual systemctl. ensureNeo4jPassword() handles password verification on the
|
|
1313
|
+
* recovery path.
|
|
1314
|
+
*
|
|
1315
|
+
* on multi-brand hosts where a peer brand still depends on the apt
|
|
1316
|
+
* `neo4j.service` (port 7687), the stop+disable step is skipped — disabling
|
|
1317
|
+
* the system unit would kill the peer's database.
|
|
1318
|
+
*/
|
|
1319
|
+
function setupDedicatedNeo4j() {
|
|
1320
|
+
if (!NEO4J_DEDICATED)
|
|
1321
|
+
return;
|
|
1322
|
+
const brandSuffix = BRAND.hostname; // e.g., "realagent"
|
|
1323
|
+
const confDir = `/etc/neo4j-${brandSuffix}`;
|
|
1324
|
+
const dataDir = `/var/lib/neo4j-${brandSuffix}`;
|
|
1325
|
+
const logDir = `/var/log/neo4j-${brandSuffix}`;
|
|
1326
|
+
const serviceName = `neo4j-${brandSuffix}`;
|
|
1327
|
+
const httpPort = NEO4J_PORT - 213; // Preserve standard 7687/7474 offset
|
|
1328
|
+
// Per-brand state (sed, mkdir, chown, unit-write) is idempotent and runs on
|
|
1329
|
+
// every install. Only the base-config copy and initial-password rotation are
|
|
1330
|
+
// gated to first install established that state remediation must
|
|
1331
|
+
// run every install extended the same principle to the conf and
|
|
1332
|
+
// unit emission so a half-installed host (broken unit missing NEO4J_HOME)
|
|
1333
|
+
// recovers on retry without an out-of-band manual reset.
|
|
1334
|
+
const confExists = spawnSync("test", ["-f", `${confDir}/neo4j.conf`], { stdio: "pipe" }).status === 0;
|
|
1335
|
+
if (confExists) {
|
|
1336
|
+
console.log(` Dedicated Neo4j instance for ${BRAND.productName} already configured at ${confDir} — re-applying per-brand state`);
|
|
1337
|
+
logFile(` Neo4j dedicated: existing config at ${confDir}, re-applying sed/mkdir/chown/unit on every install`);
|
|
1338
|
+
}
|
|
1339
|
+
else {
|
|
1340
|
+
console.log(` Setting up dedicated Neo4j instance for ${BRAND.productName} on bolt://localhost:${NEO4J_PORT}...`);
|
|
1341
|
+
// Pre-check: neo4j user must exist (created by the apt package)
|
|
1342
|
+
const neo4jUserCheck = spawnSync("id", ["neo4j"], { stdio: "pipe" });
|
|
1343
|
+
if (neo4jUserCheck.status !== 0) {
|
|
1344
|
+
throw new Error("Neo4j system user 'neo4j' not found. Is Neo4j installed via apt?");
|
|
1345
|
+
}
|
|
1346
|
+
// Pre-check: source config must exist
|
|
1347
|
+
if (!existsSync("/etc/neo4j/neo4j.conf")) {
|
|
1348
|
+
throw new Error("/etc/neo4j/neo4j.conf not found. Cannot create dedicated instance without base config.");
|
|
1349
|
+
}
|
|
1350
|
+
// Copy base config (first install only — sed runs unconditionally below)
|
|
1351
|
+
console.log(" [privileged] cp -r");
|
|
1352
|
+
shell("cp", ["-r", "/etc/neo4j", confDir], { sudo: true });
|
|
1353
|
+
}
|
|
1354
|
+
// Idempotent per-brand state — runs on every install.
|
|
1355
|
+
// Older Neo4j 5.x templates ship a SECOND commented occurrence of these
|
|
1356
|
+
// keys in the "Other Neo4j system properties" section. An in-place
|
|
1357
|
+
// `sed s|^#?key=.*|key=value|` matches both and writes duplicate active
|
|
1358
|
+
// lines; the 5.26.26 parser turns that into a fatal startup error.
|
|
1359
|
+
// Delete-all-then-append guarantees exactly one active line per key
|
|
1360
|
+
// regardless of how many commented occurrences the template ships.
|
|
1361
|
+
const confPath = `${confDir}/neo4j.conf`;
|
|
1362
|
+
const confKeys = [
|
|
1363
|
+
["server.bolt.listen_address", `:${NEO4J_PORT}`],
|
|
1364
|
+
["server.http.listen_address", `:${httpPort}`],
|
|
1365
|
+
["server.directories.data", `${dataDir}/data`],
|
|
1366
|
+
["server.directories.logs", logDir],
|
|
1367
|
+
["server.directories.plugins", `${dataDir}/plugins`],
|
|
1368
|
+
["server.directories.import", `${dataDir}/import`],
|
|
1369
|
+
];
|
|
1370
|
+
for (const [key, value] of confKeys) {
|
|
1371
|
+
const keyRe = key.replace(/\./g, "\\.");
|
|
1372
|
+
console.log(" [privileged] sed -i (delete)");
|
|
1373
|
+
shell("sed", ["-i", `/^#\\?${keyRe}=/d`, confPath], { sudo: true });
|
|
1374
|
+
console.log(" [privileged] echo >>");
|
|
1375
|
+
shell("sh", ["-c", `echo '${key}=${value}' >> ${confPath}`], { sudo: true });
|
|
1376
|
+
}
|
|
1377
|
+
// Verify exactly one active line per key. Any n>1 aborts the install
|
|
1378
|
+
// BEFORE the systemd unit is written. n==0 means the append failed.
|
|
1379
|
+
const activeCounts = [];
|
|
1380
|
+
for (const [key] of confKeys) {
|
|
1381
|
+
const r = spawnSync("sudo", ["grep", "-c", `^${key}=`, confPath], { stdio: "pipe" });
|
|
1382
|
+
const n = parseInt((r.stdout?.toString() ?? "0").trim(), 10) || 0;
|
|
1383
|
+
activeCounts.push(`${key}:${n}`);
|
|
1384
|
+
if (n !== 1) {
|
|
1385
|
+
const msg = `[neo4j-conf] FATAL ${key} active_count=${n}`;
|
|
1386
|
+
console.error(` ${msg}`);
|
|
1387
|
+
logFile(` ${msg}`);
|
|
1388
|
+
throw new Error(msg);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
const summary = `[neo4j-conf] keys_active=${activeCounts.join(",")}`;
|
|
1392
|
+
console.log(` ${summary}`);
|
|
1393
|
+
logFile(` ${summary}`);
|
|
1394
|
+
console.log(" [privileged] mkdir -p");
|
|
1395
|
+
shell("mkdir", ["-p", `${dataDir}/data`, `${dataDir}/plugins`, `${dataDir}/import`, logDir], { sudo: true });
|
|
1396
|
+
console.log(" [privileged] chown -R");
|
|
1397
|
+
shell("chown", ["-R", "neo4j:neo4j", dataDir, logDir, confDir], { sudo: true });
|
|
1398
|
+
const serviceContent = `[Unit]
|
|
1399
|
+
Description=Neo4j Graph Database (${BRAND.productName})
|
|
1400
|
+
After=network-online.target
|
|
1401
|
+
Wants=network-online.target
|
|
1402
|
+
|
|
1403
|
+
[Service]
|
|
1404
|
+
ExecStart=/usr/bin/neo4j console
|
|
1405
|
+
Restart=on-failure
|
|
1406
|
+
User=neo4j
|
|
1407
|
+
Group=neo4j
|
|
1408
|
+
Environment="NEO4J_CONF=${confDir}" "NEO4J_HOME=${dataDir}"
|
|
1409
|
+
LimitNOFILE=60000
|
|
1410
|
+
|
|
1411
|
+
[Install]
|
|
1412
|
+
WantedBy=multi-user.target
|
|
1413
|
+
`;
|
|
1414
|
+
const tmpServicePath = `/tmp/${serviceName}.service`;
|
|
1415
|
+
writeFileSync(tmpServicePath, serviceContent);
|
|
1416
|
+
console.log(" [privileged] cp");
|
|
1417
|
+
shell("cp", [tmpServicePath, `/etc/systemd/system/${serviceName}.service`], { sudo: true });
|
|
1418
|
+
spawnSync("rm", ["-f", tmpServicePath]);
|
|
1419
|
+
logFile(` [neo4j] dedicated unit env: NEO4J_CONF=${confDir} NEO4J_HOME=${dataDir}`);
|
|
1420
|
+
if (!confExists) {
|
|
1421
|
+
// Set initial password before first start (first install only — rotation
|
|
1422
|
+
// on retry would brick an existing DB whose password is already stored).
|
|
1423
|
+
const password = randomBytes(24).toString("base64url");
|
|
1424
|
+
const persistDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
1425
|
+
mkdirSync(persistDir, { recursive: true });
|
|
1426
|
+
writeFileSync(join(persistDir, ".neo4j-password"), password, { mode: 0o600 });
|
|
1427
|
+
const configDir = resolve(INSTALL_DIR, "platform/config");
|
|
1428
|
+
mkdirSync(configDir, { recursive: true });
|
|
1429
|
+
writeFileSync(join(configDir, ".neo4j-password"), password, { mode: 0o600 });
|
|
1430
|
+
// sudo env VAR=val passes NEO4J_CONF through sudo's env_reset policy
|
|
1431
|
+
spawnSync("sudo", ["env", `NEO4J_CONF=${confDir}`, "neo4j-admin", "dbms", "set-initial-password", "--", password], {
|
|
1432
|
+
stdio: "inherit",
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
// ============================================================================
|
|
1436
|
+
// unified state remediation + start + verify.
|
|
1437
|
+
//
|
|
1438
|
+
// Runs on both fresh and recovery paths. The dedicated unit and the apt
|
|
1439
|
+
// package's system unit both exec /usr/bin/neo4j console without overriding
|
|
1440
|
+
// NEO4J_HOME, so server.directories.run resolves to /var/lib/neo4j/run for
|
|
1441
|
+
// both — the launcher refuses with "Neo4j is already running (pid:N)" if
|
|
1442
|
+
// the system unit holds the PID file. Stopping the system unit first frees
|
|
1443
|
+
// the run-state; disabling prevents it returning at next boot. reset-failed
|
|
1444
|
+
// clears any prior start-limit-hit from a half-installed Pi.
|
|
1445
|
+
// ============================================================================
|
|
1446
|
+
spawnSync("sudo", ["systemctl", "daemon-reload"], { stdio: "inherit" });
|
|
1447
|
+
console.log(" [privileged] systemctl enable");
|
|
1448
|
+
shell("systemctl", ["enable", serviceName], { sudo: true });
|
|
1449
|
+
// skip stop+disable when a peer brand on this host still depends
|
|
1450
|
+
// on the apt `neo4j.service` (port 7687). Disabling it would kill the peer's
|
|
1451
|
+
// database reproducer on Neo's laptop. The kept-active path is
|
|
1452
|
+
// mutually exclusive with the disable path: exactly one log line per install.
|
|
1453
|
+
const peerOnSystemUnit = peerBrandUsingSystemUnit();
|
|
1454
|
+
if (peerOnSystemUnit !== null) {
|
|
1455
|
+
const keptActiveMsg = ` [neo4j] system unit kept active — peer brand ${peerOnSystemUnit} depends on port ${DEFAULT_NEO4J_PORT}`;
|
|
1456
|
+
console.log(keptActiveMsg);
|
|
1457
|
+
logFile(keptActiveMsg);
|
|
1458
|
+
}
|
|
1459
|
+
else {
|
|
1460
|
+
console.log(` [neo4j] disabling system unit (brand-dedicated active on port ${NEO4J_PORT})`);
|
|
1461
|
+
logFile(` [neo4j] disabling system unit (brand-dedicated active on port ${NEO4J_PORT})`);
|
|
1462
|
+
shell("systemctl", ["stop", "neo4j"], { sudo: true });
|
|
1463
|
+
shell("systemctl", ["disable", "neo4j"], { sudo: true });
|
|
1464
|
+
}
|
|
1465
|
+
console.log(` [neo4j] reset-failed ${serviceName} before start`);
|
|
1466
|
+
logFile(` [neo4j] reset-failed ${serviceName} before start`);
|
|
1467
|
+
shell("systemctl", ["reset-failed", serviceName], { sudo: true, bestEffort: true });
|
|
1468
|
+
console.log(" [privileged] systemctl start");
|
|
1469
|
+
shell("systemctl", ["start", serviceName], { sudo: true });
|
|
1470
|
+
// Verify the dedicated unit bound its port. Password verification is
|
|
1471
|
+
// ensureNeo4jPassword()'s job (called next in the install pipeline) — that
|
|
1472
|
+
// function tests the stored password against this port and resets auth if
|
|
1473
|
+
// the password no longer matches the running instance.
|
|
1474
|
+
console.log(` Waiting for dedicated Neo4j instance on port ${NEO4J_PORT}...`);
|
|
1475
|
+
let listening = false;
|
|
1476
|
+
const portMatch = new RegExp(`:${NEO4J_PORT}\\b`);
|
|
1477
|
+
for (let i = 0; i < 15; i++) {
|
|
1478
|
+
const portCheck = spawnSync("ss", ["-tln"], { stdio: "pipe", timeout: 5000 });
|
|
1479
|
+
if (portMatch.test(portCheck.stdout?.toString() ?? "")) {
|
|
1480
|
+
listening = true;
|
|
1481
|
+
break;
|
|
1482
|
+
}
|
|
1483
|
+
spawnSync("sleep", ["2"]);
|
|
1484
|
+
}
|
|
1485
|
+
if (!listening) {
|
|
1486
|
+
// Loud failure — no silent fallback to the system instance.
|
|
1487
|
+
const portCheck = spawnSync("ss", ["-tlnp"], { stdio: "pipe", timeout: 5000 });
|
|
1488
|
+
const portLines = (portCheck.stdout?.toString() ?? "").split("\n").filter((l) => l.includes(String(NEO4J_PORT)));
|
|
1489
|
+
const diagnostic = portLines.length > 0 ? portLines.join("; ") : "nothing listening on port";
|
|
1490
|
+
const journal = spawnSync("journalctl", ["-u", serviceName, "--since", "5 min ago"], { stdio: "pipe", timeout: 5000 });
|
|
1491
|
+
const journalTail = (journal.stdout?.toString() ?? "").split("\n").slice(-20).join("\n");
|
|
1492
|
+
logFile(` Neo4j dedicated: failed to bind port ${NEO4J_PORT} — ${diagnostic}`);
|
|
1493
|
+
throw new Error(`Dedicated Neo4j instance ${serviceName} did not bind bolt://localhost:${NEO4J_PORT} within 30s.\n` +
|
|
1494
|
+
`Port ${NEO4J_PORT}: ${diagnostic}\n` +
|
|
1495
|
+
`journalctl -u ${serviceName} --since "5 min ago" | tail -20:\n${journalTail}`);
|
|
1496
|
+
}
|
|
1497
|
+
logFile(` Neo4j dedicated: config=${confDir} data=${dataDir} service=${serviceName} bolt=:${NEO4J_PORT} http=:${httpPort}`);
|
|
1498
|
+
console.log(` Dedicated Neo4j instance ready on bolt://localhost:${NEO4J_PORT}`);
|
|
1499
|
+
}
|
|
1500
|
+
function ensureOllamaServing() {
|
|
1501
|
+
const check = () => spawnSync("curl", ["-sf", "http://localhost:11434/api/tags"], {
|
|
1502
|
+
stdio: "pipe", timeout: 5_000,
|
|
1503
|
+
});
|
|
1504
|
+
if (check().status === 0)
|
|
1505
|
+
return;
|
|
1506
|
+
// Log which ollama binary we're using and its version
|
|
1507
|
+
const which = spawnSync("which", ["ollama"], { stdio: "pipe" });
|
|
1508
|
+
const ollamaPath = which.stdout?.toString().trim() || "not found";
|
|
1509
|
+
logFile(` Ollama binary: ${ollamaPath}`);
|
|
1510
|
+
const version = spawnSync("ollama", ["--version"], { stdio: "pipe", timeout: 5_000 });
|
|
1511
|
+
logFile(` Ollama version: ${version.stdout?.toString().trim() || version.stderr?.toString().trim() || `exit ${version.status}`}`);
|
|
1512
|
+
// Check if systemd service exists
|
|
1513
|
+
const svcCheck = spawnSync("systemctl", ["cat", "ollama"], { stdio: "pipe", timeout: 5_000 });
|
|
1514
|
+
logFile(` Ollama systemd service: ${svcCheck.status === 0 ? "exists" : "not found"}`);
|
|
1515
|
+
// Check if port 11434 is already in use by something else
|
|
1516
|
+
const portCheck = spawnSync("ss", ["-tlnp"], { stdio: "pipe", timeout: 5_000 });
|
|
1517
|
+
const portLines = portCheck.stdout?.toString().split("\n").filter((l) => l.includes("11434")) || [];
|
|
1518
|
+
if (portLines.length > 0)
|
|
1519
|
+
logFile(` Port 11434 in use: ${portLines.join("; ")}`);
|
|
1520
|
+
console.log(" Starting Ollama server...");
|
|
1521
|
+
logFile(" Ollama server not responding — starting ollama serve");
|
|
1522
|
+
// Capture ollama serve output to a log file for diagnostics
|
|
1523
|
+
const ollamaLog = join(LOG_DIR, "ollama-serve.log");
|
|
1524
|
+
const logFd = openSync(ollamaLog, "a");
|
|
1525
|
+
const child = spawn("ollama", ["serve"], {
|
|
1526
|
+
stdio: ["ignore", logFd, logFd], detached: true,
|
|
1527
|
+
});
|
|
1528
|
+
child.unref();
|
|
1529
|
+
closeSync(logFd);
|
|
1530
|
+
logFile(` Spawned ollama serve (PID ${child.pid}), log: ${ollamaLog}`);
|
|
1531
|
+
for (let elapsed = 0; elapsed < 30; elapsed += 3) {
|
|
1532
|
+
spawnSync("sleep", ["3"]);
|
|
1533
|
+
// Check if process is still alive
|
|
1534
|
+
const alive = child.pid && spawnSync("kill", ["-0", String(child.pid)], { stdio: "pipe" }).status === 0;
|
|
1535
|
+
logFile(` Poll ${elapsed + 3}s: pid ${child.pid} alive=${alive}`);
|
|
1536
|
+
if (check().status === 0) {
|
|
1537
|
+
logFile(` Ollama server ready after ${elapsed + 3}s`);
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
if (!alive) {
|
|
1541
|
+
// Process died — read the log to find out why
|
|
1542
|
+
const serveLog = readFileSync(ollamaLog, "utf-8").slice(-2000);
|
|
1543
|
+
logFile(` Ollama serve exited. Log tail:\n${serveLog}`);
|
|
1544
|
+
console.error(` Ollama serve exited unexpectedly. Log: ${ollamaLog}`);
|
|
1545
|
+
throw new Error(`Ollama serve failed to start. Log: ${ollamaLog}\n${serveLog}`);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
// Timed out — include diagnostics
|
|
1549
|
+
const serveLog = readFileSync(ollamaLog, "utf-8").slice(-2000);
|
|
1550
|
+
logFile(` Ollama serve timed out after 30s. Log tail:\n${serveLog}`);
|
|
1551
|
+
throw new Error(`Ollama server did not respond within 30s. Log: ${ollamaLog}\n${serveLog}`);
|
|
1552
|
+
}
|
|
1553
|
+
function installOllama(embedModel) {
|
|
1554
|
+
if (!commandExists("ollama")) {
|
|
1555
|
+
log("5", TOTAL, "Installing Ollama...");
|
|
1556
|
+
spawnSync("bash", ["-c", "curl -fsSL https://ollama.ai/install.sh | sh"], { stdio: "inherit" });
|
|
1557
|
+
}
|
|
1558
|
+
else {
|
|
1559
|
+
log("5", TOTAL, "Ollama already installed.");
|
|
1560
|
+
}
|
|
1561
|
+
ensureOllamaServing();
|
|
1562
|
+
console.log(` Pulling ${embedModel} embedding model...`);
|
|
1563
|
+
logFile(` Pulling embedding model: ${embedModel}`);
|
|
1564
|
+
shellRetry("ollama", ["pull", embedModel], { timeout: 600_000 }, 3, 15);
|
|
1565
|
+
}
|
|
1566
|
+
// uv/uvx bootstrap. Required by the `graph` MCP server which
|
|
1567
|
+
// spawns `uvx mcp-neo4j-cypher@0.6.0 --transport stdio`. Idempotent: when
|
|
1568
|
+
// uvx is already on PATH (or under $HOME/.local/bin) we skip. Non-fatal on
|
|
1569
|
+
// failure — the installer continues; the graph server will loudly fail
|
|
1570
|
+
// at session start with a clear "uvx not found" error, which is retriable
|
|
1571
|
+
// via a second installer run with network access.
|
|
1572
|
+
function installUv() {
|
|
1573
|
+
if (commandExists("uvx")) {
|
|
1574
|
+
logFile(" uv: already installed");
|
|
1575
|
+
console.log(" uv/uvx already installed.");
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
console.log(" Installing uv (Python tool runner — required by Neo4j MCP server)...");
|
|
1579
|
+
logFile(" uv: installing via astral.sh installer");
|
|
1580
|
+
// astral.sh installer auto-confirms when stdin is not a TTY (our case under
|
|
1581
|
+
// systemd-run). Historically we passed `-y`, which the script rejects with
|
|
1582
|
+
// "unknown option -y" and causes uv to never install on upgrade.
|
|
1583
|
+
const result = spawnSync("bash", ["-c", "curl -LsSf https://astral.sh/uv/install.sh | sh"], { stdio: "inherit" });
|
|
1584
|
+
if (result.status !== 0) {
|
|
1585
|
+
console.error(` WARNING: uv install exited ${result.status} — graph MCP server will fail at session start until this is retried`);
|
|
1586
|
+
logFile(` WARNING: uv install failed with status ${result.status}`);
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1589
|
+
// The installer writes uvx to $HOME/.local/bin — add it to PATH for the
|
|
1590
|
+
// remainder of this install so commandExists("uvx") works downstream.
|
|
1591
|
+
const localBin = `${process.env.HOME}/.local/bin`;
|
|
1592
|
+
if (process.env.PATH && !process.env.PATH.includes(localBin)) {
|
|
1593
|
+
process.env.PATH = `${localBin}:${process.env.PATH}`;
|
|
1594
|
+
}
|
|
1595
|
+
if (commandExists("uvx")) {
|
|
1596
|
+
logFile(" uv: install succeeded; uvx on PATH");
|
|
1597
|
+
}
|
|
1598
|
+
else {
|
|
1599
|
+
console.error(" WARNING: uv installed but uvx not on PATH — check $HOME/.local/bin");
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
function installCloudflared() {
|
|
1603
|
+
if (commandExists("cloudflared")) {
|
|
1604
|
+
log("6", TOTAL, "Cloudflared already installed.");
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
log("6", TOTAL, "Installing cloudflared...");
|
|
1608
|
+
const platform = requireSupportedPlatform(process.platform);
|
|
1609
|
+
if (platform === "darwin") {
|
|
1610
|
+
// Homebrew's `cloudflared` formula tracks the Cloudflare release stream;
|
|
1611
|
+
// tunnel-login flow stays unchanged (`feedback_no_api_token_route.md`).
|
|
1612
|
+
// `cloudflared service install` and `cloudflared tunnel route dns` are not
|
|
1613
|
+
// invoked by the installer on darwin — public reach is operator-driven
|
|
1614
|
+
// post-install (`cloudflared tunnel login` + follow-up tooling). The brew
|
|
1615
|
+
// install puts the binary on PATH so the operator can opt in later.
|
|
1616
|
+
logFile(` [create-maxy] darwin-cloudflare-skip=true reason="Mac laptop — no public hostname provisioned at install time"`);
|
|
1617
|
+
installAllBrewPackages(["cloudflared"], logFile);
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
const arch = isArm64() ? "arm64" : "amd64";
|
|
1621
|
+
const debPath = "/tmp/cloudflared.deb";
|
|
1622
|
+
shellRetry("curl", ["-fSL", "--progress-bar", `https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${arch}.deb`, "-o", debPath], { timeout: 120_000 }, 3, 10);
|
|
1623
|
+
console.log(" [privileged] dpkg -i");
|
|
1624
|
+
shell("dpkg", ["-i", debPath], { sudo: true });
|
|
1625
|
+
spawnSync("rm", ["-f", debPath]);
|
|
1626
|
+
}
|
|
1627
|
+
function installWhisperCpp() {
|
|
1628
|
+
const WHISPER_DIR = "/opt/whisper.cpp";
|
|
1629
|
+
const WHISPER_BINARY = join(WHISPER_DIR, "build/bin/whisper-cli");
|
|
1630
|
+
const WHISPER_MODEL = join(WHISPER_DIR, "models", "ggml-base.bin");
|
|
1631
|
+
if (existsSync(WHISPER_BINARY) && existsSync(WHISPER_MODEL)) {
|
|
1632
|
+
log("7", TOTAL, "whisper.cpp already installed.");
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
log("7", TOTAL, "Installing whisper.cpp (speech-to-text)...");
|
|
1636
|
+
if (!isLinux()) {
|
|
1637
|
+
console.log(" Skipping — install manually for your platform.");
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
// Build dependencies — cmake is required since whisper.cpp migrated from plain make
|
|
1641
|
+
console.log(" [privileged] apt-get install");
|
|
1642
|
+
shell("apt-get", ["install", "-y", "build-essential", "cmake"], { sudo: true });
|
|
1643
|
+
// Clone or update the repository
|
|
1644
|
+
if (!existsSync(WHISPER_DIR)) {
|
|
1645
|
+
console.log(" Cloning whisper.cpp...");
|
|
1646
|
+
console.log(" [privileged] git clone");
|
|
1647
|
+
shell("git", ["clone", "--depth", "1", "https://github.com/ggerganov/whisper.cpp.git", WHISPER_DIR], { sudo: true });
|
|
1648
|
+
}
|
|
1649
|
+
// Compile via cmake (whisper.cpp's Makefile is a thin cmake wrapper)
|
|
1650
|
+
console.log(" Compiling whisper.cpp (this takes a few minutes on Pi)...");
|
|
1651
|
+
console.log(" [privileged] cmake -B");
|
|
1652
|
+
shell("cmake", ["-B", "build"], { cwd: WHISPER_DIR, sudo: true, timeout: 120_000 });
|
|
1653
|
+
console.log(" [privileged] cmake --build");
|
|
1654
|
+
shell("cmake", ["--build", "build", "--config", "Release", "-j2"], { cwd: WHISPER_DIR, sudo: true, timeout: 600_000 });
|
|
1655
|
+
// Download the base model (~150MB)
|
|
1656
|
+
if (!existsSync(WHISPER_MODEL)) {
|
|
1657
|
+
console.log(" Downloading ggml-base model (~150MB)...");
|
|
1658
|
+
console.log(" [privileged] bash -c");
|
|
1659
|
+
shellRetry("bash", ["-c", `cd ${WHISPER_DIR} && bash models/download-ggml-model.sh base`], { sudo: true, timeout: 300_000 }, 3, 15);
|
|
1660
|
+
}
|
|
1661
|
+
console.log(" whisper.cpp installed successfully.");
|
|
1662
|
+
}
|
|
1663
|
+
/**
|
|
1664
|
+
* Provision the shared HMAC secret used to sign remote-session cookies
|
|
1665
|
+
*. Both `maxy-edge` and `maxy-ui` read this file; without it
|
|
1666
|
+
* they independently mint ephemeral secrets on first use and the
|
|
1667
|
+
* cross-process session namespace silently diverges again.
|
|
1668
|
+
*
|
|
1669
|
+
* First install: create the file (0600, 32-byte hex).
|
|
1670
|
+
* Upgrade: leave the existing file untouched — invalidating it here
|
|
1671
|
+
* would log every operator out on every upgrade.
|
|
1672
|
+
*/
|
|
1673
|
+
function provisionRemoteSessionSecret() {
|
|
1674
|
+
const persistDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
1675
|
+
const credentialsDir = join(persistDir, "credentials");
|
|
1676
|
+
const secretFile = join(credentialsDir, "remote-session-secret");
|
|
1677
|
+
if (existsSync(secretFile)) {
|
|
1678
|
+
console.log(` [install] remote-session-secret exists — preserved`);
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
mkdirSync(credentialsDir, { recursive: true, mode: 0o700 });
|
|
1682
|
+
writeFileSync(secretFile, randomBytes(32).toString("hex"), { mode: 0o600 });
|
|
1683
|
+
console.log(` [install] remote-session-secret provisioned path=${secretFile}`);
|
|
1684
|
+
}
|
|
1685
|
+
// install-time admin-auth invariant. Walks every account.json
|
|
1686
|
+
// under accountsDir and compares its admins[] to the persistent users.json,
|
|
1687
|
+
// emitting one [install-invariant] line per divergence and one summary line.
|
|
1688
|
+
// Log-only (no install abort); future sprint can promote to refuse-or-degrade
|
|
1689
|
+
// once the deployed fleet is audited clean. Mirror of
|
|
1690
|
+
// checkAdminAuthInvariant() in platform/lib/admins-write/src/index.ts.
|
|
1691
|
+
function runInstallInvariantCheck(usersFile, accountsDir) {
|
|
1692
|
+
const TAG = "[install-invariant]";
|
|
1693
|
+
const usersUserIds = new Set();
|
|
1694
|
+
if (existsSync(usersFile)) {
|
|
1695
|
+
try {
|
|
1696
|
+
const raw = readFileSync(usersFile, "utf-8").trim();
|
|
1697
|
+
if (raw) {
|
|
1698
|
+
const users = JSON.parse(raw);
|
|
1699
|
+
for (const u of users) {
|
|
1700
|
+
if (typeof u.userId === "string")
|
|
1701
|
+
usersUserIds.add(u.userId);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
catch (err) {
|
|
1706
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1707
|
+
console.log(` ${TAG} users.json unreadable usersFile=${usersFile} error=${msg}`);
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
const accountUserIds = new Set();
|
|
1711
|
+
let divergences = 0;
|
|
1712
|
+
if (existsSync(accountsDir)) {
|
|
1713
|
+
let entries = [];
|
|
1714
|
+
try {
|
|
1715
|
+
entries = readdirSync(accountsDir);
|
|
1716
|
+
}
|
|
1717
|
+
catch (err) {
|
|
1718
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1719
|
+
console.log(` ${TAG} accounts-dir unreadable accountsDir=${accountsDir} error=${msg}`);
|
|
1720
|
+
console.log(` ${TAG} check complete divergences=0 (accounts-dir unreadable)`);
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
for (const entry of entries) {
|
|
1724
|
+
if (entry.startsWith("."))
|
|
1725
|
+
continue;
|
|
1726
|
+
const accountDir = join(accountsDir, entry);
|
|
1727
|
+
try {
|
|
1728
|
+
if (!statSync(accountDir).isDirectory())
|
|
1729
|
+
continue;
|
|
1730
|
+
}
|
|
1731
|
+
catch {
|
|
1732
|
+
continue;
|
|
1733
|
+
}
|
|
1734
|
+
const accountJsonPath = join(accountDir, "account.json");
|
|
1735
|
+
if (!existsSync(accountJsonPath))
|
|
1736
|
+
continue;
|
|
1737
|
+
let admins = [];
|
|
1738
|
+
try {
|
|
1739
|
+
const config = JSON.parse(readFileSync(accountJsonPath, "utf-8"));
|
|
1740
|
+
admins = (config.admins ?? []);
|
|
1741
|
+
}
|
|
1742
|
+
catch (err) {
|
|
1743
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1744
|
+
console.log(` ${TAG} account.json unreadable source=${accountJsonPath} error=${msg}`);
|
|
1745
|
+
continue;
|
|
1746
|
+
}
|
|
1747
|
+
for (const a of admins) {
|
|
1748
|
+
if (typeof a.userId !== "string")
|
|
1749
|
+
continue;
|
|
1750
|
+
accountUserIds.add(a.userId);
|
|
1751
|
+
if (!usersUserIds.has(a.userId)) {
|
|
1752
|
+
const userIdShort = a.userId.slice(0, 8);
|
|
1753
|
+
console.log(` ${TAG} direction=account-without-users userId=${userIdShort} source=${accountJsonPath}`);
|
|
1754
|
+
divergences += 1;
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
for (const uid of usersUserIds) {
|
|
1760
|
+
if (!accountUserIds.has(uid)) {
|
|
1761
|
+
const userIdShort = uid.slice(0, 8);
|
|
1762
|
+
console.log(` ${TAG} direction=users-without-account userId=${userIdShort} source=${usersFile}`);
|
|
1763
|
+
divergences += 1;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
console.log(` ${TAG} check complete divergences=${divergences}`);
|
|
1767
|
+
}
|
|
1768
|
+
function deployPayload() {
|
|
1769
|
+
log("8", TOTAL, `Deploying ${BRAND.productName}...`);
|
|
1770
|
+
if (!existsSync(PAYLOAD_DIR)) {
|
|
1771
|
+
throw new Error(`Payload not found at ${PAYLOAD_DIR}. Package may be corrupted.`);
|
|
1772
|
+
}
|
|
1773
|
+
// Persistent config lives at ~/{configDir}/ — survives rm -rf ~/{installDir}
|
|
1774
|
+
const persistentDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
1775
|
+
const persistentPasswordFile = join(persistentDir, ".neo4j-password");
|
|
1776
|
+
// users.json lives at <persistentDir>/users.json. Writers (paths.ts,
|
|
1777
|
+
// admin-add, set-pin, seed-neo4j.sh) target the persistent file directly —
|
|
1778
|
+
// see comment in platform/ui/app/lib/paths.ts.
|
|
1779
|
+
const persistentUsersFile = join(persistentDir, "users.json");
|
|
1780
|
+
// Claude Code OAuth credentials live at
|
|
1781
|
+
// `${persistDir}/.claude/.credentials.json`. The brand main service's
|
|
1782
|
+
// `claude` SDK subprocess and admin server read from there via the
|
|
1783
|
+
// Environment=CLAUDE_CONFIG_DIR= block in port-resolution.ts. Fresh
|
|
1784
|
+
// installs prompt the operator to run `claude /login`.
|
|
1785
|
+
const persistentClaudeDir = join(persistentDir, ".claude");
|
|
1786
|
+
mkdirSync(persistentClaudeDir, { recursive: true });
|
|
1787
|
+
// pre-populate the brand `.claude.json` so the manager's
|
|
1788
|
+
// PTY-spawned `claude --verbose --remote-control` boots straight into the
|
|
1789
|
+
// session URL without stopping at the first-run theme picker or the
|
|
1790
|
+
// workspace-trust dialog. Both prompts ate the manager's 30 s url-capture
|
|
1791
|
+
// window on 0.1.6 / 0.1.8 installs (Pi 192.168.88.16 verified). The two
|
|
1792
|
+
// markers required are:
|
|
1793
|
+
// - hasCompletedOnboarding: true (suppresses theme picker)
|
|
1794
|
+
// - projects[$HOME].hasTrustDialogAccepted: true
|
|
1795
|
+
// Existing fields (firstStartTime, oauthAccount, migrationVersion, …) are
|
|
1796
|
+
// preserved so a brand re-install does not clobber state written by claude
|
|
1797
|
+
// during the previous run.
|
|
1798
|
+
const claudeStateFile = join(persistentClaudeDir, ".claude.json");
|
|
1799
|
+
const homeWorkspace = process.env.HOME ?? "/root";
|
|
1800
|
+
let claudeState = {};
|
|
1801
|
+
if (existsSync(claudeStateFile)) {
|
|
1802
|
+
try {
|
|
1803
|
+
claudeState = JSON.parse(readFileSync(claudeStateFile, "utf-8"));
|
|
1804
|
+
}
|
|
1805
|
+
catch {
|
|
1806
|
+
claudeState = {};
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
claudeState.hasCompletedOnboarding = true;
|
|
1810
|
+
claudeState.theme = claudeState.theme ?? "auto";
|
|
1811
|
+
const projects = (claudeState.projects ?? {});
|
|
1812
|
+
const homeEntry = projects[homeWorkspace] ?? {};
|
|
1813
|
+
homeEntry.hasTrustDialogAccepted = true;
|
|
1814
|
+
projects[homeWorkspace] = homeEntry;
|
|
1815
|
+
claudeState.projects = projects;
|
|
1816
|
+
writeFileSync(claudeStateFile, JSON.stringify(claudeState, null, 2) + "\n", { mode: 0o600 });
|
|
1817
|
+
console.log(` [install] claude-state primed: ${claudeStateFile} hasCompletedOnboarding=true trustedWorkspace=${homeWorkspace}`);
|
|
1818
|
+
// Brand isolation: installer does not read ~/.maxy/, ~/.cloudflared/, or
|
|
1819
|
+
// ~/.cloudflare/ on non-default brands. These are peer-brand or
|
|
1820
|
+
// shared-singleton paths.
|
|
1821
|
+
// Stop the running service before wiping directories (upgrade path).
|
|
1822
|
+
// The server holds open files in platform/ — rmSync fails with ENOTEMPTY if it's running.
|
|
1823
|
+
// darwin uses `launchctl bootout` instead of systemctl; bootout
|
|
1824
|
+
// returns synchronously when the agent has exited.
|
|
1825
|
+
if (requireSupportedPlatform(process.platform) === "darwin") {
|
|
1826
|
+
spawnSync("launchctl", ["bootout", `${gui()}/${launchdLabel()}`], { stdio: "pipe", timeout: 15_000 });
|
|
1827
|
+
}
|
|
1828
|
+
else {
|
|
1829
|
+
// systemctl stop returns when the main process exits, but ExecStopPost (e.g. VNC cleanup)
|
|
1830
|
+
// may still hold file handles. Poll is-active to wait for full deactivation.
|
|
1831
|
+
const svcName = BRAND.serviceName.replace(".service", "");
|
|
1832
|
+
spawnSync("systemctl", ["--user", "stop", svcName], { stdio: "pipe" });
|
|
1833
|
+
const MAX_STOP_WAIT = 5;
|
|
1834
|
+
for (let i = 0; i < MAX_STOP_WAIT; i++) {
|
|
1835
|
+
const result = spawnSync("systemctl", ["--user", "is-active", svcName], { stdio: "pipe" });
|
|
1836
|
+
const status = result.stdout?.toString().trim();
|
|
1837
|
+
if (status !== "active" && status !== "deactivating") {
|
|
1838
|
+
console.log(` Service stopped (${status || "not found"}).`);
|
|
1839
|
+
break;
|
|
1840
|
+
}
|
|
1841
|
+
if (i === MAX_STOP_WAIT - 1) {
|
|
1842
|
+
console.log(` [WARN] Service still ${status} after ${MAX_STOP_WAIT}s — proceeding with directory wipe.`);
|
|
1843
|
+
break;
|
|
1844
|
+
}
|
|
1845
|
+
spawnSync("sleep", ["1"]);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
// User data lives at {installDir}/data/ — outside the platform/ wipe zone.
|
|
1849
|
+
// Install path creates this fresh; runtime writers populate it on demand.
|
|
1850
|
+
mkdirSync(join(INSTALL_DIR, "data"), { recursive: true });
|
|
1851
|
+
// Wipe deployment directories to prevent stale files.
|
|
1852
|
+
// data/ is NOT wiped — it contains user data (accounts, uploads) that must survive.
|
|
1853
|
+
for (const dir of ["platform", "server", "maxy", "premium-plugins", "docs", ".claude"]) {
|
|
1854
|
+
const target = join(INSTALL_DIR, dir);
|
|
1855
|
+
if (existsSync(target)) {
|
|
1856
|
+
rmSync(target, { recursive: true, force: true });
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
mkdirSync(INSTALL_DIR, { recursive: true });
|
|
1860
|
+
// Deploy payload
|
|
1861
|
+
cpSync(PAYLOAD_DIR, INSTALL_DIR, {
|
|
1862
|
+
recursive: true,
|
|
1863
|
+
force: true,
|
|
1864
|
+
});
|
|
1865
|
+
// Link persistent config into install directory
|
|
1866
|
+
const configDir = join(INSTALL_DIR, "platform/config");
|
|
1867
|
+
mkdirSync(configDir, { recursive: true });
|
|
1868
|
+
if (existsSync(persistentPasswordFile)) {
|
|
1869
|
+
cpSync(persistentPasswordFile, join(configDir, ".neo4j-password"));
|
|
1870
|
+
console.log(" Restored Neo4j password.");
|
|
1871
|
+
}
|
|
1872
|
+
// users.json is read directly from persistentDir by both
|
|
1873
|
+
// platform/ui/app/lib/paths.ts (USERS_FILE = MAXY_DIR/users.json) and the
|
|
1874
|
+
// admin MCP plugin (USERS_FILE = CONFIG_DIR/users.json). No copy into the
|
|
1875
|
+
// wipe zone. The line below observes the row count + userId prefixes so a
|
|
1876
|
+
// future regression is grep-detectable in the install log without any
|
|
1877
|
+
// runtime change.
|
|
1878
|
+
if (existsSync(persistentUsersFile)) {
|
|
1879
|
+
try {
|
|
1880
|
+
const raw = readFileSync(persistentUsersFile, "utf-8").trim();
|
|
1881
|
+
const rows = raw ? JSON.parse(raw) : [];
|
|
1882
|
+
const ids = rows
|
|
1883
|
+
.map(r => (typeof r.userId === "string" ? r.userId.slice(0, 8) : "?"))
|
|
1884
|
+
.join(",");
|
|
1885
|
+
console.log(` [install] users.json preserved: rows=${rows.length} userIds=${ids}`);
|
|
1886
|
+
}
|
|
1887
|
+
catch (err) {
|
|
1888
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1889
|
+
console.log(` [install] users.json preserved: rows=? parse-failed error=${msg}`);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
else {
|
|
1893
|
+
console.log(" [install] users.json: no persistent file (fresh install — set-pin will create it)");
|
|
1894
|
+
}
|
|
1895
|
+
// install-time admin-auth invariant check. Walks every
|
|
1896
|
+
// data/accounts/*/account.json admins[] and compares to the persistent
|
|
1897
|
+
// users.json. Log-only (does NOT refuse install); the [install-invariant]
|
|
1898
|
+
// line surfaces the bug to the operator without bricking the device.
|
|
1899
|
+
// Inline rather than imported from platform/lib/admins-write because the
|
|
1900
|
+
// installer is its own npm package and doesn't bundle the platform lib.
|
|
1901
|
+
// Mirrors checkAdminAuthInvariant() in platform/lib/admins-write/src/index.ts;
|
|
1902
|
+
// future divergence between the two should be caught by the test suite.
|
|
1903
|
+
runInstallInvariantCheck(persistentUsersFile, join(INSTALL_DIR, "data", "accounts"));
|
|
1904
|
+
// Write version marker so the running platform knows which create-maxy produced this deployment
|
|
1905
|
+
const versionMarkerPath = join(configDir, `.${BRAND.hostname}-version`);
|
|
1906
|
+
writeFileSync(versionMarkerPath, PKG_VERSION, "utf-8");
|
|
1907
|
+
console.log(` [install] version-marker written path=${versionMarkerPath} version=${PKG_VERSION}`);
|
|
1908
|
+
console.log(` Deployed to ${INSTALL_DIR}`);
|
|
1909
|
+
}
|
|
1910
|
+
// register the local + external Claude Code plugins after the
|
|
1911
|
+
// payload is on disk. The bundler stamps `.claude-plugin/marketplace.json`
|
|
1912
|
+
// at each plugin-tree root (platform/plugins/, premium-plugins/real-agent/
|
|
1913
|
+
// plugins/, premium-plugins/) so this function discovers them generically
|
|
1914
|
+
// from the install directory rather than hardcoding names.
|
|
1915
|
+
//
|
|
1916
|
+
// Idempotent on every step: marketplace add is skipped when already in
|
|
1917
|
+
// `marketplace list`; `plugin install` is skipped when the plugin is
|
|
1918
|
+
// already in `plugin list` (matched by `<name>@<marketplace>` or by name
|
|
1919
|
+
// alone). One `[plugin-install] <name>@<src>
|
|
1920
|
+
// idempotent=<bool>` log line per attempt; failures log
|
|
1921
|
+
// `[plugin-install] ERROR <name>@<src> exit=<n> stderr=<short>` and do
|
|
1922
|
+
// NOT abort — one plugin failing must not block the rest, mirroring the
|
|
1923
|
+
// marketplace-add behaviour at src/index.ts:1018-1019.
|
|
1924
|
+
function registerLocalAndExternalPlugins() {
|
|
1925
|
+
console.log(" Registering local + external Claude Code plugins...");
|
|
1926
|
+
const localTrees = [];
|
|
1927
|
+
// The three known marketplace.json locations the bundler emits. Missing
|
|
1928
|
+
// ones are silently skipped (e.g. a brand that ships no premium plugins).
|
|
1929
|
+
const candidates = [
|
|
1930
|
+
join(INSTALL_DIR, "platform", "plugins"),
|
|
1931
|
+
join(INSTALL_DIR, "premium-plugins", "real-agent", "plugins"),
|
|
1932
|
+
join(INSTALL_DIR, "premium-plugins"),
|
|
1933
|
+
];
|
|
1934
|
+
for (const dir of candidates) {
|
|
1935
|
+
const mkPath = join(dir, ".claude-plugin", "marketplace.json");
|
|
1936
|
+
if (!existsSync(mkPath))
|
|
1937
|
+
continue;
|
|
1938
|
+
try {
|
|
1939
|
+
const parsed = JSON.parse(readFileSync(mkPath, "utf-8"));
|
|
1940
|
+
if (typeof parsed.name === "string" && parsed.name.length > 0) {
|
|
1941
|
+
localTrees.push({ name: parsed.name, dir });
|
|
1942
|
+
}
|
|
1943
|
+
else {
|
|
1944
|
+
logFile(`[plugin-marketplace] ERROR malformed marketplace at ${mkPath} reason=no-name`);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
catch (err) {
|
|
1948
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1949
|
+
logFile(`[plugin-marketplace] ERROR parse ${mkPath} error=${JSON.stringify(msg.slice(0, 200))}`);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
// Add each local marketplace. Idempotence via `marketplace list` grep.
|
|
1953
|
+
// every `claude plugin …` call carries CLAUDE_CONFIG_DIR so
|
|
1954
|
+
// marketplaces register into the per-brand config dir that the runtime
|
|
1955
|
+
// session-manager unit reads from.
|
|
1956
|
+
const mkList = spawnSync("claude", ["plugin", "marketplace", "list"], { stdio: "pipe", encoding: "utf-8", env: claudePluginEnv() });
|
|
1957
|
+
const mkListed = mkList.stdout ?? "";
|
|
1958
|
+
for (const { name, dir } of localTrees) {
|
|
1959
|
+
if (mkListed.includes(name)) {
|
|
1960
|
+
logFile(`[plugin-marketplace] added ${name} idempotent=true CLAUDE_CONFIG_DIR=${CLAUDE_CONFIG_DIR}`);
|
|
1961
|
+
continue;
|
|
1962
|
+
}
|
|
1963
|
+
const add = spawnSync("claude", ["plugin", "marketplace", "add", dir], { stdio: "pipe", encoding: "utf-8", timeout: 60_000, env: claudePluginEnv() });
|
|
1964
|
+
if (add.status === 0) {
|
|
1965
|
+
logFile(`[plugin-marketplace] added ${name} source=${dir} idempotent=false CLAUDE_CONFIG_DIR=${CLAUDE_CONFIG_DIR}`);
|
|
1966
|
+
}
|
|
1967
|
+
else {
|
|
1968
|
+
const stderrShort = (add.stderr ?? "").split("\n")[0]?.slice(0, 200) ?? "";
|
|
1969
|
+
logFile(`[plugin-marketplace] ERROR add ${name} source=${dir} exit=${add.status} stderr=${JSON.stringify(stderrShort)} CLAUDE_CONFIG_DIR=${CLAUDE_CONFIG_DIR}`);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
// Build the desired plugin list = (every local marketplace's plugins) +
|
|
1973
|
+
// (brand.json#externalPlugins). The local entries' `marketplace` field
|
|
1974
|
+
// matches the marketplace.json `name`; the external entries declare
|
|
1975
|
+
// their own.
|
|
1976
|
+
const desired = [];
|
|
1977
|
+
for (const { name: mkName, dir } of localTrees) {
|
|
1978
|
+
const mkPath = join(dir, ".claude-plugin", "marketplace.json");
|
|
1979
|
+
try {
|
|
1980
|
+
const parsed = JSON.parse(readFileSync(mkPath, "utf-8"));
|
|
1981
|
+
for (const p of parsed.plugins ?? []) {
|
|
1982
|
+
if (typeof p.name === "string" && p.name.length > 0) {
|
|
1983
|
+
desired.push({ name: p.name, marketplace: mkName });
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
catch {
|
|
1988
|
+
// Already logged above.
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
let externals = [];
|
|
1992
|
+
try {
|
|
1993
|
+
externals = parseExternalPlugins(BRAND.externalPlugins);
|
|
1994
|
+
}
|
|
1995
|
+
catch (err) {
|
|
1996
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1997
|
+
logFile(`[plugin-install] ERROR brand.externalPlugins parse error=${JSON.stringify(msg.slice(0, 200))}`);
|
|
1998
|
+
}
|
|
1999
|
+
desired.push(...externals);
|
|
2000
|
+
// The directory-source marketplaces are exactly the local trees discovered
|
|
2001
|
+
// from on-disk marketplace.json above. Remote marketplaces (the Anthropic
|
|
2002
|
+
// ones, GitHub externals) are never in this set, so they keep version-pinned
|
|
2003
|
+
// idempotence. (Task 643.)
|
|
2004
|
+
const directorySourceMarketplaces = new Set(localTrees.map(t => t.name));
|
|
2005
|
+
// Snapshot what's installed to compute the install set.
|
|
2006
|
+
const pluginList = spawnSync("claude", ["plugin", "list"], { stdio: "pipe", encoding: "utf-8", env: claudePluginEnv() });
|
|
2007
|
+
const installed = parsePluginList(pluginList.stdout ?? "");
|
|
2008
|
+
const { toInstall, toResync, alreadyInstalled } = computeInstallActions(desired, installed, directorySourceMarketplaces);
|
|
2009
|
+
for (const ref of alreadyInstalled) {
|
|
2010
|
+
logFile(`[plugin-install] ${ref.name}@${ref.marketplace} idempotent=true`);
|
|
2011
|
+
}
|
|
2012
|
+
// Single source of truth for the install invocation, shared by the resync
|
|
2013
|
+
// and fresh-install loops so a future flag/timeout/scope change cannot drift
|
|
2014
|
+
// between them.
|
|
2015
|
+
const pluginInstall = (ref) => spawnSync("claude", ["plugin", "install", `${ref.name}@${ref.marketplace}`, "--scope", "user"], { stdio: "pipe", encoding: "utf-8", timeout: 120_000, env: claudePluginEnv() });
|
|
2016
|
+
// Resync directory-source plugins (Task 643). A directory marketplace
|
|
2017
|
+
// overwrites its tree in place on upgrade with no version bump, so the
|
|
2018
|
+
// runtime cache snapshot the session loads from (installed_plugins.json
|
|
2019
|
+
// `installPath`) freezes at first-install time — `plugin install` and
|
|
2020
|
+
// `plugin update` both short-circuit on the pinned version and never
|
|
2021
|
+
// re-copy. Uninstall then install is the only sequence that rebuilds the
|
|
2022
|
+
// snapshot from the live tree; `install` reads the directory source live.
|
|
2023
|
+
// The uninstall+install pair is not atomic: a failed reinstall leaves the
|
|
2024
|
+
// plugin deregistered, surfaced by the ERROR line below — hardening tracked
|
|
2025
|
+
// in Task 645.
|
|
2026
|
+
for (const ref of toResync) {
|
|
2027
|
+
spawnSync("claude", ["plugin", "uninstall", `${ref.name}@${ref.marketplace}`, "--scope", "user"], { stdio: "pipe", encoding: "utf-8", timeout: 120_000, env: claudePluginEnv() });
|
|
2028
|
+
const install = pluginInstall(ref);
|
|
2029
|
+
if (install.status === 0) {
|
|
2030
|
+
logFile(`[plugin-install] recache ${ref.name}@${ref.marketplace}`);
|
|
2031
|
+
}
|
|
2032
|
+
else {
|
|
2033
|
+
const stderrShort = (install.stderr ?? "").split("\n")[0]?.slice(0, 200) ?? "";
|
|
2034
|
+
logFile(`[plugin-install] ERROR recache ${ref.name}@${ref.marketplace} exit=${install.status} stderr=${JSON.stringify(stderrShort)}`);
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
for (const ref of toInstall) {
|
|
2038
|
+
const install = pluginInstall(ref);
|
|
2039
|
+
if (install.status === 0) {
|
|
2040
|
+
logFile(`[plugin-install] ${ref.name}@${ref.marketplace} idempotent=false`);
|
|
2041
|
+
}
|
|
2042
|
+
else {
|
|
2043
|
+
const stderrShort = (install.stderr ?? "").split("\n")[0]?.slice(0, 200) ?? "";
|
|
2044
|
+
logFile(`[plugin-install] ERROR ${ref.name}@${ref.marketplace} exit=${install.status} stderr=${JSON.stringify(stderrShort)}`);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
// Cache-vs-live audit (Task 643). Prove the per-plugin cache snapshot the
|
|
2048
|
+
// runtime loads from now matches the live tree, so drift is a logged
|
|
2049
|
+
// post-condition rather than a "no such skill" surprise in a later session.
|
|
2050
|
+
// Read the authoritative load path from installed_plugins.json
|
|
2051
|
+
// (`installPath`) rather than reconstructing the cache path from a pinned
|
|
2052
|
+
// version string. Warn-only: a count mismatch is surfaced, not fatal — a
|
|
2053
|
+
// benign skill directory without a SKILL.md must not abort the install.
|
|
2054
|
+
const countSkills = (skillsDir) => {
|
|
2055
|
+
if (!existsSync(skillsDir))
|
|
2056
|
+
return 0;
|
|
2057
|
+
let n = 0;
|
|
2058
|
+
for (const entry of readdirSync(skillsDir)) {
|
|
2059
|
+
if (existsSync(join(skillsDir, entry, "SKILL.md")))
|
|
2060
|
+
n++;
|
|
2061
|
+
}
|
|
2062
|
+
return n;
|
|
2063
|
+
};
|
|
2064
|
+
let installedPluginsMap = {};
|
|
2065
|
+
try {
|
|
2066
|
+
const ipPath = join(CLAUDE_CONFIG_DIR, "plugins", "installed_plugins.json");
|
|
2067
|
+
const parsed = JSON.parse(readFileSync(ipPath, "utf-8"));
|
|
2068
|
+
installedPluginsMap = parsed.plugins ?? {};
|
|
2069
|
+
}
|
|
2070
|
+
catch {
|
|
2071
|
+
// No readable installed_plugins.json — audit reports cache-skills=0; any
|
|
2072
|
+
// failed install above already logged a recache/install ERROR line.
|
|
2073
|
+
}
|
|
2074
|
+
// `treeDirByMarketplace` is keyed on the directory-source marketplace names
|
|
2075
|
+
// (= `directorySourceMarketplaces`), so a present tree dir is itself the
|
|
2076
|
+
// directory-source test — no separate Set lookup needed.
|
|
2077
|
+
const treeDirByMarketplace = new Map(localTrees.map(t => [t.name, t.dir]));
|
|
2078
|
+
for (const ref of desired) {
|
|
2079
|
+
const treeDir = treeDirByMarketplace.get(ref.marketplace);
|
|
2080
|
+
if (!treeDir)
|
|
2081
|
+
continue;
|
|
2082
|
+
const liveSkills = countSkills(join(treeDir, ref.name, "skills"));
|
|
2083
|
+
// The resync installs at `--scope user`, so audit that scope's snapshot —
|
|
2084
|
+
// not whichever entry happens to be first, since a host may also carry a
|
|
2085
|
+
// stale project-scope entry for the same plugin.
|
|
2086
|
+
const entries = installedPluginsMap[`${ref.name}@${ref.marketplace}`] ?? [];
|
|
2087
|
+
const installPath = (entries.find(e => e.scope === "user") ?? entries[0])?.installPath;
|
|
2088
|
+
const cacheSkills = installPath ? countSkills(join(installPath, "skills")) : 0;
|
|
2089
|
+
logFile(`[plugin-install] audit ${ref.name}@${ref.marketplace} live-skills=${liveSkills} cache-skills=${cacheSkills}`);
|
|
2090
|
+
if (liveSkills !== cacheSkills) {
|
|
2091
|
+
logFile(`[plugin-install] WARN cache-drift ${ref.name}@${ref.marketplace} live-skills=${liveSkills} cache-skills=${cacheSkills}`);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
// post-install assertion. The per-brand CLAUDE_CONFIG_DIR is the
|
|
2095
|
+
// only surface the runtime session-manager reads from; if any expected
|
|
2096
|
+
// marketplace is missing under .claude/plugins/marketplaces/, the operator
|
|
2097
|
+
// will hit "Unknown skill: onboarding" at step 6 of the bring-up. Fail the
|
|
2098
|
+
// install loudly here rather than ship a broken PTY.
|
|
2099
|
+
//
|
|
2100
|
+
// Expected set = 3 Anthropic marketplace names (from the constant in
|
|
2101
|
+
// installClaudeCode) + every parsed localTrees name. Hardcoding the
|
|
2102
|
+
// Anthropic names keeps the assertion independent of `MARKETPLACES` scope.
|
|
2103
|
+
const expectedMarketplaces = new Set([
|
|
2104
|
+
"claude-plugins-official",
|
|
2105
|
+
"knowledge-work-plugins",
|
|
2106
|
+
"claude-for-financial-services",
|
|
2107
|
+
...localTrees.map(t => t.name),
|
|
2108
|
+
]);
|
|
2109
|
+
// `claude plugin marketplace list` is the authoritative source.
|
|
2110
|
+
// The `.claude/plugins/marketplaces/` directory only contains subdirs for
|
|
2111
|
+
// remote (git-cloned) marketplaces — local-source marketplaces registered
|
|
2112
|
+
// via `claude plugin marketplace add <directory-path>` live in user
|
|
2113
|
+
// settings and are visible only through `marketplace list`. Parsing the
|
|
2114
|
+
// CLI output covers both. The CLI emits each entry as two lines: the
|
|
2115
|
+
// leader-glyph name (` ❯ <name>`) followed by ` Source: …`, so the
|
|
2116
|
+
// name is on its own line — anchor on `❯` and require the following line
|
|
2117
|
+
// (after blanks) to start with `Source:` to confirm it is a marketplace
|
|
2118
|
+
// header, not a stray glyph in some other CLI panel.
|
|
2119
|
+
const listResult = spawnSync("claude", ["plugin", "marketplace", "list"], { stdio: "pipe", encoding: "utf-8", env: claudePluginEnv() });
|
|
2120
|
+
const listStdout = listResult.stdout ?? "";
|
|
2121
|
+
const listStderr = listResult.stderr ?? "";
|
|
2122
|
+
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "");
|
|
2123
|
+
const parseMarketplaceList = (raw) => {
|
|
2124
|
+
const lines = stripAnsi(raw).split("\n");
|
|
2125
|
+
const names = [];
|
|
2126
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2127
|
+
const nameMatch = lines[i].match(/^\s*❯\s+([A-Za-z0-9._-]+)\s*$/);
|
|
2128
|
+
if (!nameMatch)
|
|
2129
|
+
continue;
|
|
2130
|
+
let j = i + 1;
|
|
2131
|
+
while (j < lines.length && lines[j].trim() === "")
|
|
2132
|
+
j++;
|
|
2133
|
+
if (j < lines.length && /^\s*Source:/.test(lines[j])) {
|
|
2134
|
+
names.push(nameMatch[1]);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
return names;
|
|
2138
|
+
};
|
|
2139
|
+
const actualMarketplaces = parseMarketplaceList(listStdout);
|
|
2140
|
+
if (listResult.error || listResult.status !== 0 || actualMarketplaces.length === 0) {
|
|
2141
|
+
const errMsg = listResult.error ? listResult.error.message : `exit=${listResult.status}`;
|
|
2142
|
+
throw new Error(`Plugin registration produced no marketplaces — \`claude plugin marketplace list\` returned no parseable entries (${errMsg}). stdout=${JSON.stringify(listStdout)} stderr=${JSON.stringify(listStderr.split("\n")[0] ?? "")}. Check setup.log for [plugin-marketplace] ERROR lines.`);
|
|
2143
|
+
}
|
|
2144
|
+
const missing = [...expectedMarketplaces].filter(m => !actualMarketplaces.includes(m));
|
|
2145
|
+
if (missing.length > 0) {
|
|
2146
|
+
logFile(`[plugin-marketplace] assertion-fail expected=${JSON.stringify([...expectedMarketplaces])} actual=${JSON.stringify(actualMarketplaces)} missing=${JSON.stringify(missing)} CLAUDE_CONFIG_DIR=${CLAUDE_CONFIG_DIR}`);
|
|
2147
|
+
throw new Error(`Plugin registration incomplete: expected marketplaces ${JSON.stringify([...expectedMarketplaces])} but \`claude plugin marketplace list\` returned ${JSON.stringify(actualMarketplaces)}. Missing: ${JSON.stringify(missing)}. Check setup.log for [plugin-marketplace] ERROR lines.`);
|
|
2148
|
+
}
|
|
2149
|
+
logFile(`[plugin-marketplace] assertion-source=claude-plugin-marketplace-list count=${actualMarketplaces.length} marketplaces=${JSON.stringify(actualMarketplaces)} CLAUDE_CONFIG_DIR=${CLAUDE_CONFIG_DIR}`);
|
|
2150
|
+
// Task 583 — post-install assertion that the brand-scoped permissions
|
|
2151
|
+
// block survived install. Warn-only: a missing block does not abort the
|
|
2152
|
+
// install (consistent with the marketplace ERROR path that logs and
|
|
2153
|
+
// continues per-entry), but surfaces in setup.log for diagnosis.
|
|
2154
|
+
const permissionsAssert = assertBypassPermissionsSeed(CLAUDE_CONFIG_DIR);
|
|
2155
|
+
logFile(`[install-permissions] brand-settings=${permissionsAssert.status} path=${permissionsAssert.path}`);
|
|
2156
|
+
}
|
|
2157
|
+
function buildPlatform() {
|
|
2158
|
+
log("9", TOTAL, "Installing dependencies and building...");
|
|
2159
|
+
console.log(` Installing platform dependencies (${join(INSTALL_DIR, "platform")})...`);
|
|
2160
|
+
shellRetry("npm", ["install", ...NPM_NET_FLAGS], { cwd: join(INSTALL_DIR, "platform") }, 3, 15);
|
|
2161
|
+
// MCP server dist/ files are pre-compiled in the payload — no build step needed.
|
|
2162
|
+
// Server external dependencies (neo4j-driver, @whiskeysockets/baileys, zod, proper-lockfile) are listed in
|
|
2163
|
+
// server/package.json but NOT shipped as pre-built node_modules — npm pack silently
|
|
2164
|
+
// strips files from nested node_modules (e.g. rxjs/package.json), breaking require().
|
|
2165
|
+
// Install fresh on device to guarantee a complete dependency tree.
|
|
2166
|
+
//
|
|
2167
|
+
// On upgrade, wipe `node_modules` first so npm extracts a clean tree. Without
|
|
2168
|
+
// this, an interrupted previous install (network blip, operator cancellation,
|
|
2169
|
+
// power loss) can leave nested package.json files half-truncated — the most
|
|
2170
|
+
// common manifestation is `Error: Invalid package config .../rxjs/package.json`
|
|
2171
|
+
// at server startup, which loops the brand service indefinitely. The wipe
|
|
2172
|
+
// adds ~30 s to upgrades but eliminates a class of unrecoverable customer
|
|
2173
|
+
// states; reliability wins over speed for a one-shot install path.
|
|
2174
|
+
const serverNodeModules = join(INSTALL_DIR, "server", "node_modules");
|
|
2175
|
+
if (existsSync(serverNodeModules)) {
|
|
2176
|
+
console.log(" Wiping previous server/node_modules for a clean reinstall...");
|
|
2177
|
+
rmSync(serverNodeModules, { recursive: true, force: true });
|
|
2178
|
+
}
|
|
2179
|
+
console.log(` Installing server dependencies (${join(INSTALL_DIR, "server")})...`);
|
|
2180
|
+
shellRetry("npm", ["install", "--omit=dev", ...NPM_NET_FLAGS], { cwd: join(INSTALL_DIR, "server") }, 3, 15);
|
|
2181
|
+
// (maxy-code) — claude-session-manager has its own package.json
|
|
2182
|
+
// declaring hono + @hono/node-server + node-pty. node-pty is a native
|
|
2183
|
+
// binding; it MUST be installed on the Pi, not shipped pre-built (different
|
|
2184
|
+
// architecture between the build host and the Pi). Wipe + reinstall on
|
|
2185
|
+
// upgrade so a half-extracted previous install does not loop the unit.
|
|
2186
|
+
const csmDir = join(INSTALL_DIR, "platform", "services", "claude-session-manager");
|
|
2187
|
+
if (existsSync(csmDir)) {
|
|
2188
|
+
const csmNodeModules = join(csmDir, "node_modules");
|
|
2189
|
+
if (existsSync(csmNodeModules)) {
|
|
2190
|
+
console.log(" Wiping previous claude-session-manager/node_modules for a clean reinstall...");
|
|
2191
|
+
rmSync(csmNodeModules, { recursive: true, force: true });
|
|
2192
|
+
}
|
|
2193
|
+
console.log(` Installing claude-session-manager dependencies (${csmDir})...`);
|
|
2194
|
+
shellRetry("npm", ["install", "--omit=dev", ...NPM_NET_FLAGS], { cwd: csmDir }, 3, 15);
|
|
2195
|
+
}
|
|
2196
|
+
// Premium-plugin MCP servers (e.g. real-agent/loop) ship dist/ + package.json
|
|
2197
|
+
// in the payload but no node_modules — npm pack strips them, same reason as
|
|
2198
|
+
// server/. Discover every <installDir>/premium-plugins/<bundle>/plugins/<plugin>/mcp
|
|
2199
|
+
// and install --omit=dev there. dirs=0 in the summary line is itself a signal
|
|
2200
|
+
// when a brand ships premium plugins but the discovery glob is broken.
|
|
2201
|
+
const premiumMcpDirs = findPremiumMcpDirs(INSTALL_DIR);
|
|
2202
|
+
console.log(` [install] premium-mcp-install dirs=${premiumMcpDirs.length}`);
|
|
2203
|
+
for (const mcpDir of premiumMcpDirs) {
|
|
2204
|
+
const mcpNodeModules = join(mcpDir, "node_modules");
|
|
2205
|
+
if (existsSync(mcpNodeModules)) {
|
|
2206
|
+
console.log(` Wiping previous ${mcpDir}/node_modules for a clean reinstall...`);
|
|
2207
|
+
rmSync(mcpNodeModules, { recursive: true, force: true });
|
|
2208
|
+
}
|
|
2209
|
+
console.log(` Installing premium plugin MCP dependencies (${mcpDir})...`);
|
|
2210
|
+
shellRetry("npm", ["install", "--omit=dev", ...NPM_NET_FLAGS], { cwd: mcpDir }, 3, 15);
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
function setupVncViewer() {
|
|
2214
|
+
if (!isLinux())
|
|
2215
|
+
return;
|
|
2216
|
+
const novncSrc = "/usr/share/novnc";
|
|
2217
|
+
const novncDest = join(INSTALL_DIR, "server/public/novnc");
|
|
2218
|
+
if (!existsSync(join(novncSrc, "core"))) {
|
|
2219
|
+
console.log(" noVNC not found — skipping VNC viewer setup.");
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
2222
|
+
console.log(" Installing VNC viewer...");
|
|
2223
|
+
// Copy core/ and vendor/ (pako compression library) — both required by rfb.js
|
|
2224
|
+
cpSync(join(novncSrc, "core"), join(novncDest, "core"), { recursive: true, force: true });
|
|
2225
|
+
const vendorSrc = join(novncSrc, "vendor");
|
|
2226
|
+
if (existsSync(vendorSrc)) {
|
|
2227
|
+
cpSync(vendorSrc, join(novncDest, "vendor"), { recursive: true, force: true });
|
|
2228
|
+
}
|
|
2229
|
+
// Custom viewer: no toolbar, scales to fit, auto-connects with retry.
|
|
2230
|
+
//
|
|
2231
|
+
// Transport: same-origin WebSocket proxied through the Maxy server's
|
|
2232
|
+
// /websockify upgrade handler. The URL is built from location.protocol
|
|
2233
|
+
// and location.host so that:
|
|
2234
|
+
// - HTTP origin → ws://<host>:<port>/websockify (LAN)
|
|
2235
|
+
// - HTTPS origin → wss://<host>:<port>/websockify (Cloudflare tunnel)
|
|
2236
|
+
// This eliminates the mixed-content block that previously killed the
|
|
2237
|
+
// viewer when accessed via https://admin.maxy.bot.
|
|
2238
|
+
//
|
|
2239
|
+
// The viewer does NOT read a host/port from query string — those
|
|
2240
|
+
// parameters are ignored if present (kept for backward compatibility
|
|
2241
|
+
// with any cached callers) and the connection is always same-origin.
|
|
2242
|
+
//
|
|
2243
|
+
// A per-session correlation ID is generated client-side and included
|
|
2244
|
+
// in the WebSocket URL (?corrId=X) and in the POST to
|
|
2245
|
+
// /api/vnc/client-event so the server-side log can correlate the
|
|
2246
|
+
// noVNC disconnect reason to the specific WS upgrade entry.
|
|
2247
|
+
const html = `<!DOCTYPE html>
|
|
2248
|
+
<html>
|
|
2249
|
+
<head>
|
|
2250
|
+
<meta charset="utf-8">
|
|
2251
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
|
2252
|
+
<title>Connect Claude</title>
|
|
2253
|
+
<style>
|
|
2254
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
2255
|
+
html, body { width: 100%; height: 100%; background: #111; overflow: hidden; }
|
|
2256
|
+
#screen { position: relative; width: 100%; height: 100%; }
|
|
2257
|
+
#status {
|
|
2258
|
+
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
|
|
2259
|
+
color: #888; font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
2260
|
+
font-size: 14px; text-align: center; z-index: 10;
|
|
2261
|
+
transition: opacity 0.3s;
|
|
2262
|
+
}
|
|
2263
|
+
#status.hidden { opacity: 0; pointer-events: none; }
|
|
2264
|
+
.status-spinner {
|
|
2265
|
+
display: inline-block; width: 20px; height: 20px;
|
|
2266
|
+
border: 2px solid #444; border-top-color: #888; border-radius: 50%;
|
|
2267
|
+
animation: spin 0.8s linear infinite; margin-bottom: 8px;
|
|
2268
|
+
}
|
|
2269
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
2270
|
+
.status-reason { font-size: 12px; color: #666; margin-top: 6px; }
|
|
2271
|
+
</style>
|
|
2272
|
+
</head>
|
|
2273
|
+
<body>
|
|
2274
|
+
<div id="screen"></div>
|
|
2275
|
+
<div id="status">
|
|
2276
|
+
<div class="status-spinner"></div>
|
|
2277
|
+
<div>Connecting to browser…</div>
|
|
2278
|
+
<div class="status-reason"></div>
|
|
2279
|
+
</div>
|
|
2280
|
+
<script type="module">
|
|
2281
|
+
import RFB from '/novnc/core/rfb.js';
|
|
2282
|
+
|
|
2283
|
+
// Build a same-origin WebSocket URL. Protocol auto-matches the
|
|
2284
|
+
// parent page so https pages use wss and http pages use ws.
|
|
2285
|
+
const wsScheme = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
2286
|
+
// Generate a client-side correlation ID. This ID is used only for
|
|
2287
|
+
// client-side log correlation (the server assigns its own corrId
|
|
2288
|
+
// on the upgrade handler so an attacker cannot forge server
|
|
2289
|
+
// internal ids). Base-36 random concatenation yields up to 16
|
|
2290
|
+
// chars — collision-free in practice for the 1-session-at-a-time
|
|
2291
|
+
// VNC use case.
|
|
2292
|
+
const corrId = (Math.random().toString(36).slice(2, 10) + Math.random().toString(36).slice(2, 10))
|
|
2293
|
+
.replace(/[^a-z0-9]/gi, '') || 'c' + Date.now().toString(36);
|
|
2294
|
+
const wsUrl = wsScheme + '//' + location.host + '/websockify?corrId=' + encodeURIComponent(corrId);
|
|
2295
|
+
|
|
2296
|
+
const screen = document.getElementById('screen');
|
|
2297
|
+
const status = document.getElementById('status');
|
|
2298
|
+
let retryCount = 0;
|
|
2299
|
+
const MAX_RETRIES = 30;
|
|
2300
|
+
|
|
2301
|
+
// Best-effort POST of a disconnect/error reason to the server so
|
|
2302
|
+
// the noVNC-observed failure mode ends up in vnc-boot.log alongside
|
|
2303
|
+
// the server-side proxy events. Network failures are swallowed —
|
|
2304
|
+
// this is a telemetry side channel, not a critical path.
|
|
2305
|
+
function reportClientEvent(phase, reason) {
|
|
2306
|
+
try {
|
|
2307
|
+
fetch('/api/vnc/client-event', {
|
|
2308
|
+
method: 'POST',
|
|
2309
|
+
credentials: 'same-origin',
|
|
2310
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2311
|
+
body: JSON.stringify({ corrId: corrId, phase: phase, reason: reason || '' }),
|
|
2312
|
+
keepalive: true,
|
|
2313
|
+
}).catch(function() { /* swallow */ });
|
|
2314
|
+
} catch (e) { /* swallow */ }
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
// Layer-6 beacon: emit event=rfb-connected and
|
|
2318
|
+
// event=rfb-error to the operator-grep lifecycle endpoint so
|
|
2319
|
+
// server.log shows the noVNC outcome alongside [device-url:click]
|
|
2320
|
+
// / [http] / [websockify]. Same-origin POST works whether the
|
|
2321
|
+
// iframe is parented by the React VNC viewer or by vnc-popout.html.
|
|
2322
|
+
function reportVncIframeEvent(event, fields) {
|
|
2323
|
+
try {
|
|
2324
|
+
var body = JSON.stringify(Object.assign(
|
|
2325
|
+
{ event: event, surface: 'iframe' },
|
|
2326
|
+
fields || {},
|
|
2327
|
+
));
|
|
2328
|
+
fetch('/api/admin/browser-iframe/event', {
|
|
2329
|
+
method: 'POST',
|
|
2330
|
+
credentials: 'same-origin',
|
|
2331
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2332
|
+
body: body,
|
|
2333
|
+
keepalive: true,
|
|
2334
|
+
}).catch(function() { /* swallow */ });
|
|
2335
|
+
} catch (e) { /* swallow */ }
|
|
2336
|
+
}
|
|
2337
|
+
var connectStartedAt = 0;
|
|
2338
|
+
|
|
2339
|
+
function connect() {
|
|
2340
|
+
status.classList.remove('hidden');
|
|
2341
|
+
status.querySelector('.status-spinner').style.display = '';
|
|
2342
|
+
status.querySelector('div:nth-child(2)').textContent =
|
|
2343
|
+
retryCount > 0 ? 'Reconnecting… (' + retryCount + ')' : 'Connecting to browser…';
|
|
2344
|
+
status.querySelector('.status-reason').textContent = '';
|
|
2345
|
+
connectStartedAt = Date.now();
|
|
2346
|
+
|
|
2347
|
+
const rfb = new RFB(screen, wsUrl);
|
|
2348
|
+
rfb.scaleViewport = true;
|
|
2349
|
+
rfb.clipViewport = true;
|
|
2350
|
+
rfb.resizeSession = false;
|
|
2351
|
+
window.rfb = rfb;
|
|
2352
|
+
|
|
2353
|
+
rfb.addEventListener('connect', () => {
|
|
2354
|
+
status.classList.add('hidden');
|
|
2355
|
+
retryCount = 0;
|
|
2356
|
+
reportVncIframeEvent('rfb-connected', {
|
|
2357
|
+
durationMs: Date.now() - connectStartedAt,
|
|
2358
|
+
});
|
|
2359
|
+
});
|
|
2360
|
+
|
|
2361
|
+
rfb.addEventListener('disconnect', (e) => {
|
|
2362
|
+
status.classList.remove('hidden');
|
|
2363
|
+
const detail = e.detail || {};
|
|
2364
|
+
const reason = detail.reason || (detail.clean === false ? 'Connection refused' : '');
|
|
2365
|
+
reportClientEvent('disconnect', reason || (detail.clean === false ? 'unclean-close' : 'normal'));
|
|
2366
|
+
reportVncIframeEvent('rfb-error', {
|
|
2367
|
+
durationMs: Date.now() - connectStartedAt,
|
|
2368
|
+
errorCode: detail.clean === false ? 'unclean-close' : 'normal',
|
|
2369
|
+
message: reason || '',
|
|
2370
|
+
});
|
|
2371
|
+
const reasonEl = status.querySelector('.status-reason');
|
|
2372
|
+
if (retryCount < MAX_RETRIES) {
|
|
2373
|
+
retryCount++;
|
|
2374
|
+
const delay = Math.min(1000 * retryCount, 5000);
|
|
2375
|
+
status.querySelector('div:nth-child(2)').textContent = 'Reconnecting in ' + Math.ceil(delay/1000) + 's…';
|
|
2376
|
+
reasonEl.textContent = reason;
|
|
2377
|
+
setTimeout(connect, delay);
|
|
2378
|
+
} else {
|
|
2379
|
+
status.querySelector('.status-spinner').style.display = 'none';
|
|
2380
|
+
status.querySelector('div:nth-child(2)').textContent = 'Connection lost. Reload the page to retry.';
|
|
2381
|
+
reasonEl.textContent = reason;
|
|
2382
|
+
}
|
|
2383
|
+
});
|
|
2384
|
+
|
|
2385
|
+
// --- Clipboard bridge (local ↔ remote) ---
|
|
2386
|
+
|
|
2387
|
+
// Paste bridge: intercept Cmd/Ctrl+V in the capturing phase before
|
|
2388
|
+
// noVNC's keydown handler can call preventDefault(). noVNC v1.3.0
|
|
2389
|
+
// binds its handler with .bind(this) in the Keyboard constructor and
|
|
2390
|
+
// registers the bound reference via addEventListener — so monkey-
|
|
2391
|
+
// patching the unbound method on the instance is a no-op. A capturing-
|
|
2392
|
+
// phase listener on document fires before noVNC's target-phase handler
|
|
2393
|
+
// regardless of registration timing or internal structure.
|
|
2394
|
+
document.addEventListener('keydown', (e) => {
|
|
2395
|
+
if ((e.ctrlKey || e.metaKey) && (e.key === 'v' || e.key === 'V')) {
|
|
2396
|
+
e.stopImmediatePropagation();
|
|
2397
|
+
reportClientEvent('paste-bridge', 'keydown-intercepted');
|
|
2398
|
+
}
|
|
2399
|
+
}, true);
|
|
2400
|
+
|
|
2401
|
+
// Catch the paste event, sync text to VNC server clipboard, then
|
|
2402
|
+
// send Ctrl+V keystrokes so the remote app pastes the synced content.
|
|
2403
|
+
document.addEventListener('paste', (e) => {
|
|
2404
|
+
e.preventDefault();
|
|
2405
|
+
const text = (e.clipboardData || window.clipboardData)?.getData('text');
|
|
2406
|
+
if (!text || !window.rfb) {
|
|
2407
|
+
reportClientEvent('paste-bridge', 'empty-clipboard');
|
|
2408
|
+
return;
|
|
2409
|
+
}
|
|
2410
|
+
reportClientEvent('paste-bridge', 'paste-event text-length=' + text.length);
|
|
2411
|
+
window.rfb.clipboardPasteFrom(text);
|
|
2412
|
+
reportClientEvent('paste-bridge', 'clipboard-synced');
|
|
2413
|
+
setTimeout(() => {
|
|
2414
|
+
window.rfb.sendKey(0xFFE3, 'ControlLeft', true);
|
|
2415
|
+
window.rfb.sendKey(0x0076, 'v', true);
|
|
2416
|
+
window.rfb.sendKey(0x0076, 'v', false);
|
|
2417
|
+
window.rfb.sendKey(0xFFE3, 'ControlLeft', false);
|
|
2418
|
+
}, 50);
|
|
2419
|
+
});
|
|
2420
|
+
|
|
2421
|
+
// Copy bridge: when the remote clipboard changes, notify the parent frame.
|
|
2422
|
+
rfb.addEventListener('clipboard', (e) => {
|
|
2423
|
+
window.parent.postMessage({ type: 'vnc-clipboard', text: e.detail.text }, '*');
|
|
2424
|
+
});
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
connect();
|
|
2428
|
+
</script>
|
|
2429
|
+
</body>
|
|
2430
|
+
</html>`;
|
|
2431
|
+
writeFileSync(join(INSTALL_DIR, "server/public/vnc-viewer.html"), html);
|
|
2432
|
+
console.log(" VNC viewer ready at /vnc-viewer.html");
|
|
2433
|
+
}
|
|
2434
|
+
function setupAccount() {
|
|
2435
|
+
log("10", TOTAL, "Setting up...");
|
|
2436
|
+
// Account-side setup runs first: creates the account dir, writes the
|
|
2437
|
+
// Claude Code project settings (hooks only — no permissions.allow),
|
|
2438
|
+
// installs specialist templates, seeds account.json. Then seed-neo4j.sh
|
|
2439
|
+
// applies the schema and MERGEs the root :LocalBusiness / owner
|
|
2440
|
+
// :AdminUser nodes. setup-account.sh has no Neo4j env dependency;
|
|
2441
|
+
// seed-neo4j.sh hard-exits without NEO4J_URI, so we derive the URI +
|
|
2442
|
+
// password (a missing password file is a hard error here — the
|
|
2443
|
+
// upstream ensureNeo4jPassword() would have thrown if it couldn't
|
|
2444
|
+
// reach the brand's Neo4j).
|
|
2445
|
+
const setupScript = join(INSTALL_DIR, "platform/scripts/setup-account.sh");
|
|
2446
|
+
if (existsSync(setupScript)) {
|
|
2447
|
+
shell("bash", [setupScript], { cwd: INSTALL_DIR });
|
|
2448
|
+
}
|
|
2449
|
+
const passwordFile = join(INSTALL_DIR, "platform/config/.neo4j-password");
|
|
2450
|
+
if (!existsSync(passwordFile)) {
|
|
2451
|
+
throw new Error(`Neo4j password file missing at ${passwordFile} — required by setup step.`);
|
|
2452
|
+
}
|
|
2453
|
+
const password = readFileSync(passwordFile, "utf-8").trim();
|
|
2454
|
+
const neo4jUri = `bolt://localhost:${NEO4J_PORT}`;
|
|
2455
|
+
const neo4jEnv = { ...process.env, NEO4J_URI: neo4jUri, NEO4J_PASSWORD: password };
|
|
2456
|
+
const seedScript = join(INSTALL_DIR, "platform/scripts/seed-neo4j.sh");
|
|
2457
|
+
if (existsSync(seedScript)) {
|
|
2458
|
+
console.log(` [neo4j] passing NEO4J_URI=${neo4jUri} to seed`);
|
|
2459
|
+
logFile(` [neo4j] passing NEO4J_URI=${neo4jUri} to seed`);
|
|
2460
|
+
shell("bash", [seedScript], { cwd: INSTALL_DIR, env: neo4jEnv });
|
|
2461
|
+
}
|
|
2462
|
+
// Task 165 — register every bundled specialist subagent at
|
|
2463
|
+
// $CLAUDE_CONFIG_DIR/agents/<name>.md. Claude Code's `--agent <name>`
|
|
2464
|
+
// discovery only resolves under this path; without these symlinks,
|
|
2465
|
+
// every `--agent database-operator` etc. silently runs as the admin
|
|
2466
|
+
// agent on admin IDENTITY/SOUL scaffolding. Symlinks (not copies) so
|
|
2467
|
+
// a `npx -y @rubytech/create-maxy-code` upgrade picks up template
|
|
2468
|
+
// changes without a second seed run.
|
|
2469
|
+
registerSpecialistAgents();
|
|
2470
|
+
// install-time defaults. Stamp `account.json` with the
|
|
2471
|
+
// brand's default plugin set + default outputStyle / thinkingView, and
|
|
2472
|
+
// write a minimal admin SOUL.md. The agent boots into a working state
|
|
2473
|
+
// with no onboarding state machine; the operator builds personality up
|
|
2474
|
+
// through use. The graph-write gate blocks user-domain writes
|
|
2475
|
+
// until AdminUser + LocalBusiness/personal-profile Person exist, so the
|
|
2476
|
+
// persona is elicited the first time the operator triggers a write.
|
|
2477
|
+
const accountId = resolveInstallAccountId();
|
|
2478
|
+
if (accountId) {
|
|
2479
|
+
writeInstallDefaults(accountId);
|
|
2480
|
+
}
|
|
2481
|
+
else {
|
|
2482
|
+
console.log(" [install-defaults] SKIPPED reason=no-account-discovered");
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
function writeInstallDefaults(accountId) {
|
|
2486
|
+
const accountDir = join(INSTALL_DIR, "data/accounts", accountId);
|
|
2487
|
+
const accountJsonPath = join(accountDir, "account.json");
|
|
2488
|
+
const enabledPlugins = BRAND.plugins?.defaultEnabled ?? [];
|
|
2489
|
+
let config = {};
|
|
2490
|
+
if (existsSync(accountJsonPath)) {
|
|
2491
|
+
try {
|
|
2492
|
+
config = JSON.parse(readFileSync(accountJsonPath, "utf-8"));
|
|
2493
|
+
}
|
|
2494
|
+
catch (err) {
|
|
2495
|
+
console.error(` [install-defaults] account.json unreadable at ${accountJsonPath}: ${err.message}`);
|
|
2496
|
+
logFile(` [install-defaults] account.json unreadable: ${err.message}`);
|
|
2497
|
+
return;
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
config.enabledPlugins = enabledPlugins;
|
|
2501
|
+
config.outputStyle = "default";
|
|
2502
|
+
config.thinkingView = "default";
|
|
2503
|
+
if (TIER_FLAG) {
|
|
2504
|
+
config = applyTierToAccountConfig(config, TIER_FLAG);
|
|
2505
|
+
}
|
|
2506
|
+
writeFileSync(accountJsonPath, JSON.stringify(config, null, 2) + "\n");
|
|
2507
|
+
console.log(` [install-defaults] account-json plugins=${enabledPlugins.length} outputStyle=default thinkingView=default`);
|
|
2508
|
+
logFile(` [install-defaults] account-json plugins=${enabledPlugins.length} outputStyle=default thinkingView=default`);
|
|
2509
|
+
if (TIER_FLAG) {
|
|
2510
|
+
const tierLine = ` [install-defaults] tier=${TIER_FLAG} mode=personal source=cli-flag`;
|
|
2511
|
+
console.log(tierLine);
|
|
2512
|
+
logFile(tierLine);
|
|
2513
|
+
}
|
|
2514
|
+
// Admin SOUL.md is user-controlled personalisation: create it from the
|
|
2515
|
+
// bundled template (with ${PRODUCT_NAME} substituted) on a fresh install,
|
|
2516
|
+
// and never overwrite it once it exists. The operator builds the agent's
|
|
2517
|
+
// personality up through use, so an existing SOUL must survive every
|
|
2518
|
+
// upgrade. Task 511: an unconditional write here reset SOUL to the 7-byte
|
|
2519
|
+
// template on every full install. Mirrors the public SOUL guard below and
|
|
2520
|
+
// the create-if-missing branch in setup-account.sh.
|
|
2521
|
+
const soulPath = join(accountDir, "agents/admin/SOUL.md");
|
|
2522
|
+
mkdirSync(dirname(soulPath), { recursive: true });
|
|
2523
|
+
if (existsSync(soulPath)) {
|
|
2524
|
+
const preservedBytes = statSync(soulPath).size;
|
|
2525
|
+
console.log(` [install-defaults] soul-md preserved path=${soulPath} bytes=${preservedBytes}`);
|
|
2526
|
+
logFile(` [install-defaults] soul-md preserved path=${soulPath} bytes=${preservedBytes}`);
|
|
2527
|
+
}
|
|
2528
|
+
else {
|
|
2529
|
+
const adminSoulTemplate = join(INSTALL_DIR, "platform/templates/agents/admin/SOUL.md");
|
|
2530
|
+
let soulContent;
|
|
2531
|
+
try {
|
|
2532
|
+
soulContent = readFileSync(adminSoulTemplate, "utf-8");
|
|
2533
|
+
}
|
|
2534
|
+
catch (err) {
|
|
2535
|
+
console.error(` [install-defaults] admin-soul template missing at ${adminSoulTemplate}: ${err.message}`);
|
|
2536
|
+
logFile(` [install-defaults] admin-soul template missing: ${err.message}`);
|
|
2537
|
+
return;
|
|
2538
|
+
}
|
|
2539
|
+
soulContent = soulContent.replace(/\$\{PRODUCT_NAME\}/g, BRAND.productName);
|
|
2540
|
+
writeFileSync(soulPath, soulContent);
|
|
2541
|
+
console.log(` [install-defaults] soul-md created path=${soulPath} bytes=${soulContent.length}`);
|
|
2542
|
+
logFile(` [install-defaults] soul-md created path=${soulPath} bytes=${soulContent.length}`);
|
|
2543
|
+
}
|
|
2544
|
+
// Public agent IDENTITY.md — the only template-seeded public file, always
|
|
2545
|
+
// overwritten (Rubytech-controlled toolless directive). Per Task 618/623,
|
|
2546
|
+
// each public agent's SOUL.md, KNOWLEDGE.md, and config.json are authored
|
|
2547
|
+
// client-side when the operator creates the agent through the admin UI;
|
|
2548
|
+
// they are not seeded from templates at install time. A public spawn for an
|
|
2549
|
+
// un-authored agent refuses as a dud (soul-empty / knowledge-missing), which
|
|
2550
|
+
// is the intended loud signal — do not re-introduce template SOUL/config
|
|
2551
|
+
// copies here (commit 82c5658d6 removed the template files).
|
|
2552
|
+
const publicDir = join(accountDir, "agents/public");
|
|
2553
|
+
mkdirSync(publicDir, { recursive: true });
|
|
2554
|
+
const publicTemplatesDir = join(INSTALL_DIR, "platform/templates/agents/public");
|
|
2555
|
+
const publicIdentityDst = join(publicDir, "IDENTITY.md");
|
|
2556
|
+
cpSync(join(publicTemplatesDir, "IDENTITY.md"), publicIdentityDst);
|
|
2557
|
+
console.log(` [install-defaults] public-identity path=${publicIdentityDst}`);
|
|
2558
|
+
logFile(` [install-defaults] public-identity path=${publicIdentityDst}`);
|
|
2559
|
+
}
|
|
2560
|
+
/**
|
|
2561
|
+
* Commercial-mode entitlement delivery (Task 240). Writes the decoded payload
|
|
2562
|
+
* bytes verbatim to ~/<BRAND.configDir>/entitlement.json. Signature and shape
|
|
2563
|
+
* validation happen at the verifier at session start (entitlement lib); the
|
|
2564
|
+
* installer is a dumb delivery channel — a malformed payload that survived
|
|
2565
|
+
* --entitlement-base64 parsing will be caught there and degrade the install
|
|
2566
|
+
* to anonymous-fallback, which is the correct loud-fail per doctrine.
|
|
2567
|
+
*/
|
|
2568
|
+
function writeEntitlementFromFlag(payload) {
|
|
2569
|
+
const target = entitlementPath(process.env.HOME ?? "/root", BRAND.configDir);
|
|
2570
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
2571
|
+
writeFileSync(target, payload);
|
|
2572
|
+
const line = ` [install-defaults] entitlement-delivered bytes=${payload.length} path=${target} source=cli-flag`;
|
|
2573
|
+
console.log(line);
|
|
2574
|
+
logFile(line);
|
|
2575
|
+
}
|
|
2576
|
+
// Task 394 — premium-bundle agents register through the native Claude Code
|
|
2577
|
+
// subagent registry via `.claude-plugin/plugin.json` manifests emitted by
|
|
2578
|
+
// scripts/generate-plugin-manifests.mjs at bundle time, not through a
|
|
2579
|
+
// parallel symlink channel. This function still wires the bundled platform
|
|
2580
|
+
// specialists (database-operator, project-manager, …) under
|
|
2581
|
+
// platform/templates/specialists/agents/, which are not part of a
|
|
2582
|
+
// registered plugin and rely on $CLAUDE_CONFIG_DIR/agents/ resolution.
|
|
2583
|
+
function registerSpecialistAgents() {
|
|
2584
|
+
try {
|
|
2585
|
+
registerSpecialistAgentsAt(join(INSTALL_DIR, "platform/templates/specialists/agents"), join(CLAUDE_CONFIG_DIR, "agents"), (line) => { console.log(line); logFile(line); });
|
|
2586
|
+
}
|
|
2587
|
+
catch (err) {
|
|
2588
|
+
if (err instanceof SpecialistSymlinkCollision) {
|
|
2589
|
+
console.error(`Setup failed: ${err.message}`);
|
|
2590
|
+
console.error(` Remove the file and re-run.`);
|
|
2591
|
+
process.exit(1);
|
|
2592
|
+
}
|
|
2593
|
+
throw err;
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
// ---------------------------------------------------------------------------
|
|
2597
|
+
// Account discovery (shared by installService + the post-install summary)
|
|
2598
|
+
//
|
|
2599
|
+
// `installService` stamps `Environment=ACCOUNT_ID=` into the brand
|
|
2600
|
+
// systemd unit so the writeNodeWithEdges gate has a non-undefined identity to
|
|
2601
|
+
// compare against. Pulled from `INSTALL_DIR/data/accounts/<uuid>/account.json`
|
|
2602
|
+
// written by setup-account.sh during setupAccount(). One reader, one shape, one
|
|
2603
|
+
// source of truth.
|
|
2604
|
+
//
|
|
2605
|
+
// retired the installer's cron registration — `resolveInstallAccountId`
|
|
2606
|
+
// kept its second consumer (installService) and is referenced again at print
|
|
2607
|
+
// time for the post-install banner.
|
|
2608
|
+
// ---------------------------------------------------------------------------
|
|
2609
|
+
function resolveInstallAccountId() {
|
|
2610
|
+
const accountsDir = join(INSTALL_DIR, "data/accounts");
|
|
2611
|
+
if (!existsSync(accountsDir))
|
|
2612
|
+
return "";
|
|
2613
|
+
try {
|
|
2614
|
+
for (const d of readdirSync(accountsDir)) {
|
|
2615
|
+
if (existsSync(join(accountsDir, d, "account.json")))
|
|
2616
|
+
return d;
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
catch { /* directory unreadable */ }
|
|
2620
|
+
return "";
|
|
2621
|
+
}
|
|
2622
|
+
// retired the ttyd/tmux/xterm admin terminal stack. Upgrades run
|
|
2623
|
+
// via the action runner — `systemd-run --user` transient units spawned by
|
|
2624
|
+
// POST /api/admin/actions/upgrade — whose lifetime is independent of
|
|
2625
|
+
// maxy-ui, achieving the invariant structurally rather than via
|
|
2626
|
+
// a peer edge service proxying a ttyd process. The installer no longer
|
|
2627
|
+
// provisions the ttyd binary, writes a tmux conf, or installs a ttyd
|
|
2628
|
+
// systemd unit. The corresponding admin UI (RemoteTerminal, TerminalOverlay,
|
|
2629
|
+
// xterm.js) was deleted in the same task.
|
|
2630
|
+
// reverse-DNS LaunchAgent label. Becomes both the plist's
|
|
2631
|
+
// <key>Label</key> value and the file basename
|
|
2632
|
+
// (`~/Library/LaunchAgents/com.rubytech.<hostname>.plist`). Per-brand so
|
|
2633
|
+
// installing brand B never displaces brand A's agent (mirrors the per-brand
|
|
2634
|
+
// systemd unit invariant).
|
|
2635
|
+
function launchdLabel() {
|
|
2636
|
+
return `com.rubytech.${BRAND.hostname}`;
|
|
2637
|
+
}
|
|
2638
|
+
function launchAgentsDir() {
|
|
2639
|
+
return resolve(process.env.HOME ?? "/", "Library/LaunchAgents");
|
|
2640
|
+
}
|
|
2641
|
+
function plistPath() {
|
|
2642
|
+
return join(launchAgentsDir(), `${launchdLabel()}.plist`);
|
|
2643
|
+
}
|
|
2644
|
+
function gui() {
|
|
2645
|
+
// launchd's gui domain is keyed on the user's UID. process.getuid is only
|
|
2646
|
+
// defined on POSIX (always present on darwin); typed conservatively.
|
|
2647
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
2648
|
+
return `gui/${uid}`;
|
|
2649
|
+
}
|
|
2650
|
+
// darwin LaunchAgent supervisor. Mirrors the systemd-user body
|
|
2651
|
+
// below at the success-criteria level: process registered with the user's
|
|
2652
|
+
// session manager, KeepAlive respawns on crash, RunAtLoad starts the agent
|
|
2653
|
+
// on every login. Out-of-scope today: brew-resolved node path,
|
|
2654
|
+
// dedicated Neo4j on a non-default port. Falls back to /usr/local/bin/node
|
|
2655
|
+
// (homebrew Intel + manual Apple-silicon installs) will replace
|
|
2656
|
+
// this with the resolver.
|
|
2657
|
+
function installServiceDarwin() {
|
|
2658
|
+
const persistDir = resolve(process.env.HOME ?? "/", BRAND.configDir);
|
|
2659
|
+
const logsDir = join(persistDir, "logs");
|
|
2660
|
+
mkdirSync(logsDir, { recursive: true });
|
|
2661
|
+
// Mirror the Linux brand systemd unit's `Environment=` lines into .env.
|
|
2662
|
+
// Linux stamps every var explicitly on the unit; darwin has no systemd
|
|
2663
|
+
// EnvironmentFile primitive, so the wrapper sources .env before exec'ing
|
|
2664
|
+
// node. Any var the boot path or per-request handlers require (PORT,
|
|
2665
|
+
// MAXY_UI_INTERNAL_PORT, CLAUDE_SESSION_MANAGER_PORT, ACCOUNT_ID,
|
|
2666
|
+
// MAXY_PLATFORM_ROOT, NEO4J_URI, CLAUDE_CONFIG_DIR, NODE_ENV) must be
|
|
2667
|
+
// written here or the server hard-fails — boot-throw for module-init
|
|
2668
|
+
// consumers, per-request 500s for handlers like /api/admin/claude-sessions*
|
|
2669
|
+
// that requirePortEnv lazily on the manager port.
|
|
2670
|
+
// VNC/X11 vars (DISPLAY, RFB_PORT, WEBSOCKIFY_PORT, CDP_PORT) are Pi-only
|
|
2671
|
+
// and excluded — darwin runs no kiosk display stack.
|
|
2672
|
+
//
|
|
2673
|
+
// Darwin collapses the two-process model: Linux runs a separate
|
|
2674
|
+
// `maxy-edge.service` on 0.0.0.0:PORT that reverse-proxies into the brand
|
|
2675
|
+
// server on 127.0.0.1:MAXY_UI_INTERNAL_PORT, with edge surviving brand
|
|
2676
|
+
// restarts so the browser WebSocket stays connected. Edge also owns the
|
|
2677
|
+
// VNC websockify stack, which darwin doesn't have. On darwin the brand
|
|
2678
|
+
// server binds the public port directly:
|
|
2679
|
+
// HOSTNAME=0.0.0.0 → bind all interfaces (LAN reachable)
|
|
2680
|
+
// MAXY_UI_INTERNAL_PORT=PORT → same port, no +1 split
|
|
2681
|
+
// Tradeoff: no zero-downtime restarts; a brand restart drops live
|
|
2682
|
+
// websockets. Acceptable on a single-operator dev Mac.
|
|
2683
|
+
const installAccountId = resolveInstallAccountId();
|
|
2684
|
+
if (!installAccountId) {
|
|
2685
|
+
throw new Error(`installServiceDarwin: no account discovered at ${INSTALL_DIR}/data/accounts/<uuid>/account.json — ` +
|
|
2686
|
+
`setupAccount() (setup-account.sh) should have created one. Refusing to write .env ` +
|
|
2687
|
+
`without ACCOUNT_ID; the boot validator would FATAL on every kickstart.`);
|
|
2688
|
+
}
|
|
2689
|
+
const envPath = join(persistDir, ".env");
|
|
2690
|
+
try {
|
|
2691
|
+
let envContent = "";
|
|
2692
|
+
try {
|
|
2693
|
+
envContent = readFileSync(envPath, "utf-8");
|
|
2694
|
+
}
|
|
2695
|
+
catch { /* first install */ }
|
|
2696
|
+
for (const [key, value] of [
|
|
2697
|
+
["NODE_ENV", "production"],
|
|
2698
|
+
["DISPLAY_MODE", DISPLAY_MODE],
|
|
2699
|
+
["EMBED_MODEL", EMBED_MODEL],
|
|
2700
|
+
["EMBED_DIMENSIONS", String(EMBED_DIMS)],
|
|
2701
|
+
["NEO4J_URI", `bolt://localhost:${NEO4J_PORT}`],
|
|
2702
|
+
["PORT", String(PORT)],
|
|
2703
|
+
["MAXY_UI_INTERNAL_PORT", String(PORT)],
|
|
2704
|
+
["CLAUDE_SESSION_MANAGER_PORT", String(BRAND.claudeSessionManagerPort)],
|
|
2705
|
+
["ACCOUNT_ID", installAccountId],
|
|
2706
|
+
["MAXY_PLATFORM_ROOT", `${INSTALL_DIR}/platform`],
|
|
2707
|
+
["CLAUDE_CONFIG_DIR", `${persistDir}/.claude`],
|
|
2708
|
+
["HOSTNAME", "0.0.0.0"],
|
|
2709
|
+
["KEEP_ALIVE_TIMEOUT", "61000"],
|
|
2710
|
+
// launchd hands the server a bare PATH (/usr/bin:/bin:/usr/sbin:/sbin)
|
|
2711
|
+
// that excludes the two Homebrew prefixes the `claude` binary lives in
|
|
2712
|
+
// under Apple Silicon and Intel. The wrapper sources this file with
|
|
2713
|
+
// `set -a; . "$envPath"; set +a`, so `$PATH` expands at source-time
|
|
2714
|
+
// against the bare launchd PATH and Homebrew prefixes get prepended for
|
|
2715
|
+
// every spawned descendant (claude-session-manager, MCP plugin spawns,
|
|
2716
|
+
// route-level spawn('claude', ...)). Task 386.
|
|
2717
|
+
["PATH", "/opt/homebrew/bin:/usr/local/bin:$PATH"],
|
|
2718
|
+
]) {
|
|
2719
|
+
const re = new RegExp(`^${key}=.*$`, "m");
|
|
2720
|
+
if (re.test(envContent)) {
|
|
2721
|
+
envContent = envContent.replace(re, `${key}=${value}`);
|
|
2722
|
+
}
|
|
2723
|
+
else {
|
|
2724
|
+
envContent = envContent.trimEnd() + (envContent.length > 0 ? "\n" : "") + `${key}=${value}\n`;
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
writeFileSync(envPath, envContent);
|
|
2728
|
+
logFile(` .env: DISPLAY_MODE=${DISPLAY_MODE}, EMBED_MODEL=${EMBED_MODEL}, EMBED_DIMENSIONS=${EMBED_DIMS}, NEO4J_URI=bolt://localhost:${NEO4J_PORT}, PORT=${PORT}, MAXY_UI_INTERNAL_PORT=${PORT} (darwin-collapsed), CLAUDE_SESSION_MANAGER_PORT=${BRAND.claudeSessionManagerPort}, HOSTNAME=0.0.0.0, ACCOUNT_ID=${installAccountId.slice(0, 8)}…, CLAUDE_CONFIG_DIR=${persistDir}/.claude`);
|
|
2729
|
+
}
|
|
2730
|
+
catch (err) {
|
|
2731
|
+
console.error(` WARNING: failed to write .env to ${envPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2732
|
+
}
|
|
2733
|
+
// Render the plist. The wrapper shell script reads .env before exec'ing
|
|
2734
|
+
// node so the runtime config (PORT, MAXY_PLATFORM_ROOT, NEO4J_URI) lands
|
|
2735
|
+
// in the child env. Without this, ProgramArguments executes node directly
|
|
2736
|
+
// and the .env values are unread — server binds the wrong port.
|
|
2737
|
+
const wrapperPath = join(persistDir, "launchd-wrapper.sh");
|
|
2738
|
+
// Resolve node binary at install time so the wrapper picks the right
|
|
2739
|
+
// path on both Intel (/usr/local/bin/node) and Apple Silicon
|
|
2740
|
+
// (/opt/homebrew/bin/node) — Homebrew's prefix differs by arch.
|
|
2741
|
+
const nodeProbe = spawnSync("command", ["-v", "node"], { encoding: "utf-8", shell: true });
|
|
2742
|
+
const nodeBin = (nodeProbe.stdout ?? "").trim() || "/usr/local/bin/node";
|
|
2743
|
+
const wrapperBody = [
|
|
2744
|
+
"#!/bin/bash",
|
|
2745
|
+
"# generated by create-maxy installService(). Reads.env then",
|
|
2746
|
+
"# execs node so launchd's child inherits PORT, NEO4J_URI, etc. Replaces",
|
|
2747
|
+
"# the systemd EnvironmentFile= directive that has no launchd analogue.",
|
|
2748
|
+
`set -a; [ -f "${envPath}" ] && . "${envPath}"; set +a`,
|
|
2749
|
+
`cd "${INSTALL_DIR}/server"`,
|
|
2750
|
+
`exec ${nodeBin} --require ./server-init.cjs server.js`,
|
|
2751
|
+
"",
|
|
2752
|
+
].join("\n");
|
|
2753
|
+
writeFileSync(wrapperPath, wrapperBody);
|
|
2754
|
+
chmodSync(wrapperPath, 0o755);
|
|
2755
|
+
const label = launchdLabel();
|
|
2756
|
+
const plist = renderPlist({
|
|
2757
|
+
label,
|
|
2758
|
+
programArguments: ["/bin/bash", wrapperPath],
|
|
2759
|
+
stdoutPath: join(logsDir, "server.log"),
|
|
2760
|
+
stderrPath: join(logsDir, "server.log"),
|
|
2761
|
+
keepAlive: true,
|
|
2762
|
+
runAtLoad: true,
|
|
2763
|
+
workingDirectory: `${INSTALL_DIR}/server`,
|
|
2764
|
+
});
|
|
2765
|
+
mkdirSync(launchAgentsDir(), { recursive: true });
|
|
2766
|
+
const path = plistPath();
|
|
2767
|
+
writeFileSync(path, plist);
|
|
2768
|
+
logFile(` ${path} written (${plist.length} bytes)`);
|
|
2769
|
+
// Idempotent re-install: bootout the previous instance (if any) first so
|
|
2770
|
+
// the second `bootstrap` does not exit 5 ("already loaded"). Best-effort —
|
|
2771
|
+
// a missing service is the expected case on fresh installs.
|
|
2772
|
+
spawnSync("launchctl", ["bootout", `${gui()}/${label}`], { stdio: "pipe" });
|
|
2773
|
+
const bootstrap = spawnSync("launchctl", ["bootstrap", gui(), path], {
|
|
2774
|
+
stdio: "pipe",
|
|
2775
|
+
encoding: "utf-8",
|
|
2776
|
+
timeout: 15_000,
|
|
2777
|
+
});
|
|
2778
|
+
if (bootstrap.status === 0) {
|
|
2779
|
+
console.log(` [launchd] bootstrap ${gui()}/${label} ok`);
|
|
2780
|
+
logFile(` [launchd] bootstrap ${gui()}/${label} ok`);
|
|
2781
|
+
logFile(` [create-maxy] launchd-plist=${path} loaded=true`);
|
|
2782
|
+
}
|
|
2783
|
+
else {
|
|
2784
|
+
const stderr = (bootstrap.stderr ?? "").trim();
|
|
2785
|
+
console.error(` [launchd] bootstrap returned ${bootstrap.status}: ${stderr}`);
|
|
2786
|
+
logFile(` [launchd] bootstrap returned ${bootstrap.status}: ${stderr}`);
|
|
2787
|
+
logFile(` [create-maxy] launchd-plist=${path} loaded=false exit=${bootstrap.status}`);
|
|
2788
|
+
throw new Error(`launchctl bootstrap ${gui()} ${path} failed (exit ${bootstrap.status}): ${stderr}`);
|
|
2789
|
+
}
|
|
2790
|
+
// Wait for the server to come up.
|
|
2791
|
+
console.log(" Waiting for web server...");
|
|
2792
|
+
let webServerUp = false;
|
|
2793
|
+
for (let i = 0; i < 20; i++) {
|
|
2794
|
+
try {
|
|
2795
|
+
execFileSync("curl", ["-sf", `http://localhost:${PORT}`, "-o", "/dev/null"], { timeout: 3000 });
|
|
2796
|
+
webServerUp = true;
|
|
2797
|
+
break;
|
|
2798
|
+
}
|
|
2799
|
+
catch {
|
|
2800
|
+
spawnSync("sleep", ["2"]);
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
if (!webServerUp) {
|
|
2804
|
+
console.log(` Server may still be starting. Check http://localhost:${PORT} in a moment.`);
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
function installService() {
|
|
2808
|
+
log("11", TOTAL, `Starting ${BRAND.productName}...`);
|
|
2809
|
+
// Linux falls through to the systemd-user body below; darwin renders +
|
|
2810
|
+
// bootstraps a LaunchAgent and returns; unsupported throws the literal
|
|
2811
|
+
// refusal at requireSupportedPlatform.
|
|
2812
|
+
const platform = requireSupportedPlatform(process.platform);
|
|
2813
|
+
if (platform === "darwin") {
|
|
2814
|
+
installServiceDarwin();
|
|
2815
|
+
return;
|
|
2816
|
+
}
|
|
2817
|
+
// Persist UDP buffer sizes for cloudflared QUIC stability (applied on every boot via sysctl.d)
|
|
2818
|
+
const sysctlTmpPath = `/tmp/99-${BRAND.hostname}-quic.conf`;
|
|
2819
|
+
const sysctlDestPath = `/etc/sysctl.d/99-${BRAND.hostname}-quic.conf`;
|
|
2820
|
+
try {
|
|
2821
|
+
const sysctlConf = "net.core.rmem_max=7340032\nnet.core.wmem_max=7340032\n";
|
|
2822
|
+
writeFileSync(sysctlTmpPath, sysctlConf);
|
|
2823
|
+
console.log(" [privileged] cp");
|
|
2824
|
+
shell("cp", [sysctlTmpPath, sysctlDestPath], { sudo: true });
|
|
2825
|
+
spawnSync("rm", ["-f", sysctlTmpPath]);
|
|
2826
|
+
spawnSync("sudo", ["sysctl", "--system"], { stdio: "ignore", timeout: 10_000 });
|
|
2827
|
+
}
|
|
2828
|
+
catch { /* non-critical — values applied on next reboot */ }
|
|
2829
|
+
const serviceDir = resolve(process.env.HOME ?? "/root", ".config/systemd/user");
|
|
2830
|
+
mkdirSync(serviceDir, { recursive: true });
|
|
2831
|
+
// Create systemd user service
|
|
2832
|
+
const persistDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
2833
|
+
mkdirSync(join(persistDir, "logs"), { recursive: true });
|
|
2834
|
+
// Write install-time config to .env (systemd reads via EnvironmentFile).
|
|
2835
|
+
// Preserves existing .env content (e.g. PORT overrides) — only
|
|
2836
|
+
// replaces or appends each managed line.
|
|
2837
|
+
const envPath = join(persistDir, ".env");
|
|
2838
|
+
try {
|
|
2839
|
+
let envContent = "";
|
|
2840
|
+
try {
|
|
2841
|
+
envContent = readFileSync(envPath, "utf-8");
|
|
2842
|
+
}
|
|
2843
|
+
catch { /* first install */ }
|
|
2844
|
+
// DISPLAY_MODE
|
|
2845
|
+
if (/^DISPLAY_MODE=.*/m.test(envContent)) {
|
|
2846
|
+
envContent = envContent.replace(/^DISPLAY_MODE=.*/m, `DISPLAY_MODE=${DISPLAY_MODE}`);
|
|
2847
|
+
}
|
|
2848
|
+
else {
|
|
2849
|
+
envContent = envContent.trimEnd() + (envContent.length > 0 ? "\n" : "") + `DISPLAY_MODE=${DISPLAY_MODE}\n`;
|
|
2850
|
+
}
|
|
2851
|
+
// EMBED_MODEL
|
|
2852
|
+
if (/^EMBED_MODEL=.*/m.test(envContent)) {
|
|
2853
|
+
envContent = envContent.replace(/^EMBED_MODEL=.*/m, `EMBED_MODEL=${EMBED_MODEL}`);
|
|
2854
|
+
}
|
|
2855
|
+
else {
|
|
2856
|
+
envContent = envContent.trimEnd() + (envContent.length > 0 ? "\n" : "") + `EMBED_MODEL=${EMBED_MODEL}\n`;
|
|
2857
|
+
}
|
|
2858
|
+
// EMBED_DIMENSIONS
|
|
2859
|
+
if (/^EMBED_DIMENSIONS=.*/m.test(envContent)) {
|
|
2860
|
+
envContent = envContent.replace(/^EMBED_DIMENSIONS=.*/m, `EMBED_DIMENSIONS=${EMBED_DIMS}`);
|
|
2861
|
+
}
|
|
2862
|
+
else {
|
|
2863
|
+
envContent = envContent.trimEnd() + (envContent.length > 0 ? "\n" : "") + `EMBED_DIMENSIONS=${EMBED_DIMS}\n`;
|
|
2864
|
+
}
|
|
2865
|
+
// NEO4J_URI — always written so the platform connects to the correct instance.
|
|
2866
|
+
// For shared instances this is bolt://localhost:7687 (the default seed-neo4j.sh
|
|
2867
|
+
// would use anyway), but writing it explicitly makes the .env self-documenting
|
|
2868
|
+
// and ensures upgrade detection works for any future port change.
|
|
2869
|
+
const neo4jUri = `bolt://localhost:${NEO4J_PORT}`;
|
|
2870
|
+
if (/^NEO4J_URI=.*/m.test(envContent)) {
|
|
2871
|
+
envContent = envContent.replace(/^NEO4J_URI=.*/m, `NEO4J_URI=${neo4jUri}`);
|
|
2872
|
+
}
|
|
2873
|
+
else {
|
|
2874
|
+
envContent = envContent.trimEnd() + (envContent.length > 0 ? "\n" : "") + `NEO4J_URI=${neo4jUri}\n`;
|
|
2875
|
+
}
|
|
2876
|
+
writeFileSync(envPath, envContent);
|
|
2877
|
+
logFile(` .env: DISPLAY_MODE=${DISPLAY_MODE}, EMBED_MODEL=${EMBED_MODEL}, EMBED_DIMENSIONS=${EMBED_DIMS}, NEO4J_URI=${neo4jUri}`);
|
|
2878
|
+
}
|
|
2879
|
+
catch (err) {
|
|
2880
|
+
console.error(` WARNING: failed to write .env to ${envPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2881
|
+
}
|
|
2882
|
+
// Propagate to child processes — seed-neo4j.sh reads both variables.
|
|
2883
|
+
process.env.EMBED_DIMENSIONS = String(EMBED_DIMS);
|
|
2884
|
+
process.env.NEO4J_URI = `bolt://localhost:${NEO4J_PORT}`;
|
|
2885
|
+
// maxy-ui runs on an internal-only port so a restart does not
|
|
2886
|
+
// drop the public TCP socket. maxy-edge.service owns the public port and
|
|
2887
|
+
// the VNC stack; maxy-ui sits behind it on 127.0.0.1:MAXY_UI_INTERNAL_PORT.
|
|
2888
|
+
// PORT + 1 (derived) avoids a fixed-port collision if the operator chose a
|
|
2889
|
+
// non-default --port.
|
|
2890
|
+
//
|
|
2891
|
+
// PORT in maxy.service's Environment block is the PUBLIC port,
|
|
2892
|
+
// not the internal one. Previously we wrote `Environment=PORT=<internal>`,
|
|
2893
|
+
// which collided with the install-time reader further down, which
|
|
2894
|
+
// correctly treats Environment=PORT= as public. The overload caused +1
|
|
2895
|
+
// drift per upgrade: each run read the internal value, treated it as
|
|
2896
|
+
// public, wrote internal = old_internal + 1. maxy-ui now binds
|
|
2897
|
+
// MAXY_UI_INTERNAL_PORT (with a fallback to PORT for mixed-state installs).
|
|
2898
|
+
const MAXY_UI_INTERNAL_PORT = PORT + 1;
|
|
2899
|
+
const edgeUnitShort = `${BRAND.hostname}-edge`;
|
|
2900
|
+
const edgeUnitName = `${edgeUnitShort}.service`;
|
|
2901
|
+
// Per-brand X display. Same value used for the edge unit's
|
|
2902
|
+
// DISPLAY env (stamped via __VNC_DISPLAY__ a few lines down) so the main
|
|
2903
|
+
// brand service and the edge service agree on which display Chromium runs.
|
|
2904
|
+
// brand.json (BRAND) is the single source of truth for
|
|
2905
|
+
// these fields at install time. The vncDisplay-derived offset rule lives
|
|
2906
|
+
// in the brand-creation tooling; at this point in the installer BRAND
|
|
2907
|
+
// already represents a parsed, validated brand manifest, and any missing
|
|
2908
|
+
// field is a brand-publish defect. Loud-fail rather than silently
|
|
2909
|
+
// substituting an offset (silent-fallback-masks-root-cause recurrence).
|
|
2910
|
+
if (typeof BRAND.vncDisplay !== "number") {
|
|
2911
|
+
console.error(`[create-maxy] error reason=cdp-port-unresolved brand=${BRAND.configDir} field=vncDisplay`);
|
|
2912
|
+
throw new Error(`brand.json missing required field: vncDisplay`);
|
|
2913
|
+
}
|
|
2914
|
+
const VNC_DISPLAY = BRAND.vncDisplay;
|
|
2915
|
+
for (const field of ["rfbPort", "websockifyPort", "cdpPort"]) {
|
|
2916
|
+
if (typeof BRAND[field] !== "number") {
|
|
2917
|
+
console.error(`[create-maxy] error reason=cdp-port-unresolved brand=${BRAND.configDir} field=${field}`);
|
|
2918
|
+
throw new Error(`brand.json missing required field: ${field}`);
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
const RFB_PORT = BRAND.rfbPort;
|
|
2922
|
+
const WEBSOCKIFY_PORT_BRAND = BRAND.websockifyPort;
|
|
2923
|
+
const CDP_PORT_BRAND = BRAND.cdpPort;
|
|
2924
|
+
const CLAUDE_SESSION_MANAGER_PORT_BRAND = BRAND.claudeSessionManagerPort;
|
|
2925
|
+
// pre-flight — refuse to write service files if any of the
|
|
2926
|
+
// three brand-scoped ports is already held by a process that is NOT this
|
|
2927
|
+
// brand's own on-demand browser nor a peer brand's edge stack.
|
|
2928
|
+
//
|
|
2929
|
+
// Classification (chromium Xtigervnc + websockify) reads
|
|
2930
|
+
// `/proc/<pid>/cmdline` and applies a holder-specific argv anchor.
|
|
2931
|
+
// covered chromium-only via `--user-data-dir=` closed the gap on
|
|
2932
|
+
// Xtigervnc (no such flag — anchor on the `:N` display literal) and
|
|
2933
|
+
// websockify (anchor on bind port). Brand identities (configDir,
|
|
2934
|
+
// vncDisplay, websockifyPort) come from brand-registry.json which the
|
|
2935
|
+
// bundler stamps at build time from every brands/<brand>/brand.json.
|
|
2936
|
+
//
|
|
2937
|
+
// Decisions per holder:
|
|
2938
|
+
// OWN_BRAND — SIGTERM, recheck, SIGKILL on stragglers, exit-1 only if
|
|
2939
|
+
// the port is still held after both signals.
|
|
2940
|
+
// PEER_BRAND — log OK and return (per-brand port sets are disjoint).
|
|
2941
|
+
// UNRELATED — refuse to write service files; emit operator override.
|
|
2942
|
+
// macOS dev hosts (no ss) fall through the catch and skip pre-flight
|
|
2943
|
+
// entirely — the runtime check in vnc.sh covers Linux production.
|
|
2944
|
+
const ownBrand = {
|
|
2945
|
+
configDir: BRAND.configDir,
|
|
2946
|
+
vncDisplay: VNC_DISPLAY,
|
|
2947
|
+
websockifyPort: WEBSOCKIFY_PORT_BRAND,
|
|
2948
|
+
};
|
|
2949
|
+
// Peer registry — load from payload/platform/config/brand-registry.json
|
|
2950
|
+
// when present. Older bundles ship without the
|
|
2951
|
+
// registry; in that case peerBrands stays empty. PEER_BRAND classification
|
|
2952
|
+
// for Xtigervnc/websockify is a defence-in-depth case anyway (port sets
|
|
2953
|
+
// are disjoint), so the empty-list fallback is safe — peer
|
|
2954
|
+
// chromium will fall through to UNRELATED, matching earlier behaviour
|
|
2955
|
+
// for the only realistic scenario (a stale peer browser on the wrong CDP
|
|
2956
|
+
// port).
|
|
2957
|
+
const peerBrands = (() => {
|
|
2958
|
+
const registryPath = join(PAYLOAD_DIR, "platform", "config", "brand-registry.json");
|
|
2959
|
+
if (!existsSync(registryPath)) {
|
|
2960
|
+
logFile(` [preflight] brand-registry.json not in payload — peer matching disabled`);
|
|
2961
|
+
return [];
|
|
2962
|
+
}
|
|
2963
|
+
try {
|
|
2964
|
+
const raw = JSON.parse(readFileSync(registryPath, "utf-8"));
|
|
2965
|
+
const entries = [];
|
|
2966
|
+
for (const b of raw.brands ?? []) {
|
|
2967
|
+
if (b.hostname === BRAND.hostname)
|
|
2968
|
+
continue;
|
|
2969
|
+
if (typeof b.configDir !== "string" || typeof b.vncDisplay !== "number" || typeof b.websockifyPort !== "number")
|
|
2970
|
+
continue;
|
|
2971
|
+
entries.push({ configDir: b.configDir, vncDisplay: b.vncDisplay, websockifyPort: b.websockifyPort });
|
|
2972
|
+
}
|
|
2973
|
+
return entries;
|
|
2974
|
+
}
|
|
2975
|
+
catch (err) {
|
|
2976
|
+
logFile(` [preflight] brand-registry.json parse failed: ${err instanceof Error ? err.message : String(err)} — peer matching disabled`);
|
|
2977
|
+
return [];
|
|
2978
|
+
}
|
|
2979
|
+
})();
|
|
2980
|
+
const ssReadHolder = (port) => {
|
|
2981
|
+
return execFileSync("ss", ["-tlnpH", `sport = :${port}`], {
|
|
2982
|
+
encoding: "utf-8", timeout: 3000, stdio: ["ignore", "pipe", "ignore"],
|
|
2983
|
+
});
|
|
2984
|
+
};
|
|
2985
|
+
// Pass raw NUL-separated cmdline to the classifier so it can argv-anchor
|
|
2986
|
+
// on `--user-data-dir=`. Replacing NUL with space here would defeat that.
|
|
2987
|
+
const readCmdline = (pid) => readFileSync(`/proc/${pid}/cmdline`, "utf-8");
|
|
2988
|
+
const sleepMs = (ms) => { spawnSync("sleep", [(ms / 1000).toString()]); };
|
|
2989
|
+
// Tightly scoped variant for retry-path ss reads. Failures here (timeout,
|
|
2990
|
+
// ENOMEM, signal) are structural — never the macOS-no-ss case (we already
|
|
2991
|
+
// succeeded once) — so they get a structured exit, not a stack trace.
|
|
2992
|
+
const ssReadOrAbort = (label, port) => {
|
|
2993
|
+
try {
|
|
2994
|
+
return ssReadHolder(port);
|
|
2995
|
+
}
|
|
2996
|
+
catch (err) {
|
|
2997
|
+
console.error(` ERROR: [preflight] ${label}=${port} ss recheck failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2998
|
+
console.error(` Resolve manually before retrying.`);
|
|
2999
|
+
process.exit(1);
|
|
3000
|
+
}
|
|
3001
|
+
};
|
|
3002
|
+
// Distinguish ESRCH (process already gone — expected) from EPERM/EINVAL
|
|
3003
|
+
// (alarming — signals we can't deliver, possibly a recycled pid). Returns
|
|
3004
|
+
// true for clean kill or ESRCH, false otherwise (caller logs a warning).
|
|
3005
|
+
const killNoThrow = (pid, signal) => {
|
|
3006
|
+
try {
|
|
3007
|
+
process.kill(pid, signal);
|
|
3008
|
+
return true;
|
|
3009
|
+
}
|
|
3010
|
+
catch (err) {
|
|
3011
|
+
const code = err.code;
|
|
3012
|
+
if (code === "ESRCH")
|
|
3013
|
+
return true;
|
|
3014
|
+
logFile(` [preflight] kill(${pid}, ${signal}) failed code=${code ?? "unknown"}`);
|
|
3015
|
+
return false;
|
|
3016
|
+
}
|
|
3017
|
+
};
|
|
3018
|
+
const classify = (ssOutput) => classifyPortHolder({
|
|
3019
|
+
ssOutput, ownBrand, peerBrands, getCmdline: readCmdline,
|
|
3020
|
+
});
|
|
3021
|
+
// log line varies by detected holder so the operator can see
|
|
3022
|
+
// which OWN_BRAND stack is being killed. The kill loop is identical for
|
|
3023
|
+
// all three holders (SIGTERM → 300ms → recheck → SIGKILL → recheck), so
|
|
3024
|
+
// only the announce line differs.
|
|
3025
|
+
const ownBrandAnnounceLine = (label, port, c) => {
|
|
3026
|
+
if (c.holderType === "xtigervnc") {
|
|
3027
|
+
return ` [preflight] ${label}=${port} held by OWN brand Xtigervnc display=:${c.vncDisplay} pid=${c.pid} — sending SIGTERM`;
|
|
3028
|
+
}
|
|
3029
|
+
if (c.holderType === "websockify") {
|
|
3030
|
+
return ` [preflight] ${label}=${port} held by OWN brand websockify pid=${c.pid} — sending SIGTERM`;
|
|
3031
|
+
}
|
|
3032
|
+
// Default — chromium / unknown OWN_BRAND
|
|
3033
|
+
return ` [preflight] ${label}=${port} held by OWN brand process pid=${c.pid} profile=${c.profilePath} — sending SIGTERM`;
|
|
3034
|
+
};
|
|
3035
|
+
const checkInstallPortFree = (label, port) => {
|
|
3036
|
+
let firstSsOutput;
|
|
3037
|
+
try {
|
|
3038
|
+
firstSsOutput = ssReadHolder(port);
|
|
3039
|
+
}
|
|
3040
|
+
catch (err) {
|
|
3041
|
+
// ss may not be present on macOS dev hosts — skip the pre-flight there
|
|
3042
|
+
// rather than abort the install. The runtime check in vnc.sh covers
|
|
3043
|
+
// production-like Linux installs where this matters. This catch is
|
|
3044
|
+
// narrow on purpose: only the first ss invocation may legitimately
|
|
3045
|
+
// fail (binary missing); retry-path failures use ssReadOrAbort.
|
|
3046
|
+
logFile(` [preflight] ${label}=${port} check skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
3047
|
+
return;
|
|
3048
|
+
}
|
|
3049
|
+
let r = classify(firstSsOutput);
|
|
3050
|
+
// ENOENT race — process exited between ss and cmdline read. Port is
|
|
3051
|
+
// probably free now; one re-check resolves it deterministically.
|
|
3052
|
+
if (r.cmdlineReadFailed)
|
|
3053
|
+
r = classify(ssReadOrAbort(label, port));
|
|
3054
|
+
if (r.kind === "EMPTY")
|
|
3055
|
+
return;
|
|
3056
|
+
if (r.kind === "PEER_BRAND") {
|
|
3057
|
+
logFile(` [preflight] ${label}=${port} held by a peer brand's stack — OK (per-brand ports are disjoint by construction)`);
|
|
3058
|
+
return;
|
|
3059
|
+
}
|
|
3060
|
+
if (r.kind === "OWN_BRAND" && r.pid !== undefined) {
|
|
3061
|
+
logFile(ownBrandAnnounceLine(label, port, r));
|
|
3062
|
+
killNoThrow(r.pid, "SIGTERM");
|
|
3063
|
+
sleepMs(300);
|
|
3064
|
+
const after = classify(ssReadOrAbort(label, port));
|
|
3065
|
+
if (after.kind === "EMPTY") {
|
|
3066
|
+
logFile(` [preflight] ${label}=${port} freed`);
|
|
3067
|
+
return;
|
|
3068
|
+
}
|
|
3069
|
+
if (after.kind === "OWN_BRAND" && after.pid === r.pid) {
|
|
3070
|
+
logFile(` [preflight] ${label}=${port} survived SIGTERM — sending SIGKILL`);
|
|
3071
|
+
killNoThrow(r.pid, "SIGKILL");
|
|
3072
|
+
sleepMs(300);
|
|
3073
|
+
const final = classify(ssReadOrAbort(label, port));
|
|
3074
|
+
if (final.kind === "EMPTY") {
|
|
3075
|
+
logFile(` [preflight] ${label}=${port} freed`);
|
|
3076
|
+
return;
|
|
3077
|
+
}
|
|
3078
|
+
console.error(` ERROR: [preflight] ${label}=${port} OWN_BRAND auto-kill failed pid=${r.pid} — resolve manually before retrying.`);
|
|
3079
|
+
process.exit(1);
|
|
3080
|
+
}
|
|
3081
|
+
// A different OWN_BRAND pid took the port. The brand's user services
|
|
3082
|
+
// are respawning Chromium — installer cannot win this race. Stop the
|
|
3083
|
+
// services, then retry.
|
|
3084
|
+
if (after.kind === "OWN_BRAND") {
|
|
3085
|
+
console.error(` ERROR: [preflight] ${label}=${port} brand respawned a new OWN_BRAND pid (was ${r.pid}, now ${after.pid}).`);
|
|
3086
|
+
console.error(` Stop the brand's user services first: \`systemctl --user stop ${BRAND.hostname}-edge ${BRAND.hostname}\`, then re-run the installer.`);
|
|
3087
|
+
process.exit(1);
|
|
3088
|
+
}
|
|
3089
|
+
// PEER_BRAND or UNRELATED took the port post-kill — fall through to
|
|
3090
|
+
// those branches by re-classifying the surviving holder.
|
|
3091
|
+
r = after;
|
|
3092
|
+
if (r.kind === "EMPTY")
|
|
3093
|
+
return;
|
|
3094
|
+
if (r.kind === "PEER_BRAND") {
|
|
3095
|
+
logFile(` [preflight] ${label}=${port} now held by a peer brand's stack — OK`);
|
|
3096
|
+
return;
|
|
3097
|
+
}
|
|
3098
|
+
// r.kind === "UNRELATED" — fall through to the operator-override block.
|
|
3099
|
+
}
|
|
3100
|
+
// UNRELATED — preserve the operator-override path verbatim.
|
|
3101
|
+
console.error(` ERROR: [preflight:collision] brand=${BRAND.hostname} ${label}=${port} held by an unrelated process:`);
|
|
3102
|
+
console.error(` ${firstSsOutput.trim()}`);
|
|
3103
|
+
if (r.cmdline)
|
|
3104
|
+
console.error(` cmdline: ${r.cmdline}`);
|
|
3105
|
+
console.error(` Refusing to write service files; resolve the collision before retrying.`);
|
|
3106
|
+
console.error(` Operator override: edit brands/${BRAND.hostname}/brand.json, set/add \`${label}\` to a free port, re-bundle, re-install.`);
|
|
3107
|
+
process.exit(1);
|
|
3108
|
+
};
|
|
3109
|
+
checkInstallPortFree("rfbPort", RFB_PORT);
|
|
3110
|
+
checkInstallPortFree("websockifyPort", WEBSOCKIFY_PORT_BRAND);
|
|
3111
|
+
checkInstallPortFree("cdpPort", CDP_PORT_BRAND);
|
|
3112
|
+
// ACCOUNT_ID stamped into the brand unit so the writeNodeWithEdges
|
|
3113
|
+
// gate at platform/lib/graph-write/src/index.ts:170 has a real identity to
|
|
3114
|
+
// compare against (instead of process.env.ACCOUNT_ID === undefined). Resolved
|
|
3115
|
+
// here AFTER setupAccount() ran upstream — setup-account.sh wrote account.json,
|
|
3116
|
+
// so an empty resolution at this point is a corrupted install (e.g. the seed
|
|
3117
|
+
// failed silently, or accounts/ was wiped between setup and unit-write).
|
|
3118
|
+
const installAccountId = resolveInstallAccountId();
|
|
3119
|
+
if (!installAccountId) {
|
|
3120
|
+
throw new Error(`installService: no account discovered at ${INSTALL_DIR}/data/accounts/<uuid>/account.json — ` +
|
|
3121
|
+
`setupAccount() (setup-account.sh) should have created one. Refusing to write a systemd unit ` +
|
|
3122
|
+
`without ACCOUNT_ID; the boot validator would FATAL on every restart.`);
|
|
3123
|
+
}
|
|
3124
|
+
const serviceFile = buildMaxyUnitFile({
|
|
3125
|
+
productName: BRAND.productName,
|
|
3126
|
+
brandHostname: BRAND.hostname,
|
|
3127
|
+
neo4jDedicated: NEO4J_DEDICATED,
|
|
3128
|
+
installDir: INSTALL_DIR,
|
|
3129
|
+
persistDir,
|
|
3130
|
+
port: PORT,
|
|
3131
|
+
maxyUiInternalPort: MAXY_UI_INTERNAL_PORT,
|
|
3132
|
+
vncDisplay: VNC_DISPLAY,
|
|
3133
|
+
rfbPort: RFB_PORT,
|
|
3134
|
+
websockifyPort: WEBSOCKIFY_PORT_BRAND,
|
|
3135
|
+
cdpPort: CDP_PORT_BRAND,
|
|
3136
|
+
claudeSessionManagerPort: CLAUDE_SESSION_MANAGER_PORT_BRAND,
|
|
3137
|
+
chromiumBin: RESOLVED_CHROMIUM_BIN,
|
|
3138
|
+
accountId: installAccountId,
|
|
3139
|
+
});
|
|
3140
|
+
writeFileSync(join(serviceDir, BRAND.serviceName), serviceFile);
|
|
3141
|
+
// (maxy-code) — write the claude-session-manager unit. The main
|
|
3142
|
+
// brand unit Requires= + After= this one, so systemd starts it first.
|
|
3143
|
+
const claudeSessionManagerUnitName = `${BRAND.hostname}-claude-session-manager.service`;
|
|
3144
|
+
const claudeSessionManagerUnit = buildClaudeSessionManagerUnitFile({
|
|
3145
|
+
productName: BRAND.productName,
|
|
3146
|
+
brandHostname: BRAND.hostname,
|
|
3147
|
+
installDir: INSTALL_DIR,
|
|
3148
|
+
persistDir,
|
|
3149
|
+
claudeSessionManagerPort: CLAUDE_SESSION_MANAGER_PORT_BRAND,
|
|
3150
|
+
brandPort: PORT,
|
|
3151
|
+
maxyUiInternalPort: MAXY_UI_INTERNAL_PORT,
|
|
3152
|
+
accountId: installAccountId,
|
|
3153
|
+
cdpPort: CDP_PORT_BRAND,
|
|
3154
|
+
});
|
|
3155
|
+
writeFileSync(join(serviceDir, claudeSessionManagerUnitName), claudeSessionManagerUnit);
|
|
3156
|
+
logFile(` ${claudeSessionManagerUnitName}: CLAUDE_SESSION_MANAGER_PORT=${CLAUDE_SESSION_MANAGER_PORT_BRAND} MAXY_UI_INTERNAL_PORT=${MAXY_UI_INTERNAL_PORT}`);
|
|
3157
|
+
// Task 250 — write the brand-agnostic `claude-ptys.slice` unit. Every
|
|
3158
|
+
// brand's session manager drops its per-spawn scope units under this
|
|
3159
|
+
// slice. Writing it once per install is idempotent (the content is
|
|
3160
|
+
// identical across brands and across runs); subsequent brand installs
|
|
3161
|
+
// re-stamp the same content. No service restart is needed — `daemon-reload`
|
|
3162
|
+
// below picks it up.
|
|
3163
|
+
const claudePtysSliceName = "claude-ptys.slice";
|
|
3164
|
+
writeFileSync(join(serviceDir, claudePtysSliceName), buildClaudePtysSliceUnitFile());
|
|
3165
|
+
logFile(` ${claudePtysSliceName}: Task 250 slice for claude-session-*.scope cohort`);
|
|
3166
|
+
// Task 602 — write the brand-agnostic `cloudflared.slice` unit. Every
|
|
3167
|
+
// cloudflared-<brand>.scope spawned by resume-tunnel.sh drops under this
|
|
3168
|
+
// slice. Writing once per install is idempotent; daemon-reload below picks it up.
|
|
3169
|
+
const cloudflaredSliceName = "cloudflared.slice";
|
|
3170
|
+
writeFileSync(join(serviceDir, cloudflaredSliceName), buildCloudflaredSliceUnitFile());
|
|
3171
|
+
logFile(` ${cloudflaredSliceName}: Task 602 slice for cloudflared-*.scope cohort`);
|
|
3172
|
+
// Task 600 — per-brand RSS sampler: a long-running user service that
|
|
3173
|
+
// periodically writes per-claude.exe RSS + aggregate RSS to disk.
|
|
3174
|
+
// Named per-brand (BRAND.hostname prefix) so two brands on the same
|
|
3175
|
+
// device each own their own sampler writing to their own log path.
|
|
3176
|
+
const rssSamplerUnitName = `${BRAND.hostname}-rss-sampler.service`;
|
|
3177
|
+
const rssSamplerScriptPath = resolve(INSTALL_DIR, "platform/scripts/rss-sampler.sh");
|
|
3178
|
+
const rssSamplerLogPath = join(persistDir, "rss-sampler.log");
|
|
3179
|
+
const rssSamplerUnit = `[Unit]
|
|
3180
|
+
Description=${BRAND.productName} RSS Sampler
|
|
3181
|
+
|
|
3182
|
+
[Service]
|
|
3183
|
+
Type=simple
|
|
3184
|
+
ExecStart=/bin/bash ${rssSamplerScriptPath}
|
|
3185
|
+
Environment=LOG_PATH=${rssSamplerLogPath}
|
|
3186
|
+
Restart=on-failure
|
|
3187
|
+
RestartSec=10
|
|
3188
|
+
|
|
3189
|
+
[Install]
|
|
3190
|
+
WantedBy=default.target
|
|
3191
|
+
`;
|
|
3192
|
+
writeFileSync(join(serviceDir, rssSamplerUnitName), rssSamplerUnit);
|
|
3193
|
+
logFile(` rss-sampler installed: ${rssSamplerUnitName} LOG_PATH=${rssSamplerLogPath}`);
|
|
3194
|
+
// the edge service: always-on front door that owns the public
|
|
3195
|
+
// port (PORT) and the VNC stack (Xtigervnc + websockify). Its lifecycle is
|
|
3196
|
+
// independent of the main brand service, so an in-place upgrade triggered
|
|
3197
|
+
// from the admin terminal can restart the main brand service without
|
|
3198
|
+
// disconnecting the browser's remote terminal WebSocket.
|
|
3199
|
+
//
|
|
3200
|
+
// the unit is per-brand so two brands on the same device each
|
|
3201
|
+
// own their own edge listener on their own EDGE_PORT — installing brand B
|
|
3202
|
+
// never rewrites brand A's unit or steals brand A's public port. Upgrades
|
|
3203
|
+
// from pre-662 installs require the manual recovery paragraph in
|
|
3204
|
+
// .docs/deployment.md before re-running this installer; auto-migration is
|
|
3205
|
+
// intentionally scoped out.
|
|
3206
|
+
// VNC_DISPLAY (defined above for the main brand unit) is also stamped into
|
|
3207
|
+
// the edge unit so both services agree on the X display. Per-brand display
|
|
3208
|
+
// closes the cross-brand cookie leak documented.
|
|
3209
|
+
const edgeTemplatePath = resolve(INSTALL_DIR, "platform/templates/systemd/edge.service.template");
|
|
3210
|
+
if (existsSync(edgeTemplatePath)) {
|
|
3211
|
+
const edgeServiceContent = readFileSync(edgeTemplatePath, "utf-8")
|
|
3212
|
+
.replace(/__INSTALL_DIR__/g, INSTALL_DIR)
|
|
3213
|
+
.replace(/__EDGE_PORT__/g, String(PORT))
|
|
3214
|
+
.replace(/__MAXY_UI_PORT__/g, String(MAXY_UI_INTERNAL_PORT))
|
|
3215
|
+
.replace(/__PERSIST_DIR__/g, persistDir)
|
|
3216
|
+
.replace(/__VNC_DISPLAY__/g, String(VNC_DISPLAY))
|
|
3217
|
+
.replace(/__WEBSOCKIFY_PORT__/g, String(WEBSOCKIFY_PORT_BRAND))
|
|
3218
|
+
.replace(/__CDP_PORT__/g, String(CDP_PORT_BRAND))
|
|
3219
|
+
.replace(/__RFB_PORT__/g, String(RFB_PORT));
|
|
3220
|
+
writeFileSync(join(serviceDir, edgeUnitName), edgeServiceContent);
|
|
3221
|
+
logFile(` ${edgeUnitName}: EDGE_PORT=${PORT} MAXY_UI_PORT=${MAXY_UI_INTERNAL_PORT} VNC_DISPLAY=:${VNC_DISPLAY} RFB_PORT=${RFB_PORT} WEBSOCKIFY_PORT=${WEBSOCKIFY_PORT_BRAND} CDP_PORT=${CDP_PORT_BRAND}`);
|
|
3222
|
+
}
|
|
3223
|
+
else {
|
|
3224
|
+
console.error(` WARNING: edge.service.template missing at ${edgeTemplatePath} — VNC transport unavailable`);
|
|
3225
|
+
}
|
|
3226
|
+
// the unit declares Environment=PATH=%h/.local/bin:... so the graph
|
|
3227
|
+
// MCP shim's spawn("uvx", ...) resolves against uv's install location. Without
|
|
3228
|
+
// this line, the service inherits the default systemd-user PATH — which
|
|
3229
|
+
// excludes ~/.local/bin — and the shim exits ENOENT before any query lands.
|
|
3230
|
+
logFile(` ${BRAND.serviceName}: PATH env includes %h/.local/bin for uvx resolution`);
|
|
3231
|
+
// WiFi AP provisioning service — system-level (requires root for hostapd/dnsmasq).
|
|
3232
|
+
// Runs at boot to check connectivity. If no saved WiFi and no ethernet, activates
|
|
3233
|
+
// a temporary AP with a captive portal for first-time WiFi configuration.
|
|
3234
|
+
const currentUser = execFileSync("whoami", [], { encoding: "utf-8" }).trim();
|
|
3235
|
+
const wifiProvisionService = `[Unit]
|
|
3236
|
+
Description=${BRAND.productName} WiFi Provisioning
|
|
3237
|
+
After=NetworkManager.service
|
|
3238
|
+
Before=${BRAND.serviceName}
|
|
3239
|
+
|
|
3240
|
+
[Service]
|
|
3241
|
+
Type=simple
|
|
3242
|
+
ExecStart=/bin/bash ${INSTALL_DIR}/platform/scripts/wifi-provision.sh
|
|
3243
|
+
ExecStopPost=/bin/bash ${INSTALL_DIR}/platform/scripts/wifi-provision.sh cleanup
|
|
3244
|
+
Environment=MAXY_PLATFORM_ROOT=${INSTALL_DIR}/platform
|
|
3245
|
+
Environment=INSTALL_USER=${currentUser}
|
|
3246
|
+
Environment=INSTALL_HOME=${process.env.HOME ?? "/home/" + currentUser}
|
|
3247
|
+
TimeoutStartSec=300
|
|
3248
|
+
TimeoutStopSec=30
|
|
3249
|
+
|
|
3250
|
+
[Install]
|
|
3251
|
+
WantedBy=multi-user.target
|
|
3252
|
+
`;
|
|
3253
|
+
const systemServiceDir = "/etc/systemd/system";
|
|
3254
|
+
const wifiProvisionPath = join(systemServiceDir, "wifi-provision.service");
|
|
3255
|
+
try {
|
|
3256
|
+
const tmpPath = "/tmp/wifi-provision.service";
|
|
3257
|
+
writeFileSync(tmpPath, wifiProvisionService);
|
|
3258
|
+
console.log(" [privileged] cp");
|
|
3259
|
+
shell("cp", [tmpPath, wifiProvisionPath], { sudo: true });
|
|
3260
|
+
spawnSync("rm", ["-f", tmpPath]);
|
|
3261
|
+
spawnSync("sudo", ["systemctl", "daemon-reload"], { stdio: "inherit" });
|
|
3262
|
+
spawnSync("sudo", ["systemctl", "enable", "wifi-provision"], { stdio: "inherit" });
|
|
3263
|
+
logFile(" WiFi provisioning service installed and enabled");
|
|
3264
|
+
}
|
|
3265
|
+
catch (err) {
|
|
3266
|
+
console.error(` WARNING: Failed to install wifi-provision service: ${err instanceof Error ? err.message : String(err)}`);
|
|
3267
|
+
}
|
|
3268
|
+
// Disable hostapd and dnsmasq system services (we manage them manually
|
|
3269
|
+
// from wifi-provision.sh — the system services would conflict).
|
|
3270
|
+
spawnSync("sudo", ["systemctl", "stop", "hostapd"], { stdio: "ignore" });
|
|
3271
|
+
spawnSync("sudo", ["systemctl", "disable", "hostapd"], { stdio: "ignore" });
|
|
3272
|
+
spawnSync("sudo", ["systemctl", "stop", "dnsmasq"], { stdio: "ignore" });
|
|
3273
|
+
spawnSync("sudo", ["systemctl", "disable", "dnsmasq"], { stdio: "ignore" });
|
|
3274
|
+
// Enable lingering so user services run without login
|
|
3275
|
+
try {
|
|
3276
|
+
spawnSync("sudo", ["loginctl", "enable-linger", currentUser], { stdio: "inherit" });
|
|
3277
|
+
}
|
|
3278
|
+
catch { /* not critical */ }
|
|
3279
|
+
// Task 504: persist the systemd journal so per-unit lifecycle events
|
|
3280
|
+
// (SIGTERM/SIGKILL transitions, dependency-propagated stops, restart job
|
|
3281
|
+
// records) survive reboots and remain greppable via `journalctl --user -u
|
|
3282
|
+
// <brand>.service` after a restart loop quiets.
|
|
3283
|
+
//
|
|
3284
|
+
// Why a drop-in and not the Task-249 `mkdir /var/log/journal`: the directory
|
|
3285
|
+
// heuristic only takes effect under journald's default `Storage=auto`. A
|
|
3286
|
+
// base image (common on RPi to spare the SD card) that ships an explicit
|
|
3287
|
+
// `Storage=volatile` overrides directory existence, so the bare mkdir is a
|
|
3288
|
+
// no-op there — exactly the state observed on the realagent-code device. An
|
|
3289
|
+
// explicit `Storage=persistent` drop-in wins regardless of the base default,
|
|
3290
|
+
// and journald itself creates `/var/log/journal/<machine-id>` on restart, so
|
|
3291
|
+
// the mkdir is no longer needed.
|
|
3292
|
+
//
|
|
3293
|
+
// journald is one system instance, so the drop-in is host-wide with a neutral
|
|
3294
|
+
// filename — idempotent across brands and reinstalls. Restart journald only
|
|
3295
|
+
// when the drop-in content actually changes, to avoid churning the system
|
|
3296
|
+
// journal on every reinstall. Best-effort: a failure here warns, never aborts
|
|
3297
|
+
// the install.
|
|
3298
|
+
try {
|
|
3299
|
+
const journaldDropinDir = "/etc/systemd/journald.conf.d";
|
|
3300
|
+
const journaldDropinPath = `${journaldDropinDir}/10-maxy-persistent.conf`;
|
|
3301
|
+
const desiredJournaldConf = "[Journal]\nStorage=persistent\n";
|
|
3302
|
+
let journaldCurrent = "";
|
|
3303
|
+
if (existsSync(journaldDropinPath)) {
|
|
3304
|
+
try {
|
|
3305
|
+
journaldCurrent = readFileSync(journaldDropinPath, "utf-8");
|
|
3306
|
+
}
|
|
3307
|
+
catch {
|
|
3308
|
+
// Unreadable existing drop-in — treat as changed so we rewrite + restart.
|
|
3309
|
+
journaldCurrent = "";
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
if (journaldCurrent === desiredJournaldConf) {
|
|
3313
|
+
logFile(` journald persistence: already active (${journaldDropinPath} matches Storage=persistent)`);
|
|
3314
|
+
}
|
|
3315
|
+
else {
|
|
3316
|
+
const journaldTmpPath = "/tmp/10-maxy-persistent.conf";
|
|
3317
|
+
writeFileSync(journaldTmpPath, desiredJournaldConf);
|
|
3318
|
+
console.log(" [privileged] install journald Storage=persistent drop-in");
|
|
3319
|
+
shell("mkdir", ["-p", journaldDropinDir], { sudo: true });
|
|
3320
|
+
shell("cp", [journaldTmpPath, journaldDropinPath], { sudo: true });
|
|
3321
|
+
spawnSync("rm", ["-f", journaldTmpPath]);
|
|
3322
|
+
console.log(" [privileged] systemctl restart systemd-journald");
|
|
3323
|
+
spawnSync("sudo", ["systemctl", "restart", "systemd-journald"], { stdio: "inherit", timeout: 15_000 });
|
|
3324
|
+
logFile(` journald persistence: enabled (wrote ${journaldDropinPath} Storage=persistent + restarted journald)`);
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
catch (err) {
|
|
3328
|
+
console.error(` WARNING: failed to enable journald persistence: ${err instanceof Error ? err.message : String(err)}`);
|
|
3329
|
+
}
|
|
3330
|
+
// Task 504: capture the initiator of a brand-service restart so the next
|
|
3331
|
+
// recurrence of the restart loop is attributable to its source from
|
|
3332
|
+
// device-retained evidence. The brand admin process logs only that it
|
|
3333
|
+
// *received* SIGTERM (not the sender), and a JS signal handler cannot read
|
|
3334
|
+
// si_pid. auditd is the device-side primitive that names the initiator:
|
|
3335
|
+
//
|
|
3336
|
+
// - `kill` syscall with sig=15 (SIGTERM): names a process that sends
|
|
3337
|
+
// SIGTERM directly to the brand pid. For a systemd-managed stop the
|
|
3338
|
+
// sender is the user systemd manager itself, so this rule alone does NOT
|
|
3339
|
+
// name the upstream actor of a managed restart — but it does catch a
|
|
3340
|
+
// direct, non-systemd kill.
|
|
3341
|
+
// - `execve` of systemctl: names the pid/cgroup that drives a CLI restart
|
|
3342
|
+
// (`systemctl --user restart <unit>`), which is the upstream actor the
|
|
3343
|
+
// kill rule misses for the systemd-mediated path.
|
|
3344
|
+
// - Once auditd runs, systemd emits native SERVICE_START/SERVICE_STOP audit
|
|
3345
|
+
// records with no rule needed, covering a restart issued over raw D-Bus
|
|
3346
|
+
// (no systemctl binary, so the execve rule would miss it).
|
|
3347
|
+
//
|
|
3348
|
+
// Rules are deliberately narrow (SIGTERM + systemctl execve only) so audit
|
|
3349
|
+
// write volume stays bounded on a low-power device; auditd's default log
|
|
3350
|
+
// rotation bounds disk further. Everything here is best-effort: a box with no
|
|
3351
|
+
// apt mirror, or a kernel without audit support, warns and the install still
|
|
3352
|
+
// completes. Load via `augenrules --load` (merges rules.d into the running
|
|
3353
|
+
// daemon without a disruptive auditd restart).
|
|
3354
|
+
try {
|
|
3355
|
+
console.log(" [privileged] apt-get install auditd");
|
|
3356
|
+
shell("apt-get", ["install", "-y", "auditd"], { sudo: true, bestEffort: true });
|
|
3357
|
+
const auditRulesPath = "/etc/audit/rules.d/90-maxy-restart-forensics.rules";
|
|
3358
|
+
// Rule ordering matters: auditctl loads rules in file order, and a rule a
|
|
3359
|
+
// kernel rejects (the arch=b32 kill rule on an arm64 kernel without 32-bit
|
|
3360
|
+
// compat) must not sit ahead of rules that should load. So the b64 kill
|
|
3361
|
+
// rule and the systemctl watch come first; the b32 kill rule (the one that
|
|
3362
|
+
// may be rejected) comes last. The systemctl capture uses a file watch
|
|
3363
|
+
// (`-w … -p x`), the idiomatic form for "audit execution of this binary" —
|
|
3364
|
+
// it records the caller pid/cgroup and is arch-agnostic, avoiding the
|
|
3365
|
+
// invalid `arch`+`path`+`perm` field mix.
|
|
3366
|
+
const auditRules = [
|
|
3367
|
+
"# Maxy restart-loop forensics (Task 504) — name the initiator of a",
|
|
3368
|
+
"# brand-service restart on the next recurrence.",
|
|
3369
|
+
"# SIGTERM sender (a0=target pid, a1=signal).",
|
|
3370
|
+
"-a always,exit -F arch=b64 -S kill -F a1=15 -k maxy_sigterm",
|
|
3371
|
+
"# systemctl execution — names the caller pid/cgroup of a CLI restart.",
|
|
3372
|
+
"-w /usr/bin/systemctl -p x -k maxy_systemctl",
|
|
3373
|
+
"# b32 kill sibling last: a 32-bit-compat caller is still captured, and a",
|
|
3374
|
+
"# rejection on an arm64 kernel without compat cannot block the rules above.",
|
|
3375
|
+
"-a always,exit -F arch=b32 -S kill -F a1=15 -k maxy_sigterm",
|
|
3376
|
+
"",
|
|
3377
|
+
].join("\n");
|
|
3378
|
+
const auditTmpPath = "/tmp/90-maxy-restart-forensics.rules";
|
|
3379
|
+
writeFileSync(auditTmpPath, auditRules);
|
|
3380
|
+
console.log(" [privileged] install auditd restart-forensics rules");
|
|
3381
|
+
shell("mkdir", ["-p", "/etc/audit/rules.d"], { sudo: true, bestEffort: true });
|
|
3382
|
+
shell("cp", [auditTmpPath, auditRulesPath], { sudo: true, bestEffort: true });
|
|
3383
|
+
spawnSync("rm", ["-f", auditTmpPath]);
|
|
3384
|
+
console.log(" [privileged] augenrules --load");
|
|
3385
|
+
shell("augenrules", ["--load"], { sudo: true, bestEffort: true });
|
|
3386
|
+
logFile(` auditd restart forensics: rules at ${auditRulesPath} (keys maxy_sigterm, maxy_systemctl) — verify on device with \`ausearch -k maxy_sigterm\``);
|
|
3387
|
+
}
|
|
3388
|
+
catch (err) {
|
|
3389
|
+
console.error(` WARNING: failed to install auditd restart forensics: ${err instanceof Error ? err.message : String(err)}`);
|
|
3390
|
+
}
|
|
3391
|
+
// Reload and (re)start.
|
|
3392
|
+
//
|
|
3393
|
+
// ordering: on upgrades, the old main brand service still holds
|
|
3394
|
+
// the public port (PORT). Stop it FIRST so the edge can bind that socket;
|
|
3395
|
+
// starting the edge first would race against the old main brand service
|
|
3396
|
+
// and fail with EADDRINUSE. Fresh installs: the stop is a no-op.
|
|
3397
|
+
const unitName = BRAND.serviceName.replace(".service", "");
|
|
3398
|
+
const claudeSessionManagerUnitShort = claudeSessionManagerUnitName.replace(".service", "");
|
|
3399
|
+
spawnSync("systemctl", ["--user", "stop", unitName], { stdio: "inherit" });
|
|
3400
|
+
spawnSync("systemctl", ["--user", "daemon-reload"], { stdio: "inherit" });
|
|
3401
|
+
spawnSync("systemctl", ["--user", "enable", edgeUnitShort], { stdio: "inherit" });
|
|
3402
|
+
spawnSync("systemctl", ["--user", "enable", claudeSessionManagerUnitShort], { stdio: "inherit" });
|
|
3403
|
+
spawnSync("systemctl", ["--user", "enable", unitName], { stdio: "inherit" });
|
|
3404
|
+
spawnSync("systemctl", ["--user", "enable", `${BRAND.hostname}-rss-sampler`], { stdio: "inherit" });
|
|
3405
|
+
// edge first: binds public port + starts VNC stack. Then claude-session-manager
|
|
3406
|
+
// (so the main brand unit's Requires= is satisfied). Then main brand service.
|
|
3407
|
+
// rss-sampler is independent — start last, failure is non-blocking.
|
|
3408
|
+
spawnSync("systemctl", ["--user", "restart", edgeUnitShort], { stdio: "inherit" });
|
|
3409
|
+
spawnSync("systemctl", ["--user", "restart", claudeSessionManagerUnitShort], { stdio: "inherit" });
|
|
3410
|
+
spawnSync("systemctl", ["--user", "restart", unitName], { stdio: "inherit" });
|
|
3411
|
+
spawnSync("systemctl", ["--user", "restart", `${BRAND.hostname}-rss-sampler`], { stdio: "inherit" });
|
|
3412
|
+
// Session manager runs in its own systemd user unit and can boot-fail silently
|
|
3413
|
+
// (e.g. mcp-block-missing). The web server below comes up regardless, so the
|
|
3414
|
+
// install terminal would otherwise report success while every API call 503s.
|
|
3415
|
+
// Surface the boot failure on the trusted surface and refuse to declare success.
|
|
3416
|
+
console.log(" Waiting for session manager...");
|
|
3417
|
+
let sessionManagerActive = false;
|
|
3418
|
+
for (let i = 0; i < 5; i++) {
|
|
3419
|
+
const probe = spawnSync("systemctl", ["--user", "is-active", claudeSessionManagerUnitShort], { stdio: "pipe", encoding: "utf-8" });
|
|
3420
|
+
if ((probe.stdout ?? "").trim() === "active") {
|
|
3421
|
+
sessionManagerActive = true;
|
|
3422
|
+
break;
|
|
3423
|
+
}
|
|
3424
|
+
spawnSync("sleep", ["2"]);
|
|
3425
|
+
}
|
|
3426
|
+
if (!sessionManagerActive) {
|
|
3427
|
+
const status = spawnSync("systemctl", ["--user", "status", claudeSessionManagerUnitShort, "--no-pager", "-n", "20"], { stdio: "pipe", encoding: "utf-8" });
|
|
3428
|
+
const statusOutput = `${status.stdout ?? ""}${status.stderr ?? ""}`.trimEnd();
|
|
3429
|
+
console.error(statusOutput);
|
|
3430
|
+
logFile(statusOutput);
|
|
3431
|
+
const failureLine = `Session manager (${claudeSessionManagerUnitShort}) failed to start. See above for boot-failed reason.`;
|
|
3432
|
+
console.error(failureLine);
|
|
3433
|
+
logFile(failureLine);
|
|
3434
|
+
process.exit(1);
|
|
3435
|
+
}
|
|
3436
|
+
// Wait for the server to come up
|
|
3437
|
+
console.log(" Waiting for web server...");
|
|
3438
|
+
let webServerUp = false;
|
|
3439
|
+
for (let i = 0; i < 20; i++) {
|
|
3440
|
+
try {
|
|
3441
|
+
execFileSync("curl", ["-sf", `http://localhost:${PORT}`, "-o", "/dev/null"], { timeout: 3000 });
|
|
3442
|
+
webServerUp = true;
|
|
3443
|
+
break;
|
|
3444
|
+
}
|
|
3445
|
+
catch {
|
|
3446
|
+
spawnSync("sleep", ["2"]);
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
if (!webServerUp) {
|
|
3450
|
+
console.log(` Server may still be starting. Check http://${DEVICE_HOSTNAME}.local:${PORT} in a moment.`);
|
|
3451
|
+
}
|
|
3452
|
+
// Validate CDP: the programmatic Playwright MCP server connects to Chromium
|
|
3453
|
+
// via --cdp-endpoint http://127.0.0.1:9222. In virtual (VNC) mode, Chromium is
|
|
3454
|
+
// started by vnc.sh (ExecStartPre) before the web server — a failed probe here
|
|
3455
|
+
// indicates a genuine VNC boot failure and must fail the install loudly.
|
|
3456
|
+
// In native mode, Chromium is launched on-demand by the server and is not
|
|
3457
|
+
// expected to be bound at install time — the probe is skipped.
|
|
3458
|
+
if (DISPLAY_MODE === "native") {
|
|
3459
|
+
console.log(" [cdp-check] skipped reason=native-display (on-demand Chromium)");
|
|
3460
|
+
}
|
|
3461
|
+
else {
|
|
3462
|
+
console.log(` Verifying browser automation (CDP on port ${CDP_PORT_BRAND})...`);
|
|
3463
|
+
const cdpCheck = spawnSync("curl", ["-sf", `http://127.0.0.1:${CDP_PORT_BRAND}/json/version`, "-o", "/dev/null"], {
|
|
3464
|
+
timeout: 5000,
|
|
3465
|
+
stdio: "pipe",
|
|
3466
|
+
});
|
|
3467
|
+
if (cdpCheck.status === 0) {
|
|
3468
|
+
console.log(" Browser automation ready (CDP connected).");
|
|
3469
|
+
}
|
|
3470
|
+
else {
|
|
3471
|
+
const vncLogPath = resolve(process.env.HOME ?? "/root", BRAND.configDir, "logs/vnc-boot.log");
|
|
3472
|
+
let vncLog = "";
|
|
3473
|
+
try {
|
|
3474
|
+
vncLog = readFileSync(vncLogPath, "utf-8").slice(-2000);
|
|
3475
|
+
}
|
|
3476
|
+
catch {
|
|
3477
|
+
vncLog = `(no boot log found at ${vncLogPath})`;
|
|
3478
|
+
}
|
|
3479
|
+
console.error("");
|
|
3480
|
+
console.error(`Setup failed: Browser automation unavailable — CDP port ${CDP_PORT_BRAND} not responding`);
|
|
3481
|
+
console.error(` ERROR: Browser automation unavailable — CDP port ${CDP_PORT_BRAND} not responding.`);
|
|
3482
|
+
console.error(" Chromium should be started by vnc.sh (ExecStartPre). Check the boot log:");
|
|
3483
|
+
console.error("");
|
|
3484
|
+
console.error(vncLog);
|
|
3485
|
+
process.exit(1);
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
// ---------------------------------------------------------------------------
|
|
3490
|
+
// Samba provisioning. Runs after installService() so SMB never
|
|
3491
|
+
// blocks the admin server starting; pure decisions live in samba-provision.ts.
|
|
3492
|
+
//
|
|
3493
|
+
// Four steps, each emitting one [install-invariant] samba-provision-<step>
|
|
3494
|
+
// marker:
|
|
3495
|
+
// apt — install the `samba` package
|
|
3496
|
+
// conf — write the brand stanza + LAN-only globals to /etc/samba/smb.conf
|
|
3497
|
+
// user — smbpasswd the install-owner Linux user; deferred at install time
|
|
3498
|
+
// on fresh Pi installs (no plaintext PIN until the operator runs set-pin)
|
|
3499
|
+
// units — systemctl enable --now smbd nmbd
|
|
3500
|
+
//
|
|
3501
|
+
// The platform's set-pin route handler closes the deferral loop by running
|
|
3502
|
+
// `smbpasswd -a -s <install-owner>` inline whenever the PIN is set or
|
|
3503
|
+
// rotated. Install owner is the Unix user the installer is running as —
|
|
3504
|
+
// `admin` on a Pi/Hetzner box, `neo` (etc.) on a self-hosted laptop. It is
|
|
3505
|
+
// persisted to `~/.<brand>/.install-owner` so every later read uses the same
|
|
3506
|
+
// value the installer wrote.
|
|
3507
|
+
// ---------------------------------------------------------------------------
|
|
3508
|
+
function emitSambaMarker(step, state) {
|
|
3509
|
+
const line = formatSambaMarker(step, state);
|
|
3510
|
+
console.log(` ${line}`);
|
|
3511
|
+
logFile(` ${line}`);
|
|
3512
|
+
}
|
|
3513
|
+
function provisionSamba() {
|
|
3514
|
+
if (!isLinux()) {
|
|
3515
|
+
logFile(` samba-provision skipped: platform=${process.platform}`);
|
|
3516
|
+
return;
|
|
3517
|
+
}
|
|
3518
|
+
const brand = BRAND.hostname;
|
|
3519
|
+
const sharePath = INSTALL_DIR;
|
|
3520
|
+
// Install owner — the Unix user that owns this brand's install on the
|
|
3521
|
+
// device. On a Pi/Hetzner box this is `admin`; on a self-hosted laptop it
|
|
3522
|
+
// is the operator's Unix user (`neo` etc.). Persisted to a file next to
|
|
3523
|
+
// the other install identity (`.neo4j-password`, `.admin-pin`) so the
|
|
3524
|
+
// platform's set-pin route and the uninstall path read the same value the
|
|
3525
|
+
// installer wrote, rather than re-detecting at request time. Task 534.
|
|
3526
|
+
const installOwner = userInfo().username;
|
|
3527
|
+
const installOwnerPersistDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
3528
|
+
mkdirSync(installOwnerPersistDir, { recursive: true });
|
|
3529
|
+
writeFileSync(join(installOwnerPersistDir, ".install-owner"), `${installOwner}\n`, { mode: 0o644 });
|
|
3530
|
+
// Step 1 — apt install samba. Precheck `dpkg -s samba` first; if the
|
|
3531
|
+
// package is already installed, skip apt entirely so the install does not
|
|
3532
|
+
// contend with `unattended-upgrades` for `/var/lib/dpkg/lock-frontend`
|
|
3533
|
+
// every time. Mirrors the [1/11] system-deps step.
|
|
3534
|
+
try {
|
|
3535
|
+
const dpkgCheck = spawnSync("dpkg", ["-s", "samba"], { stdio: "pipe", timeout: 5_000 });
|
|
3536
|
+
if (dpkgCheck.status === 0) {
|
|
3537
|
+
logFile(" samba already installed — skipping apt install.");
|
|
3538
|
+
emitSambaMarker("apt", "ok-already-installed");
|
|
3539
|
+
}
|
|
3540
|
+
else {
|
|
3541
|
+
installAptGroup("samba", ["samba"]);
|
|
3542
|
+
emitSambaMarker("apt", "ok");
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
catch (err) {
|
|
3546
|
+
const stderr = err instanceof Error ? err.message : String(err);
|
|
3547
|
+
emitSambaMarker("apt", `fail: ${stderr}`);
|
|
3548
|
+
throw err;
|
|
3549
|
+
}
|
|
3550
|
+
// Step 2 — write the brand stanza into /etc/samba/smb.conf.
|
|
3551
|
+
const lanInterface = pickLanInterface(networkInterfaces());
|
|
3552
|
+
if (!lanInterface) {
|
|
3553
|
+
emitSambaMarker("conf", "fail: no non-loopback IPv4 interface");
|
|
3554
|
+
throw new Error("samba-provision: no LAN interface with IPv4 — cannot bind smbd safely");
|
|
3555
|
+
}
|
|
3556
|
+
const SMB_CONF = "/etc/samba/smb.conf";
|
|
3557
|
+
let existing = "";
|
|
3558
|
+
if (existsSync(SMB_CONF)) {
|
|
3559
|
+
try {
|
|
3560
|
+
const cat = spawnSync("sudo", ["cat", SMB_CONF], { encoding: "utf-8", stdio: "pipe", timeout: 5_000 });
|
|
3561
|
+
if (cat.status === 0)
|
|
3562
|
+
existing = cat.stdout ?? "";
|
|
3563
|
+
}
|
|
3564
|
+
catch { /* treat as empty */ }
|
|
3565
|
+
}
|
|
3566
|
+
const merged = mergeSmbConf({ existing, brand, sharePath, lanInterface, installOwner });
|
|
3567
|
+
try {
|
|
3568
|
+
const tee = spawnSync("sudo", ["tee", SMB_CONF], { input: merged, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 10_000 });
|
|
3569
|
+
if (tee.status !== 0) {
|
|
3570
|
+
throw new Error(`sudo tee ${SMB_CONF} exited ${tee.status}: ${(tee.stderr ?? "").trim()}`);
|
|
3571
|
+
}
|
|
3572
|
+
const testparm = spawnSync("sudo", ["testparm", "-s", "--suppress-prompt"], { stdio: "pipe", encoding: "utf-8", timeout: 10_000 });
|
|
3573
|
+
if (testparm.status !== 0) {
|
|
3574
|
+
throw new Error(`testparm rejected merged smb.conf: ${(testparm.stderr ?? "").trim()}`);
|
|
3575
|
+
}
|
|
3576
|
+
// Grant passwordless sudo to the install-owner Unix user for the two
|
|
3577
|
+
// smbpasswd invocations the platform's set-pin route makes (add-user-
|
|
3578
|
+
// with-stdin and remove-user). Without this, the user systemd-unit-
|
|
3579
|
+
// spawned admin server cannot sync the SMB password when the operator
|
|
3580
|
+
// sets/rotates the PIN. Scoped tight: only these two literal arg-vectors
|
|
3581
|
+
// are permitted; every other smbpasswd flavour still prompts. Principal
|
|
3582
|
+
// and `-a -s <user>` / `-x <user>` arguments are the install owner, not
|
|
3583
|
+
// a literal `admin`, so the grant works on a Pi (owner=admin) and on a
|
|
3584
|
+
// laptop (owner=neo) without divergence. Task 534.
|
|
3585
|
+
const SUDOERS = "/etc/sudoers.d/maxy-samba";
|
|
3586
|
+
const sudoersBody = "# maxy-code Samba provisioning (smbpasswd sync from platform set-pin route).\n" +
|
|
3587
|
+
`${installOwner} ALL=(root) NOPASSWD: /usr/bin/smbpasswd -a -s ${installOwner}, /usr/bin/smbpasswd -x ${installOwner}\n`;
|
|
3588
|
+
const sudoersTee = spawnSync("sudo", ["tee", SUDOERS], { input: sudoersBody, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 5_000 });
|
|
3589
|
+
if (sudoersTee.status !== 0) {
|
|
3590
|
+
throw new Error(`sudo tee ${SUDOERS} exited ${sudoersTee.status}: ${(sudoersTee.stderr ?? "").trim()}`);
|
|
3591
|
+
}
|
|
3592
|
+
spawnSync("sudo", ["chmod", "0440", SUDOERS], { stdio: "pipe", timeout: 5_000 });
|
|
3593
|
+
const visudoCheck = spawnSync("sudo", ["visudo", "-c", "-f", SUDOERS], { stdio: "pipe", encoding: "utf-8", timeout: 5_000 });
|
|
3594
|
+
if (visudoCheck.status !== 0) {
|
|
3595
|
+
throw new Error(`visudo rejected ${SUDOERS}: ${(visudoCheck.stderr ?? "").trim()}`);
|
|
3596
|
+
}
|
|
3597
|
+
emitSambaMarker("conf", `ok lan=${lanInterface} owner=${installOwner}`);
|
|
3598
|
+
}
|
|
3599
|
+
catch (err) {
|
|
3600
|
+
const stderr = err instanceof Error ? err.message : String(err);
|
|
3601
|
+
emitSambaMarker("conf", `fail: ${stderr}`);
|
|
3602
|
+
throw err;
|
|
3603
|
+
}
|
|
3604
|
+
// Step 3 — smbpasswd for the install-owner Unix user. At install time the
|
|
3605
|
+
// plaintext PIN is not in any file on disk (users.json stores the SHA-256
|
|
3606
|
+
// hash only); the platform's set-pin route is responsible for syncing on
|
|
3607
|
+
// PIN set/rotate. Mark as deferred so the four-marker contract still fires
|
|
3608
|
+
// unbroken. Owner is recorded in the marker state so the install log shows
|
|
3609
|
+
// which Unix user the smbpasswd entry will target. Task 534.
|
|
3610
|
+
emitSambaMarker("user", `deferred reason=no-plaintext-pin-at-install owner=${installOwner} (set-pin route syncs)`);
|
|
3611
|
+
// Step 4 — enable + start smbd and nmbd.
|
|
3612
|
+
try {
|
|
3613
|
+
shell("systemctl", ["enable", "--now", "smbd", "nmbd"], { sudo: true, timeout: 30_000 });
|
|
3614
|
+
emitSambaMarker("units", "ok");
|
|
3615
|
+
}
|
|
3616
|
+
catch (err) {
|
|
3617
|
+
const stderr = err instanceof Error ? err.message : String(err);
|
|
3618
|
+
emitSambaMarker("units", `fail: ${stderr}`);
|
|
3619
|
+
throw err;
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
// ---------------------------------------------------------------------------
|
|
3623
|
+
// Main
|
|
3624
|
+
// ---------------------------------------------------------------------------
|
|
3625
|
+
// Route to uninstall if --uninstall flag is present
|
|
3626
|
+
const _args = process.argv.slice(2);
|
|
3627
|
+
if (_args.includes("--uninstall")) {
|
|
3628
|
+
const { runUninstall } = await import("./uninstall.js");
|
|
3629
|
+
const exportIdx = _args.indexOf("--export-data");
|
|
3630
|
+
const exportPath = exportIdx !== -1 ? _args[exportIdx + 1] : undefined;
|
|
3631
|
+
const skipConfirm = _args.includes("--yes");
|
|
3632
|
+
if (exportIdx !== -1 && !exportPath) {
|
|
3633
|
+
console.error("Setup failed: --export-data requires a path argument");
|
|
3634
|
+
console.error("--export-data requires a path argument.");
|
|
3635
|
+
process.exit(1);
|
|
3636
|
+
}
|
|
3637
|
+
await runUninstall({ exportPath, skipConfirm });
|
|
3638
|
+
process.exit(0);
|
|
3639
|
+
}
|
|
3640
|
+
// ---------------------------------------------------------------------------
|
|
3641
|
+
// Port — install-time flag, not a brand attribute.
|
|
3642
|
+
//
|
|
3643
|
+
// Priority: --port flag > .env override > existing service file > default 19200.
|
|
3644
|
+
// systemd applies EnvironmentFile=-~/.{brand}/.env AFTER Environment=PORT,
|
|
3645
|
+
// so .env is the final runtime truth and must be checked first on upgrade.
|
|
3646
|
+
//
|
|
3647
|
+
// this block also performs one-shot port-drift recovery against
|
|
3648
|
+
// maxy-edge.service's Environment=EDGE_PORT=. See port-resolution.ts for the
|
|
3649
|
+
// pure logic; unit tests live at __tests__/port-canonicalisation.test.mjs.
|
|
3650
|
+
// ---------------------------------------------------------------------------
|
|
3651
|
+
let portFlag;
|
|
3652
|
+
const portIdx = _args.indexOf("--port");
|
|
3653
|
+
if (portIdx !== -1) {
|
|
3654
|
+
const raw = _args[portIdx + 1];
|
|
3655
|
+
const parsed = raw ? parseInt(raw, 10) : NaN;
|
|
3656
|
+
if (isNaN(parsed) || parsed < 1024 || parsed > 65535) {
|
|
3657
|
+
console.error(`Setup failed: --port requires a numeric value between 1024 and 65535 (got: ${raw ?? "nothing"})`);
|
|
3658
|
+
console.error(`Error: --port requires a numeric value between 1024 and 65535 (got: ${raw ?? "nothing"}).`);
|
|
3659
|
+
process.exit(1);
|
|
3660
|
+
}
|
|
3661
|
+
portFlag = parsed;
|
|
3662
|
+
}
|
|
3663
|
+
const _portResolution = resolveInstallPortFromFs({
|
|
3664
|
+
portFlag,
|
|
3665
|
+
persistDir: resolve(process.env.HOME ?? "/root", BRAND.configDir),
|
|
3666
|
+
serviceDir: resolve(process.env.HOME ?? "/root", ".config/systemd/user"),
|
|
3667
|
+
brandServiceFileName: BRAND.serviceName,
|
|
3668
|
+
brandEdgeServiceFileName: `${BRAND.hostname}-edge.service`,
|
|
3669
|
+
});
|
|
3670
|
+
let PORT = _portResolution.port;
|
|
3671
|
+
let PORT_SOURCE = _portResolution.source;
|
|
3672
|
+
for (const line of _portResolution.driftLogs)
|
|
3673
|
+
console.log(line);
|
|
3674
|
+
// ---------------------------------------------------------------------------
|
|
3675
|
+
// Hostname — install-time flag, not solely a brand attribute.
|
|
3676
|
+
//
|
|
3677
|
+
// Priority: --hostname flag > OS detection (same-brand upgrade) > OS preservation
|
|
3678
|
+
// (another brand's service detected) > BRAND.hostname (fresh install).
|
|
3679
|
+
// When --hostname is provided, it is set unconditionally — no detection, no preservation.
|
|
3680
|
+
// ---------------------------------------------------------------------------
|
|
3681
|
+
let HOSTNAME_FLAG;
|
|
3682
|
+
const hostnameIdx = _args.indexOf("--hostname");
|
|
3683
|
+
if (hostnameIdx !== -1) {
|
|
3684
|
+
const raw = _args[hostnameIdx + 1];
|
|
3685
|
+
if (!raw || raw.startsWith("--")) {
|
|
3686
|
+
console.error("Setup failed: --hostname requires a value");
|
|
3687
|
+
console.error("Error: --hostname requires a value (e.g. --hostname muvin).");
|
|
3688
|
+
process.exit(1);
|
|
3689
|
+
}
|
|
3690
|
+
// RFC 1123: lowercase alphanumeric + hyphens, max 63 chars, no leading/trailing hyphen
|
|
3691
|
+
if (!/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/.test(raw)) {
|
|
3692
|
+
console.error(`Setup failed: --hostname value '${raw}' is invalid`);
|
|
3693
|
+
console.error(`Error: --hostname value '${raw}' is invalid. Must be lowercase letters, digits, and hyphens only (max 63 chars, no leading/trailing hyphen).`);
|
|
3694
|
+
process.exit(1);
|
|
3695
|
+
}
|
|
3696
|
+
HOSTNAME_FLAG = raw;
|
|
3697
|
+
}
|
|
3698
|
+
// ---------------------------------------------------------------------------
|
|
3699
|
+
// Founder-side tier-set flags (Task 240).
|
|
3700
|
+
//
|
|
3701
|
+
// `--tier <solo|family|pro>` stamps account.json.tier directly when the brand
|
|
3702
|
+
// is personal-mode (today's default for both maxy-code and realagent-code).
|
|
3703
|
+
// `--entitlement-base64 <b64>` decodes a signed payload and writes it to
|
|
3704
|
+
// ~/<BRAND.configDir>/entitlement.json when the brand is commercial-mode.
|
|
3705
|
+
// The verifier at session start consumes whichever path the brand is on.
|
|
3706
|
+
//
|
|
3707
|
+
// Mode-mismatch combinations reject loudly at parse time — operator gets a
|
|
3708
|
+
// redirect message pointing at the correct flag. The installer is the sole
|
|
3709
|
+
// doctrine-sanctioned device-mutation channel; the admin agent has no tier-
|
|
3710
|
+
// upgrade surface by design.
|
|
3711
|
+
// ---------------------------------------------------------------------------
|
|
3712
|
+
let TIER_FLAG;
|
|
3713
|
+
let ENTITLEMENT_PAYLOAD;
|
|
3714
|
+
const tierIdx = _args.indexOf("--tier");
|
|
3715
|
+
if (tierIdx !== -1) {
|
|
3716
|
+
try {
|
|
3717
|
+
TIER_FLAG = validateTierFlag(_args[tierIdx + 1], BRAND.commercialMode === true);
|
|
3718
|
+
}
|
|
3719
|
+
catch (err) {
|
|
3720
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3721
|
+
console.error(`Setup failed: ${msg}`);
|
|
3722
|
+
console.error(`Error: ${msg}`);
|
|
3723
|
+
process.exit(1);
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
const entitlementIdx = _args.indexOf("--entitlement-base64");
|
|
3727
|
+
if (entitlementIdx !== -1) {
|
|
3728
|
+
try {
|
|
3729
|
+
ENTITLEMENT_PAYLOAD = validateEntitlementBase64(_args[entitlementIdx + 1], BRAND.commercialMode === true);
|
|
3730
|
+
}
|
|
3731
|
+
catch (err) {
|
|
3732
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3733
|
+
console.error(`Setup failed: ${msg}`);
|
|
3734
|
+
console.error(`Error: ${msg}`);
|
|
3735
|
+
process.exit(1);
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
let DISPLAY_MODE = "virtual";
|
|
3739
|
+
let DISPLAY_MODE_SOURCE = "default";
|
|
3740
|
+
const displayIdx = _args.indexOf("--display");
|
|
3741
|
+
if (displayIdx !== -1) {
|
|
3742
|
+
const raw = _args[displayIdx + 1];
|
|
3743
|
+
if (!raw || raw.startsWith("--")) {
|
|
3744
|
+
console.error("Setup failed: --display requires a value");
|
|
3745
|
+
console.error("Error: --display requires a value: native or virtual.");
|
|
3746
|
+
process.exit(1);
|
|
3747
|
+
}
|
|
3748
|
+
if (raw !== "native" && raw !== "virtual") {
|
|
3749
|
+
console.error(`Setup failed: --display value '${raw}' is invalid`);
|
|
3750
|
+
console.error(`Error: --display value '${raw}' is invalid. Must be 'native' or 'virtual'.`);
|
|
3751
|
+
process.exit(1);
|
|
3752
|
+
}
|
|
3753
|
+
DISPLAY_MODE = raw;
|
|
3754
|
+
DISPLAY_MODE_SOURCE = "--display flag";
|
|
3755
|
+
}
|
|
3756
|
+
else {
|
|
3757
|
+
// Upgrade detection: preserve existing DISPLAY_MODE from .env
|
|
3758
|
+
const persistDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
3759
|
+
const envPath = join(persistDir, ".env");
|
|
3760
|
+
let preservedFromEnv = false;
|
|
3761
|
+
try {
|
|
3762
|
+
if (existsSync(envPath)) {
|
|
3763
|
+
const envContent = readFileSync(envPath, "utf-8");
|
|
3764
|
+
const envMatch = envContent.match(/^DISPLAY_MODE=(native|virtual)$/m);
|
|
3765
|
+
if (envMatch) {
|
|
3766
|
+
DISPLAY_MODE = envMatch[1];
|
|
3767
|
+
DISPLAY_MODE_SOURCE = ".env (preserved)";
|
|
3768
|
+
preservedFromEnv = true;
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3772
|
+
catch { /* non-critical */ }
|
|
3773
|
+
// Darwin has no VNC/X11 stack — `virtual` mode is Pi-only (tigervnc +
|
|
3774
|
+
// websockify + novnc, none of which exist on macOS). A preserved
|
|
3775
|
+
// DISPLAY_MODE=virtual is never operator intent on darwin — it is the
|
|
3776
|
+
// legacy default from installers before 0.1.127, when `virtual` was the
|
|
3777
|
+
// only initial value written. The guard fires whenever the resolved
|
|
3778
|
+
// value is not already `native`, covering both fresh installs and
|
|
3779
|
+
// upgrades that inherited the legacy default.
|
|
3780
|
+
if (process.platform === "darwin" && DISPLAY_MODE !== "native") {
|
|
3781
|
+
DISPLAY_MODE_SOURCE = preservedFromEnv
|
|
3782
|
+
? "darwin-auto (override-legacy-virtual)"
|
|
3783
|
+
: "darwin-auto (no VNC stack)";
|
|
3784
|
+
DISPLAY_MODE = "native";
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
// ---------------------------------------------------------------------------
|
|
3788
|
+
// Embedding model — install-time flag, not a brand attribute.
|
|
3789
|
+
//
|
|
3790
|
+
// Controls which Ollama embedding model is pulled and the dimension count used
|
|
3791
|
+
// for Neo4j vector indexes. Dimensions are fixed at schema-creation time; changing
|
|
3792
|
+
// them later requires dropping and recreating all vector indexes.
|
|
3793
|
+
//
|
|
3794
|
+
// Priority: --embed-model flag > .env (upgrade) > default nomic-embed-text.
|
|
3795
|
+
// --embed-dimensions is required for models not in the curated lookup table.
|
|
3796
|
+
// --embed-dimensions without --embed-model is rejected (dimensions are model-specific).
|
|
3797
|
+
// ---------------------------------------------------------------------------
|
|
3798
|
+
const EMBED_MODEL_DIMS = {
|
|
3799
|
+
"nomic-embed-text": 768,
|
|
3800
|
+
"nomic-embed-text-v1.5": 768,
|
|
3801
|
+
"mxbai-embed-large": 1024,
|
|
3802
|
+
"snowflake-arctic-embed:335m": 768,
|
|
3803
|
+
};
|
|
3804
|
+
const DEFAULT_EMBED_MODEL = "nomic-embed-text";
|
|
3805
|
+
const DEFAULT_EMBED_DIMS = 768;
|
|
3806
|
+
let EMBED_MODEL = DEFAULT_EMBED_MODEL;
|
|
3807
|
+
let EMBED_DIMS = DEFAULT_EMBED_DIMS;
|
|
3808
|
+
let EMBED_SOURCE = "default";
|
|
3809
|
+
const embedModelIdx = _args.indexOf("--embed-model");
|
|
3810
|
+
const embedDimsIdx = _args.indexOf("--embed-dimensions");
|
|
3811
|
+
if (embedDimsIdx !== -1 && embedModelIdx === -1) {
|
|
3812
|
+
console.error("Setup failed: --embed-dimensions requires --embed-model");
|
|
3813
|
+
console.error("Error: --embed-dimensions requires --embed-model (dimensions are model-specific).");
|
|
3814
|
+
process.exit(1);
|
|
3815
|
+
}
|
|
3816
|
+
if (embedModelIdx !== -1) {
|
|
3817
|
+
const raw = _args[embedModelIdx + 1];
|
|
3818
|
+
if (!raw || raw.startsWith("--")) {
|
|
3819
|
+
console.error("Setup failed: --embed-model requires a value");
|
|
3820
|
+
console.error("Error: --embed-model requires a value (e.g. --embed-model mxbai-embed-large).");
|
|
3821
|
+
process.exit(1);
|
|
3822
|
+
}
|
|
3823
|
+
EMBED_MODEL = raw;
|
|
3824
|
+
EMBED_SOURCE = "--embed-model flag";
|
|
3825
|
+
if (embedDimsIdx !== -1) {
|
|
3826
|
+
// Explicit dimensions override the lookup table
|
|
3827
|
+
const rawDims = _args[embedDimsIdx + 1];
|
|
3828
|
+
const parsed = rawDims ? parseInt(rawDims, 10) : NaN;
|
|
3829
|
+
if (isNaN(parsed) || parsed <= 0) {
|
|
3830
|
+
console.error(`Setup failed: --embed-dimensions requires a positive integer (got: ${rawDims ?? "nothing"})`);
|
|
3831
|
+
console.error(`Error: --embed-dimensions requires a positive integer (got: ${rawDims ?? "nothing"}).`);
|
|
3832
|
+
process.exit(1);
|
|
3833
|
+
}
|
|
3834
|
+
EMBED_DIMS = parsed;
|
|
3835
|
+
}
|
|
3836
|
+
else if (EMBED_MODEL in EMBED_MODEL_DIMS) {
|
|
3837
|
+
// Known model — resolve dimensions from lookup table
|
|
3838
|
+
EMBED_DIMS = EMBED_MODEL_DIMS[EMBED_MODEL];
|
|
3839
|
+
}
|
|
3840
|
+
else {
|
|
3841
|
+
// Unknown model without explicit dimensions — cannot proceed
|
|
3842
|
+
console.error(`Setup failed: unknown embedding model '${EMBED_MODEL}'`);
|
|
3843
|
+
console.error(`Error: Unknown embedding model '${EMBED_MODEL}'.`);
|
|
3844
|
+
console.error("Known models and their dimensions:");
|
|
3845
|
+
for (const [model, dims] of Object.entries(EMBED_MODEL_DIMS)) {
|
|
3846
|
+
console.error(` ${model} — ${dims} dimensions`);
|
|
3847
|
+
}
|
|
3848
|
+
console.error("\nFor a custom model, pass --embed-dimensions N explicitly:");
|
|
3849
|
+
console.error(` npx -y @rubytech/create-maxy --embed-model ${EMBED_MODEL} --embed-dimensions 512`);
|
|
3850
|
+
process.exit(1);
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
else {
|
|
3854
|
+
// No --embed-model flag: check .env for upgrade preservation
|
|
3855
|
+
const persistDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
3856
|
+
const envPath = join(persistDir, ".env");
|
|
3857
|
+
try {
|
|
3858
|
+
if (existsSync(envPath)) {
|
|
3859
|
+
const envContent = readFileSync(envPath, "utf-8");
|
|
3860
|
+
const modelMatch = envContent.match(/^EMBED_MODEL=(.+)$/m);
|
|
3861
|
+
const dimsMatch = envContent.match(/^EMBED_DIMENSIONS=(\d+)$/m);
|
|
3862
|
+
if (modelMatch) {
|
|
3863
|
+
EMBED_MODEL = modelMatch[1];
|
|
3864
|
+
EMBED_SOURCE = ".env (preserved)";
|
|
3865
|
+
if (dimsMatch) {
|
|
3866
|
+
EMBED_DIMS = parseInt(dimsMatch[1], 10);
|
|
3867
|
+
}
|
|
3868
|
+
else if (EMBED_MODEL in EMBED_MODEL_DIMS) {
|
|
3869
|
+
EMBED_DIMS = EMBED_MODEL_DIMS[EMBED_MODEL];
|
|
3870
|
+
}
|
|
3871
|
+
// Unknown model without dims in .env: keep DEFAULT_EMBED_DIMS — the schema
|
|
3872
|
+
// was created with whatever dims were configured at original install time.
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3875
|
+
}
|
|
3876
|
+
catch { /* non-critical */ }
|
|
3877
|
+
}
|
|
3878
|
+
// ---------------------------------------------------------------------------
|
|
3879
|
+
// Neo4j port — multi-brand data isolation.
|
|
3880
|
+
//
|
|
3881
|
+
// Default Maxy brand: 7687 (shared system Neo4j instance). Branded builds ship
|
|
3882
|
+
// a dedicated port in brand.json (e.g. Real Agent = 7688) so installing a
|
|
3883
|
+
// second brand on the same device gets its own database by default.
|
|
3884
|
+
//
|
|
3885
|
+
// Priority: --neo4j-port flag > .env NEO4J_URI (upgrade preserve) > BRAND.neo4jPort > 7687.
|
|
3886
|
+
// ---------------------------------------------------------------------------
|
|
3887
|
+
const DEFAULT_NEO4J_PORT = 7687;
|
|
3888
|
+
let NEO4J_PORT = BRAND.neo4jPort ?? DEFAULT_NEO4J_PORT;
|
|
3889
|
+
let NEO4J_PORT_SOURCE = BRAND.neo4jPort ? "brand.json" : "default";
|
|
3890
|
+
const neo4jPortIdx = _args.indexOf("--neo4j-port");
|
|
3891
|
+
if (neo4jPortIdx !== -1) {
|
|
3892
|
+
const raw = _args[neo4jPortIdx + 1];
|
|
3893
|
+
const parsed = raw ? parseInt(raw, 10) : NaN;
|
|
3894
|
+
if (isNaN(parsed) || parsed < 1024 || parsed > 65535) {
|
|
3895
|
+
console.error(`Setup failed: --neo4j-port requires a numeric value between 1024 and 65535 (got: ${raw ?? "nothing"})`);
|
|
3896
|
+
console.error(`Error: --neo4j-port requires a numeric value between 1024 and 65535 (got: ${raw ?? "nothing"}).`);
|
|
3897
|
+
process.exit(1);
|
|
3898
|
+
}
|
|
3899
|
+
NEO4J_PORT = parsed;
|
|
3900
|
+
NEO4J_PORT_SOURCE = "--neo4j-port flag";
|
|
3901
|
+
}
|
|
3902
|
+
else {
|
|
3903
|
+
// Upgrade detection: check .env for NEO4J_URI=bolt://localhost:{port}.
|
|
3904
|
+
// Preserves an existing install's port even if brand.json now says different.
|
|
3905
|
+
const persistDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
3906
|
+
const envPath = join(persistDir, ".env");
|
|
3907
|
+
try {
|
|
3908
|
+
if (existsSync(envPath)) {
|
|
3909
|
+
const envContent = readFileSync(envPath, "utf-8");
|
|
3910
|
+
const uriMatch = envContent.match(/^NEO4J_URI=bolt:\/\/localhost:(\d+)$/m);
|
|
3911
|
+
if (uriMatch) {
|
|
3912
|
+
const envPort = parseInt(uriMatch[1], 10);
|
|
3913
|
+
if (envPort >= 1024 && envPort <= 65535) {
|
|
3914
|
+
NEO4J_PORT = envPort;
|
|
3915
|
+
NEO4J_PORT_SOURCE = ".env (preserved)";
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
catch { /* non-critical */ }
|
|
3921
|
+
}
|
|
3922
|
+
// Dedicated = port differs from the default shared instance
|
|
3923
|
+
const NEO4J_DEDICATED = NEO4J_PORT !== DEFAULT_NEO4J_PORT;
|
|
3924
|
+
// ---------------------------------------------------------------------------
|
|
3925
|
+
// removed the per-brand ttyd port — the admin terminal stack
|
|
3926
|
+
// (ttyd, tmux, xterm.js) was retired in favour of the action runner that
|
|
3927
|
+
// spawns transient `systemd-run --user` units per upgrade or setup-tunnel
|
|
3928
|
+
// invocation. No TCP listener on the device needs to be reserved for an
|
|
3929
|
+
// interactive-shell surface any more.
|
|
3930
|
+
const PKG_VERSION = JSON.parse(readFileSync(resolve(import.meta.dirname, "../package.json"), "utf-8")).version;
|
|
3931
|
+
// ---------------------------------------------------------------------------
|
|
3932
|
+
// pre-flight platform refusal.
|
|
3933
|
+
//
|
|
3934
|
+
// Runs BEFORE initLogging() (LOG_DIR creation), port resolution, and any
|
|
3935
|
+
// brew/scutil/hostnamectl probe. Only Linux + darwin are supported;
|
|
3936
|
+
// on darwin, macOS major must be ≥ 14 (the floor required by Tasks 838/839).
|
|
3937
|
+
// Older macOS partially succeeds, then breaks at the supervisor or brew-cellar
|
|
3938
|
+
// layer with cryptic errors — refusing loudly here is the contract.
|
|
3939
|
+
//
|
|
3940
|
+
// Platform-header line is emitted on every install start so operators can grep
|
|
3941
|
+
// `[create-maxy] platform=` to confirm pre-flight ran. On darwin the line also
|
|
3942
|
+
// carries `macos=<v>` (token) so the macOS-14 floor check is visible.
|
|
3943
|
+
// ---------------------------------------------------------------------------
|
|
3944
|
+
const PLATFORM = requireSupportedPlatform(process.platform);
|
|
3945
|
+
let MACOS_VERSION = null;
|
|
3946
|
+
if (PLATFORM === "darwin") {
|
|
3947
|
+
const swVers = spawnSync("sw_vers", [], { encoding: "utf-8", stdio: "pipe", timeout: 5_000 });
|
|
3948
|
+
const parsed = parseSwVers(swVers.stdout ?? "");
|
|
3949
|
+
if (!parsed) {
|
|
3950
|
+
const head = (swVers.stdout ?? "").split("\n").slice(0, 2).join(" | ");
|
|
3951
|
+
console.error(`[create-maxy] sw_vers stdout malformed: ${head}`);
|
|
3952
|
+
console.error(`[create-maxy] platform=darwin macos=<unknown> — refusing: macOS 14+ required`);
|
|
3953
|
+
process.exit(1);
|
|
3954
|
+
}
|
|
3955
|
+
MACOS_VERSION = parsed.version;
|
|
3956
|
+
if (!isSupportedMacosVersion(MACOS_VERSION)) {
|
|
3957
|
+
console.error(`[create-maxy] platform=darwin macos=${MACOS_VERSION} — refusing: macOS 14+ required`);
|
|
3958
|
+
process.exit(1);
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
// Platform-header — first log line. Operators grep `[create-maxy] platform=`
|
|
3962
|
+
// to confirm pre-flight ran. The /* macos=<v> */ token is the marker;
|
|
3963
|
+
// keep it on this same line so 3-way merges with parallel installer edits stay
|
|
3964
|
+
// mechanical (no rewrap, no split into two log lines).
|
|
3965
|
+
const PLATFORM_HEADER = `[create-maxy] platform=${PLATFORM} arch=${process.arch}` +
|
|
3966
|
+
(MACOS_VERSION ? ` macos=${MACOS_VERSION}` : ``) +
|
|
3967
|
+
` version=${PKG_VERSION}`;
|
|
3968
|
+
// emit platform header and the resolved log/persist paths to
|
|
3969
|
+
// stdout BEFORE the log file is opened. If initLogging's mkdir or first
|
|
3970
|
+
// appendFileSync throws, the operator still sees brand/version/arch and the
|
|
3971
|
+
// exact path the log file SHOULD live at, instead of a silent exit between
|
|
3972
|
+
// the LOG_DIR mkdir and the first log write.
|
|
3973
|
+
console.log(PLATFORM_HEADER);
|
|
3974
|
+
console.log(`[create-maxy] log=${LOG_FILE} persist=${PERSIST_DIR}`);
|
|
3975
|
+
initLogging();
|
|
3976
|
+
console.log("================================================================");
|
|
3977
|
+
console.log(` ${BRAND.productName} — ${BRAND.tagline}. (${BRAND.hostname} v${PKG_VERSION})`);
|
|
3978
|
+
console.log("================================================================");
|
|
3979
|
+
console.log(` Install log: ${LOG_FILE}`);
|
|
3980
|
+
console.log(` Port: ${PORT} (${PORT_SOURCE})`);
|
|
3981
|
+
if (HOSTNAME_FLAG)
|
|
3982
|
+
console.log(` Hostname: ${HOSTNAME_FLAG} (from --hostname flag)`);
|
|
3983
|
+
console.log(` Display: ${DISPLAY_MODE} (${DISPLAY_MODE_SOURCE})`);
|
|
3984
|
+
console.log(` Embed model: ${EMBED_MODEL} (${EMBED_DIMS} dims, ${EMBED_SOURCE})`);
|
|
3985
|
+
console.log(` Neo4j: ${NEO4J_DEDICATED ? "dedicated" : "shared"} on bolt://localhost:${NEO4J_PORT} (${NEO4J_PORT_SOURCE})`);
|
|
3986
|
+
console.log("");
|
|
3987
|
+
logDiagnostics("pre-flight");
|
|
3988
|
+
logFile(` Neo4j instance: ${NEO4J_DEDICATED ? "dedicated" : "shared"} on bolt://localhost:${NEO4J_PORT}`);
|
|
3989
|
+
try {
|
|
3990
|
+
installSystemDeps();
|
|
3991
|
+
installNodejs();
|
|
3992
|
+
installClaudeCode();
|
|
3993
|
+
installNeo4j();
|
|
3994
|
+
setupDedicatedNeo4j();
|
|
3995
|
+
installOllama(EMBED_MODEL);
|
|
3996
|
+
installUv();
|
|
3997
|
+
installCloudflared();
|
|
3998
|
+
installWhisperCpp();
|
|
3999
|
+
deployPayload(); // Must happen before ensureNeo4jPassword — restores config backup
|
|
4000
|
+
// write the resolved Chromium absolute path into the deployed
|
|
4001
|
+
// platform/config/ so vnc.sh and writeChromiumWrapper read the same
|
|
4002
|
+
// value. Must run after deployPayload (config dir is
|
|
4003
|
+
// a payload subdirectory). Linux-only; no-op on darwin/non-linux.
|
|
4004
|
+
writeChromiumBinaryPathFile();
|
|
4005
|
+
// scrub plaintext neo4j passwords from any pre-fix install-*.log.
|
|
4006
|
+
// Idempotent — re-running on already-redacted logs is a no-op. Runs after
|
|
4007
|
+
// payload deploy so the bundled redact-install-logs.sh is on disk.
|
|
4008
|
+
redactInstallLogs();
|
|
4009
|
+
ensureNeo4jPassword(); // Now config/.neo4j-password is available if it existed before
|
|
4010
|
+
provisionRemoteSessionSecret(); // shared HMAC key readable by maxy-edge + maxy-ui
|
|
4011
|
+
buildPlatform();
|
|
4012
|
+
registerLocalAndExternalPlugins(); // install-time plugin registration
|
|
4013
|
+
setupVncViewer();
|
|
4014
|
+
setupAccount();
|
|
4015
|
+
if (ENTITLEMENT_PAYLOAD) {
|
|
4016
|
+
writeEntitlementFromFlag(ENTITLEMENT_PAYLOAD);
|
|
4017
|
+
}
|
|
4018
|
+
installService();
|
|
4019
|
+
// Samba on the LAN. Runs after the brand systemd unit is up so
|
|
4020
|
+
// SMB provisioning never blocks the admin server starting; loud-fail per
|
|
4021
|
+
// step. The smbpasswd `user` step is deferred at install time and synced
|
|
4022
|
+
// by the platform's /set-pin route when the operator sets a PIN.
|
|
4023
|
+
provisionSamba();
|
|
4024
|
+
console.log("");
|
|
4025
|
+
console.log("================================================================");
|
|
4026
|
+
console.log("");
|
|
4027
|
+
if (isLinux()) {
|
|
4028
|
+
console.log(` Same network (Pi / LAN): http://${DEVICE_HOSTNAME}.local:${PORT}`);
|
|
4029
|
+
console.log("");
|
|
4030
|
+
console.log(` Remote access (Hetzner / cloud) — on your local machine run:`);
|
|
4031
|
+
console.log(` ssh -L ${PORT}:localhost:${PORT} -L ${BRAND.websockifyPort}:localhost:${BRAND.websockifyPort} admin@<server-ipv4>`);
|
|
4032
|
+
console.log(` Then open:`);
|
|
4033
|
+
console.log(` Dashboard: http://localhost:${PORT}`);
|
|
4034
|
+
console.log(` VNC browser (Claude OAuth + Cloudflare): http://localhost:${BRAND.websockifyPort}/vnc.html`);
|
|
4035
|
+
}
|
|
4036
|
+
else {
|
|
4037
|
+
console.log(` Open in your browser: http://${DEVICE_HOSTNAME}.local:${PORT}`);
|
|
4038
|
+
}
|
|
4039
|
+
console.log("");
|
|
4040
|
+
console.log("================================================================");
|
|
4041
|
+
}
|
|
4042
|
+
catch (err) {
|
|
4043
|
+
console.error("");
|
|
4044
|
+
console.error(`Setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4045
|
+
console.error(` Full log: ${LOG_FILE}`);
|
|
4046
|
+
logDiagnostics("post-failure");
|
|
4047
|
+
process.exit(1);
|
|
4048
|
+
}
|