@wix/agent-skills 1.31.1 → 1.32.0
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.md +4 -12
- package/package.json +1 -1
- package/skills/wix-app/SKILL.md +39 -15
- package/skills/wix-app/references/BACKEND_API.md +1 -1
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +11 -8
- package/skills/wix-app/references/SDK_MODULE_MAP.md +1 -1
- package/skills/wix-app/references/STORES_VERSIONING.md +24 -2
- package/skills/wix-app/references/dashboard-page/DATA_SOURCES.md +9 -13
- package/skills/wix-app/references/dashboard-page/IMAGE_RENDERING.md +61 -0
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +54 -64
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +14 -21
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +37 -30
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +43 -29
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +40 -39
- package/skills/wix-app/references/editor-react-component/PARTS.md +8 -6
- package/skills/wix-app/references/editor-react-component/PROPS-VS-CSS.md +22 -2
- package/skills/wix-app/scripts/scan-a11y-eslint.cjs +39 -16
- package/skills/wix-app/scripts/sdk-curated.json +45 -0
- package/skills/wix-app/scripts/sdk-lookup.cjs +1180 -0
- package/skills/wix-base44-connector/SKILL.md +4 -2
- package/skills/wix-base44-connector/scripts/utils.cjs +5 -2
- package/skills/wix-headless-kit/SKILL.md +20 -8
- package/skills/wix-headless-kit/guides/api-run.md +92 -68
- package/skills/wix-headless-kit/guides/existing-site.md +6 -0
- package/skills/wix-headless-kit/install/AGENTS.md +3 -2
- package/skills/wix-headless-kit/install/attach.mjs +32 -27
- package/skills/wix-headless-kit/install/background.mjs +32 -0
- package/skills/wix-headless-kit/install/bootstrap.mjs +28 -11
- package/skills/wix-headless-kit/install/check.mjs +2 -2
- package/skills/wix-headless-kit/install/context.mjs +49 -5
- package/skills/wix-headless-kit/install/deploy.mjs +33 -8
- package/skills/wix-headless-kit/install/env-ignore.mjs +16 -0
- package/skills/wix-headless-kit/install/install.mjs +95 -0
- package/skills/wix-headless-kit/install/pins.json +1 -1
- package/skills/wix-headless-kit/install/proc.mjs +46 -0
- package/skills/wix-headless-kit/install/setup.mjs +16 -25
- package/skills/wix-headless-kit/install/templates.mjs +2 -2
- package/skills/wix-headless-templates/blog/app/components/blog/RichContent.tsx +3 -2
- package/skills/wix-headless-templates/blog/project/package-lock.json +1287 -1299
- package/skills/wix-headless-templates/blog/project/package.json +1 -1
- package/skills/wix-headless-templates/blog/project/src/components/blog/RichContent.tsx +3 -2
- package/skills/wix-headless-templates/blog/rest/authors.ts +5 -5
- package/skills/wix-headless-templates/blog/rest/comments.ts +4 -4
- package/skills/wix-headless-templates/blog/rest/post-likes.ts +3 -3
- package/skills/wix-headless-templates/blog/rest/posts.ts +4 -4
- package/skills/wix-headless-templates/blog/rest/taxonomy.ts +5 -5
- package/skills/wix-headless-templates/blog/seed/seed-blog.mjs +2 -1
- package/skills/wix-headless-templates/bookings/rest/booking.ts +4 -4
- package/skills/wix-headless-templates/bookings/rest/services.ts +4 -4
- package/skills/wix-headless-templates/bookings/seed/seed-bookings.mjs +2 -1
- package/skills/wix-headless-templates/cms/rest/collections.ts +3 -3
- package/skills/wix-headless-templates/cms/rest/items.ts +4 -4
- package/skills/wix-headless-templates/cms/seed/seed-cms.mjs +2 -1
- package/skills/wix-headless-templates/donations/rest/campaigns.ts +4 -4
- package/skills/wix-headless-templates/donations/rest/donate.ts +3 -3
- package/skills/wix-headless-templates/donations/seed/seed-donations.mjs +2 -1
- package/skills/wix-headless-templates/events/rest/events.ts +4 -4
- package/skills/wix-headless-templates/events/rest/orders.ts +4 -4
- package/skills/wix-headless-templates/events/rest/registration.ts +4 -4
- package/skills/wix-headless-templates/events/seed/seed-events.mjs +2 -1
- package/skills/wix-headless-templates/faq/rest/faq.ts +4 -4
- package/skills/wix-headless-templates/faq/seed/seed-faq.mjs +2 -1
- package/skills/wix-headless-templates/forms/rest/forms.ts +4 -4
- package/skills/wix-headless-templates/forms/rest/submissions.ts +4 -4
- package/skills/wix-headless-templates/forms/seed/seed-forms.mjs +2 -1
- package/skills/wix-headless-templates/members/rest/auth.ts +3 -3
- package/skills/wix-headless-templates/members/rest/members.ts +4 -4
- package/skills/wix-headless-templates/members/seed/seed-members.mjs +2 -1
- package/skills/wix-headless-templates/portfolio/rest/portfolio.ts +4 -4
- package/skills/wix-headless-templates/portfolio/seed/seed-portfolio.mjs +2 -1
- package/skills/wix-headless-templates/pricing-plans/rest/plan-access.ts +3 -3
- package/skills/wix-headless-templates/pricing-plans/rest/plans.ts +4 -4
- package/skills/wix-headless-templates/pricing-plans/rest/purchase.ts +3 -3
- package/skills/wix-headless-templates/pricing-plans/seed/seed-pricing-plans.mjs +2 -1
- package/skills/wix-headless-templates/rentals/rest/rentals.ts +4 -4
- package/skills/wix-headless-templates/rentals/seed/seed-rentals.mjs +2 -1
- package/skills/wix-headless-templates/restaurants/rest/menu.ts +4 -4
- package/skills/wix-headless-templates/restaurants/rest/ordering.ts +5 -5
- package/skills/wix-headless-templates/restaurants/rest/reservations.ts +4 -4
- package/skills/wix-headless-templates/restaurants/seed/seed-restaurants.mjs +2 -1
- package/skills/wix-headless-templates/shared/capabilities/site-search/rest/search.ts +4 -4
- package/skills/wix-headless-templates/shared/capabilities/site-search/seed/install.mjs +6 -4
- package/skills/wix-headless-templates/shared/seed/main.mjs +15 -0
- package/skills/wix-headless-templates/shared/seed/wix-cli.mjs +43 -11
- package/skills/wix-headless-templates/storefront/INSTRUCTIONS.md +1 -1
- package/skills/wix-headless-templates/storefront/rest/cart.ts +5 -5
- package/skills/wix-headless-templates/storefront/rest/catalog.ts +5 -5
- package/skills/wix-headless-templates/storefront/seed/SEED.md +27 -9
- package/skills/wix-headless-templates/storefront/seed/seed-store.mjs +2 -1
- package/skills/wix-manage/SKILL.md +31 -9
- package/skills/wix-manage/references/app-installation/install-wix-apps.md +5 -1
- package/skills/wix-manage/references/app-installation/list-installed-apps.md +19 -9
- package/skills/wix-manage/references/automations/actions.md +134 -0
- package/skills/wix-manage/references/automations/activation-status.md +64 -48
- package/skills/wix-manage/references/automations/api-catalog.md +213 -77
- package/skills/wix-manage/references/automations/automation-model.md +199 -0
- package/skills/wix-manage/references/automations/bracket-expressions.md +132 -0
- package/skills/wix-manage/references/automations/build-and-manage-automations.md +305 -26
- package/skills/wix-manage/references/automations/triggers.md +190 -0
- package/skills/wix-manage/references/bookings/booking-service-policy-setup.md +3 -0
- package/skills/wix-manage/references/bookings/external-calendar-integration.md +3 -0
- package/skills/wix-manage/references/bookings/multi-resource-service-creation.md +3 -0
- package/skills/wix-manage/references/cms/cms-data-items-crud.md +11 -2
- package/skills/wix-manage/references/cms/cms-publishing-flow.md +63 -16
- package/skills/wix-manage/references/collaborators/manage-site-collaborators.md +128 -0
- package/skills/wix-manage/references/forms/forms-dashboard-navigation.md +3 -1
- package/skills/wix-manage/references/forms/read-forms-and-submissions.md +277 -0
- package/skills/wix-manage/references/forms/update-form.md +8 -0
- package/skills/wix-manage/references/pricing-plans/create-and-update-pricing-plans.md +3 -0
- package/skills/wix-manage/references/restaurants/wix-restaurants-setup.md +3 -0
- package/skills/wix-manage/references/seo/manage-url-redirects.md +33 -4
- package/skills/wix-manage/references/sites/create-headless-site.md +16 -50
- package/skills/wix-manage/references/sites/manage-oauth-apps.md +4 -4
- package/skills/wix-manage/references/sites/read-site-context.md +4 -0
- package/skills/wix-manage/references/sites/upload-static-site.md +368 -261
- package/skills/wix-manage/references/stores/add-store-pages-to-site.md +3 -0
- package/skills/wix-manage/references/stores/change-store-currency.md +3 -0
- package/skills/wix-manage/references/stores/create-product-catalog-v3.md +3 -0
- package/skills/wix-manage/references/stores/find-products-query-and-search-catalog-v3.md +3 -0
- package/skills/wix-manage/references/stores/update-product-with-options.md +192 -22
- package/skills/wix-vibe-headless/references/shared/app/components/WixManageBanner.jsx +3 -3
|
@@ -111,7 +111,7 @@ const wx = require(require("path").resolve(P));
|
|
|
111
111
|
- `wx.bash(cmd)` — shell over saved files (GNU grep/sed; awk is mawk; no rg)
|
|
112
112
|
- `wx.spec(docsUrl | code)` — a method's exact schema, plus the titles of the docs' own request examples saved at `examplesPath`; pass a hit's docsUrl (direct load), or raw code to query the index yourself
|
|
113
113
|
- `wx.mgmtRecipes(q?)` — management-recipe index; no arg → categories, a word → matching recipes
|
|
114
|
-
- `wx.ensureOAuthApp(token, { name, redirectUris, redirectDomains })` — the headless OAuth app, found by name or created
|
|
114
|
+
- `wx.ensureOAuthApp(token, { name, redirectUris, redirectDomains })` — the headless OAuth app, found by name or created with `origin: "base44-app"`, with the redirect lists merged into whatever it already allows; returns `{ clientId, created }`. `clientId` is what the browser mints visitor tokens from. Run it whenever `context` reports no OAuth app, or before any Wix-hosted flow that returns to this app — see Visitor authentication and Wix-hosted flows
|
|
115
115
|
- `wx.installApp(appDefId, siteId, token)` — install a Wix app on the site (Apps Installer). If discovery finds an API whose app isn't installed on the site, install it first — that's a one-call prerequisite, **not** a reason to fall back to a hand-built alternative. `appDefId` from `search` or the Apps-Created-by-Wix table; `siteId` from `context` (the site report)
|
|
116
116
|
|
|
117
117
|
Search makes one combined request across REST, management recipes, and Headless by default.
|
|
@@ -363,7 +363,9 @@ creating the OAuth app when the site has none and adding your URLs to its lists
|
|
|
363
363
|
has one. Returns only complete for URLs in those lists, and a missing one breaks silently —
|
|
364
364
|
create-checkout and the anonymous token still succeed — until a real buyer is redirected and
|
|
365
365
|
stranded on Wix. It is idempotent by name: never create a second OAuth app for the same Base44
|
|
366
|
-
app, or the first `clientId`'s returns stop working.
|
|
366
|
+
app, or the first `clientId`'s returns stop working. The app is created with `origin: "base44-app"`,
|
|
367
|
+
the name of this flow on the OAuth app; `origin` is set at creation only, so an OAuth app created
|
|
368
|
+
by hand carries the same value in its create call.
|
|
367
369
|
|
|
368
370
|
```js
|
|
369
371
|
// Register BOTH of this app's own URLs — its preview URL and its published URL — so returns work
|
|
@@ -432,7 +432,10 @@ async function installApp(appDefId, siteId, token) {
|
|
|
432
432
|
// return needs. Idempotent by name — a site reached by the "connect an existing site" flow
|
|
433
433
|
// has none, and creating a second one for the same app strands the first app's returns.
|
|
434
434
|
// Redirect lists are merged, never replaced: an app that already serves a custom domain
|
|
435
|
-
// keeps it when a preview URL is added.
|
|
435
|
+
// keeps it when a preview URL is added. `origin` names the flow that created the app; it is
|
|
436
|
+
// written once, at creation, and an update leaves it as it is.
|
|
437
|
+
const OAUTH_APP_ORIGIN = "base44-app";
|
|
438
|
+
|
|
436
439
|
async function ensureOAuthApp(token, { name, redirectUris = [], redirectDomains = [] }) {
|
|
437
440
|
const { oAuthApps = [] } = await post(
|
|
438
441
|
"https://www.wixapis.com/oauth-app/v1/oauth-apps/query", { query: {} }, token);
|
|
@@ -440,7 +443,7 @@ async function ensureOAuthApp(token, { name, redirectUris = [], redirectDomains
|
|
|
440
443
|
const merge = (a = [], b = []) => [...new Set([...a, ...b])];
|
|
441
444
|
if (!existing) {
|
|
442
445
|
const { oAuthApp } = await post("https://www.wixapis.com/oauth-app/v1/oauth-apps", {
|
|
443
|
-
oAuthApp: { name, allowedRedirectUris: redirectUris, allowedRedirectDomains: redirectDomains },
|
|
446
|
+
oAuthApp: { name, allowedRedirectUris: redirectUris, allowedRedirectDomains: redirectDomains, origin: OAUTH_APP_ORIGIN },
|
|
444
447
|
}, token);
|
|
445
448
|
return { clientId: oAuthApp.id, created: true, oAuthApp };
|
|
446
449
|
}
|
|
@@ -106,6 +106,11 @@ paths below are relative to that folder, never assumed under `<SKILL_ROOT>`.
|
|
|
106
106
|
`node <SKILL_ROOT>/install/check.mjs` says whether the skill or its templates have a newer version
|
|
107
107
|
and prints the update commands; it changes nothing.
|
|
108
108
|
|
|
109
|
+
Throughout any run, keep the site working at every step so the user can try it as it grows. Steps
|
|
110
|
+
only the site owner can take never hold a flow back: drafted content is live, and anything that
|
|
111
|
+
takes money reaches Wix's checkout before payments are connected. The owner's steps go in the
|
|
112
|
+
closing with their links, as next steps the user can take or ask for.
|
|
113
|
+
|
|
109
114
|
Throughout any run: if the user asks to send feedback to Wix, complains or gets frustrated, or the
|
|
110
115
|
run hits friction of any kind: anything that cost more turns than it should have, whether or not
|
|
111
116
|
it ended in an error (a confusing error, a doc gap, a seed that had to be re-run, a shipped file
|
|
@@ -143,8 +148,9 @@ only after an explicit yes, never automatically. Step 5 ends with the same self-
|
|
|
143
148
|
`astro.config.mjs` `integrations: [wix(), react()]`, `adapter: wixHostingAdapter()`,
|
|
144
149
|
`output: "server"`, `security: { checkOrigin: false }`, `image.domains` with
|
|
145
150
|
`static.wixstatic.com`. An Astro project made without the CLI has none of that; add it
|
|
146
|
-
before deploying, and the site serves every route. The integration
|
|
147
|
-
|
|
151
|
+
before deploying, and the site serves every route. The integration's current major
|
|
152
|
+
(`@wix/astro` 3, as in this skill's templates) requires **Astro 7**: a project on an earlier
|
|
153
|
+
major moves to 7 first, or is connected as a React host.
|
|
148
154
|
- **React and other bundlers** release their own build as files. So routes are hash routes,
|
|
149
155
|
or one emitted HTML file per route linked by its file name — decided before the first route
|
|
150
156
|
is written; any URL handed to Wix as a return target must be one the host serves. Verify by
|
|
@@ -189,10 +195,13 @@ only after an explicit yes, never automatically. Step 5 ends with the same self-
|
|
|
189
195
|
| a frontend, no config (a `package.json`, or `index.html` at the root: someone's Astro, Vite, Next, plain HTML) | **project** → adopt | `init` in place gives it a new, empty site, then deploys; `--stack` from step 1 is required; make the project what that stack needs on Wix hosting (step 1) before or right after | **yes**, from `--plan` |
|
|
190
196
|
| a config whose `.env.local` declares an active editor migration (`EDITOR_MIGRATION_STATUS=ACTIVE`), with or without the blank Astro starter the download carries | **migration** → migrate | the shipped code into the starter (or the composed template around a bare config), deployed with the migrated site's app as the client, the install starts; `ready_for_brand_layer` says `mode: "migrate"`, the parent as `siteId`, the child as `deploySiteId` (`guides/migration.md`) | no, ever |
|
|
191
197
|
| a config, no frontend | **config-only** → refuses | the site exists and has no frontend yet: `attach.mjs` (below) takes the site from the config, reuses its hosting, scaffolds and deploys. That config is what `init` leaves behind, and `init` always creates a site: this site was made for this run and is empty | **yes**: draft the plan as for create, then `attach.mjs --plan` |
|
|
192
|
-
| a config and a frontend (a `package.json`, or `index.html` inside the folder `site.outputDirectory` names) | **wix-project** → refuses | iterate: never scaffold, `init` or reseed. `deploy.mjs <vertical…> --stack <stack>` adds a solution (the client id comes from `.env.local`, the config as the fallback), then
|
|
198
|
+
| a config and a frontend (a `package.json`, or `index.html` inside the folder `site.outputDirectory` names) | **wix-project** → refuses | iterate: never scaffold, `init` or reseed. `deploy.mjs <vertical…> --stack <stack>` adds a solution (the client id comes from `.env.local`, the config as the fallback), then one install, `node <SKILL_ROOT>/install/install.mjs` (nothing when `node_modules` already satisfies the lock); a change is file edits; then release | no |
|
|
193
199
|
| a config, a `package.json`, `public/index.html` and no `src/pages/` (a site published through the drop flow, on its Wix Dev Machine) | **static-in-astro** → refuses | the pages stay plain files in `public/`: `deploy.mjs <vertical…> --stack static --out public` puts the REST layer in `public/js/wix/`, the pages are wired in place (reference mode), and no Astro pages are added; release with `build` then `release`, the URL stays | no |
|
|
200
|
+
| a config and the CLI's blank Astro starter with no work of its own (`wix-astro-blank`, only the blank template's packages: a Dev Machine's never-released site, a bare `wix create`) | **blank-starter** → refuses | `attach.mjs --business-name <brand> --vertical <v…> [--plan plan.json]` on the config's site: the vertical's composed template takes the starter's place (its lockfile with it), the install and the seed start detached; build the brand layer while they run | **yes**, from `--plan`: the site was made for this run and is empty, unless `read-site.mjs` shows content |
|
|
194
201
|
| a config, `index.html` at the root, no `package.json` (a site published through the drop flow and downloaded) | **published-static** | the config's site, no `init`: `site/` becomes the upload, the REST layer deploys into `site/js/wix/`; the `next` says to move the pages, styles and assets in; release keeps the URL | no |
|
|
195
202
|
|
|
203
|
+
A folder no row names is **wix-project**: the fill-only path — never scaffold, `init`, reseed or delete; deploy, edit, release. A wrong guess costs a slower run, never the user's work.
|
|
204
|
+
|
|
196
205
|
**Who decides the seed: where the site came from, never the brief's wording.** A site made
|
|
197
206
|
for this run is empty by construction, so it is always seeded — with the brief's content when
|
|
198
207
|
it supplies or describes any, otherwise with demo content the agent drafts per the vertical's
|
|
@@ -232,7 +241,7 @@ only after an explicit yes, never automatically. Step 5 ends with the same self-
|
|
|
232
241
|
It emits one JSON event per line and returns in **~35s**: **scaffolds** the project from the
|
|
233
242
|
vertical's composed template (`wix create` copies it: the code and its lockfile arrive with
|
|
234
243
|
the scaffold), **deploys** whatever the folder still lacks (patching `package.json` with every
|
|
235
|
-
dependency the code imports), then **starts two detached background jobs** — the dependency install (`npm ci --ignore-scripts || npm install --ignore-scripts`)
|
|
244
|
+
dependency the code imports), then **starts two detached background jobs** — the dependency install (`install/install.mjs`: nothing when `node_modules` already satisfies the lock, `npm install --ignore-scripts` over a tree that is there, else `npm ci --ignore-scripts || npm install --ignore-scripts`)
|
|
236
245
|
and the **seed** — whose logs and completion markers are in the events. The final
|
|
237
246
|
`ready_for_brand_layer` event carries the project dir, siteId, ready-made dashboard links,
|
|
238
247
|
and both markers. Relay notable events. On an `error` event, recover just that step via the
|
|
@@ -240,8 +249,8 @@ only after an explicit yes, never automatically. Step 5 ends with the same self-
|
|
|
240
249
|
|
|
241
250
|
**Recovering one step, or adding a solution later:** the pieces run on their own from the
|
|
242
251
|
project root — `node <SKILL_ROOT>/install/deploy.mjs <vertical…> --stack <stack>` (the client
|
|
243
|
-
id is read from `wix.config.json`), ONE `
|
|
244
|
-
|
|
252
|
+
id is read from `wix.config.json`), ONE `node <SKILL_ROOT>/install/install.mjs`
|
|
253
|
+
(**never a second npm install concurrently**: two npms in one
|
|
245
254
|
`node_modules` race and redo each other's work; setup already started one — wait on its
|
|
246
255
|
marker), the vertical's seed module per its `seed/SEED.md`.
|
|
247
256
|
A code change on an existing project is done when it is **released** (step 5) and the live
|
|
@@ -266,7 +275,7 @@ only after an explicit yes, never automatically. Step 5 ends with the same self-
|
|
|
266
275
|
`templates/shared/CUSTOM_OPERATIONS.md` before writing it. Use one documented path and
|
|
267
276
|
implement it; do not reverse-engineer SDK internals.
|
|
268
277
|
5. **When both background jobs have completed** — the install's marker
|
|
269
|
-
(
|
|
278
|
+
(`.install-exit`, containing `0`) and the seed's (`.seed-exit`) both exist — **verify the
|
|
270
279
|
seed succeeded** (`.seed-exit` contains `0`; `seed-result.json` has the created counts for
|
|
271
280
|
your summary — if non-zero, read `seed.log` and re-run the seed module manually). Those two
|
|
272
281
|
seed files exist **only when setup started the seed** (attach runs none: only the install
|
|
@@ -285,7 +294,10 @@ only after an explicit yes, never automatically. Step 5 ends with the same self-
|
|
|
285
294
|
`https://manage.wix.com/dashboard/<siteId>` (the `siteId` of the `ready_for_brand_layer`
|
|
286
295
|
event: on a migration preview that is the migrated site's dashboard, the release URL is the
|
|
287
296
|
preview's, the original site is unchanged, and completing the migration is the user's next
|
|
288
|
-
step in the Wix CLI once they approve — say all three).
|
|
297
|
+
step in the Wix CLI once they approve — say all three). Tell the user how to put their own
|
|
298
|
+
domain on the site, per the `wix-manage` recipe `references/domains/domain-search-purchase-and-connect.md`
|
|
299
|
+
(installed beside this skill, else `https://www.wix.com/skills/wix-manage/references/domains/domain-search-purchase-and-connect.md`):
|
|
300
|
+
bought through Wix, or one they already own. When the site takes money through
|
|
289
301
|
hosted checkout (a cart, a paid booking or rental, tickets, plans, donations) and nothing says
|
|
290
302
|
payments are already set up, say what a visitor meets at checkout until they are — "We
|
|
291
303
|
can't accept online payments. Contact us for help with your order." — and hand the two links
|
|
@@ -3,10 +3,17 @@
|
|
|
3
3
|
Read when the commands this skill is built on cannot run where you are, or stopped: your shell
|
|
4
4
|
can't reach Wix, there is no shell, or the install, the
|
|
5
5
|
CLI or the login is blocked. Wix is then reachable through the Wix MCP: every call below is an
|
|
6
|
-
`ExecuteWixAPI` call, and files reach the site through the upload recipe.
|
|
6
|
+
`ExecuteWixAPI` call, and files reach the site through the upload recipe. With no Wix MCP tools in
|
|
7
|
+
your tool list (a plugin's skills can be on disk while its Wix connector is not connected), ask the
|
|
8
|
+
user to connect Wix to this chat first. It walks the run of
|
|
7
9
|
SKILL.md step by step and names, for each, the Wix API call the CLI or the script performs and the
|
|
8
10
|
file beside this skill that carries the contract; where a script can run where you are, it says so.
|
|
9
11
|
|
|
12
|
+
As in SKILL.md's run, keep the site working at every step so the user can try it as it grows.
|
|
13
|
+
Steps only the site owner can take never hold a flow back: drafted content is live, and anything
|
|
14
|
+
that takes money reaches Wix's checkout before payments are connected. The owner's steps go in the
|
|
15
|
+
closing with their links, as next steps the user can take or ask for.
|
|
16
|
+
|
|
10
17
|
## Where these skills are
|
|
11
18
|
|
|
12
19
|
This skill is one of a set, and the guide reads from three of them:
|
|
@@ -25,13 +32,25 @@ This skill is one of a set, and the guide reads from three of them:
|
|
|
25
32
|
exact shape before writing a call.
|
|
26
33
|
|
|
27
34
|
Installed, they sit beside each other wherever your client keeps them: under `.agents/skills/` after
|
|
28
|
-
`npx skills add
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
`npx skills add` or a similar skills folder, or in a plugin's folder. A skill your client offers you
|
|
36
|
+
by name is read from there, or through the client's skill tool when it is served rather than a file,
|
|
37
|
+
with no install, clone or online read. List them first, through that tool or the folder: a catalog
|
|
38
|
+
shown at the start can leave skills out. Online, they are published alongside each other under the
|
|
39
|
+
same names (the registry also serves each without its `wix-` prefix, `headless-templates` for
|
|
40
|
+
`wix-headless-templates`, which is the `base` its manifest shows; both forms resolve), and in the
|
|
32
41
|
`wix/skills` repository on GitHub under `skills/{skill}/`; a sandbox that reaches GitHub has the
|
|
33
|
-
whole set on disk from a clone. Online pages are read with the Wix MCP's `ReadFullDocsArticle`
|
|
34
|
-
|
|
42
|
+
whole set on disk from a clone. Online pages are read with the Wix MCP's `ReadFullDocsArticle` when
|
|
43
|
+
it is connected: a client's web fetch may open only URLs the user gave or a search returned.
|
|
44
|
+
|
|
45
|
+
Online, a skill's files are found through its manifest,
|
|
46
|
+
`https://www.wix.com/skills/{skill}.manifest.json` (for the templates,
|
|
47
|
+
`headless-templates.manifest.json`): every file it holds, by path, with its size in bytes. A
|
|
48
|
+
folder's URL lists its files, and `<folder>/_content` returns them in one response. Open it once
|
|
49
|
+
before the first file and take each path from it: the files this guide names by pattern
|
|
50
|
+
(`{solution}/rest/*.ts`, `*-core.ts`), and the files an import points to. A path the manifest
|
|
51
|
+
doesn't list is a wrong path; find the file there before writing anything in its place. A copy you
|
|
52
|
+
hold, served or on disk, is current when its size in bytes matches the manifest's; a file whose size
|
|
53
|
+
differs is read online.
|
|
35
54
|
|
|
36
55
|
What such a run does not produce: the Astro build and its release. Those need a machine with the
|
|
37
56
|
CLI. Everything else the kit does to a site, it does through calls you can make, the frontend
|
|
@@ -57,7 +76,7 @@ A brief that needs no solution (a game, a landing page, a tool) is a run of two
|
|
|
57
76
|
## 2. The site
|
|
58
77
|
|
|
59
78
|
The CLI's `npm create @wix/new` is one call: `POST /headless-business-setup/v1/headless-business/provision`,
|
|
60
|
-
with `origin:
|
|
79
|
+
with `origin: "drop"`, a name, and the solutions to install as `seedOptions`. The recipe
|
|
61
80
|
is `{MANAGE}/references/sites/create-headless-site.md`; it returns the `metaSiteId` and the
|
|
62
81
|
site's OAuth client `appId`. Do not create a site any other way: a project from Create Project or
|
|
63
82
|
a template is neither headless nor publishable, and a later run with a machine cannot attach to it.
|
|
@@ -75,32 +94,26 @@ Restaurants, Donations, FAQ), goes on with the Apps Installer:
|
|
|
75
94
|
## 4. The seed
|
|
76
95
|
|
|
77
96
|
`seed-{solution}.mjs` is a sequence of REST calls, and `SEED.md` is its contract: the plan shape
|
|
78
|
-
(what to create, how many, which images), the order the entities need,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
the
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
`
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
the
|
|
96
|
-
`
|
|
97
|
-
|
|
98
|
-
`{MANAGE}/references/media/upload-media-to-wix.md`, and the file id the import returns is what the
|
|
99
|
-
product or service takes. Image prompts need the Media Manager; without
|
|
100
|
-
it, products and services stay text-only, say so. The scripts read
|
|
101
|
-
a response body directly; `ExecuteWixAPI`'s `wix.request` returns `{ status, data }`, and the body
|
|
102
|
-
is `data`. A generated image is billed when it is generated, so a read of the wrong level discards
|
|
103
|
-
a paid image.
|
|
97
|
+
(what to create, how many, which images), the order the entities need, the request bodies and the
|
|
98
|
+
traps the script encodes. Write the seed as your own `ExecuteWixAPI` script, its calls made on the
|
|
99
|
+
new site: `wix.request({ scope: "site", siteId: <the metaSiteId>, … })`. Read `SEED.md` before
|
|
100
|
+
the first create: it maps the brief's words to the fields. Build each request body in your script the
|
|
101
|
+
way the script's builder functions do. A call `SEED.md` only names is made through `{MANAGE}`'s recipe for
|
|
102
|
+
it. Keep the script's rules: create, never delete, and report what the site already held. The closing
|
|
103
|
+
names entities you drafted as placeholders.
|
|
104
|
+
|
|
105
|
+
A photo already in the site's Media Manager, such as one the user uploaded, goes in by its file id
|
|
106
|
+
(`imageMediaId` in `SEED.md`), as it is: an import by URL would copy it. A photo on a site the user
|
|
107
|
+
dropped is served with the drop, not from the Media Manager: it goes in by its live URL, imported
|
|
108
|
+
once through `{MANAGE}/references/media/upload-media-to-wix.md`, and the file id the import returns
|
|
109
|
+
is what the entity takes. An `imagePrompt` is generated through
|
|
110
|
+
`{MANAGE}/references/media/generate-image-with-ai.md` (`POST /runwareschemaless/v1/request`, the
|
|
111
|
+
body `generateImage` in `{TEMPLATES}/shared/seed/images.mjs` builds), then imported through
|
|
112
|
+
`upload-media-to-wix.md`, and the file id goes on the entity, as for a dropped photo; this is the
|
|
113
|
+
seed's default for every entity that shows a picture. An entity stays text-only only when generation
|
|
114
|
+
or the import fails; say which. The scripts read a response body directly; `ExecuteWixAPI`'s
|
|
115
|
+
`wix.request` returns `{ status, data }`, and the body is `data`. A generated image is billed when
|
|
116
|
+
it is generated, so a read of the wrong level discards a paid image.
|
|
104
117
|
|
|
105
118
|
## 5. The frontend
|
|
106
119
|
|
|
@@ -108,6 +121,9 @@ The run is not done until the pages use what the backend holds: every control th
|
|
|
108
121
|
it (add to cart, the cart, book, submit) works on the live site. A backend with untouched pages is
|
|
109
122
|
half a run.
|
|
110
123
|
|
|
124
|
+
A payment (a cart, a paid booking, a ticket, a plan, a donation) ends in Wix's hosted checkout,
|
|
125
|
+
reached through a redirect session, and is wired on the first pass, payments connected or not.
|
|
126
|
+
|
|
111
127
|
The Astro pages cannot be released here: that is a run with a machine and a CLI login, which
|
|
112
128
|
attaches to this site (`guides/existing-site.md`) rather than making a new one. What ships from here
|
|
113
129
|
is static files, dropped onto the site from step 2 through
|
|
@@ -127,49 +143,57 @@ takes the complete file set in one call and a response has a hard size limit; th
|
|
|
127
143
|
later" section downloads what the site serves, so a later drop adds files to a live set without
|
|
128
144
|
resending what is already there.
|
|
129
145
|
|
|
130
|
-
**When it cannot**, or when its output would all have to be written out by hand, a page that
|
|
131
|
-
|
|
146
|
+
**When it cannot**, or when its output would all have to be written out by hand, a page that talks
|
|
147
|
+
to the site as a visitor over plain `fetch`. One file per call, a few hundred lines, written in full inside that call: a response has a
|
|
132
148
|
hard size limit, and a call that carries the whole frontend at once exceeds it. A second file joins
|
|
133
149
|
the first the recipe's way, "Change it later": the same call downloads the live set and drops the
|
|
134
150
|
union. The
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
`createClient({ modules: { … }, auth: OAuthStrategy({ clientId }) })` with the `appId` the provision
|
|
138
|
-
call returned, and the SDK mints and refreshes the visitor token itself. Markup, a render function
|
|
151
|
+
page calls the Wix REST API with `fetch` and mints the visitor token itself with the `appId` the
|
|
152
|
+
provision call returned, as `{TEMPLATES}/shared/rest/client.ts` does. Markup, a render function
|
|
139
153
|
per state, the event handlers: that is what you write. Keep what the page shows to what the site
|
|
140
154
|
holds; nothing invented, as in SKILL.md step 4.
|
|
141
155
|
|
|
142
|
-
**The calls**, on that second path, take their shape from the kit's
|
|
143
|
-
`{TEMPLATES}/{solution}/app/wix/{solution}/*.ts` (`services.ts`, `booking.ts`, `catalog.ts`, …,
|
|
144
|
-
with `{TEMPLATES}/shared/app/wix/sdk.ts` for the client wiring): those files call the same SDK
|
|
145
|
-
modules the page imports, and they are verified. Copy the form of the call, not the file. A page
|
|
146
|
-
that talks to Wix over plain `fetch`, with no SDK, takes them from the REST twins instead:
|
|
156
|
+
**The calls**, on that second path, take their shape from the kit's REST twins for the solution:
|
|
147
157
|
`{TEMPLATES}/shared/rest/client.ts` (the visitor token and the request) and
|
|
148
|
-
`{TEMPLATES}/{solution}/rest/*.ts` (the calls
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
to read, how a price or a duration is formatted, which slot is bookable, how an
|
|
153
|
-
those core functions into the page as they are, with only the types removed
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
of what the cores return, and the event handlers.
|
|
158
|
+
`{TEMPLATES}/{solution}/rest/*.ts` (the calls), each importing its helpers by their real paths.
|
|
159
|
+
They are verified. Copy the form of the call, not the file.
|
|
160
|
+
The cores,
|
|
161
|
+
`{TEMPLATES}/{solution}/app/wix/{solution}/*-core.ts`, hold the rules the page needs and nothing else
|
|
162
|
+
does: which fields to read, how a price or a duration is formatted, which slot is bookable, how an
|
|
163
|
+
id is read. Copy those core functions into the page as they are, with only the types removed, and
|
|
164
|
+
run every API response through them: a raw field read by hand is where a page breaks (a name can be
|
|
165
|
+
a translatable object, a quantity a nested one). What you write is the markup, the rendering of what
|
|
166
|
+
the cores return, and the event handlers.
|
|
158
167
|
|
|
159
168
|
**A call those files don't cover** is confirmed before it is written, the way `{DOCS}` describes:
|
|
160
|
-
the method's
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
the method's REST page, and its schema (SearchWixAPISpec on the method's docs URL) for the exact
|
|
170
|
+
request and response. A shape that recurs, as an example of what the kit's files already encode:
|
|
171
|
+
an entity's id arrives as `_id` on some objects and `id` on others (the kit reads both,
|
|
172
|
+
`rawId`). Dates travel as local wall-clock strings in the business time zone.
|
|
173
|
+
|
|
174
|
+
**The check**, before every upload: a page cannot be opened here, so a wrong shape fails silently in
|
|
175
|
+
the browser. In one `ExecuteWixAPI` script, make each control's calls the way the page makes them,
|
|
176
|
+
as a visitor, with the bodies from `{solution}/rest/*.ts`. Then read the solution's cores,
|
|
177
|
+
`{TEMPLATES}/{solution}/app/wix/{solution}/*-core.ts` (online, that folder's `_content` returns them
|
|
178
|
+
in one response), and run each response through them in your sandbox's Node, as the page does:
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
async function () {
|
|
182
|
+
const api = "https://www.wixapis.com", clientId = "<appId>";
|
|
183
|
+
const post = (path, body, token) => fetch(api + path, { method: "POST",
|
|
184
|
+
headers: { "Content-Type": "application/json", ...(token && { Authorization: token }) },
|
|
185
|
+
body: JSON.stringify(body) }).then(async (r) => ({ path, status: r.status, data: await r.json() }));
|
|
186
|
+
const { data: { access_token: token } } = await post("/oauth2/token", { clientId, grantType: "anonymous" });
|
|
187
|
+
const results = [];
|
|
188
|
+
// each call the control makes, in order: results.push(await post(path, body, token));
|
|
189
|
+
// a payment's last call is the redirect session the solution's rest/*.ts creates
|
|
190
|
+
return results;
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Done when every call returns 200, the cores turn each response into the names, prices, quantities or
|
|
195
|
+
times the page shows, and a payment's redirect session returns a URL. The upload waits for that: a
|
|
196
|
+
failing call is fixed first, on the site or in the page. The closing reports the check.
|
|
173
197
|
|
|
174
198
|
## 6. Closing
|
|
175
199
|
|
|
@@ -27,6 +27,12 @@ shape as setup (SKILL.md step 3):
|
|
|
27
27
|
node <SKILL_ROOT>/install/attach.mjs --site <siteId> --business-name "<site name>" --vertical <vertical>[,<vertical>] [--plan plan.json]
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
The same call takes a folder that already holds the site's config and the CLI's blank Astro
|
|
31
|
+
starter with no work of its own (`context.mjs` says `blank-starter`: a Wix Dev Machine for a
|
|
32
|
+
never-released site, or a bare `wix create`): `--site` comes from the config, the vertical's
|
|
33
|
+
composed template takes the starter's place, its lockfile with it, and the install and the seed
|
|
34
|
+
run detached as below. Do not install by hand there; the brand layer is yours while they run.
|
|
35
|
+
|
|
30
36
|
`init`/`wix create` always create a site, so they are not used here. attach does what they do
|
|
31
37
|
after creating one — the site's OAuth app, Wix hosting, `wix.config.json` — against the site
|
|
32
38
|
given, copies the first vertical's composed template, deploys the rest, and starts the install
|
|
@@ -17,8 +17,9 @@ call is memory, and an empty or error reply to it says nothing about the site.
|
|
|
17
17
|
|
|
18
18
|
## Skills
|
|
19
19
|
|
|
20
|
-
Installed at `.agents/skills/`
|
|
21
|
-
`npx skills add wix/skills --skill {{SKILL}} --skill wix-docs --skill wix-manage
|
|
20
|
+
Installed at `.agents/skills/` by
|
|
21
|
+
`npx skills add wix/skills --skill {{SKILL}} --skill wix-docs --skill wix-manage`; a Wix plugin keeps
|
|
22
|
+
its copy, under the same names, in the client's own skills folder.
|
|
22
23
|
|
|
23
24
|
- `{{SKILL}}` — the code in this app and how to extend it. The shipped code is the
|
|
24
25
|
`wix-headless-templates` skill: `node .agents/skills/{{SKILL}}/install/templates.mjs` prints where it
|
|
@@ -46,13 +46,16 @@
|
|
|
46
46
|
// Emits ONE JSON event per line (attached, scaffolded, deployed, install_started,
|
|
47
47
|
// ready_for_brand_layer, or error). Requires a logged-in Wix CLI (`npx @wix/cli@<pinned> whoami`, the version in pins.json)
|
|
48
48
|
// whose account owns or co-manages the site.
|
|
49
|
-
import {
|
|
50
|
-
import { cpSync, existsSync, mkdirSync,
|
|
49
|
+
import { spawnSync } from "node:child_process";
|
|
50
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from "node:fs";
|
|
51
51
|
import { basename, dirname, join, resolve } from "node:path";
|
|
52
52
|
import { fileURLToPath } from "node:url";
|
|
53
53
|
import { writeAgentsMd } from "./agents-md.mjs";
|
|
54
|
+
import { ignoreEnvLocal } from "./env-ignore.mjs";
|
|
54
55
|
import { frontendPresent, siteContext } from "./context.mjs";
|
|
55
56
|
import { listVerticals, templatesDir } from "./templates.mjs";
|
|
57
|
+
import { failure, npmSync, startBackground } from "./proc.mjs";
|
|
58
|
+
import { blankStarter } from "./context.mjs";
|
|
56
59
|
const PINS = JSON.parse(readFileSync(new URL("./pins.json", import.meta.url), "utf8"));
|
|
57
60
|
const WIX_CLI = `@wix/cli@${PINS["@wix/cli"]}`;
|
|
58
61
|
|
|
@@ -90,6 +93,10 @@ const cwdConfig = has("wix.config.json") ? JSON.parse(readFileSync(join(cwd, "wi
|
|
|
90
93
|
// config's site.outputDirectory names (a static site laid out for release) — install/context.mjs.
|
|
91
94
|
const fp = frontendPresent(cwd);
|
|
92
95
|
const hasProject = fp.packageJson || fp.rootIndex || fp.outputIndex;
|
|
96
|
+
// The CLI's blank starter with no work of its own (a Dev Machine's never-released site, a bare
|
|
97
|
+
// `wix create`): not a project to link, a scaffold to complete — the vertical's composed template
|
|
98
|
+
// takes its place (deploy.mjs), the install and the seed run detached as for a new site.
|
|
99
|
+
const starter = !!cwdConfig && fp.packageJson && !(fp.publicIndex && !fp.astroPages) && blankStarter(cwd);
|
|
93
100
|
// A migration preview (wix.config.json names a deploy-only site, .env.local names the site being
|
|
94
101
|
// migrated) is already provisioned: its app, hosting and credentials came with the folder. Nothing
|
|
95
102
|
// here applies — setup.mjs takes it (SKILL.md step 3).
|
|
@@ -122,24 +129,24 @@ if (hosting0 === "self" && stack === "astro" && !hasProject && !stackFlag) {
|
|
|
122
129
|
fail("args", "--hosting self scaffolds nothing: pass --stack for the frontend you will host (react|lib|static), or attach from inside the project on disk");
|
|
123
130
|
}
|
|
124
131
|
if (!subfolder) {
|
|
125
|
-
if (hasProject && cwdConfig) {
|
|
126
|
-
fail("place", "this folder is already a Wix project with a frontend (wix.config.json and a project): nothing to attach. deploy.mjs <vertical> adds this skill's code or a solution to it; then
|
|
132
|
+
if (hasProject && cwdConfig && !starter) {
|
|
133
|
+
fail("place", "this folder is already a Wix project with a frontend (wix.config.json and a project): nothing to attach. deploy.mjs <vertical> adds this skill's code or a solution to it; then node <SKILL_ROOT>/install/install.mjs");
|
|
127
134
|
}
|
|
128
135
|
if (cwdConfig && (cwdConfig.siteId ?? cwdConfig.projectId) !== siteId) {
|
|
129
136
|
fail("place", `this folder is attached to site ${cwdConfig.siteId ?? cwdConfig.projectId}, not ${siteId} — a folder is never re-pointed; attach the other site with --subfolder`);
|
|
130
137
|
}
|
|
131
|
-
if (hasProject && !stackFlag) {
|
|
138
|
+
if (hasProject && !starter && !stackFlag) {
|
|
132
139
|
fail("args", "attaching a project on disk needs --stack astro|react|lib|static — the stack you resolved in SKILL.md step 1 for this project");
|
|
133
140
|
}
|
|
134
141
|
}
|
|
135
|
-
const mode = !subfolder && hasProject ? "link" : hosting0 === "self" ? "config-only" : "scaffold";
|
|
136
|
-
emit("folder", { mode, stack, hosting: hosting0, project: hasProject, config: cwdConfig ? "same site" : null, siteOrigin });
|
|
142
|
+
const mode = !subfolder && hasProject && !starter ? "link" : hosting0 === "self" ? "config-only" : "scaffold";
|
|
143
|
+
emit("folder", { mode, stack, hosting: hosting0, project: hasProject, starter, config: cwdConfig ? "same site" : null, siteOrigin });
|
|
137
144
|
|
|
138
145
|
// ---- http ---------------------------------------------------------------------------------------
|
|
139
146
|
const cliToken = (site) => {
|
|
140
|
-
const r =
|
|
147
|
+
const r = npmSync("npx", ["-y", WIX_CLI, "token", ...(site ? ["--site", site] : [])], { encoding: "utf8", timeout: 120_000 });
|
|
141
148
|
const t = (r.stdout || "").trim();
|
|
142
|
-
if (r.status !== 0 || !t) fail("auth", (r
|
|
149
|
+
if (r.status !== 0 || !t) fail("auth", failure(r, `no token — is the Wix CLI logged in? (npx ${WIX_CLI} whoami)`).slice(-400));
|
|
143
150
|
return t;
|
|
144
151
|
};
|
|
145
152
|
async function call(base, path, { method = "POST", token, site, body, query } = {}) {
|
|
@@ -163,6 +170,13 @@ async function call(base, path, { method = "POST", token, site, body, query } =
|
|
|
163
170
|
}
|
|
164
171
|
|
|
165
172
|
const folderName = flag("folder-name") ?? (businessName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "site");
|
|
173
|
+
const renamePackage = (dir, name) => {
|
|
174
|
+
const pkgPath = join(dir, "package.json");
|
|
175
|
+
if (!existsSync(pkgPath)) return;
|
|
176
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
177
|
+
pkg.name = name;
|
|
178
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
179
|
+
};
|
|
166
180
|
const projectDir = subfolder ? resolve(cwd, folderName) : cwd;
|
|
167
181
|
if (subfolder && existsSync(join(projectDir, "wix.config.json"))) fail("args", `${folderName}/ is already a Wix project — pick --folder-name, or run deploy.mjs there`);
|
|
168
182
|
|
|
@@ -277,13 +291,9 @@ if (stack === "astro" && mode === "scaffold") {
|
|
|
277
291
|
// here because the CLI's create makes a new site and this folder gets an existing one. Further
|
|
278
292
|
// verticals are deployed below.
|
|
279
293
|
cpSync(join(templatesDir({ need: `${verticals[0]}/project` }), verticals[0], "project"), projectDir, { recursive: true, force: false, errorOnExist: false });
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
284
|
-
const gi = join(projectDir, ".gitignore");
|
|
285
|
-
const cur = existsSync(gi) ? readFileSync(gi, "utf8") : "";
|
|
286
|
-
if (!/\.env/.test(cur)) writeFileSync(gi, cur + "\n# local env (pulled from Wix)\n.env.local\n.env\n");
|
|
294
|
+
// The blank starter keeps its name through deploy, which recognizes it by name and gives it the
|
|
295
|
+
// composed package.json and lockfile; the project takes the folder's name after that (below).
|
|
296
|
+
if (!starter) renamePackage(projectDir, folderName);
|
|
287
297
|
}
|
|
288
298
|
writeFileSync(join(projectDir, "wix.config.json"), JSON.stringify({ appId, siteId }, null, 2) + "\n");
|
|
289
299
|
// The env the CLI's build reads. Same content `wix env pull` writes; written here so the build
|
|
@@ -297,6 +307,7 @@ writeFileSync(join(projectDir, ".env.local"), [
|
|
|
297
307
|
`WIX_CLIENT_SECRET=${quote(secrets.appSecret)}`,
|
|
298
308
|
"",
|
|
299
309
|
].join("\n"));
|
|
310
|
+
ignoreEnvLocal(projectDir); // in every mode: a linked project's own .gitignore may not cover it
|
|
300
311
|
emit(mode === "link" ? "linked" : mode === "config-only" ? "configured" : "scaffolded", { folder: folderName, stack, template: mode === "scaffold" && stack === "astro" ? join(TEMPLATES, verticals[0], "project") : null });
|
|
301
312
|
// the agent config files `wix create` writes (attach never runs the CLI's scaffold at all); fill-only
|
|
302
313
|
emit("agent_configs", writeAgentsMd(projectDir, { skill: basename(SKILL_ROOT), stack }));
|
|
@@ -313,20 +324,19 @@ if (mode !== "link" && (stack !== "astro" || mode === "config-only")) {
|
|
|
313
324
|
let deployResult = {};
|
|
314
325
|
{
|
|
315
326
|
const deploy = spawnSync("node", [join(SKILL_ROOT, "install", "deploy.mjs"), ...verticals, "--stack", stack, ...(planPath ? ["--plan", resolve(planPath)] : [])], { cwd: projectDir, encoding: "utf8", timeout: 60_000 });
|
|
316
|
-
if (deploy.status !== 0) fail("deploy", deploy
|
|
327
|
+
if (deploy.status !== 0) fail("deploy", failure(deploy));
|
|
317
328
|
try { deployResult = JSON.parse(deploy.stdout); } catch { /* keep going */ }
|
|
318
329
|
if (deployResult.error) fail("deploy", deployResult.error);
|
|
319
330
|
emit("deployed", deployResult);
|
|
331
|
+
if (starter) renamePackage(projectDir, folderName);
|
|
320
332
|
}
|
|
321
333
|
|
|
322
334
|
// ---- 4 · dependency install, detached (any project with a package.json) --------------------------
|
|
323
335
|
let install = null;
|
|
324
336
|
if (existsSync(join(projectDir, "package.json"))) {
|
|
325
337
|
const installLog = join(projectDir, "npm-install.log");
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
child.unref();
|
|
329
|
-
install = { log: installLog, doneMarker: "node_modules/.package-lock.json" };
|
|
338
|
+
startBackground("install", [], { cwd: projectDir, log: installLog });
|
|
339
|
+
install = { log: installLog, doneMarker: ".install-exit", success: "file contains 0" };
|
|
330
340
|
emit("install_started", install);
|
|
331
341
|
}
|
|
332
342
|
|
|
@@ -339,12 +349,7 @@ if (planPath) {
|
|
|
339
349
|
const seedDir = join(TEMPLATES, verticals[0], "seed");
|
|
340
350
|
const seedName = existsSync(seedDir) ? readdirSync(seedDir).find((f) => f.startsWith("seed-") && f.endsWith(".mjs")) : undefined;
|
|
341
351
|
if (!seedName) fail("seed", `no seed module found under ${seedDir}`);
|
|
342
|
-
|
|
343
|
-
"sh",
|
|
344
|
-
["-c", `node "${join(seedDir, seedName)}" "${resolve(planPath)}" > seed-result.json 2> seed.log; echo $? > .seed-exit`],
|
|
345
|
-
{ cwd: projectDir, detached: true, stdio: "ignore" },
|
|
346
|
-
);
|
|
347
|
-
seedChild.unref();
|
|
352
|
+
startBackground("seed", [join(seedDir, seedName), resolve(planPath)], { cwd: projectDir });
|
|
348
353
|
seed = { resultFile: "seed-result.json", log: "seed.log", doneMarker: ".seed-exit", success: "file contains 0" };
|
|
349
354
|
emit("seeding_started", { vertical: verticals[0], ...seed, ...(verticals.length > 1 ? { note: `the plan seeds ${verticals[0]}; the other verticals' seeds run afterwards, each with its own plan` } : {}) });
|
|
350
355
|
} else {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// The detached jobs setup.mjs and attach.mjs start (through proc.mjs startBackground), run by node
|
|
2
|
+
// so they need no shell on any OS. Each leaves the files the caller syncs on:
|
|
3
|
+
//
|
|
4
|
+
// node background.mjs install install.mjs: nothing when node_modules already satisfies
|
|
5
|
+
// the lock, npm install when a tree is there, else npm ci
|
|
6
|
+
// (falling back to npm install); --ignore-scripts, output
|
|
7
|
+
// inherited (the install log); the exit code to .install-exit
|
|
8
|
+
// node background.mjs seed <seed.mjs> <plan> the seed's stdout to seed-result.json, its stderr to
|
|
9
|
+
// seed.log, then its exit code to .seed-exit
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { closeSync, openSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { runInstall } from "./install.mjs";
|
|
13
|
+
|
|
14
|
+
const [job, ...args] = process.argv.slice(2);
|
|
15
|
+
|
|
16
|
+
if (job === "install") {
|
|
17
|
+
const r = runInstall(process.cwd());
|
|
18
|
+
console.log(`install: ${r.decision}${r.lock ? ` (${r.lock.missing} of ${r.lock.total} lock entries were missing)` : ""}`);
|
|
19
|
+
process.exit(r.status);
|
|
20
|
+
} else if (job === "seed") {
|
|
21
|
+
const [seedFile, plan] = args;
|
|
22
|
+
const out = openSync("seed-result.json", "w");
|
|
23
|
+
const err = openSync("seed.log", "w");
|
|
24
|
+
const r = spawnSync(process.execPath, [seedFile, plan], { stdio: ["ignore", out, err], windowsHide: true });
|
|
25
|
+
closeSync(out);
|
|
26
|
+
closeSync(err);
|
|
27
|
+
if (r.error) writeFileSync("seed.log", `the seed could not start: ${r.error.message}\n`, { flag: "a" });
|
|
28
|
+
writeFileSync(".seed-exit", `${r.status ?? 1}\n`);
|
|
29
|
+
} else {
|
|
30
|
+
console.error("usage: background.mjs install <ci|install> | seed <seed.mjs> <plan.json>");
|
|
31
|
+
process.exit(2);
|
|
32
|
+
}
|