@rubytech/create-maxy-code 0.1.283 → 0.1.284

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.283",
3
+ "version": "0.1.284",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -24,19 +24,19 @@ function seedSkill(plugin, skill, body) {
24
24
  const fm = (name, description) => `---\nname: ${name}\ndescription: ${description}\n---\n\n# ${name}\n`;
25
25
  describe("searchSkills", () => {
26
26
  it("description-only match returns the owning plugin + canonical slug path top-ranked", () => {
27
- seedSkill("quotation", "quotation", fm("quotation", "Use when an operator's own way of pricing jobs needs to become something the assistant can apply — set up my pricing, learn how I quote, re-learning their method from past quotes."));
27
+ seedSkill("siteoffice-job", "quotation", fm("quotation", "Use when an operator's own way of pricing jobs needs to become something the assistant can apply — set up my pricing, learn how I quote, re-learning their method from past quotes."));
28
28
  seedSkill("admin", "qr-code", fm("qr-code", "Generate QR codes. Trigger phrases: create a QR, make a QR code."));
29
29
  seedSkill("admin", "datetime", fm("datetime", "Timezone queries and relative-date arithmetic."));
30
30
  const hits = searchSkills(root, "pricing algorithm");
31
31
  expect(hits.length).toBeGreaterThan(0);
32
- expect(hits[0].pluginName).toBe("quotation");
32
+ expect(hits[0].pluginName).toBe("siteoffice-job");
33
33
  expect(hits[0].skillName).toBe("quotation");
34
34
  expect(hits[0].file).toBe("skills/quotation/SKILL.md");
35
35
  });
36
36
  it("a common trigger word does not let a noise skill outrank the distinctive match", () => {
37
37
  // "create" is a noise term (appears in many descriptions); "quote" is
38
38
  // distinctive. quotation must still win for "create a quote".
39
- seedSkill("quotation", "quotation", fm("quotation", "Onboarding a new business's pricing, re-learning their method from past quotes, checking the method reproduces past quotes."));
39
+ seedSkill("siteoffice-job", "quotation", fm("quotation", "Onboarding a new business's pricing, re-learning their method from past quotes, checking the method reproduces past quotes."));
40
40
  seedSkill("admin", "qr-code", fm("qr-code", "Create a QR code. Trigger: create a QR, create a code."));
41
41
  seedSkill("admin", "publish-site", fm("publish-site", "Create and publish a static site online."));
42
42
  seedSkill("admin", "deck-pages", fm("deck-pages", "Create a slide deck of pages."));
@@ -1 +1 @@
1
- {"version":3,"file":"skill-search.test.js","sourceRoot":"","sources":["../../src/__tests__/skill-search.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"skill-search.test.js","sourceRoot":"","sources":["../../src/__tests__/skill-search.test.ts"],"names":[],"mappings":"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"}
@@ -18,7 +18,7 @@ It composes two patterns you must read alongside it:
18
18
  - **`cloudflare/references/d1-data-capture.md`** — the form → Pages Function → D1 → sweep mechanics, and the token-scope breakage (§ 0). Everything about minting the Pages-+-D1 token, `wrangler.toml`, and `wrangler d1 execute --remote` lives there; this skill does not repeat it. Note its rule that the D1 **query** endpoint rejects a D1-Read token — every `wrangler d1 execute` here uses a **D1-Edit** token, reads included.
19
19
  - **`business-assistant/references/invoicing.md`** — PDF generation via `browser-navigate` + `browser-pdf-save` (its step 4). The **deploy-time base render** (§ 5) uses that exact pattern. Dispatch (§ 6) does **not** — it stamps the persisted base with a PDF library, no browser and no network.
20
20
 
21
- Auth for every `wrangler`/API call is a minted narrow token per `cloudflare/references/api.md` — detect the master's type (`cfat_…` account-scoped vs `cfut_…` user-scoped) and route endpoints by prefix per that reference. Deploy mechanics are `cloudflare/references/hosting-sites.md`.
21
+ Auth for every `wrangler`/API call is the **reused per-scope narrow token** of `cloudflare/references/api.md` (one deterministic token per scope minted once if absent, persisted to the secrets file, then loaded and reused; Task 732), routed by the master's type (`cfat_…` account-scoped vs `cfut_…` user-scoped) by prefix per that reference. Deploy mechanics are `cloudflare/references/hosting-sites.md`.
22
22
 
23
23
  ---
24
24
 
@@ -91,7 +91,9 @@ CLOUDFLARE_API_TOKEN="${MINTED_ACCESS}" curl -sS \
91
91
  | jq '.result | length'
92
92
  ```
93
93
 
94
- **Token scope.** Gating needs one minted narrow token (per `api.md` § Minting a narrow token, routed by prefix with mint→verify backoff as § 8.8 notes) carrying **Access application + policy write** *and* **`Access: Identity Providers Write`**. Resolve those permission-group ids the same way `d1-data-capture.md` § 0 resolves the Pages/D1 ids — one `GET …/tokens/permission_groups`. Token *creation* stays an account-level concern; this skill names the scopes and consumes whatever token type the account supplies. `${MINTED_ACCESS}` below is that token.
94
+ **Token scope.** Gating needs the **reused per-scope Access token** (`<brand>-access` — minted once if absent, persisted, then loaded and reused per `api.md` § Provisioning and reusing a stable per-scope token; routed by prefix, with the mint→verify backoff of § 8.8 applying only on the mint-once path) carrying **Access application + policy write** *and* **`Access: Identity Providers Write`**. Resolve those permission-group ids the same way `d1-data-capture.md` § 0 resolves the Pages/D1 ids — one `GET …/tokens/permission_groups` — but **"Access: Apps and Policies Write" can return duplicate ids** from that call, and minting with the wrong variant poisons the token: it then returns `10000` on **every** call, **reads included**. So **verify the token with a read before relying on it** — a `GET …/access/apps` that returns `10000` is the wrong-id signature (distinct from § 8.8's transient post-mint `10000`, which clears within ~30s); re-resolve and mint with the other variant. Token *creation* stays an account-level concern; this skill names the scopes and consumes whatever token type the account supplies. `${MINTED_ACCESS}` below is that token.
95
+
96
+ **Access-app updates are `PUT`, not `PATCH`.** When you update an existing Access app — most often to widen its destinations — send `PUT …/access/apps/{app_id}` with the **full app body** (re-supply `name`, `destinations`, `type`, `session_duration`, …). `PATCH` returns `10405 "Method not allowed for this authentication scheme"` under API-token auth; there is no working partial-update verb (`api.md` § Access).
95
97
 
96
98
  **Create IdP → app → policy, in that order:**
97
99
 
@@ -103,7 +105,7 @@ CLOUDFLARE_API_TOKEN="${MINTED_ACCESS}" curl -sS \
103
105
  --data '{"name":"One-time PIN","type":"onetimepin"}' | jq '{ok:.success, type:.result.type}'
104
106
  ```
105
107
 
106
- 2. **Create the Access application** scoped to the deployed page's hostname **and** path (the `<project>.pages.dev` host and the signing document's path), so the gate covers exactly that document.
108
+ 2. **Create the Access application** scoped to the deployed host's **document path(s) `and` `/api/*`** the signing document's path(s) plus the capture endpoints `POST /api/accept` and `GET /api/status` (§ 4). Leave `/` and the static assets **public**. Gating only the document path is a **bypass**: with `/api/*` open, anyone can `POST /api/accept` and forge an acceptance, and `GET /api/status` is readable ungated. Leaving `/` public is deliberate and load-bearing — it is exactly what lets the neutral root portal (below) load **without a PIN**; the document path(s) and `/api/*` are the only gated destinations.
107
109
  3. **Attach a policy** whose `include` allowlist names the **signer's email** (and the business owner's). Only those addresses can authenticate and view the page.
108
110
 
109
111
  The IdP call shapes are given inline above; the Access **app** and **policy** endpoints live in `api.md`'s endpoint map, and the permission-group ids are resolved by the `api.md` method (`GET …/tokens/permission_groups`).
@@ -112,7 +114,9 @@ The IdP call shapes are given inline above; the Access **app** and **policy** en
112
114
 
113
115
  **Dispatch is unaffected (§ 6).** The sweep stamps the **persisted base** (`base.pdf`, § 1), never by fetching the gated page, so a gate never turns a dispatched "PDF" into an Access login screen — § 8.3 makes that structural. Keep it in mind whenever a gated document is swept.
114
116
 
115
- **Root landing pagea neutral portal, required for every gated site.** The § 1 project ships only the document page(s), so there is **no `/` page**. Cloudflare Access routinely lands a visitor on the **bare root `/`**: after login it does not always deep-link back to the requested path, and its logout endpoint (`/cdn-cgi/access/logout`) returns the user to `/?__cf_access_message=logged_out`. With no `/` page both land on a **404** a client-facing portal showing a raw 404 the moment a signer logs out. So a gated site **must** ship a root `index.html`. It is a **neutral portal**, not a redirect: a single host can carry **many** gated documents (each its own slug/path/`DOC_REF` § 3's D1 table is shared across a brand's documents, and § 1a scopes the Access app to host **and** path), and they all share one `/`. The root **cannot know which document** a visitor came from Access provides **no logout `returnTo`/redirect parameter**, so the root cannot route by origin — so it must name no document at all.
117
+ **Logout links carry `returnTo` point them at the public root (the primary loop-fix).** Cloudflare Access **does** honour a logout redirect: `GET /cdn-cgi/access/logout?returnTo=<https origin root>` 302s straight to that root and **strips** the stale `__cf_access_message` param. The parameter is **camelCase `returnTo` only** `return_to` and `redirect_url` are ignored. So build every signer-facing logout link as `/cdn-cgi/access/logout?returnTo=https://<host>/`, with `returnTo` pointing at the **public root**, never at the gated document path (which would just re-challenge for a PIN). A logout link built this way lands the signer cleanly on the public root with no stale paramthis is what actually breaks the "enter PIN logged-out screen" loop.
118
+
119
+ **Root landing page — a neutral portal, the belt-and-suspenders backstop.** The § 1 project ships only the document page(s), so there is **no `/` page** unless you add one. Even with `returnTo` available, Cloudflare Access still occasionally lands a visitor on the **bare root `/`** — a logout link that omitted `returnTo`, or a post-login that did not deep-link back to the requested path — and with no `/` page that is a **404** on a client-facing portal. So a gated site **must** ship a root `index.html` as the backstop for those bare-root landings. It is a **neutral portal**, not a redirect: a single host can carry **many** gated documents (each its own slug/path/`DOC_REF` — § 3's D1 table is shared across a brand's documents, and § 1a gates each document's path **and** `/api/*`), and they all share one `/`. The root names **no** document — logout links point `returnTo` at the bare root, not at any document path — so a single portal serving every document's bare-root landing must name none.
116
120
 
117
121
  ```html
118
122
  <!doctype html>
@@ -483,6 +487,8 @@ When the durable routine (§ 8.2) runs, or when the operator asks for new accept
483
487
 
484
488
  The acceptance flow is proven end-to-end on the **live** Pages deployment — the D1 row, not the POST status, is the contract. Every `wrangler d1 execute` uses the **D1-Edit** token (reads included; the query endpoint rejects D1 Read):
485
489
 
490
+ **When the document is Access-gated (§ 1a), `/api/*` is gated too** — so the `curl` POSTs to `/api/accept` and the `GET /api/status` below are themselves **Access-challenged (302 to login), not direct 200s**. Run this end-to-end verification either **before** the gate is applied, or from a context that can authenticate to Access — the same authenticated-session requirement § 5's base render already carries. The `wrangler d1 execute` rows reach D1 through the Cloudflare API, not the gated host, so they verify unchanged.
491
+
486
492
  ```bash
487
493
  # 1. POST a first acceptance to the live Function.
488
494
  curl -sS -X POST -H "Content-Type: application/json" \
@@ -587,6 +593,8 @@ A hand-built document ships the old bare "agree to terms" checkbox (no intent/co
587
593
 
588
594
  A `cfut_…` master at an account endpoint returns `9109`; a freshly-minted token returns `10000` for a few seconds. Both are explicit, reproducible API responses — `api.md` § Token type routes by prefix and § verify-with-backoff absorbs the `10000` window. Diagnostic path: the verify call's JSON `errors[].code`.
589
595
 
596
+ **A `10000` that does *not* clear is a different failure — the wrong permission-group id.** The "Access: Apps and Policies Write" group can return **duplicate ids** from `GET …/tokens/permission_groups` (§ 1a), and minting with the wrong variant poisons the Access token: it returns `10000` on **every** call, **reads included**, and never clears. The transient post-mint `10000` clears within ~30s of backoff; a `10000` that **persists on a `GET …/access/apps` read** is the poisoned-token signature — re-resolve the permission-group id and mint with the other variant. This is why § 1a verifies a freshly-minted Access token with a read before relying on it.
597
+
590
598
  ### 8.9 Access gate active with zero enabled IdPs (no-event lockout)
591
599
 
592
600
  Only relevant when the document is Access-gated (§ 1a). An Access app is active on the page while the org has **no** identity provider, so the login screen offers no method and everyone — operator included — is locked out. It is a **no-event** failure: the gate emits no log and does not reproduce until someone tries to log in, by which point they are already locked out. Detection is a **standing audit reconciling active Access apps against enabled login methods**:
@@ -629,3 +637,19 @@ Two distinct regressions of the § 1a neutral portal, both caught by the § 8.10
629
637
  - **Sticky-param interstitial.** The root **branches on `__cf_access_message=logged_out`** — showing a "signed out" interstitial when the param is present. Because Cloudflare preserves that param through the **entire re-login redirect**, a re-authenticated signer is landed back on `/` with the **stale** param and shown a logged-out screen *while logged in*. Signature: any reference to `__cf_access_message` in the root. The fix is the neutral portal — it makes no auth-state claim, so it is correct in every state.
630
638
 
631
639
  The neutral portal must therefore contain **no** document-specific path **and no** auth-state claim; it shows the same content unconditionally.
640
+
641
+ ### 8.12 Per-deployment preview URLs ungated (confidential-document leak, no-event)
642
+
643
+ Only relevant when the document is Access-gated (§ 1a). A host-scoped Access app covers the **canonical host** but **not** Cloudflare's per-deployment hostnames: every Pages deploy also serves at `https://<hash>.<project>.pages.dev/…`, and that hostname is **not** behind the host-scoped gate. Confirmed on a live gated build: `https://<hash>.<project>.pages.dev/<doc-path>` served **200, ungated**, while the canonical host was gated — so a confidential document leaks via any deployment-hash URL. It is a **no-event** failure: the leaked read logs nothing and does not reproduce until someone holds a preview URL. Remedy — one of:
644
+
645
+ - **Disable preview deployments** for the project, so only the production host exists; or
646
+ - **Gate `*.pages.dev`** with an Access app, so the per-deployment hostnames are challenged too.
647
+
648
+ Signal — a deploy-time / standing check tied to gating: for every gated project, assert preview deployments are disabled (or `*.pages.dev` is gated), then confirm by fetching one live `<hash>.<project>.pages.dev/<doc-path>` and asserting it is **challenged (302 to the Access login), not 200**:
649
+
650
+ ```bash
651
+ # A 200 here is the leak. Expect a 302 to the Access login.
652
+ curl -sS -o /dev/null -w '%{http_code}\n' "https://<hash>.<project>.pages.dev/<doc-path>"
653
+ ```
654
+
655
+ A project with preview deployments enabled and an Access app scoped only to the canonical host is the failure signature.
@@ -186,7 +186,7 @@ The endpoints below are the **account-scoped** (`cfat_…`) family — the commo
186
186
  - `GET /accounts/${ACC}/tokens` — list all tokens on the account (id, name, status, `expires_on`). Use it to find a per-scope token to reuse and to enumerate strays before a reconcile. User-scoped twin: `GET /user/tokens`.
187
187
  - `POST /accounts/${ACC}/tokens` — mint a per-scope token (see § Provisioning and reusing a stable per-scope token). Mint only when the list above shows the scope absent.
188
188
  - `DELETE /accounts/${ACC}/tokens/{id}` — revoke a token by id (the reconcile/cleanup verb). User-scoped twin: `DELETE /user/tokens/{id}`. Route by the master's prefix exactly as verify/mint do — `cfat_…` → `/accounts/${ACC}/tokens…`, `cfut_…` → `/user/tokens…`; the cross-type call returns `9109`.
189
- - `GET /accounts/${ACC}/tokens/permission_groups` — resolve permission-group ids for scoping.
189
+ - `GET /accounts/${ACC}/tokens/permission_groups` — resolve permission-group ids for scoping. **Caveat — some groups return duplicate ids.** A single named group (observed for **"Access: Apps and Policies Write"**) can appear **more than once** in this list under different ids. Minting with the wrong variant yields a token that returns `10000` on **every** call, **reads included** — a permanently poisoned token, not the transient post-mint `10000` window of § A freshly-minted token. **Verify a freshly-minted Access token with a read** (e.g. `GET /accounts/${ACC}/access/apps`) before relying on it: a `10000` on that read means the wrong permission-group id was used — re-resolve and mint with the other variant.
190
190
 
191
191
  ### DNS records (zone-scoped; needs **Zone · DNS · Edit**)
192
192
  - `GET /zones/${ZONE}/dns_records` — list / find a record id.
@@ -210,6 +210,7 @@ curl -sS -X POST -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: applicati
210
210
 
211
211
  ### Access (Zero Trust) applications + policies
212
212
  - `POST /accounts/${ACC}/access/apps` and `POST /accounts/${ACC}/access/apps/{app_id}/policies` — author an Access application + policy programmatically. The dashboard click-path in `dashboard-guide.md` § "Author an Access policy" is the alternative for operators who prefer to click.
213
+ - `PUT /accounts/${ACC}/access/apps/{app_id}` — update an existing Access application (e.g. to widen its destinations). The body is the **full app object**, not a partial patch — re-supply `name`, `destinations`/`domain`, `type`, `session_duration`, and the rest, not just the changed fields. **`PATCH` does not work under API-token auth**: it returns `10405 "Method not allowed for this authentication scheme"`. Use `PUT` with the complete body; there is no working partial-update verb for this auth scheme.
213
214
 
214
215
  ---
215
216