@rubytech/create-maxy-code 0.1.470 → 0.1.472
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__/google-client-id-env.test.js +68 -0
- package/dist/index.js +26 -18
- package/dist/port-resolution.js +7 -1
- package/package.json +1 -1
- package/payload/platform/config/brand.json +1 -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 +5 -0
- package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +1 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +98 -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/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/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 +99 -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 +59 -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 +126 -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/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 +8 -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/project-manager.md +1 -1
- package/payload/server/{chunk-JRIBGO45.js → chunk-BSTIQX52.js} +44 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/activity.html +19 -0
- package/payload/server/public/assets/{AdminLoginScreens-Bed8SDxY.js → AdminLoginScreens-BfamGqS7.js} +1 -1
- package/payload/server/public/assets/AdminShell-5nHOAzXO.js +2 -0
- package/payload/server/public/assets/{Checkbox-BC_KSbpS.js → Checkbox-CD6aHwac.js} +1 -1
- package/payload/server/public/assets/activity-Dj2nJzPw.js +1 -0
- package/payload/server/public/assets/{admin-DfWgJVI3.js → admin-Cpcw_YE-.js} +1 -1
- package/payload/server/public/assets/{arc-qKfXIt8v.js → arc-BrHWN2Kr.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-CZQzGRSM.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DkVrGn7G.js → architectureDiagram-Q4EWVU46-Clt9NOr0.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DqavswGH.js → blockDiagram-DXYQGD6D-BOe_jmXi.js} +1 -1
- package/payload/server/public/assets/{browser-C0vkv-hH.js → browser-0znOwk1W.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-B7fyKiXe.js → c4Diagram-AHTNJAMY-B64FaKMV.js} +1 -1
- package/payload/server/public/assets/calendar-CykigHdg.js +1 -0
- package/payload/server/public/assets/channel-zDUAN9Ks.js +1 -0
- package/payload/server/public/assets/chat-DIOJZ411.js +1 -0
- package/payload/server/public/assets/chevron-left-Cq7UvOI0.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-DsgMtGkv.js → chunk-2KRD3SAO-BGAUfWvM.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-CkdISfNt.js → chunk-336JU56O-PFi6uQZ0.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-iJnqNRug.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-CYpiF2bl.js → chunk-4BX2VUAB-CkxXXCiF.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-CWyc_F7v.js → chunk-4TB4RGXK-B-7tN4IA.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-BbzKMw2Z.js → chunk-55IACEB6-CBkNmqrt.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-u19Nai9e.js → chunk-5FUZZQ4R-wa9ujn7i.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-Ba8by6Uy.js → chunk-5PVQY5BW-DIk-xBaE.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-BwUuxtyb.js → chunk-67CJDMHE-BbMV1lgW.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-BRH9iM9L.js → chunk-7N4EOEYR-C49Mv-nQ.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-BGoztgLL.js → chunk-AA7GKIK3-Dy_pJOp_.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-C5GDAZTl.js → chunk-BSJP7CBP-Ce-OmPqn.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-ChY32VPS.js → chunk-CIAEETIT-tnjq-APC.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-CPSD3p6p.js → chunk-EDXVE4YY-Bvv_deZ2.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-D1oL-iUr.js → chunk-ENJZ2VHE-CmxCPKyU.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-CPvYEr54.js → chunk-FMBD7UC4-Top6y08S.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-9Yyj7Vbz.js → chunk-FOC6F5B3-B9LPNCUS.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-BaYgh8FR.js → chunk-ICPOFSXX-Bi-_c351.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-5ucpeRvm.js → chunk-K5T4RW27-DAnRM04h.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-DW-FHk7T.js → chunk-KGLVRYIC-C0QYcbLe.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-CGGVfAyG.js → chunk-LIHQZDEY-BjvA5ykt.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-Bv8EUa5d.js → chunk-ORNJ4GCN-Cs46Gj7z.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-C1mXJnyw.js → chunk-OYMX7WX6-BBpimab5.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-Qlc2PFAj.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-PwSQz6EP.js → chunk-U2HBQHQK-CQUSBHoK.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP--NPHTBlU.js → chunk-X2U36JSP-Dm8TevG0.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-Bbc7Da32.js → chunk-XPW4576I-B10QuxYb.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-D-9v8e--.js → chunk-YZCP3GAM-vaGsu4Wk.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-CMPg670Q.js → chunk-ZZ45TVLE-D1Qi22bL.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-CFsKL52p.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BWBUUCiO.js +1 -0
- package/payload/server/public/assets/clone-h5XhkDUL.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-NVSb1Bhy.js → cose-bilkent-S5V4N54A-CRDvLRXs.js} +1 -1
- package/payload/server/public/assets/{dagre-QfLcO-1j.js → dagre-CEmqJTt3.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-V2Ai1DFl.js → dagre-KV5264BT-LXMTo7Fp.js} +1 -1
- package/payload/server/public/assets/data-lHgqlhmu.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-Caw21693.js → diagram-5BDNPKRD-zyis6nPt.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-CgwVkT2q.js → diagram-G4DWMVQ6-BiU0XAAD.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DFUrLZhs.js → diagram-MMDJMWI5-rGFiz1SX.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-CEEiEuZJ.js → diagram-TYMM5635-B0wak1ZJ.js} +1 -1
- package/payload/server/public/assets/{dist-Cvgxm7qv.js → dist-B2N0v8hZ.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-CG10NrT3.js → erDiagram-SMLLAGMA-D8AUxLyX.js} +1 -1
- package/payload/server/public/assets/{flatten-DAO0sfCM.js → flatten-naJ5PJ4f.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BqkV9Okv.js → flowDiagram-DWJPFMVM-BtdUf68b.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-DRiCDQF6.js → ganttDiagram-T4ZO3ILL-C-lkzDmf.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-CBZPQJLj.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-BZV2voyy.js → gitGraphDiagram-UUTBAWPF-DRUKumq2.js} +1 -1
- package/payload/server/public/assets/{graph-BZiudUGr.js → graph-JV5_xF3V.js} +3 -3
- package/payload/server/public/assets/{graph-labels-BzmmJwCR.js → graph-labels-DPYORjBH.js} +1 -1
- package/payload/server/public/assets/{graphlib-DGPThX8M.js → graphlib-zRsaoFEC.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-1vIoX-kP.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-KHcXRaan.js +2 -0
- package/payload/server/public/assets/{isEmpty-Cb_U81C6.js → isEmpty-Bnpg7b8f.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-BKsAxSvp.js → ishikawaDiagram-UXIWVN3A-BguMgoR8.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CmliuOd-.js → journeyDiagram-VCZTEJTY-CXhyK1n-.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DAlElMb-.js → kanban-definition-6JOO6SKY-C0UCWVx-.js} +1 -1
- package/payload/server/public/assets/{line-DKZqLKAC.js → line--uhS9YDE.js} +1 -1
- package/payload/server/public/assets/{linear-CbSGdh1x.js → linear-CAOWPodQ.js} +1 -1
- package/payload/server/public/assets/{maximize-2-Bhdsv0U-.js → maximize-2-8vKr3h21.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-DFvbao9C.js → mermaid-parser.core-D6kPx7vc.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-C0vEBxDm.js → mermaid.core-CTd4DS6O.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CShASUKd.js → mindmap-definition-QFDTVHPH-_AyvyG8C.js} +1 -1
- package/payload/server/public/assets/operator-Liul1QEq.js +1 -0
- package/payload/server/public/assets/{ordinal-H0tVmBDP.js → ordinal-Wx4m9zHk.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-CTU0gCGb.js +1 -0
- package/payload/server/public/assets/page-CYRA87CB.js +1 -0
- package/payload/server/public/assets/{page-BoPorLg-.js → page-Cg7B1RQx.js} +4 -4
- package/payload/server/public/assets/{pdf-render-jAQU0nXt.js → pdf-render-CeX6d2v7.js} +1 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-D-EEHxhC.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-CBZHpDAJ.js → pieDiagram-DEJITSTG-6TsUDkkQ.js} +1 -1
- package/payload/server/public/assets/{public-DqJk6qgW.js → public-B4HMtpjt.js} +1 -1
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-n3bB3K91.js → quadrantDiagram-34T5L4WZ-CWw9gOI4.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-DHXATZGB.js +1 -0
- package/payload/server/public/assets/{reduce-DAvwat-a.js → reduce-BUmc8RDR.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-DiDvQK3c.js → requirementDiagram-MS252O5E-DCOlxNvx.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-D_5cETGt.js → rotate-ccw-sz5IQSnr.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DWDQQZmd.js → sankeyDiagram-XADWPNL6-CW4Xbir3.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-DmAqRlBT.js → sequenceDiagram-FGHM5R23-vhb_pBLG.js} +1 -1
- package/payload/server/public/assets/{src-DV4LKv2E.js → src-DHSQDYx-.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-D4R-n6bZ.js → stateDiagram-FHFEXIEX-Cb1WExEX.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CxOIKvXh.js +1 -0
- package/payload/server/public/assets/tasks-D9oYgEzs.js +1 -0
- package/payload/server/public/assets/{time-entry-format-DAwmmR3i.js → time-entry-format-CSuCexpM.js} +1 -1
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CjiHGP4l.js → timeline-definition-GMOUNBTQ-BAHTf_AF.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CC7ouMFd.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-CaCg27im.js +1 -0
- package/payload/server/public/assets/triangle-alert-ClS81-CU.js +1 -0
- package/payload/server/public/assets/{useCopyFeedback-rOWd_lzl.js → useCopyFeedback-Cn6YgeNx.js} +1 -1
- package/payload/server/public/assets/{useSelectionMode-DDmLmPph.js → useSelectionMode-BaD-ZZRk.js} +1 -1
- package/payload/server/public/assets/{useSubAccountSwitcher-pu0jjLCz.css → useSubAccountSwitcher-CNZL3PKn.css} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-CvjWJSGv.js +9 -0
- package/payload/server/public/assets/{useVoiceRecorder-azG8zSUI.js → useVoiceRecorder-BOzHrDAU.js} +2 -2
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CBdDQ7c-.js → vennDiagram-DHZGUBPP-DtEttvW4.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-bMPsqQZ7.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BlIQPKLb.js → wardleyDiagram-NUSXRM2D-K06y6CVJ.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CslKgh9B.js → xychartDiagram-5P7HB3ND-D66c40lp.js} +1 -1
- package/payload/server/public/browser.html +6 -6
- package/payload/server/public/calendar.html +7 -7
- package/payload/server/public/chat.html +14 -14
- package/payload/server/public/data.html +13 -12
- package/payload/server/public/graph.html +12 -12
- package/payload/server/public/index.html +16 -15
- package/payload/server/public/operator.html +16 -16
- package/payload/server/public/public.html +14 -14
- package/payload/server/public/tasks.html +6 -6
- package/payload/server/server.js +558 -260
- package/payload/server/public/assets/AdminShell-BLh9O6dn.js +0 -2
- package/payload/server/public/assets/architecture-YZFGNWBL-FPHT7s-C.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/info-OMHHGYJF-BicCJTqU.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-tf-8rOez.js +0 -2
- 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/pie-ZZUOXDRM-DzlGQW3J.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-BHo2YD8b.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/treeView-SZITEDCU-BtRr5vXp.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-D8p0sbyo.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-CU-lOxTd.js +0 -9
- 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-k8hYPe-U.js} +0 -0
- /package/payload/server/public/assets/{array-iYP9fzJp.js → array-BFDiaBgf.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-Ch0xptA4.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-BrdA1gDi.js → defaultLocale-ZeknFqNe.js} +0 -0
- /package/payload/server/public/assets/{init-Bx0S6pkp.js → init-0VJVrkRJ.js} +0 -0
- /package/payload/server/public/assets/{katex-BsiH_a6d.js → katex-Br7bH10N.js} +0 -0
- /package/payload/server/public/assets/{path-BLSgTy3w.js → path-INs8XTPH.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-ClBMtM1g.js → preload-helper-D_sPnjeL.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-BMOtDaaS.js → rough.esm-DU9bZLvZ.js} +0 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Regression test for portal-brand-css.mjs - the assemble-time :root rewriter
|
|
3
|
+
# that bakes a brand's consent.palette into an assembled data portal's
|
|
4
|
+
# stylesheet. Pure file transformation: no network, no Cloudflare credential,
|
|
5
|
+
# no wrangler.
|
|
6
|
+
#
|
|
7
|
+
# Covers:
|
|
8
|
+
# 1. a full palette sets all five mapped variables, exit 0
|
|
9
|
+
# 2. everything outside the :root block is byte-identical to the template
|
|
10
|
+
# 3. a brand with no consent.palette leaves the file byte-identical, exit 0
|
|
11
|
+
# 4. a partial palette sets only the fields it carries
|
|
12
|
+
# 5. --dp-muted is never brand-written, even if the palette carries a muted
|
|
13
|
+
# 6. a value failing the CSS colour rule fails closed: exit 1, file unchanged
|
|
14
|
+
# 7. running twice is idempotent
|
|
15
|
+
# 8. a stylesheet with no :root block fails closed: exit 1, file unchanged
|
|
16
|
+
# 9. a missing --css argument fails closed (exit 1)
|
|
17
|
+
set -u
|
|
18
|
+
|
|
19
|
+
BIN_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
20
|
+
SCRIPT="$BIN_DIR/portal-brand-css.mjs"
|
|
21
|
+
TEMPLATE="$BIN_DIR/../skills/data-portal/template/public/portal.css"
|
|
22
|
+
[ -f "$SCRIPT" ] || { echo "FAIL: $SCRIPT not found" >&2; exit 1; }
|
|
23
|
+
[ -f "$TEMPLATE" ] || { echo "FAIL: $TEMPLATE not found" >&2; exit 1; }
|
|
24
|
+
# Case 2 strips the :root block with perl and diffs what is left. Without perl
|
|
25
|
+
# both sides strip to nothing and the diff succeeds, so the one check that
|
|
26
|
+
# guards byte-identity would report PASS while asserting nothing. Refuse instead.
|
|
27
|
+
command -v perl >/dev/null || { echo "FAIL: perl not found; case 2 would pass vacuously" >&2; exit 1; }
|
|
28
|
+
|
|
29
|
+
PASS=0; FAIL=0
|
|
30
|
+
ok() { echo "PASS: $1"; PASS=$((PASS+1)); }
|
|
31
|
+
bad() { echo "FAIL: $1" >&2; FAIL=$((FAIL+1)); }
|
|
32
|
+
|
|
33
|
+
WORK=$(mktemp -d)
|
|
34
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
35
|
+
|
|
36
|
+
# Write a brand.json carrying the given consent.palette object literal, or no
|
|
37
|
+
# palette at all when passed NONE. Echoes the path.
|
|
38
|
+
brand() {
|
|
39
|
+
local path="$WORK/$1"
|
|
40
|
+
if [ "$2" = "NONE" ]; then
|
|
41
|
+
printf '{"name":"t","consent":{}}\n' > "$path"
|
|
42
|
+
else
|
|
43
|
+
printf '{"name":"t","consent":{"palette":%s}}\n' "$2" > "$path"
|
|
44
|
+
fi
|
|
45
|
+
echo "$path"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Fresh copy of the real template stylesheet. Using the real file rather than a
|
|
49
|
+
# fixture is deliberate: a template edit that breaks the rewriter fails here.
|
|
50
|
+
css() { local path="$WORK/$1"; cp "$TEMPLATE" "$path"; echo "$path"; }
|
|
51
|
+
|
|
52
|
+
run() {
|
|
53
|
+
local of ef
|
|
54
|
+
of=$(mktemp); ef=$(mktemp)
|
|
55
|
+
node "$SCRIPT" "$@" >"$of" 2>"$ef"
|
|
56
|
+
RC=$?
|
|
57
|
+
OUT=$(cat "$of"); ERR=$(cat "$ef"); rm -f "$of" "$ef"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# The file with its :root block removed, for byte-identity comparison.
|
|
61
|
+
sans_root() { perl -0777 -pe 's/:root\s*\{[^}]*\}//' "$1"; }
|
|
62
|
+
|
|
63
|
+
FULL='{"bg":"#FAFAF8","fg":"#1a1a1a","accent":"#ea7232","accentFg":"#FFFFFF","border":"#E0E0DB"}'
|
|
64
|
+
|
|
65
|
+
# 1. full palette sets all five
|
|
66
|
+
B=$(brand full.json "$FULL"); C=$(css full.css)
|
|
67
|
+
run --css "$C" --brand "$B"
|
|
68
|
+
{ [ "$RC" -eq 0 ] \
|
|
69
|
+
&& grep -q -- '--dp-accent: #ea7232;' "$C" \
|
|
70
|
+
&& grep -q -- '--dp-accent-ink: #FFFFFF;' "$C" \
|
|
71
|
+
&& grep -q -- '--dp-paper: #FAFAF8;' "$C" \
|
|
72
|
+
&& grep -q -- '--dp-ink: #1a1a1a;' "$C" \
|
|
73
|
+
&& grep -q -- '--dp-rule: #E0E0DB;' "$C"; } \
|
|
74
|
+
&& ok "full palette sets all five mapped variables (exit=$RC)" \
|
|
75
|
+
|| bad "full palette (exit=$RC err='$ERR')"
|
|
76
|
+
|
|
77
|
+
# 2. everything outside :root is byte-identical to the template
|
|
78
|
+
{ [ -n "$(sans_root "$TEMPLATE")" ] && diff <(sans_root "$TEMPLATE") <(sans_root "$C") >/dev/null; } \
|
|
79
|
+
&& ok "substitution touched only the :root block" \
|
|
80
|
+
|| bad "bytes outside :root changed, or the strip produced nothing to compare"
|
|
81
|
+
|
|
82
|
+
# 3. no consent.palette -> file byte-identical, exit 0
|
|
83
|
+
B=$(brand none.json NONE); C=$(css none.css)
|
|
84
|
+
run --css "$C" --brand "$B"
|
|
85
|
+
{ [ "$RC" -eq 0 ] && diff "$TEMPLATE" "$C" >/dev/null; } \
|
|
86
|
+
&& ok "brand with no consent.palette leaves the file untouched (exit=$RC)" \
|
|
87
|
+
|| bad "no-palette case (exit=$RC err='$ERR')"
|
|
88
|
+
|
|
89
|
+
# 4. partial palette sets only what it carries
|
|
90
|
+
B=$(brand part.json '{"accent":"#CCFF00"}'); C=$(css part.css)
|
|
91
|
+
run --css "$C" --brand "$B"
|
|
92
|
+
{ [ "$RC" -eq 0 ] \
|
|
93
|
+
&& grep -q -- '--dp-accent: #CCFF00;' "$C" \
|
|
94
|
+
&& grep -q -- '--dp-accent-ink: #ffffff;' "$C" \
|
|
95
|
+
&& grep -q -- '--dp-ink: #14181f;' "$C"; } \
|
|
96
|
+
&& ok "partial palette leaves unmapped fields at template defaults (exit=$RC)" \
|
|
97
|
+
|| bad "partial palette (exit=$RC err='$ERR')"
|
|
98
|
+
|
|
99
|
+
# 5. --dp-muted is never brand-written
|
|
100
|
+
B=$(brand muted.json '{"muted":"#123456","accent":"#ea7232"}'); C=$(css muted.css)
|
|
101
|
+
run --css "$C" --brand "$B"
|
|
102
|
+
{ [ "$RC" -eq 0 ] && grep -q -- '--dp-muted: #667085;' "$C" && ! grep -q '#123456' "$C"; } \
|
|
103
|
+
&& ok "--dp-muted keeps the template default (exit=$RC)" \
|
|
104
|
+
|| bad "--dp-muted (exit=$RC err='$ERR')"
|
|
105
|
+
|
|
106
|
+
# 6. an unsafe value fails closed and writes nothing
|
|
107
|
+
B=$(brand bad.json '{"accent":"red;background:url(//x)"}'); C=$(css bad.css)
|
|
108
|
+
BEFORE=$(cat "$C")
|
|
109
|
+
run --css "$C" --brand "$B"
|
|
110
|
+
{ [ "$RC" -ne 0 ] && [ "$(cat "$C")" = "$BEFORE" ] && [ -n "$ERR" ]; } \
|
|
111
|
+
&& ok "a value failing the CSS colour rule exits $RC and writes nothing" \
|
|
112
|
+
|| bad "unsafe value not refused (exit=$RC)"
|
|
113
|
+
|
|
114
|
+
# 7. idempotent
|
|
115
|
+
B=$(brand idem.json "$FULL"); C=$(css idem.css)
|
|
116
|
+
run --css "$C" --brand "$B"; ONCE=$(cat "$C")
|
|
117
|
+
run --css "$C" --brand "$B"; TWICE=$(cat "$C")
|
|
118
|
+
{ [ "$RC" -eq 0 ] && [ "$ONCE" = "$TWICE" ]; } \
|
|
119
|
+
&& ok "second run is a no-op (exit=$RC)" \
|
|
120
|
+
|| bad "not idempotent (exit=$RC)"
|
|
121
|
+
|
|
122
|
+
# 8. no :root block fails closed
|
|
123
|
+
B=$(brand root.json "$FULL"); C="$WORK/noroot.css"
|
|
124
|
+
printf 'body { color: red; }\n' > "$C"; BEFORE=$(cat "$C")
|
|
125
|
+
run --css "$C" --brand "$B"
|
|
126
|
+
{ [ "$RC" -ne 0 ] && [ "$(cat "$C")" = "$BEFORE" ]; } \
|
|
127
|
+
&& ok "a stylesheet with no :root block is refused (exit=$RC)" \
|
|
128
|
+
|| bad "missing :root not refused (exit=$RC)"
|
|
129
|
+
|
|
130
|
+
# 9. missing --css fails closed
|
|
131
|
+
B=$(brand args.json "$FULL")
|
|
132
|
+
run --brand "$B"
|
|
133
|
+
{ [ "$RC" -ne 0 ] && [ -n "$ERR" ]; } \
|
|
134
|
+
&& ok "missing --css is refused (exit=$RC)" \
|
|
135
|
+
|| bad "missing --css not refused (exit=$RC)"
|
|
136
|
+
|
|
137
|
+
echo
|
|
138
|
+
echo "passed: $PASS failed: $FAIL"
|
|
139
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Bake a brand's client-facing palette into an assembled data portal's
|
|
3
|
+
// stylesheet, by rewriting the one `:root` block in place.
|
|
4
|
+
//
|
|
5
|
+
// This is a script and not a sentence in SKILL.md for the same reason
|
|
6
|
+
// portal-enrol.mjs is one: "replace the :root block and leave the rest alone"
|
|
7
|
+
// is not a constraint when a model performs it by hand. The portal's done-gate
|
|
8
|
+
// asks for the rest of the stylesheet to be byte-identical to the template, and
|
|
9
|
+
// only a transformation that can be run and diffed makes that checkable.
|
|
10
|
+
//
|
|
11
|
+
// The palette read is `consent.palette`, not `defaultColors`. consent.palette
|
|
12
|
+
// is the brand's existing answer to "what colours does this brand show a
|
|
13
|
+
// customer on a surface that is not the install", which is exactly what the
|
|
14
|
+
// portal is; and its accent/accentFg and bg/fg are designed contrast pairs.
|
|
15
|
+
// Taking the accent from defaultColors.primary while taking the foreground from
|
|
16
|
+
// consent.palette.accentFg mixes two palettes, and on a shipped brand that
|
|
17
|
+
// yields #0F3A61 text on a #0F3A61 button.
|
|
18
|
+
//
|
|
19
|
+
// Usage:
|
|
20
|
+
// node platform/plugins/cloudflare/bin/portal-brand-css.mjs \
|
|
21
|
+
// --css <accountDir>/pages/<project>/public/portal.css
|
|
22
|
+
|
|
23
|
+
import { readFileSync, writeFileSync } from 'node:fs'
|
|
24
|
+
import { fileURLToPath } from 'node:url'
|
|
25
|
+
|
|
26
|
+
/** The install's stamped brand manifest. Resolved from this file's own location
|
|
27
|
+
* rather than cwd, because the skill's other commands run from the install root
|
|
28
|
+
* and this one must not silently depend on that. `--brand` overrides it, which
|
|
29
|
+
* is what lets the regression test run off-device: platform/config/ is written
|
|
30
|
+
* by the bundler and does not exist in the source tree. */
|
|
31
|
+
const DEFAULT_BRAND = fileURLToPath(new URL('../../../config/brand.json', import.meta.url))
|
|
32
|
+
|
|
33
|
+
/** Copied verbatim from packages/create-maxy-code/scripts/bundle.js, which
|
|
34
|
+
* applies it to defaultColors for the same reason: a brand value lands inside a
|
|
35
|
+
* CSS declaration, so anything that is not a colour is an injection. The
|
|
36
|
+
* bundler does NOT validate consent.palette, so this is the only gate on it. */
|
|
37
|
+
const CSS_COLOR_RE = /^(#[0-9a-fA-F]{3,8}|rgba?\([^)]{1,60}\))$/
|
|
38
|
+
|
|
39
|
+
/** The whole mapping. `--dp-muted` is deliberately absent: no brand carries a
|
|
40
|
+
* muted tone, and deriving one from another field would be a colour the brand
|
|
41
|
+
* never chose. It keeps the template default on every brand. */
|
|
42
|
+
const MAP = [
|
|
43
|
+
['--dp-accent', 'accent'],
|
|
44
|
+
['--dp-accent-ink', 'accentFg'],
|
|
45
|
+
['--dp-paper', 'bg'],
|
|
46
|
+
['--dp-ink', 'fg'],
|
|
47
|
+
['--dp-rule', 'border'],
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
/** @param {string} message */
|
|
51
|
+
function die(message) {
|
|
52
|
+
console.error(`portal-brand-css: ${message}`)
|
|
53
|
+
process.exit(1)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** @param {string} flag @returns {string | undefined} */
|
|
57
|
+
function arg(flag) {
|
|
58
|
+
const i = process.argv.indexOf(flag)
|
|
59
|
+
return i === -1 ? undefined : process.argv[i + 1]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const cssPath = arg('--css')
|
|
63
|
+
const brandPath = arg('--brand') ?? DEFAULT_BRAND
|
|
64
|
+
|
|
65
|
+
if (!cssPath) die('--css <path to the assembled portal.css> is required')
|
|
66
|
+
|
|
67
|
+
let css
|
|
68
|
+
try {
|
|
69
|
+
css = readFileSync(/** @type {string} */ (cssPath), 'utf8')
|
|
70
|
+
} catch (err) {
|
|
71
|
+
die(`cannot read --css ${cssPath}: ${/** @type {Error} */ (err).message}`)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let brand
|
|
75
|
+
try {
|
|
76
|
+
brand = JSON.parse(readFileSync(brandPath, 'utf8'))
|
|
77
|
+
} catch (err) {
|
|
78
|
+
// An unreadable or malformed brand manifest is a broken install, not a brand
|
|
79
|
+
// that declined to declare a palette. Refuse, rather than silently shipping
|
|
80
|
+
// the template's default and calling it the brand's colour.
|
|
81
|
+
die(`cannot read brand manifest ${brandPath}: ${/** @type {Error} */ (err).message}`)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const palette = brand?.consent?.palette
|
|
85
|
+
if (!palette || typeof palette !== 'object') {
|
|
86
|
+
// Not a failure. A brand that declares no client-facing palette gets the
|
|
87
|
+
// template defaults, which are legible on their own.
|
|
88
|
+
console.log('portal-brand-css: brand declares no consent.palette; template defaults kept')
|
|
89
|
+
process.exit(0)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Exactly one :root block, or refuse. Two would make "which one wins" a
|
|
93
|
+
// question the stylesheet answers by source order and this script would answer
|
|
94
|
+
// by position; none means the template this tree was assembled from is not the
|
|
95
|
+
// template this script knows.
|
|
96
|
+
const roots = css.match(/:root\s*\{[^}]*\}/g) ?? []
|
|
97
|
+
if (roots.length !== 1) {
|
|
98
|
+
die(`expected exactly one :root block in ${cssPath}, found ${roots.length}`)
|
|
99
|
+
}
|
|
100
|
+
const root = roots[0]
|
|
101
|
+
|
|
102
|
+
let next = root
|
|
103
|
+
const applied = []
|
|
104
|
+
for (const [cssVar, field] of MAP) {
|
|
105
|
+
const value = palette[field]
|
|
106
|
+
if (value === undefined || value === null || value === '') continue
|
|
107
|
+
if (typeof value !== 'string' || !CSS_COLOR_RE.test(value)) {
|
|
108
|
+
die(`consent.palette.${field} is not a valid CSS colour: ${JSON.stringify(value)}`)
|
|
109
|
+
}
|
|
110
|
+
// The declaration must already be in the block. If it is not, the template
|
|
111
|
+
// and this mapping have drifted apart, and appending a declaration the
|
|
112
|
+
// template never had would hide that.
|
|
113
|
+
const declRe = new RegExp(`(${cssVar}\\s*:\\s*)[^;]*(;)`)
|
|
114
|
+
if (!declRe.test(next)) {
|
|
115
|
+
die(`${cssVar} is not declared in the :root block of ${cssPath}`)
|
|
116
|
+
}
|
|
117
|
+
next = next.replace(declRe, `$1${value}$2`)
|
|
118
|
+
applied.push(`${cssVar}=${value}`)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Function replacement, so a `$` sequence in the rewritten block is never read
|
|
122
|
+
// as a replacement pattern.
|
|
123
|
+
if (next !== root) {
|
|
124
|
+
writeFileSync(/** @type {string} */ (cssPath), css.replace(root, () => next), 'utf8')
|
|
125
|
+
}
|
|
126
|
+
console.log(
|
|
127
|
+
`portal-brand-css: ${applied.length ? applied.join(' ') : 'no mapped fields in consent.palette'}`,
|
|
128
|
+
)
|
|
@@ -39,7 +39,7 @@ async function d1(dir: string) {
|
|
|
39
39
|
const db = resolve(root, 'portal.db')
|
|
40
40
|
const schema = resolve(
|
|
41
41
|
dirname(fileURLToPath(import.meta.url)),
|
|
42
|
-
'../../skills/data-portal/
|
|
42
|
+
'../../skills/data-portal/schema.sql',
|
|
43
43
|
)
|
|
44
44
|
// SQL as an argv entry, not stdin: execFile has no `input` option. The schema
|
|
45
45
|
// goes via `.read` because it opens with a `--` comment, which sqlite3 would
|
|
@@ -4,6 +4,31 @@ import { join } from 'node:path'
|
|
|
4
4
|
|
|
5
5
|
const skill = () => readFileSync(join(__dirname, '../../skills/data-portal/SKILL.md'), 'utf8')
|
|
6
6
|
|
|
7
|
+
// resolveSiteDir (platform/ui/server/routes/storage-broker.ts) resolves
|
|
8
|
+
// <accountsDir>/<account>/sites/<site> and refuses anything else with
|
|
9
|
+
// invalid-site. A tree assembled where the skill used to say could not be
|
|
10
|
+
// deployed at all.
|
|
11
|
+
describe('data-portal SKILL.md names paths the broker accepts', () => {
|
|
12
|
+
it('assembles into sites/, not pages/', () => {
|
|
13
|
+
expect(skill()).toMatch(/<accountDir>\/sites\/<project>\//)
|
|
14
|
+
expect(skill()).not.toMatch(/assembled tree.*\bpages\/<project>\//)
|
|
15
|
+
expect(skill()).not.toMatch(/Copy `template\/` to [^\n]*pages\//)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('never tells the assembler to copy a public/ level', () => {
|
|
19
|
+
expect(skill()).not.toMatch(/template\/public/)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
// site-deploy's canonical-tree section names pages/<project>/ and instructs
|
|
23
|
+
// reconciling stray trees into it. An assembler that follows it after
|
|
24
|
+
// assembling at sites/ moves the tree somewhere the broker refuses, so the
|
|
25
|
+
// skill has to say not to, for as long as the two disagree.
|
|
26
|
+
it('warns against relocating the tree to site-deploy canonical path', () => {
|
|
27
|
+
expect(skill()).toMatch(/Leave the tree where you assembled it/)
|
|
28
|
+
expect(skill()).toMatch(/invalid-site/)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
7
32
|
describe('data-portal SKILL.md', () => {
|
|
8
33
|
it('opens with frontmatter carrying name and description', () => {
|
|
9
34
|
// Without these the skill is invisible to the available-skills reminder and
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { readFileSync, existsSync, readdirSync } from 'node:fs'
|
|
3
3
|
import { join } from 'node:path'
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const SKILL = join(__dirname, '../../skills/data-portal')
|
|
6
|
+
const TEMPLATE = join(SKILL, 'template')
|
|
6
7
|
const wrangler = () => readFileSync(join(TEMPLATE, 'wrangler.toml'), 'utf8')
|
|
7
|
-
const schema = () => readFileSync(join(
|
|
8
|
+
const schema = () => readFileSync(join(SKILL, 'schema.sql'), 'utf8')
|
|
8
9
|
|
|
9
10
|
describe('wrangler.toml', () => {
|
|
10
11
|
it('binds D1 as DB', () => {
|
|
@@ -53,3 +54,54 @@ describe('schema.sql', () => {
|
|
|
53
54
|
expect(schema()).toMatch(/CREATE UNIQUE INDEX IF NOT EXISTS auth_attempts_scope_window/i)
|
|
54
55
|
})
|
|
55
56
|
})
|
|
57
|
+
|
|
58
|
+
// The broker runs `wrangler pages deploy <dir>` with <dir> = the site root, so
|
|
59
|
+
// the positional directory IS the asset root and pages_build_output_dir is
|
|
60
|
+
// overridden. A nested public/ therefore publishes the site one level too deep
|
|
61
|
+
// and `/` returns 404 — the live failure Task 1774 closes.
|
|
62
|
+
describe('template layout matches what the deploy publishes', () => {
|
|
63
|
+
it('has no public/ level', () => {
|
|
64
|
+
expect(existsSync(join(TEMPLATE, 'public'))).toBe(false)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('serves index.html, portal.css and portal.js from the tree root', () => {
|
|
68
|
+
for (const f of ['index.html', 'portal.css', 'portal.js']) {
|
|
69
|
+
expect(existsSync(join(TEMPLATE, f))).toBe(true)
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('keeps functions/ at the tree root', () => {
|
|
74
|
+
expect(existsSync(join(TEMPLATE, 'functions', 'api', 'auth.ts'))).toBe(true)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('sets pages_build_output_dir to the tree root', () => {
|
|
78
|
+
expect(wrangler()).toMatch(/pages_build_output_dir\s*=\s*"\."/)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// Wrangler's Pages upload ignore list is fixed (_worker.js, _redirects,
|
|
82
|
+
// _headers, _routes.json, functions, .DS_Store, node_modules, .git,
|
|
83
|
+
// .wrangler) and Pages honours no .assetsignore, so every other file in the
|
|
84
|
+
// tree is served publicly. schema.sql has no reason to be, so it lives beside
|
|
85
|
+
// template/ rather than inside it. wrangler.toml has to stay and its exposure
|
|
86
|
+
// is tracked separately.
|
|
87
|
+
it('keeps schema.sql out of the publishable tree', () => {
|
|
88
|
+
expect(existsSync(join(TEMPLATE, 'schema.sql'))).toBe(false)
|
|
89
|
+
expect(existsSync(join(SKILL, 'schema.sql'))).toBe(true)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
// Dotfiles are excluded because the ignore list already covers the ones that
|
|
93
|
+
// matter (.DS_Store, .git, .wrangler) and a stray Finder .DS_Store would
|
|
94
|
+
// otherwise fail this test for a reason it does not name.
|
|
95
|
+
it('publishes nothing beyond the site files, functions/ and wrangler.toml', () => {
|
|
96
|
+
const published = readdirSync(TEMPLATE)
|
|
97
|
+
.filter((e) => !e.startsWith('.'))
|
|
98
|
+
.sort()
|
|
99
|
+
expect(published).toEqual([
|
|
100
|
+
'functions',
|
|
101
|
+
'index.html',
|
|
102
|
+
'portal.css',
|
|
103
|
+
'portal.js',
|
|
104
|
+
'wrangler.toml',
|
|
105
|
+
])
|
|
106
|
+
})
|
|
107
|
+
})
|
|
@@ -76,12 +76,39 @@ files. The gate enforces it too, but do not enrol a name it will reject.
|
|
|
76
76
|
|
|
77
77
|
## Assemble the canonical tree
|
|
78
78
|
|
|
79
|
-
Copy `template/` to `<accountDir>/
|
|
79
|
+
Copy `template/` to `<accountDir>/sites/<project>/` and fill `wrangler.toml`'s `__PROJECT_NAME__`,
|
|
80
80
|
`__D1_DATABASE_NAME__`, `__D1_DATABASE_ID__`, and `__R2_BUCKET_NAME__`. **Leave no `__...__`
|
|
81
81
|
placeholder in the assembled tree.**
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
`sites/<project>/` is the only location the broker will publish: `resolveSiteDir` resolves
|
|
84
|
+
`<accountDir>/sites/<name>` and refuses anything else with `invalid-site`. The tree is flat because
|
|
85
|
+
the deploy publishes the directory it is handed — `index.html`, `portal.css`, `portal.js`,
|
|
86
|
+
`functions/` and `wrangler.toml` all sit at its root, with no nested asset level, and
|
|
87
|
+
`pages_build_output_dir` reads `"."` to match. A nested asset level publishes the site one level too
|
|
88
|
+
deep, which serves 404 at the domain root.
|
|
89
|
+
|
|
90
|
+
The deploy serves everything in that tree except a fixed ignore list — `functions/`, `_worker.js`,
|
|
91
|
+
`_redirects`, `_headers`, `_routes.json`, `node_modules`, `.git`, `.DS_Store`, `.wrangler` — and
|
|
92
|
+
`pages deploy` reads no `.assetsignore`, so anything else you leave there is public. Nothing else
|
|
93
|
+
belongs in it. `schema.sql` therefore lives beside `template/` in the skill, not inside it. Name the D1 database
|
|
94
|
+
with the same `bin/cf-store-name.sh` scheme as the bucket, and apply
|
|
95
|
+
`platform/plugins/cloudflare/skills/data-portal/schema.sql` to the remote database from there. It is
|
|
96
|
+
`IF NOT EXISTS` throughout, so re-applying is safe.
|
|
97
|
+
|
|
98
|
+
The copied `portal.css` carries the template's default palette, which is nobody's brand. Apply the
|
|
99
|
+
install's before handing the tree to the deploy:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
node platform/plugins/cloudflare/bin/portal-brand-css.mjs \
|
|
103
|
+
--css <accountDir>/pages/<project>/public/portal.css
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
It rewrites that stylesheet's one `:root` block from `consent.palette` in the install's brand
|
|
107
|
+
manifest and touches nothing else in the file. Do not set the colours by hand: the script validates
|
|
108
|
+
every value against the same rule the bundler applies to brand colours, refuses without writing
|
|
109
|
+
anything if the stylesheet or a value is not what it expects, and leaves the template defaults in
|
|
110
|
+
place for a brand that declares no palette — which is the case a hand-edit turns into an empty
|
|
111
|
+
declaration nobody sees until the page loads.
|
|
85
112
|
|
|
86
113
|
## Deploy through site-deploy
|
|
87
114
|
|
|
@@ -97,6 +124,14 @@ Hand the assembled tree to `cloudflare:site-deploy`. Both bindings — D1 and R2
|
|
|
97
124
|
config in `wrangler.toml`, carried in the deploy payload under the Pages scope; the deploy needs no
|
|
98
125
|
R2 token. Only the bucket *creation* in step 1 needs R2 write, and the broker holds that.
|
|
99
126
|
|
|
127
|
+
Read `site-deploy` for its auth, custom-domain and DNS guidance, but **not** for the tree location.
|
|
128
|
+
Its canonical-tree section names `<accountDir>/pages/<project>/` and tells you to reconcile stray
|
|
129
|
+
trees into it; that section describes the house path, which publishes with an explicit positional
|
|
130
|
+
output dir. A portal publishes through `storage-pages-deploy`, which accepts `<accountDir>/sites/`
|
|
131
|
+
and nothing else. Leave the tree where you assembled it. Moving it to `pages/` to satisfy that
|
|
132
|
+
section is the one way to turn a working portal into `invalid-site`. The two skills disagree on this
|
|
133
|
+
today and the disagreement is tracked, not resolved.
|
|
134
|
+
|
|
100
135
|
## Done-gate
|
|
101
136
|
|
|
102
137
|
The portal is not done until all five hold on the live domain:
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
/* Data portal styles. Plain CSS, dp- prefixed
|
|
2
|
-
|
|
1
|
+
/* Data portal styles. Plain CSS, dp- prefixed. The :root values below are
|
|
2
|
+
template defaults and are nobody's brand: the assemble step runs
|
|
3
|
+
bin/portal-brand-css.mjs, which rewrites this block from the install's
|
|
4
|
+
consent.palette. They must stay legible on their own, because a brand that
|
|
5
|
+
declares no palette keeps them. --dp-muted is not brand-carried and survives
|
|
6
|
+
on every brand. */
|
|
3
7
|
:root {
|
|
4
8
|
--dp-ink: #14181f;
|
|
5
9
|
--dp-muted: #667085;
|
|
@@ -13,6 +17,13 @@
|
|
|
13
17
|
box-sizing: border-box;
|
|
14
18
|
}
|
|
15
19
|
|
|
20
|
+
/* `hidden` is only display:none in the UA stylesheet, so any author `display`
|
|
21
|
+
beats it — .dp-login's display:flex did exactly that and left the sign-in
|
|
22
|
+
form on screen after sign-in. Keep the attribute authoritative everywhere. */
|
|
23
|
+
[hidden] {
|
|
24
|
+
display: none !important;
|
|
25
|
+
}
|
|
26
|
+
|
|
16
27
|
body {
|
|
17
28
|
margin: 0;
|
|
18
29
|
padding: 2rem 1rem;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# __D1_DATABASE_ID__ the id printed by `wrangler d1 create`
|
|
6
6
|
# __R2_BUCKET_NAME__ the R2 bucket name (<accountId>-<purpose>)
|
|
7
7
|
name = "__PROJECT_NAME__"
|
|
8
|
-
pages_build_output_dir = "
|
|
8
|
+
pages_build_output_dir = "."
|
|
9
9
|
compatibility_date = "2024-01-01"
|
|
10
10
|
compatibility_flags = ["nodejs_compat"]
|
|
11
11
|
|
|
@@ -336,6 +336,54 @@ A 0.000 reading that persists across many polls while `top` shows load
|
|
|
336
336
|
is the delta-cache regression signature (the single-flight promise was
|
|
337
337
|
not released).
|
|
338
338
|
|
|
339
|
+
## Activity view
|
|
340
|
+
|
|
341
|
+
Source:
|
|
342
|
+
[`server/routes/admin/activity.ts`](../../../ui/server/routes/admin/activity.ts)
|
|
343
|
+
(house-gated proxy) and
|
|
344
|
+
[`platform/ui/app/activity/page.tsx`](../../../ui/app/activity/page.tsx)
|
|
345
|
+
(page). CSS lives under `.activity*` in
|
|
346
|
+
[`platform/ui/app/globals.css`](../../../ui/app/globals.css).
|
|
347
|
+
|
|
348
|
+
**What it shows.** `/activity` lists every live session across every account
|
|
349
|
+
in the install, one row per session: account, title, model, status, age, idle,
|
|
350
|
+
turn count, RSS, CPU %, cumulative tokens, and imputed GBP cost. Columns sort
|
|
351
|
+
on click. The sidebar session list is scoped to the caller's own account, so
|
|
352
|
+
this is the only surface that answers "where is the activity" install-wide.
|
|
353
|
+
|
|
354
|
+
**House account only.** The manager binds loopback and gates nothing, so the
|
|
355
|
+
scoping lives on the admin route. The house is resolved by the same three-arm
|
|
356
|
+
predicate the spawn path uses (`resolveAccountDir`): exactly one `role:"house"`
|
|
357
|
+
account, else a sole account declaring no role, else deny — two houses is
|
|
358
|
+
corruption and denies rather than picking one. Both operands are null-checked,
|
|
359
|
+
because `caller === house` alone is true for two nulls. A caller pinned
|
|
360
|
+
elsewhere receives 403 and a logged `op=gate decision=deny` carrying its
|
|
361
|
+
reason. The `HeaderMenu` item is admin-variant only for the same reason.
|
|
362
|
+
|
|
363
|
+
Be precise about what this is. Under the install-wide access model every
|
|
364
|
+
authenticated install admin can re-pin to any account via
|
|
365
|
+
`POST /api/admin/session/switch-account`, so the gate is a scope selector, not
|
|
366
|
+
an authority boundary between privilege levels.
|
|
367
|
+
|
|
368
|
+
**Where the numbers come from.** The manager assembles them at its own
|
|
369
|
+
`GET /activity`, joining the live PTY set, the watcher's per-row `accountId`,
|
|
370
|
+
the JSONL enumerator (turns, model, tokens, last message), the PTY census
|
|
371
|
+
(RSS and CPU), and a bounded metering fan-out. The admin route adds the one
|
|
372
|
+
fact the manager cannot see: the `data/accounts` directory count, taken
|
|
373
|
+
independently of `listValidAccounts()` so a rejected account shows as
|
|
374
|
+
`onDisk > valid` rather than silently shortening the list.
|
|
375
|
+
|
|
376
|
+
**Reading the cells.** A dash means unmeasured, never zero — an unmeasured CPU
|
|
377
|
+
is not an idle session. Cost is the price arm matching the row's own model,
|
|
378
|
+
because the metering core prices one token count under both Opus and Sonnet
|
|
379
|
+
assumptions without knowing which ran; a model in neither family shows a dash.
|
|
380
|
+
Every dashed cost carries its reason as a tooltip, so an unpriced model, a
|
|
381
|
+
failed parse and a session with no transcript are distinguishable rather than
|
|
382
|
+
three readings of the same glyph.
|
|
383
|
+
A session whose sidecar carries no account shows `untagged` rather than being
|
|
384
|
+
folded into the house. The census age is rendered because the snapshot is a 60s
|
|
385
|
+
sample and a dead collector emits nothing.
|
|
386
|
+
|
|
339
387
|
## Sidebar surfaces
|
|
340
388
|
|
|
341
389
|
The sidebar is the entire left column of the admin UI. Its full layout,
|
|
@@ -327,7 +327,7 @@ This tool is read-only and available to both public and admin agents.
|
|
|
327
327
|
|
|
328
328
|
### When conversations are created
|
|
329
329
|
|
|
330
|
-
`:Conversation` nodes on webchat (admin login, "New conversation" in the burger, a new public visitor) are created lazily. Opening the chat or logging in does not write anything to the graph — {{productName}} only records the conversation once the user sends a second message. This keeps `conversation-search` and the Conversations modal free of one-turn abandoned threads. WhatsApp and Telegram take the opposite posture: every inbound — DM or group, allowed or activation-off, agent-invoked or gated — MERGEs the `:Conversation` and writes a forensic `:Message:WhatsAppMessage` row before any access-control decision. The graph is the durable record of every message the device received, not just the ones the agent replied to. See `.docs/web-chat.md` "Deferred conversation persistence"
|
|
330
|
+
`:Conversation` nodes on webchat (admin login, "New conversation" in the burger, a new public visitor) are created lazily. Opening the chat or logging in does not write anything to the graph — {{productName}} only records the conversation once the user sends a second message. This keeps `conversation-search` and the Conversations modal free of one-turn abandoned threads. WhatsApp and Telegram take the opposite posture: every inbound — DM or group, allowed or activation-off, agent-invoked or gated — MERGEs the `:Conversation` and writes a forensic `:Message:WhatsAppMessage` row before any access-control decision. The graph is the durable record of every message the device received, not just the ones the agent replied to. That forensic row is addressed by cacheKey (`default:<phone>`) on the socket-owning account and deliberately has no session behind it. A WhatsApp sender therefore carries a **second** `:Conversation`: the anchored row written at spawn on the account the session actually runs under, keyed by the session's own deterministic id, which is the row every `MATCH (c:Conversation {sessionId: $SESSION_ID})` resolves. Two rows, two jobs, not duplicates. See `.docs/web-chat.md` "Deferred conversation persistence", `.docs/whatsapp.md` "Session continuity", and `.docs/session-conversation-identity.md` for the per-channel identity contract.
|
|
331
331
|
|
|
332
332
|
Each row in the Conversations modal exposes a `View logs` row-action that opens a popover with three links — **Stream**, **Errors**, **SSE** — each of which targets `/api/admin/logs?type={stream|error|sse}&sessionId={full-id}` in a new tab. The row's 8-char id chip is click-to-copy; hover reveals the full `sessionId` as a tooltip. See `.docs/web-chat.md` "In-chat retrieval" for the route contract and `console.debug` observability.
|
|
333
333
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Ledger
|
|
2
|
+
|
|
3
|
+
A cash book with receivables and payables. It records what you invoiced, what you were billed, what was paid, and money that moved without an invoice, then tells you what is still outstanding.
|
|
4
|
+
|
|
5
|
+
## What it is not
|
|
6
|
+
|
|
7
|
+
It is not double-entry bookkeeping. There is no chart of accounts, no journals, no trial balance. It computes no VAT and handles a single currency. It does not read bank feeds and it does not talk to QuickBooks.
|
|
8
|
+
|
|
9
|
+
If you need statutory accounts, this is not that. It is the answer to "who owes me what, what do I owe, and where did the money go".
|
|
10
|
+
|
|
11
|
+
## The seven things you can ask for
|
|
12
|
+
|
|
13
|
+
**Record an invoice you raised.** A reference, who owes you, the amount, and when it is due.
|
|
14
|
+
|
|
15
|
+
**Record a bill you received.** The same, in the other direction.
|
|
16
|
+
|
|
17
|
+
**Record a payment.** Against either an invoice or a bill. You give the reference, the amount, the date and how it was paid.
|
|
18
|
+
|
|
19
|
+
**Record a credit.** Against an invoice you raised. It reduces what is owed exactly as a payment does.
|
|
20
|
+
|
|
21
|
+
**Record a cash movement.** Money in or out that belongs to no invoice: bank charges, a cash sale, drawings, a refund.
|
|
22
|
+
|
|
23
|
+
**Ask what is outstanding on one document.** You get the total, what has been paid, what has been credited, and what remains.
|
|
24
|
+
|
|
25
|
+
**Ask for a statement.** Everything owed to you and everything you owe, sorted into 0-30, 31-60, 61-90 and 90+ days overdue, plus net cash.
|
|
26
|
+
|
|
27
|
+
## Three behaviours worth knowing about
|
|
28
|
+
|
|
29
|
+
**What is outstanding is never stored.** It is worked out from the payments each time you ask. This means the figure cannot fall out of step with the payments behind it. If it were stored, a payment recorded by any route that forgot to update it would leave the invoice quietly reporting a wrong number, and nothing would fail.
|
|
30
|
+
|
|
31
|
+
**Paying more than is owed is refused.** If an invoice has 4,400 left and you try to record 5,000, nothing is written and you are told the figure that would settle it. It is not rounded down to 4,400 and it is not recorded anyway. A ledger that quietly accepts an impossible payment is worse than one that says no.
|
|
32
|
+
|
|
33
|
+
**Every payment is read back.** After a payment is written, the invoice is re-read from the graph and the outstanding figure is checked against what was expected. If they disagree you are told to stop recording against that invoice until it is sorted out. Writing something is not the same as it having landed.
|
|
34
|
+
|
|
35
|
+
## The health check
|
|
36
|
+
|
|
37
|
+
Some ways this can go wrong announce themselves. Others do not: a payment that ends up attached to no invoice, a stale balance field reappearing, or the same payment recorded twice under two different ids. None of those produce an error at the time, because nothing goes wrong at the moment they happen.
|
|
38
|
+
|
|
39
|
+
So there is a check that runs by itself every fifteen minutes and writes one line to the log beginning `[ledger-census]`. When it is healthy the counters are all zero. When something is wrong the line ends with `alarm=true` and says which of the four it found.
|
|
40
|
+
|
|
41
|
+
You can also ask for that check on demand at any time, and it reports the same thing in plain words.
|
|
42
|
+
|
|
43
|
+
If you never see the line at all, that is itself the problem: it means the periodic check is not running.
|
|
@@ -106,6 +106,10 @@ Email ingestion is **operator-initiated and human-in-the-loop**. There is no bac
|
|
|
106
106
|
- **Graph history** (`email-graph-query`) — stored email-thread `:ConversationArchive {source:'email'}` rows (with `:Section` chunks) in Neo4j. Use for recall, semantic search, email history questions.
|
|
107
107
|
- **General knowledge** (`memory-search`) — cross-type knowledge graph. Use when the question spans all memory, not just emails.
|
|
108
108
|
|
|
109
|
+
**Naming a mailbox.** When more than one mailbox is configured on the account, `email-read` and `email-search` refuse a call that omits `mailbox`, naming every stored identity. This is deliberate — guessing between them would silently read the wrong mailbox. Call `email-status` to list the configured identities, then pass `mailbox` on every read. A single-mailbox account needs no `mailbox` argument.
|
|
110
|
+
|
|
111
|
+
**Both readers return the newest matches.** When more messages match than `limit` admits, the newest `limit` are returned and the output names the split, e.g. `Showing the newest 20 of 33 matches in the scanned window`. `33` counts matches inside the fetched window, not the whole mailbox. Treat that line as "there is more behind this" and page back with the `next_before_uid` printed alongside it — a truncated result always carries a cursor, and that cursor is the oldest message shown, so the next page lands exactly on what was dropped rather than skipping it. A result with no truncation line showed everything that matched in its window. This is reported separately from the recipient-filter line because the two do not coincide: truncation is only reachable when a recipient filter is active (that is what widens the window past `limit`), but a filter that hides nothing prints no recipient line while still truncating.
|
|
112
|
+
|
|
109
113
|
## Skills
|
|
110
114
|
|
|
111
115
|
| Skill | Purpose |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display-truncation.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/display-truncation.test.ts"],"names":[],"mappings":""}
|