@topy-ai/maggie 0.7.37 → 0.7.38
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/README-zh-TW.md +11 -4
- package/README.md +17 -6
- package/bundled-contracts/maggiedash/README.md +1 -1
- package/bundled-contracts/maggiedash/booking-host-adapter-v1.json +3 -1
- package/bundled-contracts/maggiedash/execution-board.json +12 -11
- package/bundled-skills/maggie-booking/SKILL.md +9 -7
- package/bundled-tools/clis/maggie_booking.py +70 -5
- package/bundled-tools/clis/maggie_dash.py +6 -0
- package/package.json +1 -1
package/README-zh-TW.md
CHANGED
|
@@ -8,7 +8,7 @@ Codex、Claude Code 與相容的 coding agents。
|
|
|
8
8
|
## 安裝
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
npx @topy-ai/maggie@0.7.
|
|
11
|
+
npx @topy-ai/maggie@0.7.38 init --agent all
|
|
12
12
|
npx @topy-ai/maggie doctor --project .
|
|
13
13
|
```
|
|
14
14
|
|
|
@@ -25,7 +25,7 @@ maggie doctor --project . --require-bootstrap --strict
|
|
|
25
25
|
deployment、memory、feedback 和 MaggieDash。內容先 draft/review,外部寫入、
|
|
26
26
|
publish 與 production deployment 需要明確確認。
|
|
27
27
|
|
|
28
|
-
目前 release 是 `0.7.
|
|
28
|
+
目前 release 是 `0.7.38`,加入可安裝的 `maggie-booking` workflow:MaggieDash Booking host bootstrap、傳統 email/password 團隊登入與 granular permissions、Stripe payment boundary、Resend outbox/template 管理、public customer booking flow、runtime/ops evidence checks,以及完整的 Booking 維護文件。全新的 Astro host 會自動補上可攜的 Node SSR adapter;已有 Cloudflare、Vercel、Netlify 或 Deno adapter 的專案會保留原本的部署選擇。Production activation 仍會 fail-closed,直到 host migration、owner、Stripe、scheduler 和 authenticated runtime evidence 都完成。上一版 `0.7.36` 完成下一批已驗證 feedback:修正 doctor placeholder scope、Tailwind renderable source scope、internal link/fragment audit、unstable baseline classification、site-wide baseline reason、base64url asset hash、installed-file hash update safety,並加入 provider-routed Gemini v2、venue timezone/DST booking parsing、social-card release path、notification lifecycle contract 和 mobile hero video playback contract。provider-bound video generation 與 transactional delivery 已記錄在 issue #39;前一批延後功能仍在 issue #38,不會直接成為 active memory。0.7.33 記錄並審查一筆更正回饋;確認 0.7.32 已正確處理 batch index,沒有修改已驗證的 shared behavior。0.7.32 加入 feedback 一基索引支援、MaggieDash panel 的 source/freshness/error evidence、Consent Mode 與實際 write/no-op reconciliation、SEO baseline recapture、sitemap origin rebasing、content-diff scope、migration ledger reconciliation、deployment credential preflight、release provenance/runtime preflight、icon release gate、opt-in blog auto-publish gate,以及 least-privilege VPS deployer。也修復 top-level `maggie feedback` dispatcher parity。0.7.31 加入 update 差異摘要、atomic installer copy、subset install manifest merge,以及 inventory drift 時 doctor non-zero。0.7.30 補上 feedback tracking issue closeout gate 與 batch 14/15 audit ledger。0.7.29 修正 npm package landing page 使用英文 README;0.7.28 加入 versioned Gemini model policy、明確 fallback 與 provenance、bundled-first CLI
|
|
29
29
|
dispatch、`maggie --version`、feedback batch review 聚合與重複偵測、marketplace enrichment evidence、booking
|
|
30
30
|
worker/resolver evidence,以及 read-only migration preflight。它也包含 host-owned mobile app surface contract、signed-in
|
|
31
31
|
camera-state QA、直接 Astro route resolution、correlated feedback batch,以及
|
|
@@ -70,8 +70,11 @@ maggie booking install --project . --confirm --install-dependencies
|
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
這個選項會依照專案現有的 npm/pnpm/yarn/bun lockfile,只安裝缺少的
|
|
73
|
-
React、Phosphor、Tailwind、PostgreSQL 和
|
|
74
|
-
|
|
73
|
+
React、Phosphor、Tailwind、PostgreSQL、Stripe 和 SSR adapter 套件,保留已存在的
|
|
74
|
+
版本,並更新相對應的 lockfile。空白 Astro 專案會補上可攜的
|
|
75
|
+
`@astrojs/node` 與 `astro.config.mjs`;已有 `astro.config.*` 或
|
|
76
|
+
Cloudflare/Vercel/Netlify/Deno adapter 的專案會保留原本的部署選擇。
|
|
77
|
+
它不能和 `--dry-run` 或 `--diff` 一起使用。
|
|
75
78
|
|
|
76
79
|
Booking 的 browser evidence gate 現在涵蓋全部 15 個 manager routes,在 mobile、
|
|
77
80
|
tablet、desktop 三種 viewport 共需要 45 個 screens,包含 waitlist、locations、
|
|
@@ -100,6 +103,10 @@ Booking 內置七種 Resend 事件模板。Owner、administrator 和 Booking man
|
|
|
100
103
|
舊版本。每個事件只接受核准的 placeholder,worker 發送前會 escape value;
|
|
101
104
|
provider credentials 和 recipient address 只留在 server/worker,不會返回瀏覽器。
|
|
102
105
|
|
|
106
|
+
Owner 設定 Stripe secrets 後,Booking Overview 會提供 server-side 的
|
|
107
|
+
「Verify Stripe connection」操作:檢查帳號與 webhook 設定,只保存不含 secrets
|
|
108
|
+
的 capability 狀態,並寫入 audited、可安全重試的結果;Stripe keys 不會進入瀏覽器。
|
|
109
|
+
|
|
103
110
|
新版 release gates:
|
|
104
111
|
|
|
105
112
|
```bash
|
package/README.md
CHANGED
|
@@ -60,7 +60,9 @@ Maggie keeps the existing project foundation and asks for decisions before
|
|
|
60
60
|
shared routes, analytics, or publishing boundaries change. The current
|
|
61
61
|
package ships 19 installable skills and a local-first MaggieDash foundation.
|
|
62
62
|
|
|
63
|
-
The current release is `0.7.
|
|
63
|
+
The current release is `0.7.38`. It adds adapter-aware fresh Astro Booking
|
|
64
|
+
installation: blank hosts receive a portable Node SSR config while existing
|
|
65
|
+
deployment adapters remain host-owned. It also adds the installable `maggie-booking`
|
|
64
66
|
workflow for MaggieDash Booking: host bootstrap, traditional email/password
|
|
65
67
|
team access with granular permissions, Stripe payment boundaries, Resend
|
|
66
68
|
outbox and tenant email-template management, public customer booking flow,
|
|
@@ -525,8 +527,8 @@ artifact schemas.
|
|
|
525
527
|
Recommended upgrade sequence for the current release:
|
|
526
528
|
|
|
527
529
|
```bash
|
|
528
|
-
npx @topy-ai/maggie@0.7.
|
|
529
|
-
npx @topy-ai/maggie@0.7.
|
|
530
|
+
npx @topy-ai/maggie@0.7.38 update --project . --force
|
|
531
|
+
npx @topy-ai/maggie@0.7.38 cleanup --project .
|
|
530
532
|
```
|
|
531
533
|
|
|
532
534
|
Maintainers should pass npm credentials through the repository helper, never
|
|
@@ -635,14 +637,18 @@ maggie booking install --project . --confirm
|
|
|
635
637
|
|
|
636
638
|
For a fresh Astro host, let the explicit install also add only the missing
|
|
637
639
|
Booking runtime packages through the host's existing npm/pnpm/yarn/bun
|
|
638
|
-
package manager
|
|
640
|
+
package manager. This includes the server-rendering adapter when the project
|
|
641
|
+
has no existing Astro deployment adapter:
|
|
639
642
|
|
|
640
643
|
```bash
|
|
641
644
|
maggie booking install --project . --confirm --install-dependencies
|
|
642
645
|
```
|
|
643
646
|
|
|
644
|
-
Existing dependency versions are preserved.
|
|
645
|
-
|
|
647
|
+
Existing dependency versions are preserved. A blank Astro project receives a
|
|
648
|
+
portable `@astrojs/node` adapter and `astro.config.mjs`; an existing
|
|
649
|
+
`astro.config.*` and Cloudflare/Vercel/Netlify/Deno adapter remain untouched.
|
|
650
|
+
This option updates the matching lockfile and is intentionally incompatible
|
|
651
|
+
with `--dry-run` and `--diff`.
|
|
646
652
|
|
|
647
653
|
For a reviewed new host, `--bootstrap` also applies the installed idempotent
|
|
648
654
|
Booking schema using the host's existing `DATABASE_URL`:
|
|
@@ -698,6 +704,11 @@ reports required environment-key presence and the explicit schema,
|
|
|
698
704
|
owner-registration, Stripe webhook, and Resend-worker steps without printing
|
|
699
705
|
secret values or mutating the host.
|
|
700
706
|
|
|
707
|
+
After the owner adds the Stripe secrets, the Booking Overview includes a
|
|
708
|
+
server-side “Verify Stripe connection” action. It checks the account and
|
|
709
|
+
webhook configuration, stores only non-secret capability status, and records
|
|
710
|
+
an audited idempotent result; Stripe keys never enter the browser.
|
|
711
|
+
|
|
701
712
|
Booking also includes a tenant-scoped Notifications screen for the Resend
|
|
702
713
|
delivery outbox. It shows redacted delivery state and sanitized errors through
|
|
703
714
|
`GET /api/maggie/booking/notifications.json`; an owner or manager can replay a
|
|
@@ -53,7 +53,7 @@ import.
|
|
|
53
53
|
items with stable ordering, nesting, and safe destinations;
|
|
54
54
|
- [`dashboard-runtime-v1.json`](dashboard-runtime-v1.json) defines evidence
|
|
55
55
|
that an admin screen mounted, ran its checks, and completed its data calls.
|
|
56
|
-
- MaggieDash 0.2.
|
|
56
|
+
- MaggieDash 0.2.7 also ships a screen-level host adapter contract and
|
|
57
57
|
provider-neutral install starters in the first-party distribution. The
|
|
58
58
|
`host-adapter-v2` contract lists endpoint methods, public routes, response
|
|
59
59
|
content types, and required fields; `maggie dash conformance` validates
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"auth": { "managerSession": "host-session-cookie", "emailPassword": true, "passkeys": false },
|
|
8
8
|
"requiredCapabilities": ["bookingOverview", "bookingCalendar", "bookingLifecycle", "customerLookup", "bookingWaitlist", "serviceCatalog", "locationSetup", "staffSetup", "resourceSetup", "paymentReconciliation", "bookingReports", "bookingSettings", "bookingAudit", "notificationOperations", "bookingAccess"],
|
|
9
9
|
"endpointIds": [
|
|
10
|
-
"booking.overview.read", "booking.reports.read", "booking.calendar.read", "booking.bookings.read", "booking.booking.read", "booking.booking.create", "booking.booking.transition", "booking.booking.reschedule", "booking.availability.read", "booking.holds.create", "booking.holds.release", "booking.customers.read", "booking.customer.read", "booking.waitlist.read", "booking.waitlist.update", "booking.catalog.read", "booking.catalog.write", "booking.locations.read", "booking.location.write", "booking.staff.read", "booking.staff.write", "booking.resources.read", "booking.resources.write", "booking.schedule.read", "booking.schedule.write", "booking.payments.read", "booking.payment.start", "booking.payment.reconcile", "booking.refund.start", "booking.notifications.read", "booking.notification.replay", "booking.health.read", "booking.payment-health.read", "booking.settings.read", "booking.settings.update", "booking.audit.read", "booking.events.read", "booking.runtime.read", "booking.provider.webhook"
|
|
10
|
+
"booking.overview.read", "booking.reports.read", "booking.calendar.read", "booking.bookings.read", "booking.booking.read", "booking.booking.create", "booking.booking.transition", "booking.booking.reschedule", "booking.availability.read", "booking.holds.create", "booking.holds.release", "booking.customers.read", "booking.customer.read", "booking.waitlist.read", "booking.waitlist.update", "booking.catalog.read", "booking.catalog.write", "booking.locations.read", "booking.location.write", "booking.staff.read", "booking.staff.write", "booking.resources.read", "booking.resources.write", "booking.schedule.read", "booking.schedule.write", "booking.payments.read", "booking.payment.start", "booking.payment.reconcile", "booking.refund.start", "booking.notifications.read", "booking.notification.replay", "booking.health.read", "booking.payment-health.read", "booking.payment-health.verify", "booking.settings.read", "booking.settings.update", "booking.audit.read", "booking.events.read", "booking.runtime.read", "booking.provider.webhook"
|
|
11
11
|
],
|
|
12
12
|
"canonicalRoutes": {
|
|
13
13
|
"booking.payment.start": "POST /payments/start.json",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"booking.notifications.read": "GET /notifications.json?status={status}",
|
|
16
16
|
"booking.notification.replay": "POST /notifications/{id}/replay.json",
|
|
17
17
|
"booking.payment-health.read": "GET /payment-health.json",
|
|
18
|
+
"booking.payment-health.verify": "POST /payment-health.json",
|
|
18
19
|
"booking.provider.webhook": "POST /webhooks/{provider}"
|
|
19
20
|
},
|
|
20
21
|
"serviceMethods": {
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
"booking.notification.replay": "replayNotification",
|
|
52
53
|
"booking.health.read": "readHealth",
|
|
53
54
|
"booking.payment-health.read": "readPaymentHealth",
|
|
55
|
+
"booking.payment-health.verify": "verifyPaymentConnection",
|
|
54
56
|
"booking.settings.read": "readSettings",
|
|
55
57
|
"booking.settings.update": "updateSettings",
|
|
56
58
|
"booking.audit.read": "listAudit",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"status": "passed",
|
|
80
80
|
"plan": ["Define request validation and safe error mapping", "Implement read DTO builders", "Implement mutation command dispatch with transaction hooks", "Keep host database/credentials injected"],
|
|
81
81
|
"implementation": ["backend/booking/service.mjs", "backend/booking/dto.mjs", "backend/booking/request.mjs", "backend/booking/route-handler.mjs"],
|
|
82
|
-
"tests": ["reference host endpoint conformance", "booking service validator including canonical list-object responses", "booking route-handler validator across all
|
|
82
|
+
"tests": ["reference host endpoint conformance", "booking service validator including canonical list-object responses", "booking route-handler validator across all 40 routes", "safe error and redaction tests", "project authorization fixtures"],
|
|
83
83
|
"docs": ["docs/maggiedash-booking/HOST-ADAPTER.md", "backend/README.md"],
|
|
84
84
|
"acceptance": ["Host routes return canonical DTOs", "No SQL/provider/raw credentials cross the boundary", "All failures use the versioned safe error shape"]
|
|
85
85
|
},
|
|
@@ -198,7 +198,7 @@
|
|
|
198
198
|
"implementation": ["scripts/capture-booking-runtime-evidence.mjs", "scripts/validate-booking-runtime-plan.mjs", "scripts/validate-booking-runtime-evidence.mjs", "scripts/capture-booking-browser-evidence.mjs", "scripts/validate-booking-browser-evidence.mjs", ".github/workflows/booking-runtime-evidence.yml", ".github/workflows/booking-browser-evidence.yml"],
|
|
199
199
|
"tests": ["maggie booking runtime-validate", "npm run booking:runtime:plan:test", "runtime evidence validator", "browser evidence validator including console/request/layoutErrors", "real host runtime plan run", "real host Playwright run"],
|
|
200
200
|
"docs": ["docs/maggiedash-booking/QA-TEST-PLAN.md", "docs/maggiedash-booking/PROGRESS.md"],
|
|
201
|
-
"acceptance": ["All
|
|
201
|
+
"acceptance": ["All 40 manager endpoints have 2xx evidence", "All 15 manager screens pass at 3 viewports", "No console errors, failed requests, overflow, or login leakage"]
|
|
202
202
|
},
|
|
203
203
|
{
|
|
204
204
|
"id": "BK-032",
|
|
@@ -315,7 +315,7 @@
|
|
|
315
315
|
"implementation": ["contracts/maggiedash/booking-host-adapter-v1.json", "tools/clis/maggie_booking.py", "tools/tests/test_maggie_booking.py", "docs/maggiedash-booking/SKILLS-AND-CLI.md"],
|
|
316
316
|
"tests": ["python3 -m unittest tools.tests.test_maggie_booking -v", "maggie booking contract", "maggie booking tasks validate", "maggie booking inspect --project <installed-projection>", "python3 -m py_compile"],
|
|
317
317
|
"docs": ["docs/maggiedash-booking/SKILLS-AND-CLI.md", "docs/maggiedash-booking/PROGRESS.md"],
|
|
318
|
-
"acceptance": ["The compact contract maps all
|
|
318
|
+
"acceptance": ["The compact contract maps all 40 manager endpoints and nine customer methods", "Installed inspect fails when service types or validator are missing", "Release gate includes the service contract checks", "A regression test prevents service surface drift"]
|
|
319
319
|
},
|
|
320
320
|
{
|
|
321
321
|
"id": "BK-041",
|
|
@@ -1218,7 +1218,7 @@
|
|
|
1218
1218
|
"acceptance": [
|
|
1219
1219
|
"A blank host can create its first location without a fixture ID",
|
|
1220
1220
|
"Location writes validate timezone, scope, permission, uniqueness, and idempotent replay",
|
|
1221
|
-
"The central and ORA location screen,
|
|
1221
|
+
"The central and ORA location screen, 40-route contract, reference host, and configuration gates pass"
|
|
1222
1222
|
]
|
|
1223
1223
|
},
|
|
1224
1224
|
{
|
|
@@ -1230,7 +1230,7 @@
|
|
|
1230
1230
|
"status": "passed",
|
|
1231
1231
|
"plan": [
|
|
1232
1232
|
"Add the location capability and two location endpoint IDs to the public installer contract",
|
|
1233
|
-
"Align service-method mappings and the CLI detailed-contract count with MaggieDash's
|
|
1233
|
+
"Align service-method mappings and the CLI detailed-contract count with MaggieDash's 40-route contract",
|
|
1234
1234
|
"Add regression coverage so future route additions cannot leave the installer contract stale"
|
|
1235
1235
|
],
|
|
1236
1236
|
"implementation": [
|
|
@@ -1252,7 +1252,7 @@
|
|
|
1252
1252
|
"docs/maggiedash-booking/README.md"
|
|
1253
1253
|
],
|
|
1254
1254
|
"acceptance": [
|
|
1255
|
-
"The ai-cmo-skills installer contract declares all
|
|
1255
|
+
"The ai-cmo-skills installer contract declares all 40 manager endpoints",
|
|
1256
1256
|
"The location capability and list/update service methods are present",
|
|
1257
1257
|
"The public CLI and MaggieDash contract validators agree without host secrets or database access"
|
|
1258
1258
|
]
|
|
@@ -1263,7 +1263,7 @@
|
|
|
1263
1263
|
"title": "Verify and release the public MaggieDash install source",
|
|
1264
1264
|
"repository": "MaggieDash + release owner",
|
|
1265
1265
|
"dependsOn": ["BK-077"],
|
|
1266
|
-
"status": "
|
|
1266
|
+
"status": "passed",
|
|
1267
1267
|
"plan": [
|
|
1268
1268
|
"Commit and publish the local MaggieDash manifest hostBootstrap declaration and Booking source changes",
|
|
1269
1269
|
"Run the default GitHub-source installer against a disposable Astro project",
|
|
@@ -1291,7 +1291,7 @@
|
|
|
1291
1291
|
"acceptance": [
|
|
1292
1292
|
"The default public source declares an Astro host bootstrap under hostBootstrap.astro",
|
|
1293
1293
|
"A fresh Astro project can install Booking without a local MaggieDash checkout",
|
|
1294
|
-
"The released source contains the same
|
|
1294
|
+
"The released source contains the same 40-route contract and worker/schema artifacts as the validated local source"
|
|
1295
1295
|
]
|
|
1296
1296
|
},
|
|
1297
1297
|
{
|
|
@@ -1326,7 +1326,7 @@
|
|
|
1326
1326
|
"docs/maggiedash-booking/EXECUTION-BOARD.json"
|
|
1327
1327
|
],
|
|
1328
1328
|
"acceptance": [
|
|
1329
|
-
"The npm CLI validates the current
|
|
1329
|
+
"The npm CLI validates the current 40-route Booking contract from bundled-contracts",
|
|
1330
1330
|
"The npm CLI can validate Stripe and the packaged 69-task execution board",
|
|
1331
1331
|
"Source-checkout and npm-package path layouts both remain supported"
|
|
1332
1332
|
]
|
|
@@ -1369,7 +1369,7 @@
|
|
|
1369
1369
|
"Notification list results are tenant-scoped and omit raw recipient email/provider payloads",
|
|
1370
1370
|
"Only an authorized owner/manager can replay a dead-letter notification",
|
|
1371
1371
|
"Replay requires reason and idempotency, resets delivery state, and records event plus audit evidence",
|
|
1372
|
-
"Central, installer contract, package bundle, ORA mirror, and build evidence agree on
|
|
1372
|
+
"Central, installer contract, package bundle, ORA mirror, and build evidence agree on 40 manager routes"
|
|
1373
1373
|
]
|
|
1374
1374
|
},
|
|
1375
1375
|
{
|
|
@@ -1802,7 +1802,7 @@
|
|
|
1802
1802
|
"status": "passed",
|
|
1803
1803
|
"plan": [
|
|
1804
1804
|
"Detect the host project's existing npm, pnpm, yarn, or bun lockfile",
|
|
1805
|
-
"Add only missing React, Phosphor, Tailwind, PostgreSQL, and
|
|
1805
|
+
"Add only missing React, Phosphor, Tailwind, PostgreSQL, Stripe, and SSR adapter packages through the selected package manager",
|
|
1806
1806
|
"Keep dependency installation explicit and incompatible with read-only install modes",
|
|
1807
1807
|
"Cover idempotency, package-manager selection, and failure-safe output in the CLI tests"
|
|
1808
1808
|
],
|
|
@@ -1829,6 +1829,7 @@
|
|
|
1829
1829
|
],
|
|
1830
1830
|
"acceptance": [
|
|
1831
1831
|
"A fresh Astro host can opt into one-command dependency completion without replacing existing versions",
|
|
1832
|
+
"A blank Astro host receives a server-rendered Node adapter/config while an existing deployment adapter remains host-owned",
|
|
1832
1833
|
"The selected package manager and missing package names are visible without printing secrets",
|
|
1833
1834
|
"No package install occurs during --dry-run or --diff",
|
|
1834
1835
|
"A project with all required packages is unchanged and no package-manager process runs"
|
|
@@ -88,13 +88,15 @@ maggie booking install --project . --confirm --install-dependencies
|
|
|
88
88
|
|
|
89
89
|
This is an explicit dependency mutation. It preserves existing package
|
|
90
90
|
versions, updates the matching lockfile, and never installs a second copy of a
|
|
91
|
-
package already present in `dependencies` or `devDependencies`.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
package already present in `dependencies` or `devDependencies`. For a blank
|
|
92
|
+
Astro host it also adds the compatible `@astrojs/node` SSR adapter and the
|
|
93
|
+
portable server config; an existing `astro.config.*` and deployment adapter
|
|
94
|
+
remain host-owned.
|
|
95
|
+
|
|
96
|
+
The default installer source is the public MaggieDash `0.2.8` distribution. It
|
|
97
|
+
contains the released Astro `hostBootstrap` manifest and 40-route Booking
|
|
98
|
+
source. A fresh public-source install, `maggie booking inspect`, and
|
|
99
|
+
`astro build` are the release evidence; a local-source result alone is not.
|
|
98
100
|
|
|
99
101
|
For a new host with a reviewed `DATABASE_URL`, the explicit one-command
|
|
100
102
|
bootstrap also applies the idempotent Booking schema:
|
|
@@ -403,6 +403,14 @@ REQUIRED_HOST_DEPENDENCIES = (
|
|
|
403
403
|
"tailwindcss",
|
|
404
404
|
)
|
|
405
405
|
|
|
406
|
+
ASTRO_ADAPTER_DEPENDENCIES = (
|
|
407
|
+
"@astrojs/node",
|
|
408
|
+
"@astrojs/cloudflare",
|
|
409
|
+
"@astrojs/netlify",
|
|
410
|
+
"@astrojs/vercel",
|
|
411
|
+
"@astrojs/deno",
|
|
412
|
+
)
|
|
413
|
+
|
|
406
414
|
# Keep the host install path reproducible without taking ownership of the
|
|
407
415
|
# host's Astro version. Existing dependencies are left untouched; only
|
|
408
416
|
# missing packages are added by `--install-dependencies`.
|
|
@@ -415,17 +423,74 @@ HOST_DEPENDENCY_RANGES = {
|
|
|
415
423
|
"react-dom": "^19.0.0",
|
|
416
424
|
"stripe": "^22.0.0",
|
|
417
425
|
"tailwindcss": "^4.0.0",
|
|
426
|
+
"@astrojs/node": "^9.5.0",
|
|
427
|
+
"@astrojs/cloudflare": "^12.0.0",
|
|
428
|
+
"@astrojs/netlify": "^6.0.0",
|
|
429
|
+
"@astrojs/vercel": "^8.0.0",
|
|
430
|
+
"@astrojs/deno": "^6.0.0",
|
|
418
431
|
}
|
|
419
432
|
|
|
420
433
|
|
|
434
|
+
def astro_config_files(root: Path) -> list[Path]:
|
|
435
|
+
return sorted(root.glob("astro.config.*"))
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def astro_adapter_dependency(root: Path) -> str | None:
|
|
439
|
+
"""Choose the existing Astro adapter, or the portable Node default.
|
|
440
|
+
|
|
441
|
+
A host-owned config takes precedence. A fresh Astro project gets the Node
|
|
442
|
+
adapter because Booking includes server routes; existing Cloudflare,
|
|
443
|
+
Netlify, Vercel, or Deno deployments are never replaced or supplemented.
|
|
444
|
+
"""
|
|
445
|
+
try:
|
|
446
|
+
package = json.loads((root / "package.json").read_text(encoding="utf-8"))
|
|
447
|
+
except (OSError, json.JSONDecodeError):
|
|
448
|
+
package = {}
|
|
449
|
+
installed = set(package.get("dependencies") or {}) | set(package.get("devDependencies") or {})
|
|
450
|
+
configs = astro_config_files(root)
|
|
451
|
+
if "astro" not in installed and not configs and not any(root.glob("src/**/*.astro")):
|
|
452
|
+
return None
|
|
453
|
+
for name in ASTRO_ADAPTER_DEPENDENCIES:
|
|
454
|
+
if name in installed:
|
|
455
|
+
return name
|
|
456
|
+
config_text = "\n".join(path.read_text(encoding="utf-8", errors="ignore") for path in configs if path.is_file())
|
|
457
|
+
for name in ASTRO_ADAPTER_DEPENDENCIES:
|
|
458
|
+
if name in config_text:
|
|
459
|
+
return name
|
|
460
|
+
if not configs:
|
|
461
|
+
return "@astrojs/node"
|
|
462
|
+
# A project-specific adapter may be imported from a private package or
|
|
463
|
+
# local module. Do not guess or install Node over that host decision.
|
|
464
|
+
if "adapter" in config_text:
|
|
465
|
+
return None
|
|
466
|
+
return "@astrojs/node"
|
|
467
|
+
|
|
468
|
+
|
|
421
469
|
def missing_host_dependencies(root: Path) -> list[str]:
|
|
422
470
|
"""Return only host packages absent from both dependency sections."""
|
|
423
471
|
try:
|
|
424
472
|
package = json.loads((root / "package.json").read_text(encoding="utf-8"))
|
|
425
473
|
except (OSError, json.JSONDecodeError):
|
|
426
|
-
|
|
474
|
+
package = {}
|
|
427
475
|
installed = set(package.get("dependencies") or {}) | set(package.get("devDependencies") or {})
|
|
428
|
-
|
|
476
|
+
required = list(REQUIRED_HOST_DEPENDENCIES)
|
|
477
|
+
adapter = astro_adapter_dependency(root)
|
|
478
|
+
if adapter and adapter not in required:
|
|
479
|
+
required.append(adapter)
|
|
480
|
+
return sorted(name for name in required if name not in installed)
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def dependency_range(name: str, root: Path) -> str:
|
|
484
|
+
if name != "@astrojs/node":
|
|
485
|
+
return HOST_DEPENDENCY_RANGES[name]
|
|
486
|
+
try:
|
|
487
|
+
package = json.loads((root / "package.json").read_text(encoding="utf-8"))
|
|
488
|
+
except (OSError, json.JSONDecodeError):
|
|
489
|
+
package = {}
|
|
490
|
+
version = str((package.get("dependencies") or {}).get("astro") or (package.get("devDependencies") or {}).get("astro") or "")
|
|
491
|
+
match = re.search(r"(?:^|[^0-9])([0-9]+)(?:\.[0-9]+)?(?:\.[0-9]+)?", version)
|
|
492
|
+
major = int(match.group(1)) if match else 5
|
|
493
|
+
return "^9.5.0" if major <= 5 else "^10.0.0" if major == 6 else "^11.0.0"
|
|
429
494
|
|
|
430
495
|
|
|
431
496
|
def host_package_manager(root: Path) -> tuple[str, list[str]]:
|
|
@@ -440,7 +505,7 @@ def host_package_manager(root: Path) -> tuple[str, list[str]]:
|
|
|
440
505
|
manager = next((name for filename, name in lockfiles if (root / filename).is_file()), "npm")
|
|
441
506
|
if shutil.which(manager) is None:
|
|
442
507
|
raise RuntimeError(f"{manager} is required to install Booking host dependencies but is not available on PATH")
|
|
443
|
-
packages = [f"{name}@{
|
|
508
|
+
packages = [f"{name}@{dependency_range(name, root)}" for name in missing_host_dependencies(root)]
|
|
444
509
|
if manager == "npm":
|
|
445
510
|
return manager, ["npm", "install", "--save", "--no-audit", "--no-fund", *packages]
|
|
446
511
|
if manager == "pnpm":
|
|
@@ -460,7 +525,7 @@ def install_host_dependencies(root: Path, runner=None) -> dict:
|
|
|
460
525
|
completed = execute(command, cwd=root, check=False)
|
|
461
526
|
if completed.returncode != 0:
|
|
462
527
|
raise RuntimeError(f"{manager} failed while installing {len(missing)} missing Booking host dependencies")
|
|
463
|
-
return {"status": "installed", "missing": missing, "packageManager": manager, "command": [manager, "add" if manager != "npm" else "install", *[f"{name}@{
|
|
528
|
+
return {"status": "installed", "missing": missing, "packageManager": manager, "command": [manager, "add" if manager != "npm" else "install", *[f"{name}@{dependency_range(name, root)}" for name in missing]]}
|
|
464
529
|
|
|
465
530
|
|
|
466
531
|
def _meaningful_env_value(value: str) -> bool:
|
|
@@ -545,7 +610,7 @@ def booking_setup_result(root: Path, environment: dict[str, str] | None = None)
|
|
|
545
610
|
except (OSError, json.JSONDecodeError):
|
|
546
611
|
package = {}
|
|
547
612
|
installed_dependencies = set((package.get("dependencies") or {})) | set((package.get("devDependencies") or {}))
|
|
548
|
-
missing_dependencies =
|
|
613
|
+
missing_dependencies = missing_host_dependencies(root)
|
|
549
614
|
has_sender = has("EMAIL_FROM") or has("RESEND_FROM_EMAIL")
|
|
550
615
|
token_present = has("BOOKING_TOKEN_SECRET")
|
|
551
616
|
token_valid = token_present and len(values.get("BOOKING_TOKEN_SECRET", "")) >= 32
|
|
@@ -262,6 +262,12 @@ def manifest_host_file_pairs(source: Path, manifest: dict[str, object], root: Pa
|
|
|
262
262
|
for path in sorted(source_item.rglob("*")):
|
|
263
263
|
if path.is_file():
|
|
264
264
|
child = path.relative_to(source_item)
|
|
265
|
+
# A deployment config is host-owned. The default Node config is
|
|
266
|
+
# useful for a blank Astro project, but adding a second config to
|
|
267
|
+
# a host that already has astro.config.ts/cloudflare/etc. can make
|
|
268
|
+
# Astro ambiguous or change its deployment target.
|
|
269
|
+
if framework == "astro" and child.name == "astro.config.mjs" and any(root.glob("astro.config.*")):
|
|
270
|
+
continue
|
|
265
271
|
pairs.append((path, root / target_relative / child, source_relative / child))
|
|
266
272
|
return pairs
|
|
267
273
|
|