@topy-ai/maggie 0.7.35 → 0.7.37
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 +45 -2
- package/README.md +108 -4
- package/bin/maggie.js +76 -19
- package/bundled-contracts/maggie-seo/model-policy-v2.schema.json +29 -0
- package/bundled-contracts/maggie-service-booking/notification-lifecycle-v1.schema.json +29 -0
- package/bundled-contracts/maggiedash/README.md +3 -0
- package/bundled-contracts/maggiedash/booking-access-v1.json +29 -0
- package/bundled-contracts/maggiedash/booking-customer-surface-v1.json +46 -0
- package/bundled-contracts/maggiedash/booking-email-templates-v1.json +38 -0
- package/bundled-contracts/maggiedash/booking-host-adapter-v1.json +76 -0
- package/bundled-contracts/maggiedash/booking-ops-evidence-v1.json +19 -0
- package/bundled-contracts/maggiedash/execution-board.json +1886 -0
- package/bundled-contracts/maggiedash/stripe-booking-capabilities-v1.json +68 -0
- package/bundled-skills/README.md +1 -0
- package/bundled-skills/catalog.json +4 -0
- package/bundled-skills/maggie-blog-bootstrap/SKILL.md +6 -0
- package/bundled-skills/maggie-booking/SKILL.md +462 -0
- package/bundled-skills/maggie-design/SKILL.md +15 -0
- package/bundled-skills/maggie-seo-geo/SKILL.md +24 -2
- package/bundled-skills/maggie-service-booking/SKILL.md +19 -0
- package/bundled-templates/astro-blog/starter/README.md +4 -0
- package/bundled-templates/astro-blog/starter/src/styles/global.css +6 -0
- package/bundled-tools/clis/maggie.py +36 -2
- package/bundled-tools/clis/maggie_booking.py +1080 -0
- package/bundled-tools/clis/maggie_dash.py +181 -5
- package/bundled-tools/clis/maggie_release.py +24 -0
- package/bundled-tools/clis/maggie_service_booking.py +38 -2
- package/bundled-tools/clis/site_audit.py +114 -15
- package/bundled-tools/runtime/site_baseline.py +11 -1
- 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.37 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.37`,加入可安裝的 `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 維護文件。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,以及
|
|
@@ -57,6 +57,49 @@ section registry、短期 agent content bridge,以及 translation out-of-band
|
|
|
57
57
|
browser evidence、rendered canary 與 deployment preflight。缺少必要 adapter 時
|
|
58
58
|
回傳 `inconclusive`,只有五組 evidence 全部通過才回傳 `passed`。
|
|
59
59
|
|
|
60
|
+
Booking 也有 tenant-scoped 的 Notifications 畫面,管理 Resend transactional
|
|
61
|
+
delivery outbox:可查看去識別化的狀態與 sanitized error;owner/manager 只有
|
|
62
|
+
在填寫 reason 並使用 idempotency key 時,才可以 replay `dead_letter` 項目。
|
|
63
|
+
這是 Maggie 的 server-side delivery queue 與 audit 管理,不取代 Resend 自己的
|
|
64
|
+
domain、audience、campaign 或 analytics console。
|
|
65
|
+
|
|
66
|
+
在全新的 Astro 專案中,可以讓安裝流程補上缺少的 Booking runtime 套件:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
maggie booking install --project . --confirm --install-dependencies
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
這個選項會依照專案現有的 npm/pnpm/yarn/bun lockfile,只安裝缺少的
|
|
73
|
+
React、Phosphor、Tailwind、PostgreSQL 和 Stripe 套件,保留已存在的版本,
|
|
74
|
+
並更新相對應的 lockfile;它不能和 `--dry-run` 或 `--diff` 一起使用。
|
|
75
|
+
|
|
76
|
+
Booking 的 browser evidence gate 現在涵蓋全部 15 個 manager routes,在 mobile、
|
|
77
|
+
tablet、desktop 三種 viewport 共需要 45 個 screens,包含 waitlist、locations、
|
|
78
|
+
notifications、reports 和 audit。Production release 仍需要 authenticated HTTPS
|
|
79
|
+
capture,並且 console、request、layout errors 都必須為空。
|
|
80
|
+
|
|
81
|
+
可在不連線資料庫或 provider 的情況下,獨立驗證管理員設定員工登入的 access
|
|
82
|
+
contract:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
maggie booking access-contract
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Booking Team access 支援透過 Resend 發送傳統 email/password 邀請、`manager`、
|
|
89
|
+
`staff`、`finance` 角色、細粒度 permissions、staff profile 綁定、密碼重設、
|
|
90
|
+
撤銷權限與 audit;不使用 passkey,也不會自動重新啟用 disabled account。
|
|
91
|
+
|
|
92
|
+
可用以下命令獨立驗證 tenant-scoped Booking email template contract:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
maggie booking email-templates-contract
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Booking 內置七種 Resend 事件模板。Owner、administrator 和 Booking manager
|
|
99
|
+
可以編輯 draft、用 sandbox sample data 預覽、發佈 audited version,或 rollback
|
|
100
|
+
舊版本。每個事件只接受核准的 placeholder,worker 發送前會 escape value;
|
|
101
|
+
provider credentials 和 recipient address 只留在 server/worker,不會返回瀏覽器。
|
|
102
|
+
|
|
60
103
|
新版 release gates:
|
|
61
104
|
|
|
62
105
|
```bash
|
package/README.md
CHANGED
|
@@ -60,14 +60,30 @@ 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.37`. It adds the installable `maggie-booking`
|
|
64
|
+
workflow for MaggieDash Booking: host bootstrap, traditional email/password
|
|
65
|
+
team access with granular permissions, Stripe payment boundaries, Resend
|
|
66
|
+
outbox and tenant email-template management, public customer booking flow,
|
|
67
|
+
runtime/ops evidence checks, and the maintained Booking implementation docs.
|
|
68
|
+
Production activation remains fail-closed until the host migration, owner,
|
|
69
|
+
Stripe, scheduler, and authenticated runtime evidence are supplied.
|
|
70
|
+
|
|
71
|
+
The previous `0.7.36` release closed the next validated feedback batch:
|
|
64
72
|
production demo fixtures are development-only, Dash infers the persisted
|
|
65
73
|
project id, clone asset extraction includes responsive/background assets, SEO
|
|
66
74
|
structure hashes ignore content-hashed asset names, decorative empty alt text
|
|
67
75
|
is accepted, and missing starter routes return a real 404. It also adds a
|
|
68
76
|
Tailwind v4 starter entry point, explicit `PUBLIC_SITE_URL` validation, a
|
|
69
77
|
portable installed-CLI script path, and a bundled Astro starter without local
|
|
70
|
-
dependencies or build caches.
|
|
78
|
+
dependencies or build caches. Batch 20 also adds rendered-output placeholder
|
|
79
|
+
scoping, Tailwind renderable source boundaries, same-origin link/fragment
|
|
80
|
+
checks, unstable baseline classification, site-wide baseline reasons,
|
|
81
|
+
base64url asset-hash normalization, installed-file update hashes, provider-
|
|
82
|
+
routed Gemini v2, venue-timezone/DST booking parsing, social-card release-path
|
|
83
|
+
discovery, a booking notification lifecycle contract, and mobile hero-video
|
|
84
|
+
playback guidance. Provider-bound video generation and transactional delivery
|
|
85
|
+
are tracked in [issue #39](https://github.com/TOPY-AI-LTD/ai-cmo-skills/issues/39).
|
|
86
|
+
Deferred feature requests from the prior batch remain in
|
|
71
87
|
[issue #38](https://github.com/TOPY-AI-LTD/ai-cmo-skills/issues/38), not active
|
|
72
88
|
memory. The 0.7.33 release records the reviewed correction for the
|
|
73
89
|
batch-index feedback workflow while keeping the verified 0.7.32 behavior
|
|
@@ -509,8 +525,8 @@ artifact schemas.
|
|
|
509
525
|
Recommended upgrade sequence for the current release:
|
|
510
526
|
|
|
511
527
|
```bash
|
|
512
|
-
npx @topy-ai/maggie@0.7.
|
|
513
|
-
npx @topy-ai/maggie@0.7.
|
|
528
|
+
npx @topy-ai/maggie@0.7.37 update --project . --force
|
|
529
|
+
npx @topy-ai/maggie@0.7.37 cleanup --project .
|
|
514
530
|
```
|
|
515
531
|
|
|
516
532
|
Maintainers should pass npm credentials through the repository helper, never
|
|
@@ -607,6 +623,94 @@ for local development or a configured private Git URL. The host project keeps
|
|
|
607
623
|
ownership of routes, email/password auth, database, provider credentials, and
|
|
608
624
|
the `/api/maggie/*` adapter.
|
|
609
625
|
|
|
626
|
+
For an Astro project that also needs Booking, use the Booking installer. It
|
|
627
|
+
installs the separate `./_maggie/booking` workspace plus missing email/password
|
|
628
|
+
routes, first-owner registration, PostgreSQL/Stripe host adapter, public
|
|
629
|
+
booking route, middleware scaffold, schema command, Resend worker, and an
|
|
630
|
+
`.env.maggie-booking.example`:
|
|
631
|
+
|
|
632
|
+
```bash
|
|
633
|
+
maggie booking install --project . --confirm
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
For a fresh Astro host, let the explicit install also add only the missing
|
|
637
|
+
Booking runtime packages through the host's existing npm/pnpm/yarn/bun
|
|
638
|
+
package manager:
|
|
639
|
+
|
|
640
|
+
```bash
|
|
641
|
+
maggie booking install --project . --confirm --install-dependencies
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
Existing dependency versions are preserved. This option updates the matching
|
|
645
|
+
lockfile and is intentionally incompatible with `--dry-run` and `--diff`.
|
|
646
|
+
|
|
647
|
+
For a reviewed new host, `--bootstrap` also applies the installed idempotent
|
|
648
|
+
Booking schema using the host's existing `DATABASE_URL`:
|
|
649
|
+
|
|
650
|
+
```bash
|
|
651
|
+
maggie booking install --project . --confirm --bootstrap
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
This remains opt-in and does not create the owner, configure Stripe, send
|
|
655
|
+
email, or deploy. If the host has no `BOOKING_TOKEN_SECRET`, bootstrap creates
|
|
656
|
+
one in the local `.env` without printing it. Existing secrets are preserved;
|
|
657
|
+
short configured values fail closed. External secret stores must provision the
|
|
658
|
+
key themselves.
|
|
659
|
+
|
|
660
|
+
Run the read-only onboarding checklist after installation:
|
|
661
|
+
|
|
662
|
+
```bash
|
|
663
|
+
maggie booking setup --project .
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
Validate the administrator-managed staff access boundary independently of a
|
|
667
|
+
database or provider:
|
|
668
|
+
|
|
669
|
+
```bash
|
|
670
|
+
maggie booking access-contract
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
The installed Booking workspace includes a Team access panel. An owner or
|
|
674
|
+
administrator can invite email/password accounts through Resend, assign
|
|
675
|
+
`manager`, `staff`, or `finance`, link a staff profile, edit granular
|
|
676
|
+
permissions, reset or resend setup credentials, and revoke Booking access.
|
|
677
|
+
The access contract is tenant-scoped, audited, and never silently reactivates
|
|
678
|
+
a disabled account.
|
|
679
|
+
|
|
680
|
+
Validate the tenant-scoped Booking email-template contract independently:
|
|
681
|
+
|
|
682
|
+
```bash
|
|
683
|
+
maggie booking email-templates-contract
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
The Booking workspace includes seven Resend event templates. Owner,
|
|
687
|
+
administrator, and Booking managers can edit drafts, preview sandboxed sample
|
|
688
|
+
data, publish audited versions, and roll back earlier versions. Event-specific
|
|
689
|
+
placeholders are allowlisted and escaped before the host worker sends through
|
|
690
|
+
Resend; provider credentials and recipient addresses remain server-only.
|
|
691
|
+
|
|
692
|
+
The default `--host auto` only selects Astro when detected. Existing host files
|
|
693
|
+
are preserved; a conventional `src/middleware.ts` receives the generated
|
|
694
|
+
`/_maggie` rewrite block automatically, while unsupported middleware is
|
|
695
|
+
preserved and reported for manual integration. This command does not apply migrations,
|
|
696
|
+
create the owner, or configure Stripe/Resend secrets. The setup checklist
|
|
697
|
+
reports required environment-key presence and the explicit schema,
|
|
698
|
+
owner-registration, Stripe webhook, and Resend-worker steps without printing
|
|
699
|
+
secret values or mutating the host.
|
|
700
|
+
|
|
701
|
+
Booking also includes a tenant-scoped Notifications screen for the Resend
|
|
702
|
+
delivery outbox. It shows redacted delivery state and sanitized errors through
|
|
703
|
+
`GET /api/maggie/booking/notifications.json`; an owner or manager can replay a
|
|
704
|
+
`dead_letter` item through the UI only with a reason and idempotency key. This
|
|
705
|
+
manages Maggie's server-side transactional delivery queue and audit trail; it
|
|
706
|
+
does not replace Resend's own domain, audience, campaign, or analytics console.
|
|
707
|
+
|
|
708
|
+
The Booking browser-evidence gate covers all 15 manager routes at mobile,
|
|
709
|
+
tablet, and desktop viewports (45 required screens), including waitlist,
|
|
710
|
+
locations, notifications, reports, and audit. A production release still
|
|
711
|
+
requires an authenticated HTTPS capture with zero console, request, and
|
|
712
|
+
layout errors.
|
|
713
|
+
|
|
610
714
|
Compare an existing dashboard before replacing it:
|
|
611
715
|
|
|
612
716
|
```bash
|
package/bin/maggie.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
6
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
|
|
9
9
|
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -58,6 +58,7 @@ Usage:
|
|
|
58
58
|
maggie doctor [--project PATH]
|
|
59
59
|
maggie bootstrap interview [project]
|
|
60
60
|
maggie dash init|install|status|migrate|transition|variant|cms --project PATH [options]
|
|
61
|
+
maggie booking install|inspect|setup|contract|runtime-validate|ops-validate|release-gate|stripe-audit|tasks --project PATH [options]
|
|
61
62
|
maggie dash sections <validate|prompt|keys|remap-translations|fanout-validate|locale-validate|variant-copy-validate|media-validate|bindings-validate|idempotency-validate|reconcile> [options]
|
|
62
63
|
maggie dash inventory --pages-file FILE [--require-page-kinds] [--require-source-coverage]
|
|
63
64
|
maggie dash api-contract --spec FILE
|
|
@@ -132,9 +133,9 @@ Usage:
|
|
|
132
133
|
maggie seo performance|images|sitemap|indexnow|social-cards|head-tags [options] (sitemap supports strict validate and agent-files)
|
|
133
134
|
maggie feedback <collect|preview|batch|batch-review|submit|list> [options]
|
|
134
135
|
maggie qa <start|record|summary|export|assertion-audit> [options]
|
|
135
|
-
maggie site-audit URL [--crawl] [--access-log FILE] [--require-sitemap-request] [--languages en-GB,es-MX,ja-JP] [--check-hreflang]
|
|
136
|
+
maggie site-audit URL [--crawl] [--access-log FILE] [--require-sitemap-request] [--languages en-GB,es-MX,ja-JP] [--check-hreflang] [--check-internal-links]
|
|
136
137
|
maggie site-audit URL --crawl --save-baseline FILE --reviewer NAME
|
|
137
|
-
maggie site-audit URL --crawl --baseline FILE
|
|
138
|
+
maggie site-audit URL --crawl --baseline FILE [--reason-all REASON]
|
|
138
139
|
maggie browser-audit URL --browse PATH --output DIR --required SELECTOR [--sticky SELECTOR]
|
|
139
140
|
maggie verification coverage --contract FILE --evidence FILE
|
|
140
141
|
maggie ops audit|preflight|verify|lockfiles|seed-manifest|google-capabilities|favicon-check --project PATH
|
|
@@ -282,21 +283,25 @@ function diffSummary(source, target) {
|
|
|
282
283
|
return `${added} local additions, ${removed} package lines removed across ${blocks} changed blocks (${localLines.length} local lines vs ${packageLines.length} package lines)`;
|
|
283
284
|
}
|
|
284
285
|
|
|
285
|
-
function syncTree(source, target, force) {
|
|
286
|
+
function syncTree(source, target, force, root = null, managedFiles = {}) {
|
|
286
287
|
let changed = 0;
|
|
287
288
|
for (const entry of readdirSync(source, { withFileTypes: true })) {
|
|
288
289
|
const sourcePath = join(source, entry.name);
|
|
289
290
|
const targetPath = join(target, entry.name);
|
|
290
291
|
if (entry.isDirectory()) {
|
|
291
|
-
changed += syncTree(sourcePath, targetPath, force);
|
|
292
|
+
changed += syncTree(sourcePath, targetPath, force, root, managedFiles);
|
|
292
293
|
continue;
|
|
293
294
|
}
|
|
295
|
+
const managedPath = root ? relative(root, targetPath) : targetPath;
|
|
296
|
+
const localDigest = existsSync(targetPath) ? digest(targetPath) : null;
|
|
297
|
+
const packageDigest = digest(sourcePath);
|
|
298
|
+
const wasManaged = managedFiles && managedFiles[managedPath] === localDigest;
|
|
294
299
|
if (!existsSync(targetPath)) {
|
|
295
300
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
296
301
|
writeFileSync(targetPath, readFileSync(sourcePath));
|
|
297
302
|
console.log(`installed ${targetPath}`);
|
|
298
303
|
changed++;
|
|
299
|
-
} else if (force ||
|
|
304
|
+
} else if (force || packageDigest === localDigest || wasManaged) {
|
|
300
305
|
writeFileSync(targetPath, readFileSync(sourcePath));
|
|
301
306
|
console.log(`updated ${targetPath}`);
|
|
302
307
|
changed++;
|
|
@@ -307,6 +312,59 @@ function syncTree(source, target, force) {
|
|
|
307
312
|
return changed;
|
|
308
313
|
}
|
|
309
314
|
|
|
315
|
+
function collectManagedFiles(source, target, root, managedFiles, previousManagedFiles = {}) {
|
|
316
|
+
if (!existsSync(source) || !existsSync(target)) return;
|
|
317
|
+
const sourceStat = readdirSync;
|
|
318
|
+
let entries;
|
|
319
|
+
try {
|
|
320
|
+
entries = sourceStat(source, { withFileTypes: true });
|
|
321
|
+
} catch {
|
|
322
|
+
const targetDigest = digest(target);
|
|
323
|
+
const relativePath = relative(root, target);
|
|
324
|
+
if (targetDigest === digest(source) || previousManagedFiles[relativePath] === targetDigest) managedFiles[relativePath] = targetDigest;
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
for (const entry of entries) {
|
|
328
|
+
collectManagedFiles(join(source, entry.name), join(target, entry.name), root, managedFiles, previousManagedFiles);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function managedPairs(root, roots, skills) {
|
|
333
|
+
const pairs = [];
|
|
334
|
+
const add = (source, target) => { if (existsSync(source) && existsSync(target)) pairs.push([source, target]); };
|
|
335
|
+
for (const agentRoot of roots) {
|
|
336
|
+
for (const skill of skills) add(join(SKILLS_ROOT, skill), join(agentRoot, "skills", skill));
|
|
337
|
+
add(REFERENCES_ROOT, join(agentRoot, "references"));
|
|
338
|
+
}
|
|
339
|
+
for (const group of ["clis", "runtime", "integrations"]) add(join(TOOLS_ROOT, group), join(root, "tools", group));
|
|
340
|
+
add(DESIGN_ROOT, join(root, ".maggie", "design-reference"));
|
|
341
|
+
add(MARKETPLACE_ROOT, join(root, "marketplace"));
|
|
342
|
+
add(CONTRACTS_ROOT, join(root, "contracts"));
|
|
343
|
+
add(join(TEMPLATES_ROOT, "maggiedash"), join(root, "templates", "maggiedash"));
|
|
344
|
+
return pairs;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function managedFileDigests(root, roots, skills, previousManagedFiles = {}) {
|
|
348
|
+
const managedFiles = {};
|
|
349
|
+
for (const [source, target] of managedPairs(root, roots, skills)) collectManagedFiles(source, target, root, managedFiles, previousManagedFiles);
|
|
350
|
+
return Object.fromEntries(Object.entries(managedFiles).sort(([a], [b]) => a.localeCompare(b)));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function readInstallManifest(root) {
|
|
354
|
+
try {
|
|
355
|
+
const path = join(root, STATE_DIR, "install.json");
|
|
356
|
+
return existsSync(path) ? JSON.parse(readFileSync(path, "utf8")) : {};
|
|
357
|
+
} catch {
|
|
358
|
+
return {};
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function writeInstallManifest(root, data) {
|
|
363
|
+
const stateDir = join(root, STATE_DIR);
|
|
364
|
+
mkdirSync(stateDir, { recursive: true });
|
|
365
|
+
writeFileSync(join(stateDir, "install.json"), JSON.stringify(data, null, 2) + "\n");
|
|
366
|
+
}
|
|
367
|
+
|
|
310
368
|
function install(args) {
|
|
311
369
|
const root = projectRoot(args);
|
|
312
370
|
const skills = selectedSkills(args);
|
|
@@ -336,9 +394,7 @@ function install(args) {
|
|
|
336
394
|
if (existsSync(MARKETPLACE_ROOT)) copyIfMissing(MARKETPLACE_ROOT, join(root, "marketplace"));
|
|
337
395
|
if (existsSync(CONTRACTS_ROOT)) copyIfMissing(CONTRACTS_ROOT, join(root, "contracts"));
|
|
338
396
|
if (existsSync(join(TEMPLATES_ROOT, "maggiedash"))) copyIfMissing(join(TEMPLATES_ROOT, "maggiedash"), join(root, "templates", "maggiedash"));
|
|
339
|
-
|
|
340
|
-
mkdirSync(stateDir, { recursive: true });
|
|
341
|
-
writeFileSync(join(stateDir, "install.json"), JSON.stringify({ version: PACKAGE_VERSION, agents: roots.map((item) => item.slice(root.length + 1)), skills: mergedSkillNames(root, skills), installed_at: new Date().toISOString() }, null, 2) + "\n");
|
|
397
|
+
writeInstallManifest(root, { version: PACKAGE_VERSION, agents: roots.map((item) => item.slice(root.length + 1)), skills: mergedSkillNames(root, skills), managedFiles: managedFileDigests(root, roots, skills), installed_at: new Date().toISOString() });
|
|
342
398
|
console.log(`Maggie installed in ${root}`);
|
|
343
399
|
console.log("Run `maggie doctor --project .` before using mutating workflows.");
|
|
344
400
|
}
|
|
@@ -348,6 +404,8 @@ function update(args) {
|
|
|
348
404
|
const skills = selectedSkills(args);
|
|
349
405
|
const force = args.includes("--force");
|
|
350
406
|
const roots = agentRoots(args, root);
|
|
407
|
+
const previousManifest = readInstallManifest(root);
|
|
408
|
+
const previousManagedFiles = previousManifest.managedFiles && typeof previousManifest.managedFiles === "object" ? previousManifest.managedFiles : {};
|
|
351
409
|
if (!existsSync(SKILLS_ROOT)) throw new Error("bundled skills are missing; run npm pack from the package source");
|
|
352
410
|
let updated = 0;
|
|
353
411
|
for (const agentRoot of roots) {
|
|
@@ -358,24 +416,22 @@ function update(args) {
|
|
|
358
416
|
console.log(`absent ${target} (run install to add it)`);
|
|
359
417
|
continue;
|
|
360
418
|
}
|
|
361
|
-
updated += syncTree(source, target, force);
|
|
419
|
+
updated += syncTree(source, target, force, root, previousManagedFiles);
|
|
362
420
|
}
|
|
363
|
-
if (existsSync(REFERENCES_ROOT) && existsSync(join(agentRoot, "references"))) updated += syncTree(REFERENCES_ROOT, join(agentRoot, "references"), force);
|
|
421
|
+
if (existsSync(REFERENCES_ROOT) && existsSync(join(agentRoot, "references"))) updated += syncTree(REFERENCES_ROOT, join(agentRoot, "references"), force, root, previousManagedFiles);
|
|
364
422
|
}
|
|
365
423
|
const tools = join(root, "tools");
|
|
366
424
|
if (existsSync(TOOLS_ROOT)) for (const group of ["clis", "runtime", "integrations"]) if (existsSync(join(TOOLS_ROOT, group))) {
|
|
367
425
|
const target = join(tools, group);
|
|
368
|
-
if (existsSync(target)) updated += syncTree(join(TOOLS_ROOT, group), target, force);
|
|
426
|
+
if (existsSync(target)) updated += syncTree(join(TOOLS_ROOT, group), target, force, root, previousManagedFiles);
|
|
369
427
|
}
|
|
370
428
|
if (existsSync(join(DESIGN_ROOT, "SPA-DESIGN.md")) && existsSync(join(root, ".maggie", "design-reference"))) {
|
|
371
|
-
updated += syncTree(DESIGN_ROOT, join(root, ".maggie", "design-reference"), force);
|
|
429
|
+
updated += syncTree(DESIGN_ROOT, join(root, ".maggie", "design-reference"), force, root, previousManagedFiles);
|
|
372
430
|
}
|
|
373
|
-
if (existsSync(MARKETPLACE_ROOT) && existsSync(join(root, "marketplace"))) updated += syncTree(MARKETPLACE_ROOT, join(root, "marketplace"), force);
|
|
374
|
-
if (existsSync(CONTRACTS_ROOT) && existsSync(join(root, "contracts"))) updated += syncTree(CONTRACTS_ROOT, join(root, "contracts"), force);
|
|
375
|
-
if (existsSync(join(TEMPLATES_ROOT, "maggiedash")) && existsSync(join(root, "templates", "maggiedash"))) updated += syncTree(join(TEMPLATES_ROOT, "maggiedash"), join(root, "templates", "maggiedash"), force);
|
|
376
|
-
|
|
377
|
-
mkdirSync(stateDir, { recursive: true });
|
|
378
|
-
writeFileSync(join(stateDir, "install.json"), JSON.stringify({ version: PACKAGE_VERSION, agents: roots.map((item) => item.slice(root.length + 1)), skills: mergedSkillNames(root, skills), updated_at: new Date().toISOString() }, null, 2) + "\n");
|
|
431
|
+
if (existsSync(MARKETPLACE_ROOT) && existsSync(join(root, "marketplace"))) updated += syncTree(MARKETPLACE_ROOT, join(root, "marketplace"), force, root, previousManagedFiles);
|
|
432
|
+
if (existsSync(CONTRACTS_ROOT) && existsSync(join(root, "contracts"))) updated += syncTree(CONTRACTS_ROOT, join(root, "contracts"), force, root, previousManagedFiles);
|
|
433
|
+
if (existsSync(join(TEMPLATES_ROOT, "maggiedash")) && existsSync(join(root, "templates", "maggiedash"))) updated += syncTree(join(TEMPLATES_ROOT, "maggiedash"), join(root, "templates", "maggiedash"), force, root, previousManagedFiles);
|
|
434
|
+
writeInstallManifest(root, { version: PACKAGE_VERSION, agents: roots.map((item) => item.slice(root.length + 1)), skills: mergedSkillNames(root, skills), managedFiles: managedFileDigests(root, roots, skills, previousManagedFiles), updated_at: new Date().toISOString() });
|
|
379
435
|
console.log(`Maggie update complete: ${updated} files changed`);
|
|
380
436
|
if (!force) console.log("Local files with changes were preserved. Review the output and rerun with --force only when replacement is intended.");
|
|
381
437
|
}
|
|
@@ -535,6 +591,7 @@ try {
|
|
|
535
591
|
else if (command === "browser-audit") workflowCli("maggie_browser_audit.py", args);
|
|
536
592
|
else if (command === "verification") workflowCli("maggie_verification.py", args);
|
|
537
593
|
else if (command === "dash") workflowCli("maggie_dash.py", args);
|
|
594
|
+
else if (command === "booking") workflowCli("maggie_booking.py", args);
|
|
538
595
|
else if (command === "agent-content") workflowCli("maggie_agent_content.py", args);
|
|
539
596
|
else if (command === "content") workflowCli("maggie_content.py", args);
|
|
540
597
|
else if (command === "init" || command === "install") install(args);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://maggie.noblox.app/contracts/maggie-seo/model-policy-v2.schema.json",
|
|
4
|
+
"title": "Maggie provider-routed Gemini model policy v2",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["policyVersion", "model", "fallbackModel", "promptVersion", "schemaVersion", "locale", "sourceRevision", "providerRoute"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"policyVersion": {"const": "maggie-gemini-model-policy.v2"},
|
|
9
|
+
"model": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{1,99}$"},
|
|
10
|
+
"fallbackModel": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{1,99}$"},
|
|
11
|
+
"promptVersion": {"type": "string", "minLength": 1},
|
|
12
|
+
"schemaVersion": {"type": "string", "minLength": 1},
|
|
13
|
+
"locale": {"type": ["string", "null"]},
|
|
14
|
+
"sourceRevision": {"type": ["string", "null"]},
|
|
15
|
+
"providerRoute": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["provider", "endpointEnv", "apiKeyEnv", "billingMode", "maxTokens"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"provider": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{1,63}$"},
|
|
20
|
+
"endpointEnv": {"type": "string", "pattern": "^[A-Z][A-Z0-9_]{2,63}$"},
|
|
21
|
+
"apiKeyEnv": {"type": "string", "pattern": "^[A-Z][A-Z0-9_]{2,63}$"},
|
|
22
|
+
"billingMode": {"enum": ["byok", "managed"]},
|
|
23
|
+
"maxTokens": {"type": "integer", "minimum": 1, "maximum": 32768}
|
|
24
|
+
},
|
|
25
|
+
"additionalProperties": false
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"additionalProperties": false
|
|
29
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://maggie.noblox.app/contracts/maggie-service-booking/notification-lifecycle-v1.schema.json",
|
|
4
|
+
"title": "Maggie booking notification lifecycle v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schemaVersion", "bookingId", "state", "events"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"schemaVersion": {"const": "maggie-booking-notification-lifecycle.v1"},
|
|
9
|
+
"bookingId": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,119}$"},
|
|
10
|
+
"state": {"enum": ["received", "pending-confirmation", "confirmed", "alternative-proposed", "cancelled", "failed"]},
|
|
11
|
+
"events": {
|
|
12
|
+
"type": "array",
|
|
13
|
+
"items": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"required": ["eventId", "type", "occurredAt", "idempotencyKey", "status", "recipientRole"],
|
|
16
|
+
"properties": {
|
|
17
|
+
"eventId": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,119}$"},
|
|
18
|
+
"type": {"enum": ["acknowledgement", "studio-notification", "confirmation", "alternative", "cancellation"]},
|
|
19
|
+
"occurredAt": {"type": "string", "format": "date-time"},
|
|
20
|
+
"idempotencyKey": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$"},
|
|
21
|
+
"status": {"enum": ["pending", "sent", "failed", "suppressed"]},
|
|
22
|
+
"recipientRole": {"enum": ["customer", "studio"]}
|
|
23
|
+
},
|
|
24
|
+
"additionalProperties": false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"additionalProperties": false
|
|
29
|
+
}
|
|
@@ -24,6 +24,9 @@ frontend framework.
|
|
|
24
24
|
for translation keys and ordered migration;
|
|
25
25
|
- [`translation-cache-policy-v1.json`](translation-cache-policy-v1.json):
|
|
26
26
|
restart-after-out-of-band-write evidence.
|
|
27
|
+
- [`booking-email-templates-v1.json`](booking-email-templates-v1.json):
|
|
28
|
+
tenant-scoped Booking/Resend event templates, approved variables, and
|
|
29
|
+
server-only provider/recipient policy.
|
|
27
30
|
- [`quality-contracts.md`](quality-contracts.md): reviewable inputs for
|
|
28
31
|
disjoint page inventory, variant/media checks, binding resolution, repair
|
|
29
32
|
convergence, and database target identity.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://maggiedash.topy.ai/contracts/booking-access-v1.json",
|
|
4
|
+
"title": "MaggieDash Booking access management v1",
|
|
5
|
+
"schemaVersion": "maggiedash-booking-access.v1",
|
|
6
|
+
"apiPrefix": "/api/maggie/booking/access.json",
|
|
7
|
+
"auth": { "administratorSession": "host-session-cookie", "emailPassword": true, "passkeys": false, "resendInvitation": true },
|
|
8
|
+
"roles": ["manager", "staff", "finance"],
|
|
9
|
+
"permissions": [
|
|
10
|
+
"calendar.read", "bookings.read", "bookings.manage", "customers.read",
|
|
11
|
+
"catalog.read", "catalog.manage", "staff.read", "staff.manage",
|
|
12
|
+
"resources.read", "resources.manage", "schedule.read", "schedule.manage",
|
|
13
|
+
"payments.read", "payments.manage", "refunds.manage", "settings.read",
|
|
14
|
+
"settings.manage", "reports.read", "audit.read"
|
|
15
|
+
],
|
|
16
|
+
"endpointIds": ["booking.access.read", "booking.access.create", "booking.access.update", "booking.access.revoke"],
|
|
17
|
+
"canonicalRoutes": {
|
|
18
|
+
"booking.access.read": "GET /access.json",
|
|
19
|
+
"booking.access.create": "POST /access.json",
|
|
20
|
+
"booking.access.update": "PATCH /access.json",
|
|
21
|
+
"booking.access.revoke": "DELETE /access.json"
|
|
22
|
+
},
|
|
23
|
+
"policy": {
|
|
24
|
+
"tenantScoped": true,
|
|
25
|
+
"disabledAccountsStayDisabled": true,
|
|
26
|
+
"passwordMinimumLength": 12,
|
|
27
|
+
"auditActions": ["booking.access.created", "booking.access.updated", "booking.access.invited", "booking.access.password-reset", "booking.access.revoked"]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://maggiedash.topy.ai/contracts/booking-customer-surface-v1.json",
|
|
4
|
+
"title": "MaggieDash Booking customer surface v1",
|
|
5
|
+
"schemaVersion": "maggiedash-booking-customer-surface.v1",
|
|
6
|
+
"apiPrefix": "/api/maggie/booking/public",
|
|
7
|
+
"pagePath": "/_maggie/booking/book",
|
|
8
|
+
"auth": "public-noindex",
|
|
9
|
+
"noindex": true,
|
|
10
|
+
"hostResponsibilities": ["tenant-binding", "origin-and-csrf", "rate-limit", "bot-protection", "opaque-payment-return-token", "sealed-customer-manage-token", "provider-and-notification-adapters"],
|
|
11
|
+
"endpointIds": [
|
|
12
|
+
"booking.public.catalog.read",
|
|
13
|
+
"booking.public.availability.read",
|
|
14
|
+
"booking.public.waitlist.join",
|
|
15
|
+
"booking.public.hold.create",
|
|
16
|
+
"booking.public.checkout.start",
|
|
17
|
+
"booking.public.payment-return.read",
|
|
18
|
+
"booking.public.manage.read",
|
|
19
|
+
"booking.public.booking-cancel",
|
|
20
|
+
"booking.public.booking-reschedule"
|
|
21
|
+
],
|
|
22
|
+
"canonicalRoutes": {
|
|
23
|
+
"booking.public.catalog.read": "GET /catalog.json",
|
|
24
|
+
"booking.public.availability.read": "GET /availability.json?serviceVariantId={serviceVariantId}&date={date}",
|
|
25
|
+
"booking.public.waitlist.join": "POST /waitlist.json",
|
|
26
|
+
"booking.public.hold.create": "POST /holds.json",
|
|
27
|
+
"booking.public.checkout.start": "POST /checkout.json",
|
|
28
|
+
"booking.public.payment-return.read": "GET /payment-return.json?token={token}",
|
|
29
|
+
"booking.public.manage.read": "GET /manage.json?token={token}",
|
|
30
|
+
"booking.public.booking-cancel": "POST /bookings/{id}/cancel.json",
|
|
31
|
+
"booking.public.booking-reschedule": "POST /bookings/{id}/reschedule.json"
|
|
32
|
+
},
|
|
33
|
+
"responsePolicy": {
|
|
34
|
+
"cacheControl": "no-store",
|
|
35
|
+
"robots": "noindex",
|
|
36
|
+
"paymentRedirectProof": false,
|
|
37
|
+
"mutationPolicy": "all-public-mutations-require-idempotency-key-and-replay-original-result",
|
|
38
|
+
"holdInputPolicy": ["valid-email", "booking-update-consent"],
|
|
39
|
+
"sensitiveFields": ["projectId", "customerId", "providerPaymentRef", "token", "secret", "rawPayload"]
|
|
40
|
+
},
|
|
41
|
+
"tokenPolicy": {
|
|
42
|
+
"paymentReturn": "short-lived",
|
|
43
|
+
"customerManage": "30-day-sealed",
|
|
44
|
+
"rawTokenStorage": false
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://maggiedash.topy.ai/contracts/booking-email-templates-v1.json",
|
|
4
|
+
"title": "MaggieDash Booking email templates v1",
|
|
5
|
+
"schemaVersion": "maggiedash-booking-email-templates.v1",
|
|
6
|
+
"apiPrefix": "/api/maggie/booking/email-templates.json",
|
|
7
|
+
"auth": {
|
|
8
|
+
"managerSession": "host-session-cookie",
|
|
9
|
+
"emailPassword": true,
|
|
10
|
+
"passkeys": false
|
|
11
|
+
},
|
|
12
|
+
"roles": ["owner", "admin", "manager"],
|
|
13
|
+
"events": [
|
|
14
|
+
{ "id": "booking.confirmed", "variables": ["customerName", "serviceName", "when", "locationName", "bookingId", "manageUrl", "siteName"] },
|
|
15
|
+
{ "id": "booking.rescheduled", "variables": ["customerName", "serviceName", "when", "locationName", "bookingId", "manageUrl", "siteName"] },
|
|
16
|
+
{ "id": "booking.cancelled", "variables": ["customerName", "serviceName", "when", "locationName", "bookingId", "reason", "siteName"] },
|
|
17
|
+
{ "id": "booking.reminder", "variables": ["customerName", "serviceName", "when", "locationName", "bookingId", "manageUrl", "siteName"] },
|
|
18
|
+
{ "id": "payment.action_required", "variables": ["customerName", "serviceName", "bookingId", "actionUrl", "siteName"] },
|
|
19
|
+
{ "id": "payment.refunded", "variables": ["customerName", "serviceName", "bookingId", "siteName"] },
|
|
20
|
+
{ "id": "booking.waitlist.available", "variables": ["customerName", "serviceName", "when", "locationName", "bookingUrl", "siteName"] }
|
|
21
|
+
],
|
|
22
|
+
"endpointIds": ["booking.email-templates.read", "booking.email-templates.draft", "booking.email-templates.publish", "booking.email-templates.rollback"],
|
|
23
|
+
"canonicalRoutes": {
|
|
24
|
+
"booking.email-templates.read": "GET /email-templates.json",
|
|
25
|
+
"booking.email-templates.draft": "PATCH /email-templates.json?action=draft",
|
|
26
|
+
"booking.email-templates.publish": "PATCH /email-templates.json?action=publish",
|
|
27
|
+
"booking.email-templates.rollback": "PATCH /email-templates.json?action=rollback"
|
|
28
|
+
},
|
|
29
|
+
"policy": {
|
|
30
|
+
"tenantScoped": true,
|
|
31
|
+
"auditRequired": true,
|
|
32
|
+
"idempotencyRequiredForMutations": true,
|
|
33
|
+
"syntax": "{{variable}}",
|
|
34
|
+
"maxHtmlLength": 50000,
|
|
35
|
+
"providerCredentialsRemainServerOnly": true,
|
|
36
|
+
"recipientDataNeverReturned": true
|
|
37
|
+
}
|
|
38
|
+
}
|