@desplega.ai/agent-swarm 2.7.5 → 2.7.7
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/{acp-adapter-g3ads55s.js → acp-adapter-7b4vk3h5.js} +5 -4
- package/dist/{actions-zwfykqqk.js → actions-3tt153af.js} +18 -17
- package/dist/{amp-adapter-z3s0ssdt.js → amp-adapter-njpts0yr.js} +1 -1
- package/dist/{anthropic-messages-x4q6rfq5.js → anthropic-messages-xey2skwx.js} +7 -7
- package/dist/{app-vmp4fgep.js → app-nky4378f.js} +3 -3
- package/dist/{assistant-vwde653f.js → assistant-jnb669pa.js} +24 -22
- package/dist/{azure-openai-responses-s1w5n70z.js → azure-openai-responses-26t7j9ra.js} +5 -5
- package/dist/{boot-reembed-375t7tbk.js → boot-reembed-9mgvaztt.js} +1 -1
- package/dist/{boot-reembed-gq9rcpnj.js → boot-reembed-qs2djdej.js} +1 -1
- package/dist/{boot-scrub-sweep-nd938kyf.js → boot-scrub-sweep-yjpdj7s1.js} +3 -3
- package/dist/{boot-seeds-aykx51v3.js → boot-seeds-5cm6ambd.js} +4 -4
- package/dist/{boxd-k1j26g3h.js → boxd-nv507va8.js} +5 -5
- package/dist/{claude-adapter-8j4hm099.js → claude-adapter-fn287zmm.js} +5 -4
- package/dist/{claude-managed-setup-0wyynpn1.js → claude-managed-setup-wtdz2mj2.js} +1 -1
- package/dist/{claude-sdk-session-q6wj5hmy.js → claude-sdk-session-x91kqyhm.js} +9677 -9514
- package/dist/{cli-j3jvnpe8.js → cli-08bmz9z9.js} +1 -1
- package/dist/{cli-gjr2y2xk.js → cli-0agcv1v5.js} +4 -1
- package/dist/{cli-y24sc29k.js → cli-1c6tzt8g.js} +30 -8
- package/dist/{cli-mjzynrse.js → cli-35rsp8ye.js} +1 -1
- package/dist/{cli-rpm2sgv0.js → cli-3fg0bdcv.js} +19 -2
- package/dist/{cli-nn4d8dne.js → cli-5b456bpf.js} +2 -2
- package/dist/{cli-bhjt02p8.js → cli-5jnhv72d.js} +1 -1
- package/dist/{cli-fqzx7gwa.js → cli-5zx699j9.js} +2 -2
- package/dist/{cli-hf6zt5m6.js → cli-69szn7w7.js} +17 -1
- package/dist/{cli-vqypxzh4.js → cli-6g0hb4cp.js} +8 -8
- package/dist/{cli-tqgg40ek.js → cli-6pfd54vx.js} +9 -8
- package/dist/{cli-bsn8nhrq.js → cli-6vcp76qk.js} +3 -3
- package/dist/{cli-zppwg9p9.js → cli-72v799xn.js} +1 -1
- package/dist/cli-82j36f2m.js +45 -0
- package/dist/{cli-hn9hxmz9.js → cli-8305gbq0.js} +29 -16
- package/dist/{cli-z8eh3ex8.js → cli-8sckt74b.js} +8 -8
- package/dist/{cli-bnz7a7qf.js → cli-95jzxcp3.js} +1 -1
- package/dist/{cli-0bxqnvq9.js → cli-adeegh7d.js} +29 -7
- package/dist/{cli-g61qxmge.js → cli-afkqkrtw.js} +3 -3
- package/dist/{cli-7a42nfy0.js → cli-ayayvfg4.js} +24 -4
- package/dist/{cli-bqt1mptt.js → cli-az48n0nr.js} +4 -4
- package/dist/{cli-z6jkx23q.js → cli-bfv7vdg6.js} +9 -9
- package/dist/{cli-pywf3eh5.js → cli-caj33vcq.js} +5 -5
- package/dist/{cli-5pag6rmn.js → cli-cb2a8pna.js} +1 -1
- package/dist/{cli-wvaawacd.js → cli-cppkf404.js} +5236 -72
- package/dist/{cli-mry0qzym.js → cli-ddaanqkd.js} +123 -10
- package/dist/{cli-0gey5fzy.js → cli-e0kmphd6.js} +2 -2
- package/dist/{cli-zh3fp8v2.js → cli-e46hg0xw.js} +1 -1
- package/dist/{cli-jm2vfpfd.js → cli-e5bzrn01.js} +1 -1
- package/dist/{cli-qxnf8z2z.js → cli-excnnjg2.js} +165 -153
- package/dist/{cli-71ptav7x.js → cli-ferkkrg9.js} +1 -1
- package/dist/{cli-vpf89f34.js → cli-fmjd0t1y.js} +3 -3
- package/dist/{cli-rm6hqj3z.js → cli-fyt2g4wd.js} +33 -139
- package/dist/cli-g08zdaec.js +55 -0
- package/dist/{cli-ehq736cn.js → cli-g7s47cc5.js} +4 -4
- package/dist/{cli-82ae98pz.js → cli-gz6q4eve.js} +3 -3
- package/dist/{cli-82jg7fp9.js → cli-he5nxaa8.js} +1 -1
- package/dist/{cli-rqkxrmgw.js → cli-je518txm.js} +39 -14
- package/dist/{cli-8hs41zy2.js → cli-jtjtabxe.js} +1 -1
- package/dist/{cli-611a1294.js → cli-k0wwfqbg.js} +18 -1
- package/dist/{cli-nzxr556s.js → cli-k1nje3v3.js} +38 -43
- package/dist/{cli-3h4nhjn8.js → cli-k8196sh2.js} +9 -7
- package/dist/{cli-afrrppsw.js → cli-k899ehvh.js} +2 -2
- package/dist/{cli-0731hvbw.js → cli-m0da8j35.js} +23 -9
- package/dist/cli-m2z8jagn.js +12078 -0
- package/dist/{cli-8veytvbh.js → cli-mkk9rnzz.js} +5 -5
- package/dist/{cli-7wspm5gm.js → cli-n6bv12jn.js} +528 -6
- package/dist/{cli-spknvxbq.js → cli-nmzhp1b5.js} +14 -12
- package/dist/{cli-p599f1qm.js → cli-p82cnnjq.js} +1 -1
- package/dist/{cli-xtjhfmpc.js → cli-pc6n7fre.js} +1 -1
- package/dist/{cli-vbyfwh92.js → cli-pjctyea3.js} +2 -2
- package/dist/{cli-nmbmy64z.js → cli-pq73h5mz.js} +3 -3
- package/dist/{cli-aa6yxc7m.js → cli-pz4rk021.js} +1 -1
- package/dist/{cli-7wcyk2s5.js → cli-q3tkegby.js} +1 -1
- package/dist/cli-qghe0h66.js +67 -0
- package/dist/{cli-6s7vdcta.js → cli-qh4d4tvk.js} +4 -4
- package/dist/{cli-mmwm7zbf.js → cli-rtt8g4v9.js} +1 -1
- package/dist/{cli-kg8hchyg.js → cli-sa02jsqt.js} +1 -1
- package/dist/{cli-1h3wcpbw.js → cli-snpj5zbj.js} +2 -2
- package/dist/{cli-a7v854rf.js → cli-tgrv9cjx.js} +263 -12
- package/dist/{cli-a47xy8eq.js → cli-vz22fyqv.js} +186 -54
- package/dist/{cli-1ayk1eas.js → cli-wk5yrfwd.js} +285 -42
- package/dist/{cli-1338796d.js → cli-ws19768n.js} +1755 -119
- package/dist/{cli-70r77n04.js → cli-xae4g51r.js} +1 -1
- package/dist/{cli-gf5hbmdr.js → cli-yjdgea32.js} +3 -3
- package/dist/{cli-xbkdm710.js → cli-yndmfxb3.js} +3 -3
- package/dist/{cli-rhw40jgb.js → cli-yyj3j5m4.js} +2 -2
- package/dist/cli.js +26 -25
- package/dist/{cloudflare-workers-ai-system-one-bm60fjm0.js → cloudflare-workers-ai-system-one-ef2a980q.js} +2 -2
- package/dist/{codex-adapter-q6k3gnyg.js → codex-adapter-cf2yxe9m.js} +3 -2
- package/dist/{codex-hook-9efm2x1m.js → codex-hook-a0c6g6t6.js} +1 -1
- package/dist/{codex-session-runner-3mwmk3d9.js → codex-session-runner-6vfzdgbd.js} +3 -2
- package/dist/{commands-evrenjn9.js → commands-t3mfy7vn.js} +9 -3
- package/dist/{controller-xjp8vswh.js → controller-q7xrbaz0.js} +8 -3
- package/dist/{controllers-connect-pp5jnxvc.js → controllers-connect-c6nm70kg.js} +20 -19
- package/dist/{cursor-adapter-yf47w4t0.js → cursor-adapter-ftd81ga8.js} +1 -1
- package/dist/{db-zxpw6aht.js → db-dk572x25.js} +3 -3
- package/dist/{db-rgfsh91f.js → db-vemtpmcc.js} +14 -4
- package/dist/{devtools-82fzqra8.js → devtools-qxfgbsqz.js} +1 -1
- package/dist/{dispatcher-smasgws7.js → dispatcher-kh7ecxh9.js} +17 -16
- package/dist/{docker-rrjhaqt9.js → docker-c1a3gqmf.js} +89 -22
- package/dist/{dsh-adapter-ftyy8dnp.js → dsh-adapter-v8628r89.js} +1 -1
- package/dist/{e2b-w1p0bnpm.js → e2b-fr12yccx.js} +5 -5
- package/dist/{e2b-qcaj675y.js → e2b-x6ywzak3.js} +3 -3
- package/dist/{easter-egg-3d-yyts7p16.js → easter-egg-3d-nenw4x63.js} +1 -1
- package/dist/{embedding-space-1z0mbg95.js → embedding-space-0jepb239.js} +6 -6
- package/dist/{embeddings-ygeg7tm7.js → embeddings-xxt49yxg.js} +1 -1
- package/dist/{execute-14qhqp4d.js → execute-1mhw6e4x.js} +1 -1
- package/dist/{external-channel-ttgrs3sx.js → external-channel-55v12e60.js} +2 -2
- package/dist/{fx-adapter-gt6pncj2.js → fx-adapter-bs8wqrk5.js} +6 -5
- package/dist/{google-generative-ai-yvmdxyxa.js → google-generative-ai-v94hr7h0.js} +5 -5
- package/dist/{google-vertex-jrtdh9gs.js → google-vertex-g7vr6cf8.js} +5 -5
- package/dist/{grok-adapter-sm4m5k4g.js → grok-adapter-gpjqmkcq.js} +6 -5
- package/dist/handlers-zxmp1ta4.js +32 -0
- package/dist/{heartbeat-qahz4p62.js → heartbeat-k42svnr0.js} +20 -19
- package/dist/{hook-4fd5wq5w.js → hook-114m7hap.js} +4 -3
- package/dist/{hook-kj3d398m.js → hook-6nphweh0.js} +24 -9
- package/dist/{http-1ted0j1k.js → http-r8cc4wz7.js} +1196 -211
- package/dist/index-a8phxqnk.js +37 -0
- package/dist/{index-q4b917zs.js → index-e3s8wmxy.js} +1 -1
- package/dist/{index-6rq3r5zb.js → index-fpk4bcd9.js} +1 -1
- package/dist/{index-5rk6rfzr.js → index-fvzke5kx.js} +1 -1
- package/dist/index-k6h00t4c.js +30 -0
- package/dist/{index-7v51awbn.js → index-nh3wyqdt.js} +1027 -577
- package/dist/{index-yqhz88yy.js → index-pxn30mse.js} +8 -8
- package/dist/index-xrz7te8h.js +33 -0
- package/dist/{keepalive-6cf311t5.js → keepalive-x060nt9p.js} +3 -3
- package/dist/lead-956sd2yc.js +71 -0
- package/dist/{maintenance-a4bm1bmt.js → maintenance-2xxfexhf.js} +1 -1
- package/dist/{mistral-conversations-4pmwj1c0.js → mistral-conversations-3gjpdb4b.js} +4 -4
- package/dist/{oauth-refresh-sweep-3h0dfw1k.js → oauth-refresh-sweep-fckc4ar5.js} +2 -2
- package/dist/{onboard-myn5hraa.js → onboard-zc48j237.js} +5 -5
- package/dist/{openai-codex-responses-44j573nm.js → openai-codex-responses-8pbxq9h5.js} +5 -5
- package/dist/{openai-completions-a4gee2a8.js → openai-completions-rm00s912.js} +4 -4
- package/dist/{openai-decisions-w4qkrcar.js → openai-decisions-8v957r2r.js} +1 -1
- package/dist/{openai-responses-rbp6h5kr.js → openai-responses-agxpf96k.js} +5 -5
- package/dist/{opencode-adapter-p2nkb5am.js → opencode-adapter-r690h1cr.js} +4 -3
- package/dist/{otel-impl-a1a0ef6b.js → otel-impl-r5xbh81q.js} +4 -4
- package/dist/{pi-mono-adapter-rzmpb72a.js → pi-mono-adapter-c7v8sdec.js} +7 -6
- package/dist/{pricing-refresh-83drrnxj.js → pricing-refresh-3pmre11v.js} +3 -3
- package/dist/{rbac-roles-s8y6ecb5.js → rbac-roles-3vbshwsb.js} +1 -1
- package/dist/{rbac-roles-0fb6e7pj.js → rbac-roles-910mmkgz.js} +2 -2
- package/dist/{render-v2-twmbgrz4.js → render-v2-pxykkbab.js} +3 -3
- package/dist/{resume-zfqc36xj.js → resume-adda0bwy.js} +6 -6
- package/dist/{runtime-qwggdpqr.js → runtime-fnbm7stk.js} +1 -1
- package/dist/{session-access-sc9pn9da.js → session-access-2fvamrdc.js} +4 -4
- package/dist/{session-source-backfill-qh07xr8g.js → session-source-backfill-08xvv0sm.js} +2 -2
- package/dist/{setup-4fr0m7a1.js → setup-1aqmvmh1.js} +3 -3
- package/dist/{typesafe-system-one-5mavm259.js → typesafe-system-one-0bmqcxfg.js} +2 -2
- package/dist/{virtual-modules-fh4ha15b.js → virtual-modules-d2pjk9j1.js} +5 -5
- package/dist/{work-objects-0wr8zxct.js → work-objects-9s0yrm5j.js} +20 -19
- package/dist/{worker-follow-up-ktyyazyp.js → worker-follow-up-3q78fqmr.js} +17 -16
- package/dist/worker-y15hy08h.js +71 -0
- package/openapi.json +289 -4
- package/package.json +9 -8
- package/src/apps/definition.ts +6 -5
- package/src/be/db/page-theme-tokens.ts +12 -0
- package/src/be/db/script-runs.ts +23 -0
- package/src/be/db/tasks/read.ts +131 -4
- package/src/be/db/tasks/write.ts +19 -0
- package/src/be/db-client.ts +114 -11
- package/src/be/db.ts +49 -6
- package/src/be/memory/providers/sqlite-store.ts +49 -12
- package/src/be/migrations/260_runtime_pool_cache.sql +20 -0
- package/src/be/migrations/262_session_visibility_indexes.sql +18 -0
- package/src/be/migrations/263_task_list_filter_index.sql +11 -0
- package/src/be/migrations/264_slack_thread_cover_index.sql +9 -0
- package/src/be/migrations/266_script_runs_source_task.sql +11 -0
- package/src/be/migrations/269_pages_design_system.sql +23 -0
- package/src/be/runtime-pools/cache-cleanup.ts +109 -0
- package/src/be/runtime-pools/db.ts +65 -6
- package/src/be/runtime-pools/remote-controller.ts +5 -0
- package/src/be/runtime-pools/supervisor.ts +33 -2
- package/src/be/scripts/typecheck.ts +22 -0
- package/src/be/seed/schedules-seeder.ts +0 -10
- package/src/be/seed-scripts/catalog/catalog-report.inline.ts +2 -0
- package/src/be/seed-scripts/catalog/catalog-report.ts +2 -0
- package/src/be/seed-scripts/catalog/memory-eval.ts +2 -0
- package/src/be/seed-scripts/catalog/swarm-update-check.ts +150 -0
- package/src/be/seed-scripts/catalog/task-failure-audit.ts +5 -1
- package/src/be/seed-scripts/index.ts +9 -0
- package/src/be/seed-skills/bundled-files.generated.json +3 -3
- package/src/be/swarm-config-guard.ts +32 -0
- package/src/be/task-auto-title-listener.ts +127 -0
- package/src/commands/controller.ts +4 -0
- package/src/commands/entrypoint-api-readiness.ts +74 -0
- package/src/commands/onboard/agent-fs-version.ts +1 -1
- package/src/commands/profile-sync.ts +151 -16
- package/src/commands/runner.ts +15 -7
- package/src/hooks/hook.ts +20 -9
- package/src/http/index.ts +4 -1
- package/src/http/list-total-cache.ts +14 -7
- package/src/http/memory.ts +38 -9
- package/src/http/pages-public.ts +93 -129
- package/src/http/pages.ts +94 -21
- package/src/http/script-runs.ts +174 -5
- package/src/http/status.ts +10 -2
- package/src/http/tasks.ts +4 -2
- package/src/pages/browser-sdk.ts +48 -21
- package/src/pages/color-literals.ts +115 -0
- package/src/pages/page-head.ts +766 -0
- package/src/pages/version.ts +3 -0
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +1 -0
- package/src/providers/codex-app-server.ts +27 -1
- package/src/providers/fx-adapter.ts +1 -1
- package/src/providers/pi-mono-extension.ts +30 -15
- package/src/runtime-providers/controller-protocol.ts +14 -1
- package/src/runtime-providers/controller.ts +5 -0
- package/src/runtime-providers/docker.ts +108 -23
- package/src/runtime-providers/types.ts +16 -0
- package/src/script-workflows/workflow-ctx.ts +8 -0
- package/src/scripts-runtime/types/stdlib.d.ts +22 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +22 -0
- package/src/server-user.ts +1 -0
- package/src/slack/commands.ts +5 -1
- package/src/slack/handlers.ts +37 -2
- package/src/slack/left-threads.ts +27 -4
- package/src/tasks/follow-up-gate.ts +318 -0
- package/src/tasks/follow-up-redelegation-guard.ts +155 -0
- package/src/tasks/worker-follow-up.ts +21 -1
- package/src/tests/claude-artifacts-read-script.test.ts +209 -0
- package/src/tests/codex-app-server.test.ts +59 -0
- package/src/tests/codex-gateway-route.test.ts +50 -1
- package/src/tests/create-page-tool.test.ts +501 -0
- package/src/tests/db-client-busy-retry.test.ts +81 -0
- package/src/tests/db-client.test.ts +21 -0
- package/src/tests/db-task-read-contract.test.ts +9 -2
- package/src/tests/db-task-write-contract.test.ts +7 -1
- package/src/tests/db-tasks-read.test.ts +21 -0
- package/src/tests/entrypoint-api-readiness.test.ts +416 -210
- package/src/tests/explicit-model-validation.test.ts +14 -8
- package/src/tests/fixtures/pages/head-v1.html +56 -0
- package/src/tests/follow-up-gate-shadow.test.ts +513 -0
- package/src/tests/follow-up-redelivery-guard.test.ts +432 -202
- package/src/tests/identity-field-budget.test.ts +1 -1
- package/src/tests/internal-ai/credentials.test.ts +62 -0
- package/src/tests/internal-ai/task-title.test.ts +283 -0
- package/src/tests/list-total-cache.test.ts +27 -2
- package/src/tests/memory-fts-populate.test.ts +11 -9
- package/src/tests/memory-retrieval-purge.test.ts +80 -0
- package/src/tests/merge-gate-detect-changes.test.ts +145 -0
- package/src/tests/migration-260-runtime-pool-cache.test.ts +118 -0
- package/src/tests/page-proxy.test.ts +1 -1
- package/src/tests/page-session-dev-cookie.test.ts +188 -0
- package/src/tests/pages-frame-ancestors.test.ts +28 -1
- package/src/tests/pages-head-v2.test.ts +534 -0
- package/src/tests/pages-http.test.ts +330 -0
- package/src/tests/pages-public-html.test.ts +43 -0
- package/src/tests/pages-storage.test.ts +182 -0
- package/src/tests/pages-versioning.test.ts +72 -0
- package/src/tests/profile-sync-stale-writeback.test.ts +334 -0
- package/src/tests/profile-sync.test.ts +11 -15
- package/src/tests/query-audit-plans.test.ts +27 -0
- package/src/tests/runtime-cache-cleanup.test.ts +367 -0
- package/src/tests/runtime-e2b-pools.test.ts +41 -0
- package/src/tests/runtime-pools-db.test.ts +98 -0
- package/src/tests/runtime-pools-http.test.ts +29 -0
- package/src/tests/runtime-providers-docker.test.ts +253 -0
- package/src/tests/runtime-remote-controller.test.ts +66 -2
- package/src/tests/runtime-supervisor.test.ts +66 -2
- package/src/tests/scheduled-task-required-params-migration.test.ts +3 -1
- package/src/tests/script-connections.test.ts +13 -0
- package/src/tests/script-runs-http.test.ts +684 -2
- package/src/tests/seed-registry.test.ts +28 -6
- package/src/tests/seed-schedules.test.ts +61 -8
- package/src/tests/seed-scripts.test.ts +168 -2
- package/src/tests/slack-leave-thread.test.ts +145 -5
- package/src/tests/slack-render-v2-query-plans.test.ts +24 -2
- package/src/tests/status.test.ts +11 -0
- package/src/tests/swarm-diff.test.ts +121 -2
- package/src/tests/task-auto-title-config-guard.test.ts +37 -0
- package/src/tests/task-auto-title-listener.test.ts +165 -0
- package/src/tests/tasks-readable-row.test.ts +155 -0
- package/src/tests/theme-import.test.ts +758 -0
- package/src/tests/themes-page-theme.test.ts +155 -0
- package/src/tests/themes-presets.test.ts +172 -0
- package/src/tests/workflow-script-principal.test.ts +2 -1
- package/src/themes/classics.ts +949 -0
- package/src/themes/import.ts +420 -0
- package/src/themes/page-fields.ts +40 -0
- package/src/themes/page-theme.ts +102 -0
- package/src/themes/presets.ts +561 -0
- package/src/themes/tokens.ts +218 -0
- package/src/tools/app-upsert.ts +1 -1
- package/src/tools/create-page.ts +118 -12
- package/src/tools/send-task.ts +35 -47
- package/src/tools/slack-leave-thread.ts +1 -1
- package/src/tools/task-dedup.ts +45 -21
- package/src/tools/utils.ts +33 -0
- package/src/types.ts +83 -3
- package/src/utils/constants.ts +19 -0
- package/src/utils/follow-up-gate-schedule-ids.ts +13 -0
- package/src/utils/internal-ai/credentials.ts +146 -93
- package/src/utils/internal-ai/index.ts +10 -0
- package/src/utils/internal-ai/models.ts +18 -0
- package/src/utils/internal-ai/task-title.ts +282 -0
- package/src/utils/page-session.ts +37 -0
- package/templates/schedules/daily-status-report/content.md +88 -2
- package/templates/schedules/daily-swarm-update-check/content.md +16 -40
- package/templates/skills/apps/SKILL.md +1 -1
- package/templates/skills/apps/content.md +1 -1
- package/templates/skills/composio-google-docs/content.md +10 -0
- package/templates/skills/heartbeat-runbook/content.md +1 -1
- package/templates/skills/pages/config.json +1 -1
- package/templates/skills/pages/content.md +539 -86
- package/templates/skills/pages/files/examples/annotated-pr.html +21 -12
- package/templates/skills/pages/files/examples/multiplayer-board.html +41 -41
- package/templates/skills/pages/files/examples/report-page.html +125 -124
- package/templates/skills/script-workflows/content.md +19 -1
- package/templates/skills/swarm-docs/content.md +1 -0
- package/templates/skills/swarm-scripts/SKILL.md +2 -0
- package/templates/skills/swarm-scripts/content.md +2 -0
- package/dist/cli-2pnswgh8.js +0 -189
- package/dist/cli-dznrgaep.js +0 -30
- package/dist/cli-w3hcrxht.js +0 -6985
- package/dist/handlers-vwmba1m2.js +0 -31
- package/dist/index-5r71rf98.js +0 -32
- package/dist/index-anmwxs87.js +0 -29
- package/dist/index-spp0ra5c.js +0 -36
- package/dist/lead-fjy418cw.js +0 -69
- package/dist/worker-0c5tyzqe.js +0 -69
- package/templates/schedules/daily-workflow-health-audit/config.json +0 -19
- package/templates/schedules/daily-workflow-health-audit/content.md +0 -191
package/src/http/pages-public.ts
CHANGED
|
@@ -25,15 +25,26 @@ import type { IncomingMessage, ServerResponse } from "node:http";
|
|
|
25
25
|
import { z } from "zod";
|
|
26
26
|
import { getPage, incrementPageViewCount } from "../be/db";
|
|
27
27
|
import { BROWSER_SDK_JS, SWARM_UI_JS } from "../pages/browser-sdk";
|
|
28
|
+
import { COLOR_LITERAL_THRESHOLD, countColorLiterals } from "../pages/color-literals";
|
|
28
29
|
import {
|
|
29
30
|
feedbackKeyGuardScript,
|
|
30
31
|
feedbackOverlayScripts,
|
|
31
32
|
isPageFeedbackRequested,
|
|
32
33
|
} from "../pages/feedback-overlay";
|
|
34
|
+
import {
|
|
35
|
+
buildPageHeadV2,
|
|
36
|
+
PAGE_HEAD_V1,
|
|
37
|
+
PAGE_MODE_PARAM,
|
|
38
|
+
PAGE_THEME_PARAM,
|
|
39
|
+
} from "../pages/page-head";
|
|
40
|
+
import { resolvePageTheme } from "../themes/page-theme";
|
|
33
41
|
import { type Page, PageSchema } from "../types";
|
|
34
|
-
import { getAppUrl, getConfiguredAppUrls } from "../utils/constants";
|
|
35
|
-
import {
|
|
36
|
-
|
|
42
|
+
import { getAppUrl, getConfiguredAppUrls, getLocalDevAppOrigins } from "../utils/constants";
|
|
43
|
+
import {
|
|
44
|
+
extractAndVerifyCookie,
|
|
45
|
+
isLocalDevPageRequest,
|
|
46
|
+
pageSessionSetCookies,
|
|
47
|
+
} from "../utils/page-session";
|
|
37
48
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
38
49
|
import { route } from "./route-def";
|
|
39
50
|
|
|
@@ -95,96 +106,76 @@ const publicPageJsonRoute = route({
|
|
|
95
106
|
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
96
107
|
|
|
97
108
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* open in the parent window — avoids the user being trapped inside an
|
|
106
|
-
* iframe by a misbehaving page.
|
|
109
|
+
* The `<head>` defaults for a page's design version (`src/pages/page-head.ts`).
|
|
110
|
+
* Version 1 is the frozen original head and ignores the viewer params. Version
|
|
111
|
+
* 2 resolves the theme: the page `theme`, else the viewer's `__swarm-theme`
|
|
112
|
+
* preset, else Hive, plus the page `themeTokens` on top. It is dark only for
|
|
113
|
+
* `__swarm-mode=dark`, and a `?print=1` export is always light. A body with
|
|
114
|
+
* `COLOR_LITERAL_THRESHOLD` or more hardcoded colors also stays light: its
|
|
115
|
+
* hardcoded light surfaces would get the dark-mode text color.
|
|
107
116
|
*/
|
|
117
|
+
function pageHead(page: Page, queryParams: URLSearchParams, wantsPrint: boolean): string {
|
|
118
|
+
if (page.designVersion === 1) return PAGE_HEAD_V1;
|
|
119
|
+
const theme = resolvePageTheme({
|
|
120
|
+
pageTheme: page.theme,
|
|
121
|
+
viewerTheme: queryParams.get(PAGE_THEME_PARAM),
|
|
122
|
+
overrides: page.themeTokens,
|
|
123
|
+
});
|
|
124
|
+
const dark =
|
|
125
|
+
!wantsPrint &&
|
|
126
|
+
queryParams.get(PAGE_MODE_PARAM) === "dark" &&
|
|
127
|
+
countColorLiterals(page.body) < COLOR_LITERAL_THRESHOLD;
|
|
128
|
+
return buildPageHeadV2({ theme, mode: dark ? "dark" : "light" });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** A doctype at the start of a body (`\s` also matches a BOM). */
|
|
132
|
+
const LEADING_DOCTYPE_RE = /^\s*<!doctype\b[^>]*>/i;
|
|
133
|
+
/** The `<html ...>` start tag right after a leading doctype. */
|
|
134
|
+
const LEADING_HTML_TAG_RE = /^\s*<!doctype\b[^>]*>\s*<html\b[^>]*>/i;
|
|
135
|
+
|
|
108
136
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
137
|
+
* Inject the head defaults and the BROWSER_SDK script tag into an HTML body,
|
|
138
|
+
* right after the first `<head ...>` tag. The script is wrapped in
|
|
139
|
+
* `<script>...</script>` with no token injection (the SDK relies on
|
|
140
|
+
* server-side header injection at the `/@swarm/api/*` proxy boundary).
|
|
141
|
+
*
|
|
142
|
+
* A body without `<head>` (a raw fragment) still gets the SDK:
|
|
143
|
+
* - Design version 1 prepends it, as it always did.
|
|
144
|
+
* - Design version 2 (`standardsMode`) keeps the browser in standards mode,
|
|
145
|
+
* because its defaults are written for it. It prepends `<!doctype html>`
|
|
146
|
+
* when the body has no doctype, then injects after `<html ...>`, else after
|
|
147
|
+
* the doctype. Content before the doctype puts the page in quirks mode.
|
|
115
148
|
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
149
|
+
* Both head versions start with `<base target="_blank">`, so links inside the
|
|
150
|
+
* iframed page open in a new tab: a misbehaving page cannot trap the user
|
|
151
|
+
* inside the iframe.
|
|
119
152
|
*/
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
font-family: "Space Grotesk", system-ui, sans-serif;
|
|
147
|
-
margin: 0;
|
|
148
|
-
padding: 24px;
|
|
149
|
-
line-height: 1.5;
|
|
150
|
-
}
|
|
151
|
-
code, pre, kbd, samp { font-family: "Space Mono", ui-monospace, monospace; }
|
|
152
|
-
a { color: var(--swarm-primary); }
|
|
153
|
-
::selection { background: var(--swarm-primary); color: #fff; }
|
|
154
|
-
@media print {
|
|
155
|
-
/* Override theme variables so built-in primitives (swarm-diff, swarm-card)
|
|
156
|
-
that read var(--swarm-card) / var(--swarm-border) etc. inline-styled
|
|
157
|
-
backgrounds also flip to light. Without this, diff cards stay dark navy
|
|
158
|
-
on print. */
|
|
159
|
-
:root {
|
|
160
|
-
--swarm-bg: #ffffff;
|
|
161
|
-
--swarm-card: #ffffff;
|
|
162
|
-
--swarm-border: #cccccc;
|
|
163
|
-
--swarm-text: #000000;
|
|
164
|
-
--swarm-muted: #555555;
|
|
165
|
-
}
|
|
166
|
-
html, body { background: white !important; color: black !important; }
|
|
167
|
-
a { color: black !important; text-decoration: underline; }
|
|
168
|
-
/* Hide any swarm chrome the agent (or built-in primitives) tagged with
|
|
169
|
-
.no-print. Use this class on annotation badges, jump-list nav, anything
|
|
170
|
-
that shouldn't appear in the PDF export. */
|
|
171
|
-
.no-print { display: none !important; }
|
|
172
|
-
/* Avoid page-break inside cards / diff blocks. */
|
|
173
|
-
.swarm-card, swarm-diff { break-inside: avoid; }
|
|
174
|
-
}
|
|
175
|
-
</style>`;
|
|
176
|
-
|
|
177
|
-
function injectBrowserSdk(html: string, headFirst = ""): string {
|
|
178
|
-
const injection = `${headFirst}${PAGE_HEAD_DEFAULTS}<script>${BROWSER_SDK_JS}</script><script>${SWARM_UI_JS}</script>`;
|
|
179
|
-
// Use the first occurrence of `<head>` (case-insensitive). A page that
|
|
180
|
-
// doesn't have a `<head>` element (raw fragment) still gets the SDK at the
|
|
181
|
-
// front of the document.
|
|
182
|
-
const headOpenMatch = html.match(/<head\b[^>]*>/i);
|
|
183
|
-
if (headOpenMatch) {
|
|
184
|
-
const idx = headOpenMatch.index! + headOpenMatch[0].length;
|
|
185
|
-
return html.slice(0, idx) + injection + html.slice(idx);
|
|
186
|
-
}
|
|
187
|
-
return injection + html;
|
|
153
|
+
function injectBrowserSdk(
|
|
154
|
+
html: string,
|
|
155
|
+
{ head, headFirst, standardsMode }: { head: string; headFirst: string; standardsMode: boolean },
|
|
156
|
+
): string {
|
|
157
|
+
const injection = `${headFirst}${head}<script>${BROWSER_SDK_JS}</script><script>${SWARM_UI_JS}</script>`;
|
|
158
|
+
const doc = standardsMode && !LEADING_DOCTYPE_RE.test(html) ? `<!doctype html>${html}` : html;
|
|
159
|
+
// The first `<head>` (case-insensitive) wins.
|
|
160
|
+
const leading = standardsMode
|
|
161
|
+
? (LEADING_HTML_TAG_RE.exec(doc) ?? LEADING_DOCTYPE_RE.exec(doc))
|
|
162
|
+
: null;
|
|
163
|
+
const idx = headTagEnd(doc) ?? (leading ? leading.index + leading[0].length : null);
|
|
164
|
+
if (idx === null) return injection + doc;
|
|
165
|
+
return doc.slice(0, idx) + injection + doc.slice(idx);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* End offset of the first `<head ...>` start tag, or null. It finds the same
|
|
170
|
+
* tag as `/<head\b[^>]*>/i` in linear time. That regex rescans the rest of the
|
|
171
|
+
* body for every `<head` that has no `>` after it, so a planted body could
|
|
172
|
+
* block the API on each view.
|
|
173
|
+
*/
|
|
174
|
+
function headTagEnd(doc: string): number | null {
|
|
175
|
+
const start = doc.search(/<head\b/i);
|
|
176
|
+
if (start === -1) return null;
|
|
177
|
+
const close = doc.indexOf(">", start);
|
|
178
|
+
return close === -1 ? null : close + 1;
|
|
188
179
|
}
|
|
189
180
|
|
|
190
181
|
/**
|
|
@@ -350,24 +341,16 @@ function buildCsp(page: Page): string {
|
|
|
350
341
|
// `frame-ancestors` lists every origin allowed to iframe `/p/:id`. We must
|
|
351
342
|
// include the SPA origin(s). Configured app URLs may be comma-separated so
|
|
352
343
|
// portless dev (`https://ui.swarm.localhost`), a Vite port (`http://localhost:5274`),
|
|
353
|
-
// and a tunnel/staging origin can all coexist.
|
|
354
|
-
// we
|
|
355
|
-
//
|
|
344
|
+
// and a tunnel/staging origin can all coexist. Under an explicit
|
|
345
|
+
// development/test NODE_ENV we also allow the local Vite ports and
|
|
346
|
+
// `*.localhost` so swapping between them doesn't require restarting the API.
|
|
347
|
+
// Unset NODE_ENV (every deployed image) gets no localhost ancestors.
|
|
356
348
|
const configured = getConfiguredAppUrls();
|
|
357
|
-
const devFallbacks =
|
|
358
|
-
getNodeEnv() === "production"
|
|
359
|
-
? []
|
|
360
|
-
: [
|
|
361
|
-
"http://localhost:5274",
|
|
362
|
-
"http://localhost:5175",
|
|
363
|
-
"http://127.0.0.1:5274",
|
|
364
|
-
"http://127.0.0.1:5175",
|
|
365
|
-
"https://*.localhost",
|
|
366
|
-
"http://*.localhost",
|
|
367
|
-
];
|
|
349
|
+
const devFallbacks = getLocalDevAppOrigins();
|
|
368
350
|
const embedders = page.authMode === "public" ? getPageFrameAncestors() : [];
|
|
369
351
|
const ancestors = Array.from(new Set([...configured, ...devFallbacks, ...embedders]));
|
|
370
|
-
// Allow Tailwind Play CDN (`cdn.tailwindcss.com
|
|
352
|
+
// Allow Tailwind Play CDN (`cdn.tailwindcss.com`, design version 1; the
|
|
353
|
+
// pinned v4 build of design version 2 comes from jsdelivr) for scripts, Google
|
|
371
354
|
// Fonts (`fonts.googleapis.com` stylesheets + `fonts.gstatic.com` font
|
|
372
355
|
// files) for the swarm default typography, and same-origin /@swarm/api/*
|
|
373
356
|
// for the Browser SDK. Inline scripts/styles remain allowed so
|
|
@@ -471,29 +454,6 @@ function extractPasswordCandidate(
|
|
|
471
454
|
return decoded.slice(colonIdx + 1);
|
|
472
455
|
}
|
|
473
456
|
|
|
474
|
-
/**
|
|
475
|
-
* Detect whether the originating request is from a "dev" / localhost context,
|
|
476
|
-
* mirroring the same logic used by the bearer-launch endpoint in
|
|
477
|
-
* `src/http/pages.ts`. Used to decide whether to issue cookies with
|
|
478
|
-
* `SameSite=Lax` (dev) vs `SameSite=None; Secure` (prod).
|
|
479
|
-
*/
|
|
480
|
-
function isLocalhostRequest(req: IncomingMessage): boolean {
|
|
481
|
-
if (getNodeEnv() === "production") return false;
|
|
482
|
-
// Only emit `SameSite=Lax` (no Secure) when the request comes from the
|
|
483
|
-
// SAME http://localhost origin as the API — Lax cookies don't travel on
|
|
484
|
-
// cross-site fetches, so portless `*.localhost` setups (SPA on https
|
|
485
|
-
// talking to the API on http) must use `SameSite=None; Secure`. Chrome
|
|
486
|
-
// treats localhost as a secure origin so Secure is honored on HTTP.
|
|
487
|
-
const origin = (req.headers.origin as string | undefined) ?? "";
|
|
488
|
-
if (origin === "") {
|
|
489
|
-
const rawHost = req.headers.host;
|
|
490
|
-
const host = Array.isArray(rawHost) ? rawHost[0] : rawHost;
|
|
491
|
-
if (!host) return true; // best-effort dev default
|
|
492
|
-
return host.startsWith("localhost") || host.startsWith("127.0.0.1");
|
|
493
|
-
}
|
|
494
|
-
return origin.startsWith("http://localhost") || origin.startsWith("http://127.0.0.1");
|
|
495
|
-
}
|
|
496
|
-
|
|
497
457
|
// ─── Handler ────────────────────────────────────────────────────────────────
|
|
498
458
|
|
|
499
459
|
export async function handlePagesPublic(
|
|
@@ -562,7 +522,7 @@ export async function handlePagesPublic(
|
|
|
562
522
|
}
|
|
563
523
|
if (matched) {
|
|
564
524
|
inlineSetCookie = await pageSessionSetCookies(req, page.id, {
|
|
565
|
-
dev:
|
|
525
|
+
dev: isLocalDevPageRequest(req),
|
|
566
526
|
});
|
|
567
527
|
// fall through to the regular 200 path below.
|
|
568
528
|
} else {
|
|
@@ -669,7 +629,11 @@ export async function handlePagesPublic(
|
|
|
669
629
|
// The feedback key guard goes first in <head> so it runs before page scripts.
|
|
670
630
|
const feedback = isPageFeedbackRequested(queryParams);
|
|
671
631
|
const html =
|
|
672
|
-
injectBrowserSdk(page.body,
|
|
632
|
+
injectBrowserSdk(page.body, {
|
|
633
|
+
head: pageHead(page, queryParams, wantsPrint),
|
|
634
|
+
headFirst: feedback ? feedbackKeyGuardScript() : "",
|
|
635
|
+
standardsMode: page.designVersion !== 1,
|
|
636
|
+
}) +
|
|
673
637
|
(wantsPrint ? PRINT_AUTOTRIGGER_SCRIPT : "") +
|
|
674
638
|
(feedback ? feedbackOverlayScripts(page.id) : "");
|
|
675
639
|
const headers: Record<string, string | string[]> = {
|
package/src/http/pages.ts
CHANGED
|
@@ -25,6 +25,8 @@ import { resolveTemplate } from "../prompts/resolver";
|
|
|
25
25
|
// Side-effect import: registers `task.page.feedback`.
|
|
26
26
|
import "../prompts/session-templates";
|
|
27
27
|
import { createTaskWithSiblingAwareness } from "../tasks/sibling-awareness";
|
|
28
|
+
import { readThemeFields } from "../themes/page-fields";
|
|
29
|
+
import { THEME_PRESET_IDS } from "../themes/presets";
|
|
28
30
|
import {
|
|
29
31
|
AssetKeySchema,
|
|
30
32
|
type Page,
|
|
@@ -32,11 +34,11 @@ import {
|
|
|
32
34
|
PageContentTypeSchema,
|
|
33
35
|
PageSchema,
|
|
34
36
|
type PageSummary,
|
|
37
|
+
PageThemeTokensInputSchema,
|
|
35
38
|
PageVersionSchema,
|
|
36
39
|
} from "../types";
|
|
37
40
|
import { getAppUrl, getPublicMcpBaseUrl } from "../utils/constants";
|
|
38
|
-
import {
|
|
39
|
-
import { pageSessionSetCookies } from "../utils/page-session";
|
|
41
|
+
import { isLocalDevPageRequest, pageSessionSetCookies } from "../utils/page-session";
|
|
40
42
|
import { authUser, getRequestAuth, isOperatorAuth } from "../utils/request-auth-context";
|
|
41
43
|
import { resolveHttpFavoriteOwner } from "./favorite-owner";
|
|
42
44
|
import { route } from "./route-def";
|
|
@@ -94,12 +96,43 @@ const PageWithShareUrlsSchema = PageSchema.extend({
|
|
|
94
96
|
* used, which omits them entirely) — modeled as optional rather than a union
|
|
95
97
|
* since both shapes share every other field verbatim.
|
|
96
98
|
*/
|
|
97
|
-
const PageListItemSchema = PageSchema.partial({
|
|
99
|
+
const PageListItemSchema = PageSchema.partial({
|
|
100
|
+
body: true,
|
|
101
|
+
passwordHash: true,
|
|
102
|
+
theme: true,
|
|
103
|
+
themeTokens: true,
|
|
104
|
+
designVersion: true,
|
|
105
|
+
}).extend({
|
|
98
106
|
favorite: z.boolean(),
|
|
99
107
|
api_url: z.string(),
|
|
100
108
|
app_url: z.string(),
|
|
101
109
|
});
|
|
102
110
|
|
|
111
|
+
/** Design fields shared by the create and update bodies. */
|
|
112
|
+
const pageDesignBodyFields = {
|
|
113
|
+
theme: z
|
|
114
|
+
.string()
|
|
115
|
+
.nullable()
|
|
116
|
+
.optional()
|
|
117
|
+
.describe(
|
|
118
|
+
`Theme preset id that restyles the whole page. One of: ${THEME_PRESET_IDS.join(", ")}. Omit to follow the viewer's theme. Pass null on update to clear it.`,
|
|
119
|
+
),
|
|
120
|
+
themeTokens: PageThemeTokensInputSchema.nullable()
|
|
121
|
+
.optional()
|
|
122
|
+
.describe(
|
|
123
|
+
'Per-page token overrides, applied on top of the preset. Send { light, dark } maps of token name to CSS value, or { format: "css", css } with a CSS variable block (:root, .dark, @theme). Stored normalized. Names outside the token contract, and values that reference a variable outside it, are ignored and listed in ignoredTokens. Replaces the stored overrides. Pass null on update to clear them.',
|
|
124
|
+
),
|
|
125
|
+
designVersion: z
|
|
126
|
+
.number()
|
|
127
|
+
.int()
|
|
128
|
+
.min(1)
|
|
129
|
+
.max(2)
|
|
130
|
+
.optional()
|
|
131
|
+
.describe(
|
|
132
|
+
"Design system version. 2 is the swarm design system and the default for new pages. 1 is the original look that older pages keep. Omit on update to keep the stored value.",
|
|
133
|
+
),
|
|
134
|
+
};
|
|
135
|
+
|
|
103
136
|
// ─── Route Definitions ──────────────────────────────────────────────────────
|
|
104
137
|
|
|
105
138
|
const createPageRoute = route({
|
|
@@ -118,6 +151,7 @@ const createPageRoute = route({
|
|
|
118
151
|
password: z.string().min(1).optional(),
|
|
119
152
|
body: z.string(),
|
|
120
153
|
needsCredentials: z.array(z.string()).optional(),
|
|
154
|
+
...pageDesignBodyFields,
|
|
121
155
|
}),
|
|
122
156
|
responses: {
|
|
123
157
|
201: {
|
|
@@ -128,9 +162,20 @@ const createPageRoute = route({
|
|
|
128
162
|
version: z.literal(1),
|
|
129
163
|
api_url: z.string(),
|
|
130
164
|
app_url: z.string(),
|
|
165
|
+
ignoredTokens: z
|
|
166
|
+
.array(z.string())
|
|
167
|
+
.optional()
|
|
168
|
+
.describe(
|
|
169
|
+
"Theme tokens the importer ignored. A bare name is outside the token contract. Other entries give the reason, for example --font-sans (references --font-geist-sans) or --ring (in .card). Absent when none were ignored.",
|
|
170
|
+
),
|
|
171
|
+
designVersion: z
|
|
172
|
+
.number()
|
|
173
|
+
.int()
|
|
174
|
+
.min(1)
|
|
175
|
+
.describe("The design version of the new page: 2 unless the body set designVersion: 1."),
|
|
131
176
|
}),
|
|
132
177
|
},
|
|
133
|
-
400: { description: "Invalid body" },
|
|
178
|
+
400: { description: "Invalid body, unknown theme, or invalid themeTokens" },
|
|
134
179
|
409: { description: "Slug already exists for this agent" },
|
|
135
180
|
},
|
|
136
181
|
});
|
|
@@ -214,12 +259,31 @@ const updatePageRoute = route({
|
|
|
214
259
|
password: z.string().min(1).nullable().optional(),
|
|
215
260
|
body: z.string().optional(),
|
|
216
261
|
needsCredentials: z.array(z.string()).nullable().optional(),
|
|
262
|
+
...pageDesignBodyFields,
|
|
217
263
|
}),
|
|
218
264
|
responses: {
|
|
219
265
|
200: {
|
|
220
266
|
description: "Page updated",
|
|
221
|
-
schema: z.object({
|
|
267
|
+
schema: z.object({
|
|
268
|
+
id: z.string(),
|
|
269
|
+
key: AssetKeySchema,
|
|
270
|
+
version: z.number().int().min(1),
|
|
271
|
+
ignoredTokens: z
|
|
272
|
+
.array(z.string())
|
|
273
|
+
.optional()
|
|
274
|
+
.describe(
|
|
275
|
+
"Theme tokens the importer ignored. A bare name is outside the token contract. Other entries give the reason, for example --font-sans (references --font-geist-sans) or --ring (in .card). Absent when none were ignored.",
|
|
276
|
+
),
|
|
277
|
+
designVersion: z
|
|
278
|
+
.number()
|
|
279
|
+
.int()
|
|
280
|
+
.min(1)
|
|
281
|
+
.describe(
|
|
282
|
+
"The design version the page has after the update. An omitted designVersion keeps the stored one, so an older page stays on 1.",
|
|
283
|
+
),
|
|
284
|
+
}),
|
|
222
285
|
},
|
|
286
|
+
400: { description: "Unknown theme or invalid themeTokens" },
|
|
223
287
|
404: { description: "Page not found" },
|
|
224
288
|
413: { description: "Payload too large" },
|
|
225
289
|
},
|
|
@@ -429,7 +493,7 @@ const getPageVersionRoute = route({
|
|
|
429
493
|
* Cookie issuance moved to `src/utils/page-session.ts::issuePageSessionCookie`
|
|
430
494
|
* so the password-flow on `/p/:id` (step-5) can mint cookies via the same
|
|
431
495
|
* helper. `dev=true` softens the cookie for `http://localhost` (no Secure
|
|
432
|
-
* required; SameSite=Lax). Detected by `
|
|
496
|
+
* required; SameSite=Lax). Detected by `isLocalDevPageRequest()` in `src/utils/page-session.ts`.
|
|
433
497
|
*/
|
|
434
498
|
|
|
435
499
|
/**
|
|
@@ -528,20 +592,6 @@ function formatFeedbackComments(comments: z.infer<typeof PageFeedbackCommentSche
|
|
|
528
592
|
.join("\n\n");
|
|
529
593
|
}
|
|
530
594
|
|
|
531
|
-
function isDevRequest(req: IncomingMessage): boolean {
|
|
532
|
-
if (getNodeEnv() === "production") return false;
|
|
533
|
-
// We want `SameSite=Lax` only when the request itself comes from the same
|
|
534
|
-
// local-`http://localhost` origin as the API — same-site loads tolerate
|
|
535
|
-
// Lax without Secure. Anything else (including portless `*.localhost`
|
|
536
|
-
// setups talking from HTTPS to the HTTP API) must use `SameSite=None;
|
|
537
|
-
// Secure` so the cookie travels on cross-site fetches; Chrome treats
|
|
538
|
-
// localhost as a secure origin so the Secure flag is fine on HTTP.
|
|
539
|
-
const origin = (req.headers.origin as string | undefined) ?? "";
|
|
540
|
-
return (
|
|
541
|
-
origin === "" || origin.startsWith("http://localhost") || origin.startsWith("http://127.0.0.1")
|
|
542
|
-
);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
595
|
// ─── Handler ────────────────────────────────────────────────────────────────
|
|
546
596
|
|
|
547
597
|
export async function handlePages(
|
|
@@ -562,6 +612,12 @@ export async function handlePages(
|
|
|
562
612
|
return true;
|
|
563
613
|
}
|
|
564
614
|
|
|
615
|
+
const themeFields = readThemeFields(parsed.body);
|
|
616
|
+
if ("error" in themeFields) {
|
|
617
|
+
jsonError(res, themeFields.error, 400);
|
|
618
|
+
return true;
|
|
619
|
+
}
|
|
620
|
+
|
|
565
621
|
const slug = parsed.body.slug ?? slugify(parsed.body.title);
|
|
566
622
|
|
|
567
623
|
// Hash password if provided. Bun.password.hash is async (Argon2 by default;
|
|
@@ -589,6 +645,9 @@ export async function handlePages(
|
|
|
589
645
|
passwordHash,
|
|
590
646
|
body: parsed.body.body,
|
|
591
647
|
needsCredentials: parsed.body.needsCredentials,
|
|
648
|
+
theme: themeFields.theme,
|
|
649
|
+
themeTokens: themeFields.themeTokens,
|
|
650
|
+
designVersion: parsed.body.designVersion,
|
|
592
651
|
});
|
|
593
652
|
// First write has no prior snapshot — version 1 is implicit (the parent
|
|
594
653
|
// IS v1). Subsequent edits land via PUT and bump the counter.
|
|
@@ -598,6 +657,8 @@ export async function handlePages(
|
|
|
598
657
|
version: 1,
|
|
599
658
|
api_url: `${getApiBaseUrl()}/p/${page.id}`,
|
|
600
659
|
app_url: `${getAppBaseUrl()}/pages/${page.slug}`,
|
|
660
|
+
...(themeFields.ignored.length > 0 ? { ignoredTokens: themeFields.ignored } : {}),
|
|
661
|
+
designVersion: page.designVersion,
|
|
601
662
|
});
|
|
602
663
|
} catch (err) {
|
|
603
664
|
if (err instanceof AssetKeyAuthorizationError) {
|
|
@@ -790,6 +851,13 @@ export async function handlePages(
|
|
|
790
851
|
return true;
|
|
791
852
|
}
|
|
792
853
|
|
|
854
|
+
// Before the snapshot: a rejected theme must not leave a version row behind.
|
|
855
|
+
const themeFields = readThemeFields(parsed.body);
|
|
856
|
+
if ("error" in themeFields) {
|
|
857
|
+
jsonError(res, themeFields.error, 400);
|
|
858
|
+
return true;
|
|
859
|
+
}
|
|
860
|
+
|
|
793
861
|
// Hash password if a new one was provided. `null` → clear the hash.
|
|
794
862
|
let passwordHashUpdate: string | null | undefined;
|
|
795
863
|
if (parsed.body.password === null) {
|
|
@@ -830,6 +898,9 @@ export async function handlePages(
|
|
|
830
898
|
passwordHash: passwordHashUpdate,
|
|
831
899
|
body: parsed.body.body,
|
|
832
900
|
needsCredentials: parsed.body.needsCredentials ?? undefined,
|
|
901
|
+
theme: themeFields.theme,
|
|
902
|
+
themeTokens: themeFields.themeTokens,
|
|
903
|
+
designVersion: parsed.body.designVersion,
|
|
833
904
|
});
|
|
834
905
|
if (!updated) {
|
|
835
906
|
res.writeHead(404);
|
|
@@ -840,6 +911,8 @@ export async function handlePages(
|
|
|
840
911
|
id: updated.id,
|
|
841
912
|
key: updated.key,
|
|
842
913
|
version: await getPageEditCounter(updated.id),
|
|
914
|
+
...(themeFields.ignored.length > 0 ? { ignoredTokens: themeFields.ignored } : {}),
|
|
915
|
+
designVersion: updated.designVersion,
|
|
843
916
|
});
|
|
844
917
|
return true;
|
|
845
918
|
}
|
|
@@ -988,7 +1061,7 @@ export async function handlePages(
|
|
|
988
1061
|
? { name: `guest-${randomUUID().slice(0, 8)}`, op: true as const }
|
|
989
1062
|
: undefined;
|
|
990
1063
|
const cookies = await pageSessionSetCookies(req, page.id, {
|
|
991
|
-
dev:
|
|
1064
|
+
dev: isLocalDevPageRequest(req),
|
|
992
1065
|
...identity,
|
|
993
1066
|
});
|
|
994
1067
|
|