@rubytech/create-maxy-code 0.1.469 → 0.1.471
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +40 -0
- package/package.json +1 -1
- package/payload/platform/config/brand.json +2 -1
- package/payload/platform/docs/superpowers/plans/2026-07-18-graph-native-ledger.md +807 -0
- package/payload/platform/docs/superpowers/specs/2026-07-18-graph-native-ledger-design.md +241 -0
- package/payload/platform/lib/ledger-core/dist/ageing.d.ts +14 -0
- package/payload/platform/lib/ledger-core/dist/ageing.d.ts.map +1 -0
- package/payload/platform/lib/ledger-core/dist/ageing.js +38 -0
- package/payload/platform/lib/ledger-core/dist/ageing.js.map +1 -0
- package/payload/platform/lib/ledger-core/dist/balance.d.ts +25 -0
- package/payload/platform/lib/ledger-core/dist/balance.d.ts.map +1 -0
- package/payload/platform/lib/ledger-core/dist/balance.js +38 -0
- package/payload/platform/lib/ledger-core/dist/balance.js.map +1 -0
- package/payload/platform/lib/ledger-core/dist/index.d.ts +5 -0
- package/payload/platform/lib/ledger-core/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/ledger-core/dist/index.js +5 -0
- package/payload/platform/lib/ledger-core/dist/index.js.map +1 -0
- package/payload/platform/lib/ledger-core/dist/money.d.ts +12 -0
- package/payload/platform/lib/ledger-core/dist/money.d.ts.map +1 -0
- package/payload/platform/lib/ledger-core/dist/money.js +16 -0
- package/payload/platform/lib/ledger-core/dist/money.js.map +1 -0
- package/payload/platform/lib/ledger-core/dist/reconcile.d.ts +51 -0
- package/payload/platform/lib/ledger-core/dist/reconcile.d.ts.map +1 -0
- package/payload/platform/lib/ledger-core/dist/reconcile.js +77 -0
- package/payload/platform/lib/ledger-core/dist/reconcile.js.map +1 -0
- package/payload/platform/lib/ledger-core/package.json +16 -0
- package/payload/platform/lib/ledger-core/src/__tests__/ageing.test.ts +42 -0
- package/payload/platform/lib/ledger-core/src/__tests__/balance.test.ts +48 -0
- package/payload/platform/lib/ledger-core/src/__tests__/reconcile.test.ts +87 -0
- package/payload/platform/lib/ledger-core/src/ageing.ts +51 -0
- package/payload/platform/lib/ledger-core/src/balance.ts +52 -0
- package/payload/platform/lib/ledger-core/src/index.ts +4 -0
- package/payload/platform/lib/ledger-core/src/money.ts +17 -0
- package/payload/platform/lib/ledger-core/src/reconcile.ts +109 -0
- package/payload/platform/lib/ledger-core/tsconfig.json +6 -0
- package/payload/platform/neo4j/schema.cypher +16 -0
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/.claude-plugin/marketplace.json +10 -0
- package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +2 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +171 -2
- package/payload/platform/plugins/cloudflare/bin/__tests__/portal-brand-css.test.sh +139 -0
- package/payload/platform/plugins/cloudflare/bin/portal-brand-css.mjs +128 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-enrol.test.ts +1 -1
- package/payload/platform/plugins/cloudflare/mcp/__tests__/skill-contract.test.ts +25 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/template-config.test.ts +55 -3
- package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +38 -3
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/{public/portal.css → portal.css} +13 -2
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/wrangler.toml +1 -1
- package/payload/platform/plugins/docs/references/admin-ui.md +48 -0
- package/payload/platform/plugins/docs/references/google-guide.md +68 -0
- package/payload/platform/plugins/docs/references/internals.md +1 -1
- package/payload/platform/plugins/docs/references/ledger.md +43 -0
- package/payload/platform/plugins/email/PLUGIN.md +4 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/display-truncation.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/display-truncation.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/display-truncation.test.js +196 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/display-truncation.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/source-fetch-efficiency.test.js +10 -8
- package/payload/platform/plugins/email/mcp/dist/__tests__/source-fetch-efficiency.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/truncation-tool-output.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/truncation-tool-output.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/truncation-tool-output.test.js +110 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/truncation-tool-output.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +22 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js +90 -36
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-read.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-read.js +6 -2
- package/payload/platform/plugins/email/mcp/dist/tools/email-read.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-search.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-search.js +6 -2
- package/payload/platform/plugins/email/mcp/dist/tools/email-search.js.map +1 -1
- package/payload/platform/plugins/email/skills/email-ingest/SKILL.md +4 -0
- package/payload/platform/plugins/google/.claude-plugin/plugin.json +21 -0
- package/payload/platform/plugins/google/PLUGIN.md +104 -0
- package/payload/platform/plugins/google/lib/mcp-spawn-tee/index.js +193 -0
- package/payload/platform/plugins/google/lib/mcp-spawn-tee/package.json +3 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.js +60 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/calendar-tools.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/calendar-tools.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/calendar-tools.test.js +48 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/calendar-tools.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.js +25 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.js +40 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/pending-store.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/pending-store.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/pending-store.test.js +35 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/pending-store.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/token-store.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/token-store.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/token-store.test.js +85 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/token-store.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/pending-store.d.ts +25 -0
- package/payload/platform/plugins/google/mcp/dist/auth/pending-store.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/pending-store.js +87 -0
- package/payload/platform/plugins/google/mcp/dist/auth/pending-store.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/token-store.d.ts +46 -0
- package/payload/platform/plugins/google/mcp/dist/auth/token-store.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/token-store.js +138 -0
- package/payload/platform/plugins/google/mcp/dist/auth/token-store.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/index.d.ts +25 -0
- package/payload/platform/plugins/google/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/index.js +300 -0
- package/payload/platform/plugins/google/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/lib/google-client.d.ts +28 -0
- package/payload/platform/plugins/google/mcp/dist/lib/google-client.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/lib/google-client.js +102 -0
- package/payload/platform/plugins/google/mcp/dist/lib/google-client.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/lib/log.d.ts +3 -0
- package/payload/platform/plugins/google/mcp/dist/lib/log.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/lib/log.js +16 -0
- package/payload/platform/plugins/google/mcp/dist/lib/log.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-info.d.ts +12 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-info.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-info.js +23 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-info.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-list.d.ts +12 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-list.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-list.js +9 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-list.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.d.ts +42 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.js +102 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-cancel.d.ts +10 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-cancel.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-cancel.js +9 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-cancel.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-create.d.ts +20 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-create.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-create.js +25 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-create.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-event.d.ts +6 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-event.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-event.js +7 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-event.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-freebusy.d.ts +14 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-freebusy.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-freebusy.js +16 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-freebusy.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-list.d.ts +44 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-list.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-list.js +36 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-list.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-respond.d.ts +16 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-respond.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-respond.js +29 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-respond.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-update.d.ts +16 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-update.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-update.js +32 -0
- package/payload/platform/plugins/google/mcp/dist/tools/calendar-update.js.map +1 -0
- package/payload/platform/plugins/google/mcp/package.json +20 -0
- package/payload/platform/plugins/google/references/auth.md +35 -0
- package/payload/platform/plugins/ledger/.claude-plugin/plugin.json +21 -0
- package/payload/platform/plugins/ledger/PLUGIN.md +82 -0
- package/payload/platform/plugins/ledger/lib/mcp-spawn-tee/index.js +193 -0
- package/payload/platform/plugins/ledger/lib/mcp-spawn-tee/package.json +3 -0
- package/payload/platform/plugins/ledger/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/ledger/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/index.js +168 -0
- package/payload/platform/plugins/ledger/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/ids.d.ts +4 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/ids.d.ts.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/ids.js +0 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/ids.js.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/neo4j.js +40 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/read.d.ts +29 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/read.d.ts.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/read.js +138 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/read.js.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/types.d.ts +47 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/types.d.ts.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/types.js +14 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/types.js.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/balance.d.ts +18 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/balance.d.ts.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/balance.js +23 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/balance.js.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.d.ts +26 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.d.ts.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.js +33 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.js.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.d.ts +27 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.d.ts.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.js +40 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.js.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.d.ts +46 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.d.ts.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.js +68 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.js.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.d.ts +20 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.d.ts.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.js +30 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.js.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/statement.d.ts +26 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/statement.d.ts.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/statement.js +35 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/statement.js.map +1 -0
- package/payload/platform/plugins/ledger/mcp/package.json +21 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/ledger-vertical-coverage.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/ledger-vertical-coverage.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/ledger-vertical-coverage.test.js +81 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/ledger-vertical-coverage.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts +9 -7
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js +10 -8
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js.map +1 -1
- package/payload/platform/plugins/memory/references/schema-base.md +22 -0
- package/payload/platform/plugins/memory/references/schema-construction.md +0 -18
- package/payload/platform/plugins/scheduling/PLUGIN.md +20 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.js +11 -2
- package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js +3 -3
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts +138 -0
- package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/activity-rows.js +268 -0
- package/payload/platform/services/claude-session-manager/dist/activity-rows.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +20 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +6 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +42 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +10 -1
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts +80 -6
- package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-census.js +96 -20
- package/payload/platform/services/claude-session-manager/dist/pty-census.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js +5 -1
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js.map +1 -1
- package/payload/platform/templates/specialists/agents/database-operator.md +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/platform/templates/specialists/agents/project-manager.md +1 -1
- package/payload/server/{chunk-POBXIHOF.js → chunk-BSTIQX52.js} +59 -1
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/activity.html +17 -0
- package/payload/server/public/assets/AdminLoginScreens-Rf94VD9w.js +1 -0
- package/payload/server/public/assets/AdminShell-CyGKg7MG.js +2 -0
- package/payload/server/public/assets/Checkbox-B0jmI2y2.js +1 -0
- package/payload/server/public/assets/HeaderMenu-BnTYGNaB.js +9 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-pu0jjLCz.css → HeaderMenu-DtIEBHOU.css} +1 -1
- package/payload/server/public/assets/activity-BOBTWFr-.js +1 -0
- package/payload/server/public/assets/admin-HDLzwbLl.js +1 -0
- package/payload/server/public/assets/{arc-qKfXIt8v.js → arc-CFMJyMm-.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-Cypt5qHL.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DkVrGn7G.js → architectureDiagram-Q4EWVU46-CmOpXLsH.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DqavswGH.js → blockDiagram-DXYQGD6D-CmS8f9YF.js} +1 -1
- package/payload/server/public/assets/browser-D4_XLSSm.js +1 -0
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-B7fyKiXe.js → c4Diagram-AHTNJAMY-BXIjMl6A.js} +1 -1
- package/payload/server/public/assets/calendar-s5V_gbI7.js +1 -0
- package/payload/server/public/assets/channel-DhrFAm7m.js +1 -0
- package/payload/server/public/assets/chat-40WyKzGb.js +1 -0
- package/payload/server/public/assets/chevron-left-C35qGP5f.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-DsgMtGkv.js → chunk-2KRD3SAO-D4fktAwW.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-CkdISfNt.js → chunk-336JU56O-C2MKnRTj.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-BhU7-TyS.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-CYpiF2bl.js → chunk-4BX2VUAB-C13Wz3zS.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-CWyc_F7v.js → chunk-4TB4RGXK-CalYahTR.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-BbzKMw2Z.js → chunk-55IACEB6-TmOKdy8a.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-u19Nai9e.js → chunk-5FUZZQ4R-C-i0BVag.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-Ba8by6Uy.js → chunk-5PVQY5BW-D6bJMLnf.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-BwUuxtyb.js → chunk-67CJDMHE-Cif9Uu5C.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-BRH9iM9L.js → chunk-7N4EOEYR-WjTHCXnd.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-BGoztgLL.js → chunk-AA7GKIK3-CwCfdQmb.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-C5GDAZTl.js → chunk-BSJP7CBP-9A67yXwT.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-ChY32VPS.js → chunk-CIAEETIT-DPMiWSBi.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-CPSD3p6p.js → chunk-EDXVE4YY-B9PBdwiT.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-D1oL-iUr.js → chunk-ENJZ2VHE-CM5OqcQ5.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-CPvYEr54.js → chunk-FMBD7UC4-Bmt_ZRlV.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-9Yyj7Vbz.js → chunk-FOC6F5B3-DfGJGxpX.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-BaYgh8FR.js → chunk-ICPOFSXX-D6M89DA0.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-5ucpeRvm.js → chunk-K5T4RW27-DMpoaqw2.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-DW-FHk7T.js → chunk-KGLVRYIC-DYUFgri2.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-CGGVfAyG.js → chunk-LIHQZDEY-x3LmeKmx.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-Bv8EUa5d.js → chunk-ORNJ4GCN-BeuNAWkW.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-C1mXJnyw.js → chunk-OYMX7WX6-Bv6gisQ6.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-7_kS3RBy.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-PwSQz6EP.js → chunk-U2HBQHQK-C2iT9Av2.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP--NPHTBlU.js → chunk-X2U36JSP-Bd6bv87A.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-Bbc7Da32.js → chunk-XPW4576I-ByFnN4JO.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-D-9v8e--.js → chunk-YZCP3GAM-Bt8I6ZDb.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-CMPg670Q.js → chunk-ZZ45TVLE-DBMPgZ_A.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-BrWqm88p.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-D0wzG5Fd.js +1 -0
- package/payload/server/public/assets/clone-DQA-nNrl.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-NVSb1Bhy.js → cose-bilkent-S5V4N54A-DgLEaApT.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-V2Ai1DFl.js → dagre-KV5264BT-kWVPgmmL.js} +1 -1
- package/payload/server/public/assets/{dagre-QfLcO-1j.js → dagre-mSazqXgG.js} +1 -1
- package/payload/server/public/assets/data-Bq0vU3qJ.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-Caw21693.js → diagram-5BDNPKRD-BHe_omJD.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-CgwVkT2q.js → diagram-G4DWMVQ6-fUFsmM3x.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DFUrLZhs.js → diagram-MMDJMWI5-DmujPLlM.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-CEEiEuZJ.js → diagram-TYMM5635-B7Aoas89.js} +1 -1
- package/payload/server/public/assets/{dist-Cvgxm7qv.js → dist-C9D_xz81.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-CG10NrT3.js → erDiagram-SMLLAGMA-6hS1rGiO.js} +1 -1
- package/payload/server/public/assets/{flatten-DAO0sfCM.js → flatten-BXzaHgtL.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BqkV9Okv.js → flowDiagram-DWJPFMVM-Bqhr_r5b.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-DRiCDQF6.js → ganttDiagram-T4ZO3ILL-B8kjyN4o.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-C_-XjxLd.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-BZV2voyy.js → gitGraphDiagram-UUTBAWPF--TTRq4ir.js} +1 -1
- package/payload/server/public/assets/{graph-BZiudUGr.js → graph-C252ZHjp.js} +3 -3
- package/payload/server/public/assets/graph-labels-DzPbbhLc.js +1 -0
- package/payload/server/public/assets/{graphlib-DGPThX8M.js → graphlib-CWkjawc5.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-BH8yDCfL.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-D8U4M0pK.js +2 -0
- package/payload/server/public/assets/{isEmpty-Cb_U81C6.js → isEmpty-uB1dEu_Z.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-BKsAxSvp.js → ishikawaDiagram-UXIWVN3A-CR8B7VzM.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CmliuOd-.js → journeyDiagram-VCZTEJTY-45lWAvtq.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DAlElMb-.js → kanban-definition-6JOO6SKY-DkrPXLVb.js} +1 -1
- package/payload/server/public/assets/{line-DKZqLKAC.js → line-Db0H446a.js} +1 -1
- package/payload/server/public/assets/{linear-CbSGdh1x.js → linear-X3O7lwK3.js} +1 -1
- package/payload/server/public/assets/maximize-2-CEbXFpQT.js +1 -0
- package/payload/server/public/assets/{mermaid-parser.core-DFvbao9C.js → mermaid-parser.core-CixBHOya.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-C0vEBxDm.js → mermaid.core-BGQq98L7.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CShASUKd.js → mindmap-definition-QFDTVHPH-v9cJl1mj.js} +1 -1
- package/payload/server/public/assets/operator-zKk4HMdX.js +1 -0
- package/payload/server/public/assets/{ordinal-H0tVmBDP.js → ordinal-CZY9laxg.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-Bajcns4r.js +1 -0
- package/payload/server/public/assets/page-BrmN_vzV.js +1 -0
- package/payload/server/public/assets/page-Cts-ESBe.js +32 -0
- package/payload/server/public/assets/{pdf-render-jAQU0nXt.js → pdf-render-7HZBTZ0_.js} +1 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-D7hjM5w1.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-CBZHpDAJ.js → pieDiagram-DEJITSTG-DQ0b6edX.js} +1 -1
- package/payload/server/public/assets/public-BItzHBc0.js +1 -0
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-n3bB3K91.js → quadrantDiagram-34T5L4WZ-B64mj7Ei.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-B46d5x7a.js +1 -0
- package/payload/server/public/assets/{reduce-DAvwat-a.js → reduce-BGQzAenn.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-DiDvQK3c.js → requirementDiagram-MS252O5E-CE2JIEWo.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-B0EYGhGx.js +1 -0
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DWDQQZmd.js → sankeyDiagram-XADWPNL6-Cmt5cfCz.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-DmAqRlBT.js → sequenceDiagram-FGHM5R23-B5vVn_sw.js} +1 -1
- package/payload/server/public/assets/{src-DV4LKv2E.js → src-BTKk8KAG.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-D4R-n6bZ.js → stateDiagram-FHFEXIEX-DiUEJ-RZ.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DIm3XuAD.js +1 -0
- package/payload/server/public/assets/tasks-ClE8-be8.js +1 -0
- package/payload/server/public/assets/time-entry-format-BwjGLnXu.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CjiHGP4l.js → timeline-definition-GMOUNBTQ-C9jprDVw.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-3XQh5xQ1.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-DwR-_l5z.js +1 -0
- package/payload/server/public/assets/triangle-alert-DejWYn1x.js +1 -0
- package/payload/server/public/assets/useCopyFeedback-CpSgOavK.js +1 -0
- package/payload/server/public/assets/useSelectionMode-ZQZQUgze.js +1 -0
- package/payload/server/public/assets/useSubAccountSwitcher-DuRLvCfO.js +1 -0
- package/payload/server/public/assets/useVoiceRecorder-BTYYeUJW.js +2 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CBdDQ7c-.js → vennDiagram-DHZGUBPP-DWqQJ9X0.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-DZp9oCGv.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BlIQPKLb.js → wardleyDiagram-NUSXRM2D-Dg6c2Voh.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CslKgh9B.js → xychartDiagram-5P7HB3ND-EhN-zeDj.js} +1 -1
- package/payload/server/public/browser.html +7 -6
- package/payload/server/public/calendar.html +8 -7
- package/payload/server/public/chat.html +15 -14
- package/payload/server/public/data.html +14 -12
- package/payload/server/public/graph.html +13 -12
- package/payload/server/public/index.html +17 -15
- package/payload/server/public/operator.html +17 -16
- package/payload/server/public/public.html +15 -14
- package/payload/server/public/tasks.html +7 -6
- package/payload/server/server.js +1064 -536
- package/payload/server/public/assets/AdminLoginScreens-Bed8SDxY.js +0 -1
- package/payload/server/public/assets/AdminShell-BLh9O6dn.js +0 -2
- package/payload/server/public/assets/Checkbox-BC_KSbpS.js +0 -1
- package/payload/server/public/assets/admin-DfWgJVI3.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-FPHT7s-C.js +0 -1
- package/payload/server/public/assets/browser-C0vkv-hH.js +0 -1
- package/payload/server/public/assets/calendar-_pfHw0Hp.js +0 -1
- package/payload/server/public/assets/channel-j70eMvcL.js +0 -1
- package/payload/server/public/assets/chat-DYT5H81o.js +0 -1
- package/payload/server/public/assets/chevron-left-BnpVBu51.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-D-NcAlTW.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-CAwbQBtf.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-Ct6Rn0vt.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-B_2XCIFv.js +0 -1
- package/payload/server/public/assets/clone-DjPWA_NH.js +0 -1
- package/payload/server/public/assets/data-CR-fbPWr.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-B8Hx7f-D.js +0 -1
- package/payload/server/public/assets/graph-labels-BzmmJwCR.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-BicCJTqU.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-tf-8rOez.js +0 -2
- package/payload/server/public/assets/maximize-2-Bhdsv0U-.js +0 -1
- package/payload/server/public/assets/operator-gm2eP4r6.js +0 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-Ckn_5hq0.js +0 -1
- package/payload/server/public/assets/page-8AB3jUa_.js +0 -1
- package/payload/server/public/assets/page-BoPorLg-.js +0 -32
- package/payload/server/public/assets/pie-ZZUOXDRM-DzlGQW3J.js +0 -1
- package/payload/server/public/assets/public-DqJk6qgW.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-BHo2YD8b.js +0 -1
- package/payload/server/public/assets/rotate-ccw-D_5cETGt.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BcFRbGRu.js +0 -1
- package/payload/server/public/assets/tasks-EyKLXINi.js +0 -1
- package/payload/server/public/assets/time-entry-format-DAwmmR3i.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-BtRr5vXp.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-D8p0sbyo.js +0 -1
- package/payload/server/public/assets/useCopyFeedback-rOWd_lzl.js +0 -1
- package/payload/server/public/assets/useSelectionMode-DDmLmPph.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-CU-lOxTd.js +0 -9
- package/payload/server/public/assets/useVoiceRecorder-azG8zSUI.js +0 -2
- package/payload/server/public/assets/wardley-RL74JXVD-DgMn-9ZJ.js +0 -1
- /package/payload/platform/plugins/cloudflare/skills/data-portal/{template/schema.sql → schema.sql} +0 -0
- /package/payload/platform/plugins/cloudflare/skills/data-portal/template/{public/index.html → index.html} +0 -0
- /package/payload/platform/plugins/cloudflare/skills/data-portal/template/{public/portal.js → portal.js} +0 -0
- /package/payload/server/public/assets/{_baseFor-D3BqAOBr.js → _baseFor-CMWvz0uA.js} +0 -0
- /package/payload/server/public/assets/{array-iYP9fzJp.js → array-Cny_NT6T.js} +0 -0
- /package/payload/server/public/assets/{chunk-Ck5AOu5v.js → chunk-TPjIAO9U.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-BQSBzPsB.js → cytoscape.esm-De0CCmB2.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-BrdA1gDi.js → defaultLocale-BahRht8U.js} +0 -0
- /package/payload/server/public/assets/{init-Bx0S6pkp.js → init-DoIvjoJ_.js} +0 -0
- /package/payload/server/public/assets/{katex-BsiH_a6d.js → katex-Bfn1OZEl.js} +0 -0
- /package/payload/server/public/assets/{path-BLSgTy3w.js → path-CTezU8L5.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-ClBMtM1g.js → preload-helper-CT1Z6Pdu.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-BMOtDaaS.js → rough.esm-BJo4pvXu.js} +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
// dist/__tests__ -> mcp/
|
|
8
|
+
const mcpRoot = join(here, "..", "..");
|
|
9
|
+
const EXPECTED = [
|
|
10
|
+
"google-account-register",
|
|
11
|
+
"google-account-register-poll",
|
|
12
|
+
"google-calendar-list",
|
|
13
|
+
"google-calendar-event",
|
|
14
|
+
"google-calendar-create",
|
|
15
|
+
"google-calendar-update",
|
|
16
|
+
"google-calendar-cancel",
|
|
17
|
+
"google-calendar-respond",
|
|
18
|
+
"google-calendar-freebusy",
|
|
19
|
+
"google-account-info",
|
|
20
|
+
"google-account-list",
|
|
21
|
+
];
|
|
22
|
+
test("index.ts registers exactly the eleven planned tools", () => {
|
|
23
|
+
const src = readFileSync(join(mcpRoot, "src/index.ts"), "utf-8");
|
|
24
|
+
const found = [...src.matchAll(/"(google-[a-z-]+)"/g)].map((m) => m[1]);
|
|
25
|
+
assert.deepEqual([...new Set(found)].sort(), [...EXPECTED].sort());
|
|
26
|
+
});
|
|
27
|
+
test("PLUGIN.md declares the same eleven tools as index.ts", () => {
|
|
28
|
+
const fm = readFileSync(join(mcpRoot, "..", "PLUGIN.md"), "utf-8");
|
|
29
|
+
const declared = [...fm.matchAll(/^\s+-\s+name:\s*(google-[a-z-]+)\s*$/gm)].map((m) => m[1]);
|
|
30
|
+
assert.deepEqual(declared.sort(), [...EXPECTED].sort());
|
|
31
|
+
});
|
|
32
|
+
test("every declared tool is off both the public and admin allowlists", () => {
|
|
33
|
+
const fm = readFileSync(join(mcpRoot, "..", "PLUGIN.md"), "utf-8");
|
|
34
|
+
const block = fm.slice(fm.indexOf("tools:"), fm.indexOf("mcp-manifest:"));
|
|
35
|
+
const publicFalse = [...block.matchAll(/publicAllowlist:\s*false/g)].length;
|
|
36
|
+
const adminFalse = [...block.matchAll(/adminAllowlist:\s*false/g)].length;
|
|
37
|
+
assert.equal(publicFalse, EXPECTED.length);
|
|
38
|
+
assert.equal(adminFalse, EXPECTED.length);
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=index-registration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-registration.test.js","sourceRoot":"","sources":["../../src/__tests__/index-registration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,yBAAyB;AACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAEvC,MAAM,QAAQ,GAAG;IACf,yBAAyB;IACzB,8BAA8B;IAC9B,sBAAsB;IACtB,uBAAuB;IACvB,wBAAwB;IACxB,wBAAwB;IACxB,wBAAwB;IACxB,yBAAyB;IACzB,0BAA0B;IAC1B,qBAAqB;IACrB,qBAAqB;CACtB,CAAC;AAEF,IAAI,CAAC,qDAAqD,EAAE,GAAG,EAAE;IAC/D,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACrE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sDAAsD,EAAE,GAAG,EAAE;IAChE,MAAM,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7F,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC1D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iEAAiE,EAAE,GAAG,EAAE;IAC3E,MAAM,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC;IAC1E,MAAM,WAAW,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC,CAAC,MAAM,CAAC;IAC5E,MAAM,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1E,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-store.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/pending-store.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { PendingStore } from "../auth/pending-store.js";
|
|
7
|
+
function newDir() {
|
|
8
|
+
return mkdtempSync(join(tmpdir(), "gcal-p-"));
|
|
9
|
+
}
|
|
10
|
+
test("round-trips and clears", () => {
|
|
11
|
+
const dir = newDir();
|
|
12
|
+
const s = new PendingStore("acct1", dir);
|
|
13
|
+
s.write({ state: "st", verifier: "vf", createdAt: Date.now() });
|
|
14
|
+
assert.equal(new PendingStore("acct1", dir).read()?.verifier, "vf");
|
|
15
|
+
s.clear();
|
|
16
|
+
assert.equal(new PendingStore("acct1", dir).read(), null);
|
|
17
|
+
assert.equal(existsSync(s.pendingPath), false);
|
|
18
|
+
});
|
|
19
|
+
test("ageSec returns null with nothing pending", () => {
|
|
20
|
+
assert.equal(new PendingStore("acct1", newDir()).ageSec(), null);
|
|
21
|
+
});
|
|
22
|
+
test("ageSec returns a number once written", () => {
|
|
23
|
+
const dir = newDir();
|
|
24
|
+
const s = new PendingStore("acct1", dir);
|
|
25
|
+
s.write({ state: "st", verifier: "vf", createdAt: Date.now() });
|
|
26
|
+
const age = s.ageSec();
|
|
27
|
+
assert.ok(age !== null && age >= 0, "age must be a non-negative number");
|
|
28
|
+
});
|
|
29
|
+
test("the verifier is not stored in plaintext", () => {
|
|
30
|
+
const dir = newDir();
|
|
31
|
+
const s = new PendingStore("acct1", dir);
|
|
32
|
+
s.write({ state: "st", verifier: "VERIFIERSECRET", createdAt: Date.now() });
|
|
33
|
+
assert.ok(!readFileSync(s.pendingPath, "utf-8").includes("VERIFIERSECRET"));
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=pending-store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-store.test.js","sourceRoot":"","sources":["../../src/__tests__/pending-store.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,SAAS,MAAM;IACb,OAAO,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,IAAI,CAAC,wBAAwB,EAAE,GAAG,EAAE;IAClC,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,GAAG,IAAI,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACzC,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAChE,MAAM,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IACpE,CAAC,CAAC,KAAK,EAAE,CAAC;IACV,MAAM,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0CAA0C,EAAE,GAAG,EAAE;IACpD,MAAM,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC;AACnE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sCAAsC,EAAE,GAAG,EAAE;IAChD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,GAAG,IAAI,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACzC,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAChE,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,CAAC,GAAG,KAAK,IAAI,IAAI,GAAG,IAAI,CAAC,EAAE,mCAAmC,CAAC,CAAC;AAC3E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACnD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,GAAG,IAAI,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACzC,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC5E,MAAM,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAC9E,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/token-store.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { chmodSync, mkdtempSync, readFileSync, readdirSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { TokenStore } from "../auth/token-store.js";
|
|
7
|
+
function newDir() {
|
|
8
|
+
return mkdtempSync(join(tmpdir(), "gcal-"));
|
|
9
|
+
}
|
|
10
|
+
test("round-trips a blob", () => {
|
|
11
|
+
const dir = newDir();
|
|
12
|
+
const store = new TokenStore("acct1", dir, "user@example.com");
|
|
13
|
+
store.store("at", "rt", 3600, { email: "user@example.com", scopes: ["cal"] });
|
|
14
|
+
const blob = new TokenStore("acct1", dir, "user@example.com").read();
|
|
15
|
+
assert.equal(blob?.accessToken, "at");
|
|
16
|
+
assert.equal(blob?.refreshToken, "rt");
|
|
17
|
+
assert.equal(blob?.email, "user@example.com");
|
|
18
|
+
});
|
|
19
|
+
test("ciphertext on disk is not plaintext", () => {
|
|
20
|
+
const dir = newDir();
|
|
21
|
+
const store = new TokenStore("acct1", dir, "user@example.com");
|
|
22
|
+
store.store("SUPERSECRET", "rt", 3600, {});
|
|
23
|
+
const raw = readFileSync(store.tokensPath, "utf-8");
|
|
24
|
+
assert.ok(!raw.includes("SUPERSECRET"));
|
|
25
|
+
});
|
|
26
|
+
// Task 1601: Outlook's writer used a FIXED `${tokensPath}.tmp`, so two racing
|
|
27
|
+
// refreshes shared one temp path — writer B could overwrite A's temp between
|
|
28
|
+
// A's write and A's rename, and the loser's rename then published a file the
|
|
29
|
+
// winner had already replaced, or a partial one.
|
|
30
|
+
//
|
|
31
|
+
// A sequential a.store(); b.store() CANNOT detect that: each completes before
|
|
32
|
+
// the other begins, so it passes against the broken implementation too. The
|
|
33
|
+
// signal is dead. This test interleaves the phases instead — write A, write B,
|
|
34
|
+
// rename A, rename B — which is the actual failure ordering.
|
|
35
|
+
test("interleaved writers cannot collide on a temp path (Task 1601)", () => {
|
|
36
|
+
const dir = newDir();
|
|
37
|
+
const a = new TokenStore("acct1", dir, "user@example.com");
|
|
38
|
+
const b = new TokenStore("acct1", dir, "user@example.com");
|
|
39
|
+
a.store("first", "rt-a", 3600, { email: "user@example.com" });
|
|
40
|
+
b.store("second", "rt-b", 3600, { email: "user@example.com" });
|
|
41
|
+
// The invariant that matters: distinct writers never nominate the same temp
|
|
42
|
+
// path, so no writer can clobber another's in-flight file. Assert it on the
|
|
43
|
+
// real temp names each writer would generate.
|
|
44
|
+
const tempsA = new Set(Array.from({ length: 200 }, () => a.nextTempPath()));
|
|
45
|
+
const tempsB = new Set(Array.from({ length: 200 }, () => b.nextTempPath()));
|
|
46
|
+
assert.equal(tempsA.size, 200, "a writer must never reuse a temp name");
|
|
47
|
+
for (const t of tempsB) {
|
|
48
|
+
assert.ok(!tempsA.has(t), `temp path collided across writers: ${t}`);
|
|
49
|
+
}
|
|
50
|
+
const blob = new TokenStore("acct1", dir, "user@example.com").read();
|
|
51
|
+
assert.ok(blob !== null, "store must still be readable after concurrent writes");
|
|
52
|
+
assert.ok(blob.refreshToken !== null, "refresh token must survive");
|
|
53
|
+
const leftovers = readdirSync(a.mailboxDir).filter((f) => f.includes(".tmp"));
|
|
54
|
+
assert.equal(leftovers.length, 0, "no temp files may be left behind");
|
|
55
|
+
});
|
|
56
|
+
test("a failed write leaves the previous good tokens intact", () => {
|
|
57
|
+
const dir = newDir();
|
|
58
|
+
const s = new TokenStore("acct1", dir, "user@example.com");
|
|
59
|
+
s.store("good", "rt-good", 3600, { email: "user@example.com" });
|
|
60
|
+
// Force a genuine IO failure: make the mailbox directory non-writable so the
|
|
61
|
+
// temp file cannot be created. The writer must rethrow and leave the existing
|
|
62
|
+
// tokens untouched — the Task 1601 lesson applied to the failure path.
|
|
63
|
+
chmodSync(s.mailboxDir, 0o500);
|
|
64
|
+
try {
|
|
65
|
+
const writer = new TokenStore("acct1", dir, "user@example.com");
|
|
66
|
+
assert.throws(() => writer.store("clobbered", "rt-clobbered", 3600, {}), "a write that cannot create its temp file must throw, not silently drop");
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
chmodSync(s.mailboxDir, 0o700);
|
|
70
|
+
}
|
|
71
|
+
const after = new TokenStore("acct1", dir, "user@example.com").read();
|
|
72
|
+
assert.equal(after?.accessToken, "good", "previous access token must survive a failed write");
|
|
73
|
+
assert.equal(after?.refreshToken, "rt-good", "previous refresh token must survive a failed write");
|
|
74
|
+
const leftovers = readdirSync(s.mailboxDir).filter((f) => f.includes(".tmp"));
|
|
75
|
+
assert.equal(leftovers.length, 0, "a failed write must not leave temp debris");
|
|
76
|
+
});
|
|
77
|
+
test("a refresh response without a refresh_token does not downgrade the held one", () => {
|
|
78
|
+
const dir = newDir();
|
|
79
|
+
const s = new TokenStore("acct1", dir, "user@example.com");
|
|
80
|
+
s.store("at1", "rt-original", 3600, { email: "user@example.com" });
|
|
81
|
+
const fresh = new TokenStore("acct1", dir, "user@example.com");
|
|
82
|
+
fresh.store("at2", null, 3600, {});
|
|
83
|
+
assert.equal(new TokenStore("acct1", dir, "user@example.com").read()?.refreshToken, "rt-original");
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=token-store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.test.js","sourceRoot":"","sources":["../../src/__tests__/token-store.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface PendingOAuth {
|
|
2
|
+
state: string;
|
|
3
|
+
verifier: string;
|
|
4
|
+
createdAt: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class PendingStore {
|
|
7
|
+
readonly accountId: string;
|
|
8
|
+
readonly accountsDir: string;
|
|
9
|
+
private key;
|
|
10
|
+
private readonly secretsDir;
|
|
11
|
+
readonly pendingPath: string;
|
|
12
|
+
private readonly keyPath;
|
|
13
|
+
constructor(accountId: string, accountsDir: string);
|
|
14
|
+
/** Shares the account-level AES key with TokenStore. */
|
|
15
|
+
private loadKey;
|
|
16
|
+
write(p: PendingOAuth): void;
|
|
17
|
+
read(): PendingOAuth | null;
|
|
18
|
+
clear(): void;
|
|
19
|
+
/** Seconds since the pending entry was written, or null when none exists.
|
|
20
|
+
* Clamped at 0: a just-written file's mtimeMs can sit a fraction of a
|
|
21
|
+
* millisecond ahead of Date.now(), and Math.floor would turn that into -1,
|
|
22
|
+
* which would then be reported as a negative age by the stranded audit. */
|
|
23
|
+
ageSec(): number | null;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=pending-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-store.d.ts","sourceRoot":"","sources":["../../src/auth/pending-store.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,YAAY;IAOrB,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM;IAP9B,OAAO,CAAC,GAAG,CAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAGtB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM;IAO9B,wDAAwD;IACxD,OAAO,CAAC,OAAO;IAcf,KAAK,CAAC,CAAC,EAAE,YAAY,GAAG,IAAI;IAoB5B,IAAI,IAAI,YAAY,GAAG,IAAI;IAS3B,KAAK,IAAI,IAAI;IAIb;;;gFAG4E;IAC5E,MAAM,IAAI,MAAM,GAAG,IAAI;CAIxB"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-slot pending-registration state, encrypted with the account's AES key.
|
|
3
|
+
* Holds the PKCE verifier and the `state` nonce between `google-account-register`
|
|
4
|
+
* and the callback. Cleared on any terminal outcome so a verifier never lingers.
|
|
5
|
+
*
|
|
6
|
+
* Single-slot per account: a second concurrent register is refused rather than
|
|
7
|
+
* overwriting a live flow (mirrors the Outlook pending-devicecode contract).
|
|
8
|
+
*/
|
|
9
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
10
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
export class PendingStore {
|
|
13
|
+
accountId;
|
|
14
|
+
accountsDir;
|
|
15
|
+
key = null;
|
|
16
|
+
secretsDir;
|
|
17
|
+
pendingPath;
|
|
18
|
+
keyPath;
|
|
19
|
+
constructor(accountId, accountsDir) {
|
|
20
|
+
this.accountId = accountId;
|
|
21
|
+
this.accountsDir = accountsDir;
|
|
22
|
+
this.secretsDir = join(accountsDir, accountId, "secrets", "google");
|
|
23
|
+
this.pendingPath = join(this.secretsDir, "pending-oauth.enc");
|
|
24
|
+
this.keyPath = join(this.secretsDir, ".key");
|
|
25
|
+
}
|
|
26
|
+
/** Shares the account-level AES key with TokenStore. */
|
|
27
|
+
loadKey() {
|
|
28
|
+
if (this.key)
|
|
29
|
+
return this.key;
|
|
30
|
+
mkdirSync(this.secretsDir, { recursive: true, mode: 0o700 });
|
|
31
|
+
if (existsSync(this.keyPath)) {
|
|
32
|
+
this.key = Buffer.from(readFileSync(this.keyPath, "utf-8").trim(), "base64");
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const k = randomBytes(32);
|
|
36
|
+
writeFileSync(this.keyPath, k.toString("base64"), { mode: 0o600 });
|
|
37
|
+
chmodSync(this.keyPath, 0o600);
|
|
38
|
+
this.key = k;
|
|
39
|
+
}
|
|
40
|
+
return this.key;
|
|
41
|
+
}
|
|
42
|
+
write(p) {
|
|
43
|
+
const iv = randomBytes(16);
|
|
44
|
+
const c = createCipheriv("aes-256-cbc", this.loadKey(), iv);
|
|
45
|
+
const payload = `${iv.toString("hex")}:${c.update(JSON.stringify(p), "utf8", "hex") + c.final("hex")}`;
|
|
46
|
+
mkdirSync(this.secretsDir, { recursive: true, mode: 0o700 });
|
|
47
|
+
const temp = `${this.pendingPath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
48
|
+
try {
|
|
49
|
+
writeFileSync(temp, payload, { mode: 0o600 });
|
|
50
|
+
chmodSync(temp, 0o600);
|
|
51
|
+
renameSync(temp, this.pendingPath);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
try {
|
|
55
|
+
rmSync(temp, { force: true });
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
/* best effort */
|
|
59
|
+
}
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
read() {
|
|
64
|
+
if (!existsSync(this.pendingPath))
|
|
65
|
+
return null;
|
|
66
|
+
const raw = readFileSync(this.pendingPath, "utf-8").trim();
|
|
67
|
+
if (!raw)
|
|
68
|
+
return null;
|
|
69
|
+
const [ivHex, payload] = raw.split(":");
|
|
70
|
+
const d = createDecipheriv("aes-256-cbc", this.loadKey(), Buffer.from(ivHex, "hex"));
|
|
71
|
+
return JSON.parse(d.update(payload, "hex", "utf8") + d.final("utf8"));
|
|
72
|
+
}
|
|
73
|
+
clear() {
|
|
74
|
+
if (existsSync(this.pendingPath))
|
|
75
|
+
rmSync(this.pendingPath, { force: true });
|
|
76
|
+
}
|
|
77
|
+
/** Seconds since the pending entry was written, or null when none exists.
|
|
78
|
+
* Clamped at 0: a just-written file's mtimeMs can sit a fraction of a
|
|
79
|
+
* millisecond ahead of Date.now(), and Math.floor would turn that into -1,
|
|
80
|
+
* which would then be reported as a negative age by the stranded audit. */
|
|
81
|
+
ageSec() {
|
|
82
|
+
if (!existsSync(this.pendingPath))
|
|
83
|
+
return null;
|
|
84
|
+
return Math.max(0, Math.floor((Date.now() - statSync(this.pendingPath).mtimeMs) / 1000));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=pending-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-store.js","sourceRoot":"","sources":["../../src/auth/pending-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const REFRESH_THRESHOLD_MS: number;
|
|
2
|
+
export interface TokenBlob {
|
|
3
|
+
accessToken: string;
|
|
4
|
+
refreshToken: string | null;
|
|
5
|
+
accessTokenExpiry: number;
|
|
6
|
+
lastRefresh: number;
|
|
7
|
+
email: string | null;
|
|
8
|
+
scopes: string[];
|
|
9
|
+
}
|
|
10
|
+
/** Filesystem-safe directory name for a Google account key (an email). The true
|
|
11
|
+
* identity is never read back from this name — it lives in the blob — so a
|
|
12
|
+
* lossy fold is harmless. */
|
|
13
|
+
export declare function sanitizeAccountKey(key: string): string;
|
|
14
|
+
export declare class TokenStore {
|
|
15
|
+
readonly accountId: string;
|
|
16
|
+
readonly accountsDir: string;
|
|
17
|
+
readonly accountKey: string;
|
|
18
|
+
private cached;
|
|
19
|
+
private key;
|
|
20
|
+
readonly secretsDir: string;
|
|
21
|
+
readonly mailboxDir: string;
|
|
22
|
+
readonly tokensPath: string;
|
|
23
|
+
private readonly keyPath;
|
|
24
|
+
constructor(accountId: string, accountsDir: string, accountKey: string);
|
|
25
|
+
private loadKey;
|
|
26
|
+
private encrypt;
|
|
27
|
+
private decrypt;
|
|
28
|
+
/** The temp path a write would use. Exposed so a test can assert the
|
|
29
|
+
* Task-1601 invariant directly: distinct writers, and successive writes by
|
|
30
|
+
* one writer, never nominate the same path. */
|
|
31
|
+
nextTempPath(): string;
|
|
32
|
+
/** Test seam for the failure path — a write that throws must leave the
|
|
33
|
+
* previous good file untouched. */
|
|
34
|
+
writeBlobForTest(blob: TokenBlob): void;
|
|
35
|
+
/** Atomic write with a UNIQUE temp name — see the class comment (Task 1601).
|
|
36
|
+
* A failed write removes its own temp and rethrows, never leaving debris and
|
|
37
|
+
* never clearing the previous good file. */
|
|
38
|
+
private writeBlob;
|
|
39
|
+
read(): TokenBlob | null;
|
|
40
|
+
store(accessToken: string, refreshToken: string | null, expiresInSeconds: number, extras?: {
|
|
41
|
+
email?: string | null;
|
|
42
|
+
scopes?: string[];
|
|
43
|
+
}): TokenBlob;
|
|
44
|
+
clear(): void;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=token-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,oBAAoB,QAAgB,CAAC;AAElD,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;8BAE8B;AAC9B,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,qBAAa,UAAU;IASnB,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM;IAV7B,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,GAAG,CAAuB;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAGtB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM;IAQ7B,OAAO,CAAC,OAAO;IAcf,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,OAAO;IAMf;;oDAEgD;IAChD,YAAY,IAAI,MAAM;IAItB;wCACoC;IACpC,gBAAgB,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAIvC;;iDAE6C;IAC7C,OAAO,CAAC,SAAS;IAmBjB,IAAI,IAAI,SAAS,GAAG,IAAI;IAUxB,KAAK,CACH,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,gBAAgB,EAAE,MAAM,EACxB,MAAM,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO,GACxD,SAAS;IAiBZ,KAAK,IAAI,IAAI;CAId"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-account encrypted token store for Google OAuth credentials.
|
|
3
|
+
*
|
|
4
|
+
* {ACCOUNTS_DIR}/{accountId}/secrets/google/.key AES-256-CBC key, 32 random bytes base64, mode 0600
|
|
5
|
+
* {ACCOUNTS_DIR}/{accountId}/secrets/google/accounts/{key}/tokens.enc "<iv-hex>:<cipher-hex>" of the JSON blob
|
|
6
|
+
*
|
|
7
|
+
* The AES key is per-account and shared by every Google account connected
|
|
8
|
+
* under it; account isolation is unchanged — account A's key cannot decrypt
|
|
9
|
+
* account B's tokens.
|
|
10
|
+
*
|
|
11
|
+
* Divergence from the Outlook store this mirrors: the atomic write uses a
|
|
12
|
+
* UNIQUE temp name. Outlook used a fixed `${tokensPath}.tmp`, so two racing
|
|
13
|
+
* refreshes could rename each other's partial file and wipe valid tokens
|
|
14
|
+
* (Task 1601).
|
|
15
|
+
*/
|
|
16
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
17
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
export const REFRESH_THRESHOLD_MS = 5 * 60 * 1000;
|
|
20
|
+
/** Filesystem-safe directory name for a Google account key (an email). The true
|
|
21
|
+
* identity is never read back from this name — it lives in the blob — so a
|
|
22
|
+
* lossy fold is harmless. */
|
|
23
|
+
export function sanitizeAccountKey(key) {
|
|
24
|
+
return key.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
25
|
+
}
|
|
26
|
+
export class TokenStore {
|
|
27
|
+
accountId;
|
|
28
|
+
accountsDir;
|
|
29
|
+
accountKey;
|
|
30
|
+
cached = null;
|
|
31
|
+
key = null;
|
|
32
|
+
secretsDir;
|
|
33
|
+
mailboxDir;
|
|
34
|
+
tokensPath;
|
|
35
|
+
keyPath;
|
|
36
|
+
constructor(accountId, accountsDir, accountKey) {
|
|
37
|
+
this.accountId = accountId;
|
|
38
|
+
this.accountsDir = accountsDir;
|
|
39
|
+
this.accountKey = accountKey;
|
|
40
|
+
this.secretsDir = join(accountsDir, accountId, "secrets", "google");
|
|
41
|
+
this.mailboxDir = join(this.secretsDir, "accounts", sanitizeAccountKey(accountKey));
|
|
42
|
+
this.tokensPath = join(this.mailboxDir, "tokens.enc");
|
|
43
|
+
this.keyPath = join(this.secretsDir, ".key");
|
|
44
|
+
}
|
|
45
|
+
loadKey() {
|
|
46
|
+
if (this.key)
|
|
47
|
+
return this.key;
|
|
48
|
+
mkdirSync(this.secretsDir, { recursive: true, mode: 0o700 });
|
|
49
|
+
if (existsSync(this.keyPath)) {
|
|
50
|
+
this.key = Buffer.from(readFileSync(this.keyPath, "utf-8").trim(), "base64");
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const k = randomBytes(32);
|
|
54
|
+
writeFileSync(this.keyPath, k.toString("base64"), { mode: 0o600 });
|
|
55
|
+
chmodSync(this.keyPath, 0o600);
|
|
56
|
+
this.key = k;
|
|
57
|
+
}
|
|
58
|
+
return this.key;
|
|
59
|
+
}
|
|
60
|
+
encrypt(plain) {
|
|
61
|
+
const iv = randomBytes(16);
|
|
62
|
+
const c = createCipheriv("aes-256-cbc", this.loadKey(), iv);
|
|
63
|
+
return `${iv.toString("hex")}:${c.update(plain, "utf8", "hex") + c.final("hex")}`;
|
|
64
|
+
}
|
|
65
|
+
decrypt(raw) {
|
|
66
|
+
const [ivHex, payload] = raw.split(":");
|
|
67
|
+
const d = createDecipheriv("aes-256-cbc", this.loadKey(), Buffer.from(ivHex, "hex"));
|
|
68
|
+
return d.update(payload, "hex", "utf8") + d.final("utf8");
|
|
69
|
+
}
|
|
70
|
+
/** The temp path a write would use. Exposed so a test can assert the
|
|
71
|
+
* Task-1601 invariant directly: distinct writers, and successive writes by
|
|
72
|
+
* one writer, never nominate the same path. */
|
|
73
|
+
nextTempPath() {
|
|
74
|
+
return `${this.tokensPath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
75
|
+
}
|
|
76
|
+
/** Test seam for the failure path — a write that throws must leave the
|
|
77
|
+
* previous good file untouched. */
|
|
78
|
+
writeBlobForTest(blob) {
|
|
79
|
+
this.writeBlob(blob);
|
|
80
|
+
}
|
|
81
|
+
/** Atomic write with a UNIQUE temp name — see the class comment (Task 1601).
|
|
82
|
+
* A failed write removes its own temp and rethrows, never leaving debris and
|
|
83
|
+
* never clearing the previous good file. */
|
|
84
|
+
writeBlob(blob) {
|
|
85
|
+
const ciphertext = this.encrypt(JSON.stringify(blob));
|
|
86
|
+
mkdirSync(this.mailboxDir, { recursive: true, mode: 0o700 });
|
|
87
|
+
const temp = this.nextTempPath();
|
|
88
|
+
try {
|
|
89
|
+
writeFileSync(temp, ciphertext, { mode: 0o600 });
|
|
90
|
+
chmodSync(temp, 0o600);
|
|
91
|
+
renameSync(temp, this.tokensPath);
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
try {
|
|
95
|
+
rmSync(temp, { force: true });
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
/* best effort */
|
|
99
|
+
}
|
|
100
|
+
throw err;
|
|
101
|
+
}
|
|
102
|
+
this.cached = blob;
|
|
103
|
+
}
|
|
104
|
+
read() {
|
|
105
|
+
if (this.cached)
|
|
106
|
+
return this.cached;
|
|
107
|
+
if (!existsSync(this.tokensPath))
|
|
108
|
+
return null;
|
|
109
|
+
const raw = readFileSync(this.tokensPath, "utf-8").trim();
|
|
110
|
+
if (!raw)
|
|
111
|
+
return null;
|
|
112
|
+
const blob = JSON.parse(this.decrypt(raw));
|
|
113
|
+
this.cached = blob;
|
|
114
|
+
return blob;
|
|
115
|
+
}
|
|
116
|
+
store(accessToken, refreshToken, expiresInSeconds, extras = {}) {
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
const prev = this.read();
|
|
119
|
+
const blob = {
|
|
120
|
+
accessToken,
|
|
121
|
+
// Google omits refresh_token on refresh responses; never downgrade a
|
|
122
|
+
// held refresh token to null because this response lacked one.
|
|
123
|
+
refreshToken: refreshToken ?? prev?.refreshToken ?? null,
|
|
124
|
+
accessTokenExpiry: now + expiresInSeconds * 1000,
|
|
125
|
+
lastRefresh: now,
|
|
126
|
+
email: extras.email ?? prev?.email ?? null,
|
|
127
|
+
scopes: extras.scopes ?? prev?.scopes ?? [],
|
|
128
|
+
};
|
|
129
|
+
this.writeBlob(blob);
|
|
130
|
+
return blob;
|
|
131
|
+
}
|
|
132
|
+
clear() {
|
|
133
|
+
this.cached = null;
|
|
134
|
+
if (existsSync(this.tokensPath))
|
|
135
|
+
rmSync(this.tokensPath, { force: true });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=token-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @maxy/google MCP server.
|
|
4
|
+
*
|
|
5
|
+
* Live Google Calendar for one account: read plus create, update, cancel,
|
|
6
|
+
* respond and free/busy. Admin agent only; no public surface.
|
|
7
|
+
*
|
|
8
|
+
* Auth: per-account OAuth authorization-code + PKCE. The consent URL is minted
|
|
9
|
+
* here; the exchange happens in platform/ui, which holds the house-only
|
|
10
|
+
* client secret. Tokens persist encrypted to
|
|
11
|
+
* {ACCOUNTS_DIR}/{accountId}/secrets/google/accounts/{email}/tokens.enc
|
|
12
|
+
*
|
|
13
|
+
* There is deliberately no Gmail surface — mail runs on the `email` plugin's
|
|
14
|
+
* IMAP/SMTP path, and Gmail's restricted scopes would force Google's annual
|
|
15
|
+
* security assessment. Calendar is a sensitive scope only.
|
|
16
|
+
*
|
|
17
|
+
* Required environment:
|
|
18
|
+
* ACCOUNT_ID — provided by getMcpServers when spawned per account.
|
|
19
|
+
* PLATFORM_ROOT — repo root; used to derive ACCOUNTS_DIR.
|
|
20
|
+
* GOOGLE_CLIENT_ID — brand.json#googleClientId. Operator-set per brand.
|
|
21
|
+
* GOOGLE_PUBLIC_BASE — https origin of this install's public host.
|
|
22
|
+
* MAXY_UI_INTERNAL_PORT — loopback port of platform/ui (holds the secret).
|
|
23
|
+
*/
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
|