@wix/agent-skills 1.23.0 → 1.25.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.
Files changed (61) hide show
  1. package/README.md +1 -0
  2. package/package.json +2 -1
  3. package/skills/wix-app/SKILL.md +4 -1
  4. package/skills/wix-app/references/BUSINESS_MANAGER_THEME.md +108 -0
  5. package/skills/wix-app/references/BUSINESS_MANAGER_TOKENS.md +81 -0
  6. package/skills/wix-app/references/DASHBOARD_MODAL.md +41 -0
  7. package/skills/wix-app/references/DASHBOARD_PAGE.md +2 -0
  8. package/skills/wix-app/references/DASHBOARD_PLUGIN.md +19 -4
  9. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +64 -50
  10. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE.md +5 -4
  11. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_ROUTER.md +8 -7
  12. package/skills/wix-app/references/dashboard-page/DYNAMIC_PARAMETERS.md +21 -38
  13. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +4 -3
  14. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +28 -25
  15. package/skills/wix-app/references/editor-react-component/BRANDED-COMPONENTS.md +8 -2
  16. package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +12 -14
  17. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +4 -3
  18. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +66 -9
  19. package/skills/wix-app/references/editor-react-component/EDITOR-EXTENSION-CONFIGURATION.md +25 -0
  20. package/skills/wix-app/references/editor-react-component/OVERFLOW.md +146 -0
  21. package/skills/wix-app/references/editor-react-component/PARTS.md +6 -0
  22. package/skills/wix-app/scripts/scan-a11y-eslint.cjs +25 -1
  23. package/skills/wix-headless/SKILL.md +0 -1
  24. package/skills/wix-headless/references/CAPABILITIES.md +3 -3
  25. package/skills/wix-headless/references/DISCOVERY.md +1 -1
  26. package/skills/wix-headless/references/SDK_HANDOFF.md +1 -1
  27. package/skills/wix-headless/references/SEED.md +1 -1
  28. package/skills/wix-headless/references/SETUP.md +1 -1
  29. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +1 -1
  30. package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +2 -2
  31. package/skills/wix-headless/references/managed/AUTHENTICATION.md +3 -14
  32. package/skills/wix-headless/scripts/bootstrap.mjs +156 -0
  33. package/skills/{wix-headless-fast → wix-headless-kit}/SKILL.md +83 -64
  34. package/skills/{wix-headless-fast → wix-headless-kit}/cold-start/bootstrap.mjs +2 -2
  35. package/skills/{wix-headless-fast → wix-headless-kit}/cold-start/cold-start.md +26 -35
  36. package/skills/wix-headless-kit/guides/capabilities.md +76 -0
  37. package/skills/wix-headless-kit/guides/feedback.md +98 -0
  38. package/skills/{wix-headless-fast → wix-headless-kit}/guides/migration.md +20 -9
  39. package/skills/{wix-headless-fast → wix-headless-kit}/install/AGENTS.md +5 -2
  40. package/skills/{wix-headless-fast → wix-headless-kit}/install/attach.mjs +5 -2
  41. package/skills/wix-headless-kit/install/bootstrap.mjs +156 -0
  42. package/skills/{wix-headless-fast → wix-headless-kit}/install/check.mjs +2 -2
  43. package/skills/wix-headless-kit/install/context.mjs +169 -0
  44. package/skills/{wix-headless-fast → wix-headless-kit}/install/deploy.mjs +17 -4
  45. package/skills/{wix-headless-fast → wix-headless-kit}/install/setup.mjs +89 -55
  46. package/skills/{wix-headless-fast → wix-headless-kit}/install/templates.mjs +1 -1
  47. package/skills/wix-manage/SKILL.md +2 -8
  48. package/skills/wix-manage/references/cms/cms-dashboard-navigation.md +2 -2
  49. package/skills/wix-manage/references/cms/cms-data-items-crud.md +263 -31
  50. package/skills/wix-manage/references/cms/cms-publishing-flow.md +15 -15
  51. package/skills/wix-manage/references/cms/cms-schema-management.md +58 -7
  52. package/skills/wix-manage/references/sites/query-sites.md +1 -8
  53. package/skills/wix-auth/SKILL.md +0 -12
  54. package/skills/wix-auth/references/device-flow.md +0 -140
  55. package/skills/wix-headless-fast/install/context.mjs +0 -126
  56. package/skills/wix-manage/references/cms/cms-data-operations-extended.md +0 -263
  57. package/skills/wix-manage/references/cms/cms-references-and-relationships.md +0 -162
  58. /package/skills/{wix-headless-fast → wix-headless-kit}/guides/existing-site.md +0 -0
  59. /package/skills/{wix-headless-fast → wix-headless-kit}/guides/reference-mode.md +0 -0
  60. /package/skills/{wix-headless-fast → wix-headless-kit}/install/agents-md.mjs +0 -0
  61. /package/skills/{wix-headless-fast → wix-headless-kit}/install/lock.mjs +0 -0
@@ -0,0 +1,146 @@
1
+ # Overflow and Resizing
2
+
3
+ Apply when creating a component or changing its layout, content, or sizing.
4
+ Custom components own their internal layout; do not assume the editor exposes
5
+ native-container overflow settings for their DOM. The component must keep
6
+ content and controls usable within its assigned size.
7
+
8
+ ## Choose What Happens When Content Does Not Fit
9
+
10
+ - **Content-height component:** reflow at narrower widths and let normal-flow
11
+ content establish height. Do not constrain it with `height: 100%`, a fixed
12
+ height, or absolute positioning of all content.
13
+ - **Bounded-height component:** propagate the height constraint through growing
14
+ wrappers (`min-height: 0` for flex/grid items). Fit the content or give the
15
+ intended content region `overflow: auto`; keep essential controls reachable.
16
+ Choose scrolling only when it suits the requested interaction.
17
+ - **Carousel or other intentional overflow:** contain the oversized track in
18
+ its own viewport and provide working navigation/scrolling to every item.
19
+ Treat a requested count such as "show four items at a time" as the
20
+ default/initial-width presentation unless the user explicitly requires that
21
+ column count at every width. Each carousel page should still contain that
22
+ requested group at narrow widths, but its internal layout must reflow so every
23
+ required item is usable. Stacking or wrapping the current page's items
24
+ preserves the group; squeezing them into tiny columns or clipping later items
25
+ does not. Choose the installation width, gaps, padding, and usable item
26
+ minimum together so the requested count actually fits at the initial size.
27
+
28
+ A container-driven group page can preserve the requested grouping without
29
+ changing the carousel's item-count logic:
30
+
31
+ ```css
32
+ .trackViewport {
33
+ min-width: 0;
34
+ overflow: hidden;
35
+ }
36
+
37
+ .page {
38
+ box-sizing: border-box;
39
+ display: grid;
40
+ flex: 0 0 100%;
41
+ min-width: 0;
42
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
43
+ gap: 16px;
44
+ }
45
+
46
+ .item {
47
+ min-width: 0;
48
+ }
49
+ ```
50
+
51
+ At a wide initial width, the page uses the requested presentation count. When
52
+ the component is narrower than a usable item minimum, the same group wraps to
53
+ fewer columns or a single column and its content-height component grows.
54
+ Equivalent container-query or wrapping layouts are valid. A fixed
55
+ `repeat(<count>, minmax(0, 1fr))` or percentage basis derived from the count is
56
+ not sufficient as the only layout for a horizontally resizable component.
57
+
58
+ Do not apply blanket `overflow: hidden`/`clip` to the root or page as a repair.
59
+ It can hide text, focus indicators, navigation, and overlays while leaving the
60
+ layout defect intact. Clip a dedicated media/track/decorative region only when
61
+ that is its intended behavior. Preserve deliberate shadows and overlays.
62
+
63
+ ## Remove Intrinsic Sizing Traps
64
+
65
+ `width: 100%` and `box-sizing: border-box` on the root are necessary, but do not
66
+ constrain descendants. A bare `1fr` track has an automatic minimum; three such
67
+ tracks can exceed the container even when the grid itself is correctly sized.
68
+
69
+ - Use `min-width: 0` on flex/grid items that must shrink, including nested
70
+ content wrappers. For a fixed column count, use `minmax(0, 1fr)`; still adapt
71
+ the layout when columns would become too narrow to use.
72
+ - Prefer fewer columns or wrapping to squeezing text and controls. An intrinsic
73
+ collection can use the following pattern; the minimum never exceeds its
74
+ container's content width:
75
+
76
+ ```css
77
+ .grid {
78
+ box-sizing: border-box;
79
+ display: grid;
80
+ min-width: 0;
81
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
82
+ gap: 16px;
83
+ }
84
+ ```
85
+
86
+ `minmax(280px, 1fr)` alone still overflows a slot narrower than 280px.
87
+ This grid is suitable for a wrapping collection, not a substitute for a
88
+ carousel's item/navigation contract.
89
+ - For user-authored prose, allow wrapping and use `overflow-wrap: anywhere`
90
+ where long words/URLs could force intrinsic width. Use truncation only when
91
+ the product intends it and essential content remains available.
92
+ - Constrain images/media to the space available; preserve aspect ratio or use
93
+ an intentional fitted media frame. Check fixed-size media plus adjacent text,
94
+ padding, borders, and gaps together.
95
+ - Let control rows and pagination adapt without losing buttons, focus rings,
96
+ or usable target sizes. `min-width: 0` does not make fixed children or large
97
+ padding fit by itself.
98
+
99
+ Adapt to the component's width, including a narrow slot on a desktop viewport.
100
+ Do not derive the layout from `window.innerWidth` or viewport breakpoints. If
101
+ container queries are needed, establish the query container on an appropriate
102
+ ancestor; a query cannot restyle its own container based on that container's
103
+ size. Do not add size containment on an auto-height component that prevents
104
+ content from establishing its height.
105
+
106
+ ## Review Before Reporting Completion
107
+
108
+ First inspect the sizing chain in source. Identify the root, growing wrappers,
109
+ tracks, cards, text, media, and controls; check each constrained axis. Do not
110
+ infer safety from a CSS keyword or a successful build.
111
+
112
+ When a runnable browser preview is available, exercise the actual component:
113
+
114
+ 1. Vary the **component container** independently of the page viewport. Cover
115
+ its initial size, 320px, a narrower stress probe such as 240px, and a wider
116
+ slot. For a vertically resizable component, also test a short bounded height;
117
+ for content height, verify the root grows without overlapping the next block.
118
+ A horizontally resizable top-level component must keep required content
119
+ usable at 320px unless an actual supported editor minimum is part of its
120
+ contract. Treat 240px as a stress probe. Report any unsupported size and its
121
+ remaining usability limit.
122
+ 2. Use long prose and an unbroken token, larger text, changed padding/borders,
123
+ and representative item counts. For carousels, test navigation and the
124
+ longest item; include RTL when direction changes layout or navigation.
125
+ 3. Compare root/region `scrollWidth` with `clientWidth` (allow about 1px for
126
+ rounding), and inspect descendant bounds. For bounded height, also compare
127
+ `scrollHeight` and `clientHeight`. An intentional scroll region/track may
128
+ exceed its client size, but its content must remain reachable and contained.
129
+ Check the component itself: a page ancestor may clip overflow while the
130
+ document has no horizontal scrollbar. Conversely, shadows/overlays do not
131
+ automatically mean the content layout is broken.
132
+ 4. Visually inspect and use the controls/keyboard. Passing geometry checks by
133
+ clipping required content is a failure. Verify intentional scroll regions
134
+ are usable with keyboard and touch, focus stays visible, and navigation can
135
+ reach every item.
136
+
137
+ Fix confirmed component-owned defects and repeat affected cases. If the first
138
+ incorrect constraint comes from the host layout, report that boundary instead
139
+ of masking it inside the component. For existing components, preserve unrelated
140
+ public props, editor overrides, and behavior.
141
+
142
+ Report sizes/states tested and any remaining overflow or inaccessible content.
143
+ If no browser preview or fixture is available, perform the source review and
144
+ state that rendered resize behavior is **unverified**; do not claim the resize
145
+ review passed. Build, typecheck, a11y scans, and manifest generation do not
146
+ establish that layout fits.
@@ -45,6 +45,12 @@ Apply this filter to every candidate:
45
45
  share the same semantic editor surface. Distinguish position with data or a
46
46
  module-only helper class, not separate part names.
47
47
 
48
+ The manifest generator can silently omit a named part behind conditional JSX.
49
+ Keep an editor-controlled part mounted in the render it inspects. Hide it with
50
+ CSS driven by a data attribute, or disable it when it should remain visible.
51
+ After generating the manifest, confirm that its `elements` include every
52
+ intended named part.
53
+
48
54
  ## Sanity Check
49
55
 
50
56
  For each candidate, ask:
@@ -40,7 +40,9 @@ const RULE_CONFIG = {
40
40
  'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error',
41
41
  // A tabpanel with no focusable content takes tabIndex=0 (ARIA Authoring Practices).
42
42
  'jsx-a11y/no-noninteractive-tabindex': ['error', { roles: ['tabpanel'] }],
43
- 'jsx-a11y/no-redundant-roles': 'error',
43
+ // Safari drops <ul> list semantics with list-style: none; role="list" restores
44
+ // them (WebKit bug 170179). Keep all other redundant roles flagged.
45
+ 'jsx-a11y/no-redundant-roles': ['error', { ul: ['list'] }],
44
46
  'jsx-a11y/no-static-element-interactions': 'error',
45
47
  'jsx-a11y/prefer-tag-over-role': 'error',
46
48
  'jsx-a11y/role-has-required-aria-props': 'error',
@@ -74,6 +76,22 @@ function forwardsOnlySdkHandlers(tag) {
74
76
  );
75
77
  }
76
78
 
79
+ const PASSIVE_AUTOPLAY_HANDLERS = new Set([
80
+ 'onMouseEnter',
81
+ 'onMouseLeave',
82
+ 'onFocus',
83
+ 'onBlur',
84
+ 'onFocusCapture',
85
+ 'onBlurCapture',
86
+ ]);
87
+
88
+ /** Carousel roots observe hover/focus to pause autoplay; these handlers do not activate the region. */
89
+ function pausesAutoplayOnly(tag) {
90
+ if (!isSdkRoot(tag) || !tag.includes('data-pause-button-visibility=')) return false;
91
+ const handlers = [...tag.matchAll(/\b(on[A-Z]\w*)=\{[^}]*\}/g)];
92
+ return handlers.length > 0 && handlers.every(([, name]) => PASSIVE_AUTOPLAY_HANDLERS.has(name));
93
+ }
94
+
77
95
  /** Roles whose native element cannot express a styled component. */
78
96
  const ROLES_WITHOUT_NATIVE_TAG = new Set(['img', 'presentation', 'none', 'group', 'status']);
79
97
 
@@ -93,6 +111,12 @@ function roleHasNoNativeTag(tag) {
93
111
  * `rules` is skipped when the opening tag of the reported element matches `tag`.
94
112
  */
95
113
  const EXEMPTIONS = [
114
+ {
115
+ // ANIMATED-COMPONENTS.md requires the noninteractive carousel root to
116
+ // observe hover and focus so autoplay stops while a visitor is using it.
117
+ rules: new Set(['jsx-a11y/no-noninteractive-element-interactions']),
118
+ tag: pausesAutoplayOnly,
119
+ },
96
120
  {
97
121
  // SDK handlers (`onClick`, `onMouseIn`, ...) are forwarded on the root
98
122
  // element, which carries `id={id}`; the root itself is not the control.
@@ -9,7 +9,6 @@ allowed-tools:
9
9
  - Bash(npm install *)
10
10
  - Bash(npm run *)
11
11
  - Bash(node *)
12
- - Bash(bash *)
13
12
  - Bash(cd *)
14
13
  - Bash(ls *)
15
14
  - Bash(mkdir *)
@@ -67,7 +67,7 @@ The verticals the skill operates end-to-end today: **stores · blog · cms · fo
67
67
 
68
68
  ### bookings — appointments and classes
69
69
  - **Intent:** book / appointment / schedule / class / session / reserve a slot — a **service or appointment with a provider** (a stylist, trainer, tutor, clinic, studio).
70
- - **Not bookings — route to `restaurants` instead:** a **table at a restaurant** is a Table Reservations concern, not a bookable service; and a **special dining occasion guests reserve** — a wine tasting, cheese pairing, chef's table, or tasting-menu evening at a restaurant — is a **restaurants → Table Reservations *experience*** (see the restaurants entry), **not** a bookings service. Reach for `bookings` only when what's booked is a standalone service/class with a provider, never a seat at a restaurant.
70
+ - **Not bookings — route to `restaurants` instead:** a **table at a restaurant** is a Table Reservations concern, not a bookable service; and a **special dining occasion guests reserve** — a chef's table, cheese pairing, or tasting-menu evening at a restaurant — is a **restaurants → Table Reservations *experience*** (see the restaurants entry), **not** a bookings service. Reach for `bookings` only when what's booked is a standalone service/class with a provider, never a seat at a restaurant.
71
71
  - **Not bookings — route to `rentals` instead:** when the **customer chooses how long** they want something, and what's booked is a **thing rather than a person** — a room by the hour, a desk for three days, a van for the weekend. The tell is a duration *range* the customer picks within. A 60-minute haircut is `bookings`; a meeting room booked for 1–8 hours is `rentals`.
72
72
  - **Required site features:** a list of services; a page per service; a way to pick a time and book it.
73
73
  - **Implementation checklist:** show each service's name, duration, and price; show the staff or provider; show the available time slots; a book action; a confirmation after booking.
@@ -85,8 +85,8 @@ The verticals the skill operates end-to-end today: **stores · blog · cms · fo
85
85
  - **Implementation checklist:** show each plan's name, price, and billing cycle; list what each plan includes (the perks); a clear "choose plan" action; highlight a recommended tier where it makes sense.
86
86
 
87
87
  ### restaurants — menus and food
88
- - **Intent:** menu / restaurant / cafe / food / dish list / order food / dine-in / **reserve a table** / a **special dining occasion guests reserve** (wine tasting, cheese pairing, chef's table, tasting-menu evening).
89
- - **Required site features:** a menu organized into sections; each item with a name, description, and price. **Add-ons, each on demand (separate apps/features; see `SETUP.md`):** online ordering, table reservations, and — **when the brief names special dining occasions** — **experiences** (a reservation that *is* a curated occasion at the restaurant, e.g. a wine-and-cheese pairing), which ride on the Table Reservations app.
88
+ - **Intent:** menu / restaurant / cafe / food / dish list / order food / dine-in / **reserve a table** / a **special dining occasion guests reserve** (chef's table, cheese pairing, tasting-menu evening).
89
+ - **Required site features:** a menu organized into sections; each item with a name, description, and price. **Add-ons, each on demand (separate apps/features; see `SETUP.md`):** online ordering, table reservations, and — **when the brief names special dining occasions** — **experiences** (a reservation that *is* a curated occasion at the restaurant, e.g. a chef's table), which ride on the Table Reservations app.
90
90
  - **Experiences vs bookings — decide here:** an **experience** is a **reservation that is itself a special dining occasion at the restaurant** — it books seats at the restaurant's own location and carries its own name, per-guest price, schedule, and booking form. Use it for pairing sessions, tastings, and chef's tables. Use the separate **`bookings`** vertical only for a **service/appointment/class with a provider** that isn't a seat at the restaurant. For anything a restaurant hosts and seats, prefer an experience.
91
91
  - **Implementation checklist:** show the menu grouped into sections in order; each item's name, description, price, and labels (vegan, spicy…) and modifiers / variants where they exist; an order action if online ordering is wired; a reserve-a-table action if reservations are wired; **if experiences are wired, list each experience with its name, description, per-guest price, and schedule, and a reserve-seats action.**
92
92
 
@@ -52,7 +52,7 @@ For each capability, build its `intent.<cap>` block — the inputs the seed step
52
52
  | bookings | `{ serviceCount, servicesNamed: [] }` | `serviceCount: 2`, brand-derived service names |
53
53
  | rentals | `{ unitType, serviceCount, resourceCount, servicesNamed: [] }` | `unitType: "HOUR"` (`"DAY"` if the brief rents by the day), `serviceCount: 2`, `resourceCount: 2`, brand-derived item names |
54
54
  | pricing-plans | `{ planCount, tiersNamed: [] }` | `planCount: 2` (e.g. Basic / Pro), monthly billing |
55
- | restaurants | `{ menuName, sections: [{ name, itemCount }], ordering?, reservations?, experiences?: [{ name }] }` | one menu, 2 sections, `itemCount: 3` each; add-ons only when the brief names them. **`experiences[]`** = special dining occasions guests reserve (wine/cheese pairing, chef's table) — a restaurant *reservation that is an experience*, **not** the `bookings` vertical (see `CAPABILITIES.md`). |
55
+ | restaurants | `{ menuName, sections: [{ name, itemCount }], ordering?, reservations?, experiences?: [{ name }] }` | one menu, 2 sections, `itemCount: 3` each; add-ons only when the brief names them. **`experiences[]`** = special dining occasions guests reserve (chef's table, tasting menu) — a restaurant *reservation that is an experience*, **not** the `bookings` vertical (see `CAPABILITIES.md`). |
56
56
  | portfolio | `{ collections: [{ name }], projectCount }` | one brand-derived collection, `projectCount: 3` |
57
57
 
58
58
  Counts are deliberately small (the seed shows the shape, not a full catalog).
@@ -48,7 +48,7 @@ Before implementing a capability, Read the local recipe linked in its row.
48
48
  | restaurants — online ordering (add-on; only when the Orders app is in the run) | add `@wix/ecom` + `@wix/redirects` to the row above | `how-to-code-restaurant-orders.md` — ordering cart/checkout on top of the menu display. |
49
49
  | portfolio | `@wix/portfolio` (+ `media` from `@wix/sdk` to resolve image strings; **no** `@wix/ecom`/`@wix/redirects` — display-only) | `how-to-code-portfolio.md` — Portfolio v1 display-only read. |
50
50
  | restaurants — table reservations (add-on; only when the Table Reservations app is in the run) | `@wix/table-reservations` (a **distinct** package from `@wix/restaurants`; **no ecom/redirects** — a reservation is a hold, not a purchase) | `how-to-code-restaurant-reservations.md` — book-a-table hold→reserve. |
51
- | restaurants — experiences (add-on; special dining occasions guests reserve — wine/cheese pairing, chef's table) | `@wix/table-reservations` (same package; the **`experiences`** namespace + `timeSlots`/`reservations`) | `how-to-code-restaurant-reservations.md` — "Booking an experience" (query experiences → scheduled slots → reserve with `experienceId`). |
51
+ | restaurants — experiences (add-on; special dining occasions guests reserve — chef's table, tasting menu) | `@wix/table-reservations` (same package; the **`experiences`** namespace + `timeSlots`/`reservations`) | `how-to-code-restaurant-reservations.md` — "Booking an experience" (query experiences → scheduled slots → reserve with `experienceId`). |
52
52
 
53
53
  > **Cross-cutting packages** (`CAPABILITIES.md` § "Cross-cutting capabilities"): these aren't per-vertical rows but get added to the package list when the run used them. If **coupons** were seeded, add `@wix/marketing`. The eCommerce checkout packages (`@wix/ecom`, `@wix/redirects`) are already folded into the stores row above — include them once when any purchase-flow vertical is present.
54
54
 
@@ -39,7 +39,7 @@ Each entry states only the entities, where their count/content comes from, the p
39
39
  - **bookings** — *Bookable services.* Recipe: **`setup-bookings.md`**. `intent.bookings.serviceCount` services (name + short description fitting `brand`, a simple duration and price); keep the schedule minimal. **Keep:** `serviceIds[]` as **seed-time state** (pricing-plans' coverage step keys on them, below); nothing for the handoff — services are discovered **live** (`queryServices`).
40
40
  - **rentals** — *Rentable resources.* Recipe: **`setup-rentals.md`**. A resource **type** per kind of thing rented (rooms, vans, cameras), then `intent.rentals.resourceCount` **resources** inside it (Room A / Room B — parallel capacity comes from more resources, not higher capacity), then `intent.rentals.serviceCount` **rental services**, each carrying the Wix Rentals `appId`, `serviceResources` naming its resource type, a `primaryResourceType`, and a `durationRange` (`HOUR` or `DAY`) with the min/max the request implies — defaulting to **60/480 minutes** hourly and **1/5 days** daily when the brief doesn't say — and a **per-unit** price. **No category** — unlike bookings, rental services don't use one; don't create or assign one. **Order is load-bearing: resource type → resources → services** — a service whose resource type holds no resources has permanently empty availability. Seed resources **24/7** (no working-hours schedule) unless the request names opening hours; that keeps a multi-day rental to a single booking. **Keep:** `resourceTypeIds[]` as seed-time state (services key on them); nothing for the handoff — rentals are discovered **live** (`queryServicesByFilters` filtered by the rentals `appId`).
41
41
  - **pricing-plans** — *Membership tiers.* Recipe: **`setup-pricing-plans.md`**. `intent.pricing-plans.planCount` recurring plans (name, price, a monthly billing cycle) fitting `brand`. **When bookings is also in this run and the intent is a membership that covers services** (a studio/gym/class pass), also **attach the covered bookings services to the plan** — seed the bookings services first, since coverage keys on their service ids (see the recipe). **Keep:** the bookings coverage only transiently, to wire it during seeding; nothing for the handoff — plans are discovered **live** (`queryPlans`) and plan→service coverage is read **live** at checkout, never from a frozen map (see `how-to-code-pricing-plans.md`).
42
- - **restaurants** — *A menu.* Recipe: **`setup-restaurants.md`** (add-ons have their own recipes, below). Create the `itemCount` items per section first (name, description, price fitting `brand`), then the `intent.restaurants.sections` referencing those item ids, then the menu referencing the section ids — **items → sections → menu** (build bottom-up: a section is created with its `itemIds`, a menu with its `sectionIds`, so each child must exist before its parent — see `setup-restaurants.md` STEP 1–3). Text-only. **Keep (seed-time only):** `menuId`, `sectionIds[]`, `itemIds[]` to build the tree; nothing goes to the handoff — the frontend reads menus **live** (`listMenus`/`listSections`/`listItems`). **Online ordering (add-on, on demand)** — only when the request calls for ordering (not just a displayed menu): install the Orders app (per `SETUP.md` §2) and follow **`setup-restaurant-orders.md`**; **seed the menu FIRST, then ordering, in the same unit** (ordering auto-provisions per menu either way — see the concurrency note above — but keeping them one unit gives the ordering verify a menu to confirm against). **Configure the business-location address (`setup-restaurant-orders.md` STEP 0) — required for ordering to work end-to-end; without it Wix limits ordering to "testing only" and checkout breaks.** If the brief gives no address, set a placeholder and flag the owner in the handoff. Completing a *paid* order additionally needs premium + a payment method (dashboard/premium — surface, don't fail). **Keep:** nothing for the handoff — read **live** (`listOperations`/`listFulfillmentMethods`); track `operationId`/`fulfillmentMethodIds[]` transiently. **Table reservations (add-on, on demand, INDEPENDENT of ordering/menu)** — only when the request calls for reservations: install the Table Reservations app (per `SETUP.md` §2) and follow **`setup-restaurant-reservations.md`**; **no menu dependency** (reservations bind to a location, not a menu — do NOT apply the menu-first rule) and **nothing to bulk-seed** (visitors create reservations at runtime); **configure the business-location address (`setup-restaurant-reservations.md` STEP 0)** — the reservation location is the site's business location, so set a real address (placeholder + owner flag if the brief names none; shared with ordering if both are present); enabling online reservations is **premium-gated** (record the precondition, don't fail). **Keep:** nothing for the handoff — read **live** (`listReservationLocations`); track `reservationLocationId` transiently. **Experiences (add-on within Table Reservations, on demand)** — only when the request names **special dining occasions guests reserve** (wine/cheese pairing, chef's table, tasting-menu evening — an *experience*, not a `bookings` service; see `CAPABILITIES.md`): they need the **Table Reservations app** (no extra install) and a configured **business-location address** (`setup-restaurant-reservations.md` STEP 0, same as reservations) and, unlike plain reservations, they **are** seeded — create one Experience per named occasion on the default reservation location, following **`setup-restaurant-experiences.md`**. Booking an experience is **premium-gated** like online reservations (record the precondition, don't fail). **Keep:** nothing for the handoff — experiences are read **live** (`queryExperiences`); track `experienceId`(s) transiently.
42
+ - **restaurants** — *A menu.* Recipe: **`setup-restaurants.md`** (add-ons have their own recipes, below). Create the `itemCount` items per section first (name, description, price fitting `brand`), then the `intent.restaurants.sections` referencing those item ids, then the menu referencing the section ids — **items → sections → menu** (build bottom-up: a section is created with its `itemIds`, a menu with its `sectionIds`, so each child must exist before its parent — see `setup-restaurants.md` STEP 1–3). Text-only. **Keep (seed-time only):** `menuId`, `sectionIds[]`, `itemIds[]` to build the tree; nothing goes to the handoff — the frontend reads menus **live** (`listMenus`/`listSections`/`listItems`). **Online ordering (add-on, on demand)** — only when the request calls for ordering (not just a displayed menu): install the Orders app (per `SETUP.md` §2) and follow **`setup-restaurant-orders.md`**; **seed the menu FIRST, then ordering, in the same unit** (ordering auto-provisions per menu either way — see the concurrency note above — but keeping them one unit gives the ordering verify a menu to confirm against). **Configure the business-location address (`setup-restaurant-orders.md` STEP 0) — required for ordering to work end-to-end; without it Wix limits ordering to "testing only" and checkout breaks.** If the brief gives no address, set a placeholder and flag the owner in the handoff. Completing a *paid* order additionally needs premium + a payment method (dashboard/premium — surface, don't fail). **Keep:** nothing for the handoff — read **live** (`listOperations`/`listFulfillmentMethods`); track `operationId`/`fulfillmentMethodIds[]` transiently. **Table reservations (add-on, on demand, INDEPENDENT of ordering/menu)** — only when the request calls for reservations: install the Table Reservations app (per `SETUP.md` §2) and follow **`setup-restaurant-reservations.md`**; **no menu dependency** (reservations bind to a location, not a menu — do NOT apply the menu-first rule) and **nothing to bulk-seed** (visitors create reservations at runtime); **configure the business-location address (`setup-restaurant-reservations.md` STEP 0)** — the reservation location is the site's business location, so set a real address (placeholder + owner flag if the brief names none; shared with ordering if both are present); enabling online reservations is **premium-gated** (record the precondition, don't fail). **Keep:** nothing for the handoff — read **live** (`listReservationLocations`); track `reservationLocationId` transiently. **Experiences (add-on within Table Reservations, on demand)** — only when the request names **special dining occasions guests reserve** (chef's table, tasting menu, tasting-menu evening — an *experience*, not a `bookings` service; see `CAPABILITIES.md`): they need the **Table Reservations app** (no extra install) and a configured **business-location address** (`setup-restaurant-reservations.md` STEP 0, same as reservations) and, unlike plain reservations, they **are** seeded — create one Experience per named occasion on the default reservation location, following **`setup-restaurant-experiences.md`**. Booking an experience is **premium-gated** like online reservations (record the precondition, don't fail). **Keep:** nothing for the handoff — experiences are read **live** (`queryExperiences`); track `experienceId`(s) transiently.
43
43
  - **portfolio** — *A project showcase.* Recipe: **`setup-portfolio.md`**. Create the `intent.portfolio.collections` (title + short description fitting `brand`), then `intent.portfolio.projectCount` projects assigned to them — **collections before projects** (a project's `collectionIds` must hold real collection ids; see the recipe). Text-only by default (omit `coverImage`; images only when `imagery` is on — §1, §4). **Keep (seed-time only):** `collectionIds{<name>}` to assign projects into their collections; nothing goes to the handoff — the frontend lists collections and projects **live** (`collections.queryCollections` / `projects.queryProjects`), so owner-added content appears with no code change.
44
44
 
45
45
  > **Cross-cutting (on demand).** `coupons` is not a standalone capability (`CAPABILITIES.md` § "Cross-cutting capabilities") — there's no app to install and it's not in `verticals[]`. If intent calls for discounts **and** a parent vertical (stores / bookings / events / pricing-plans) is in this run, create coupons scoped to that parent and add the coupon to the `seeded` map (`seeded.coupons = { couponIds[] }`). **Coupons has no inline recipe** — read the create shape from the docs (`DOC_DISCOVERY.md` is the fallback): every coupon needs a scope with a namespace and its parent vertical's app installed (<https://dev.wix.com/docs/api-reference/business-solutions/coupons/about-wix-coupons.md>), and the create-coupon spec needs name/code/start + a scope namespace + exactly one coupon-type field (<https://dev.wix.com/docs/api-reference/business-solutions/coupons/coupons/create-a-coupon.md>). eCommerce needs no seeding — it's the runtime checkout layer that rides along.
@@ -25,7 +25,7 @@ For each capability in `verticals[]`, install its app by `appDefId` (these are t
25
25
  - **bookings** → `13d21c63-b5ec-5912-8397-c3a5ddb27a97`
26
26
  - **rentals** → `ff5d6eb1-65e4-4f9a-8b14-64d34c12cc2e` (Wix Rentals). Rentals provisions the Bookings infrastructure it runs on, so a Rentals-only install is enough — its availability comes back without adding Wix Bookings separately. Rentals has **no APIs of its own**: it is Services V2 with rentals-specific field values (`setup-rentals.md`), and its frontend package is `@wix/bookings` — there is no `@wix/rentals`. Rentals uses no categories; to also offer plain fixed-duration services, install Wix Bookings (`13d21c63-b5ec-5912-8397-c3a5ddb27a97`) as well, seed those with `setup-bookings.md`, and filter every catalog read by `appId` so the two sets stay separate.
27
27
  - **pricing-plans** → `1522827f-c56c-a5c9-2ac9-00f9e6ae12d3`
28
- - **restaurants** → `b278a256-2757-4f19-9313-c05c783bec92` (Wix Restaurants **Menus** — the seedable core). Online ordering and table reservations are **separate optional apps**, install only if intent calls for them: Orders (New) → `9a5d83fd-8570-482e-81ab-cfa88942ee60`, Table Reservations → `f9c07de2-5341-40c6-b096-8eb39de391fb`. **Experiences** (special dining occasions guests reserve — wine tastings, chef's tables; see `CAPABILITIES.md`) are a **feature of the Table Reservations app, not a separate install** — installing Table Reservations is all that's needed to create and book them (`setup-restaurant-experiences.md`).
28
+ - **restaurants** → `b278a256-2757-4f19-9313-c05c783bec92` (Wix Restaurants **Menus** — the seedable core). Online ordering and table reservations are **separate optional apps**, install only if intent calls for them: Orders (New) → `9a5d83fd-8570-482e-81ab-cfa88942ee60`, Table Reservations → `f9c07de2-5341-40c6-b096-8eb39de391fb`. **Experiences** (special dining occasions guests reserve — chef's tables, tasting menus; see `CAPABILITIES.md`) are a **feature of the Table Reservations app, not a separate install** — installing Table Reservations is all that's needed to create and book them (`setup-restaurant-experiences.md`).
29
29
  - **portfolio** → `d90652a2-f5a1-4c7c-84c4-d4cdcc41f130` (Wix Portfolio; the install ships a default sample collection + projects — `setup-portfolio.md` STEP 0 cleans them)
30
30
  - **cms** → **no install** (Wix Data is core) — skip
31
31
 
@@ -166,7 +166,7 @@ Doc: <https://dev.wix.com/docs/api-reference/business-solutions/restaurants/rese
166
166
 
167
167
  ### Booking an experience (only when experiences are in the run)
168
168
 
169
- An **experience** is a reservation that *is* a curated dining occasion (wine/cheese pairing, chef's table). It uses the **same** booking mechanism as a table, with two differences: you **list experiences** to show and choose from, and you offer bookable times **projected from the experience's own schedule** (there is *no* experience-scoped slot API — see Gotcha C) rather than from `getScheduledTimeSlots`, then stamp the chosen `experienceId` onto the reservation. Read the shapes from the docs (`.md?apiView=SDK`); the earned drift gotchas below are what the docs won't tell you.
169
+ An **experience** is a reservation that *is* a curated dining occasion (chef's table, tasting menu). It uses the **same** booking mechanism as a table, with two differences: you **list experiences** to show and choose from, and you offer bookable times **projected from the experience's own schedule** (there is *no* experience-scoped slot API — see Gotcha C) rather than from `getScheduledTimeSlots`, then stamp the chosen `experienceId` onto the reservation. Read the shapes from the docs (`.md?apiView=SDK`); the earned drift gotchas below are what the docs won't tell you.
170
170
 
171
171
  Flow (mirrors the docs' sample flow — <https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/experiences/sample-flows.md>):
172
172
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: "Setup Restaurant Experiences"
3
- description: Seeds Wix Table Reservations **Experiences** — a reservation that IS a special dining occasion at the restaurant (wine tasting, cheese pairing, chef's table, tasting-menu evening), as opposed to a plain table booking or a Bookings service. Points to the live docs for the full create payload and carries only the earned gotchas the docs don't state (field-shape drift, payment policy, premium gating, when-to-use). The occasions/prices/schedule come from the request.
3
+ description: Seeds Wix Table Reservations **Experiences** — a reservation that IS a special dining occasion at the restaurant (chef's table, cheese pairing, tasting-menu evening), as opposed to a plain table booking or a Bookings service. Points to the live docs for the full create payload and carries only the earned gotchas the docs don't state (field-shape drift, payment policy, premium gating, when-to-use). The occasions/prices/schedule come from the request.
4
4
  ---
5
5
  **RECIPE**: Business Recipe – Experiences Setup for Wix Table Reservations (Experiences API)
6
6
 
@@ -8,7 +8,7 @@ description: Seeds Wix Table Reservations **Experiences** — a reservation that
8
8
 
9
9
  ## When to use experiences (and when NOT to)
10
10
 
11
- An **experience** is a **reservation that is itself a curated dining occasion at the restaurant** — it books seats at the restaurant's own reservation location but overrides the location's defaults with its own name, description, per-guest payment policy, party-size, schedule, and booking form. Reach for it when the brief names a **wine tasting, cheese/wine pairing, chef's table, or tasting-menu evening**.
11
+ An **experience** is a **reservation that is itself a curated dining occasion at the restaurant** — it books seats at the restaurant's own reservation location but overrides the location's defaults with its own name, description, per-guest payment policy, party-size, schedule, and booking form. Reach for it when the brief names a **chef's table, cheese pairing, or tasting-menu evening**.
12
12
 
13
13
  - **Experience vs plain reservation:** a plain reservation just books a table; an experience books a *named, scheduled, often paid occasion*. Same booking mechanism, different intent.
14
14
  - **Experience vs `bookings`:** the separate `bookings` vertical is for a **service/appointment/class with a provider** (spa, tutor, studio). If a restaurant *hosts and seats* the occasion, it's an experience — not a booking. (See `CAPABILITIES.md` — "Experiences vs bookings".)
@@ -16,7 +16,7 @@ npx @wix/cli@latest whoami # exits 0 when logged in; non-zero when logged out
16
16
  If it's non-zero, **log in yourself** — don't punt to the user and stop. A foreground command that exits in seconds, one JSON event per line:
17
17
 
18
18
  ```bash
19
- node ../../entry/bootstrap.mjs # path relative to this file
19
+ node ../../scripts/bootstrap.mjs # path relative to this file
20
20
  ```
21
21
 
22
22
  | Event | Do |
@@ -25,20 +25,9 @@ node ../../entry/bootstrap.mjs # path relative to this file
25
25
  | `awaiting_user` (`verificationUri`, `userCode`, `message`) | Send `message` verbatim, then stop. Re-run the script when the user says they're done → `logged_in`. Re-running early returns the **same** code, not a new one. |
26
26
  | `cli_unreachable` / `login_failed` (`detail`) | Show `detail` and stop. |
27
27
 
28
- ### If you'd rather not execute it
28
+ ### What the script does
29
29
 
30
- Read `../../entry/bootstrap.mjs` and do the same thing yourself. What matters:
31
-
32
- ```bash
33
- # AI_AGENT must be set, or the CLI renders an interactive Ink TUI and emits no JSON.
34
- # Detach, redirecting to a FILE — a pipe dies with your process and the CLI gets EPIPE.
35
- AI_AGENT=my-agent nohup npx -y @wix/cli@latest login > /tmp/wix-login.log 2>&1 &
36
-
37
- grep -m1 awaiting_user /tmp/wix-login.log
38
- # {"event":"awaiting_user","expiresInSeconds":600,"userCode":"…","verificationUri":"…"}
39
- ```
40
-
41
- Send the user that URL + code, then stop — nothing moves until they act. The detached login lands the session on its own; resume with `whoami` (exit 0). Codes last ~10 min; after that, start a new login and surface the new code.
30
+ Read `../../scripts/bootstrap.mjs` if you want to see it: it sets `AI_AGENT` so the CLI emits JSON events instead of an interactive TUI, starts `wix login` detached with its output in a file under the OS temp dir (a pipe would die with the parent), and prints the device code. It is the one login path; there is no manual variant. Codes last ~10 min; after that, run it again and surface the new code.
42
31
 
43
32
  ## 2 · Mint the token
44
33
 
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+ import { spawn, spawnSync } from 'node:child_process';
3
+ import { existsSync, mkdirSync, openSync, readFileSync, writeFileSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+
7
+ // ── tiny event protocol (one JSON object per line) ───────────────────────────
8
+ const emit = (event, extra = {}) =>
9
+ process.stdout.write(JSON.stringify({ event, ...extra }) + '\n');
10
+ const fail = (event, extra = {}) => {
11
+ emit(event, { ok: false, ...extra });
12
+ process.exit(1);
13
+ };
14
+
15
+ // ── platform-safe binary names (npm/npx are .cmd on Windows) ─────────────────
16
+ const isWin = process.platform === 'win32';
17
+ const bin = (name) => (isWin ? `${name}.cmd` : name);
18
+ const WIX = [bin('npx'), '-y', '@wix/cli@latest']; // run the CLI via npx — no global install/mutation
19
+
20
+ // Force the CLI into non-interactive "agent" mode. Without an agent signal in
21
+ // the env, `wix login` renders an interactive Ink TUI (device code + keypress)
22
+ // that needs a raw TTY and crashes in an agent sandbox — and it never emits the
23
+ // JSON login events this script forwards. The CLI uses @vercel/detect-agent,
24
+ // whose first check is the AI_AGENT env var, so setting it guarantees agent mode
25
+ // (and the awaiting_user/success/logged_in events) for every child we spawn.
26
+ // Respect an existing value so a known runner (claude, cursor, …) keeps its name.
27
+ const AGENT_ENV = { ...process.env, AI_AGENT: process.env.AI_AGENT || 'wix-headless-skill' };
28
+
29
+ // Where the detached login parks its output between runs. It has to outlive this
30
+ // process, so it can't be a pipe and can't live in the project.
31
+ const STATE_DIR = join(tmpdir(), 'wix-headless-login');
32
+ const LOG = join(STATE_DIR, 'login.log');
33
+ const PIDFILE = join(STATE_DIR, 'login.pid');
34
+
35
+ // run a command, capture stdout+stderr (combined), return {status, out}
36
+ function capture(cmd, args, opts = {}) {
37
+ const r = spawnSync(cmd, args, { encoding: 'utf8', shell: isWin, env: AGENT_ENV, ...opts });
38
+ return { status: r.status ?? 1, out: `${r.stdout || ''}${r.stderr || ''}`, error: r.error };
39
+ }
40
+
41
+ // ── 1. CLI reachable (via npx — no install) ──────────────────────────────────
42
+ function checkCli() {
43
+ const r = capture(WIX[0], [...WIX.slice(1), '--version']);
44
+ if (r.status !== 0) fail('cli_unreachable', { detail: r.out.trim().slice(0, 400) });
45
+ // npx interleaves "npm notice …" lines with the version, so don't just take the
46
+ // last line — pick the first semver-looking token, falling back to the last line.
47
+ const version = (r.out.match(/\d+\.\d+\.\d+[^\s]*/) || [])[0] || r.out.trim().split('\n').pop();
48
+ emit('cli_ok', { version });
49
+ }
50
+
51
+ // ── 2. Reuse an existing session, or start device login ──────────────────────
52
+ const hasExistingSession = () => capture(WIX[0], [...WIX.slice(1), 'whoami']).status === 0;
53
+
54
+ function loginEvents() {
55
+ if (!existsSync(LOG)) return [];
56
+ return readFileSync(LOG, 'utf8')
57
+ .split('\n')
58
+ .map((l) => l.trim())
59
+ .filter(Boolean)
60
+ .map((l) => {
61
+ try {
62
+ return JSON.parse(l);
63
+ } catch {
64
+ return null; // non-JSON CLI chatter
65
+ }
66
+ })
67
+ .filter((e) => e && e.event);
68
+ }
69
+
70
+ const alive = (pid) => {
71
+ try {
72
+ process.kill(pid, 0);
73
+ return true;
74
+ } catch {
75
+ return false;
76
+ }
77
+ };
78
+
79
+ // A login this machine started earlier may still be waiting on the browser. Reuse
80
+ // its code instead of minting a second one — a new login invalidates nothing, but
81
+ // it hands the user a different code than the one already on their screen.
82
+ function pendingLogin() {
83
+ if (!existsSync(PIDFILE)) return null;
84
+ let state;
85
+ try {
86
+ state = JSON.parse(readFileSync(PIDFILE, 'utf8'));
87
+ } catch {
88
+ return null;
89
+ }
90
+ if (!state.pid || !alive(state.pid)) return null;
91
+ const ev = loginEvents().find((e) => e.event === 'awaiting_user');
92
+ if (!ev) return null;
93
+ // Don't hand back a code that's about to expire mid-typing.
94
+ const ageSeconds = Math.round((Date.now() - state.startedAt) / 1000);
95
+ if (ageSeconds > (ev.expiresInSeconds ?? 600) - 60) return null;
96
+ return ev;
97
+ }
98
+
99
+ // Detach so the login keeps polling after this process exits. stdio goes to a
100
+ // file, not a pipe: a pipe dies with the parent, and the CLI would get EPIPE.
101
+ function startLogin() {
102
+ mkdirSync(STATE_DIR, { recursive: true });
103
+ writeFileSync(LOG, '');
104
+ const out = openSync(LOG, 'a');
105
+ const child = spawn(WIX[0], [...WIX.slice(1), 'login'], {
106
+ detached: true,
107
+ stdio: ['ignore', out, out],
108
+ env: AGENT_ENV,
109
+ shell: isWin,
110
+ });
111
+ child.on('error', (e) => fail('login_failed', { detail: String(e) }));
112
+ writeFileSync(PIDFILE, JSON.stringify({ pid: child.pid, startedAt: Date.now() }));
113
+ child.unref();
114
+ }
115
+
116
+ async function waitForCode(timeoutMs = 60000) {
117
+ const deadline = Date.now() + timeoutMs;
118
+ while (Date.now() < deadline) {
119
+ const events = loginEvents();
120
+ const ev = events.find((e) => e.event === 'awaiting_user');
121
+ if (ev) return ev;
122
+ const bad = events.find((e) => e.event === 'login_failed');
123
+ if (bad) fail('login_failed', bad);
124
+ await new Promise((r) => setTimeout(r, 500));
125
+ }
126
+ const detail = existsSync(LOG) ? readFileSync(LOG, 'utf8').trim().slice(-1500) : '';
127
+ fail('login_failed', {
128
+ detail: detail || `wix login produced no device code within ${timeoutMs / 1000}s.`,
129
+ });
130
+ }
131
+
132
+ // The next move is the user's, so hand the code back and exit rather than holding
133
+ // the caller open for a browser round-trip. `message` is the sentence to relay.
134
+ function surrenderTo(ev) {
135
+ emit('awaiting_user', {
136
+ ...ev,
137
+ message:
138
+ `To connect your Wix account, open ${ev.verificationUri} and enter the code ` +
139
+ `${ev.userCode}. Tell me once you're done and I'll continue.`,
140
+ });
141
+ process.exit(0);
142
+ }
143
+
144
+ // ── main ────────────────────────────────────────────────────────────────────
145
+ checkCli();
146
+
147
+ if (hasExistingSession()) {
148
+ emit('logged_in');
149
+ process.exit(0);
150
+ }
151
+
152
+ const pending = pendingLogin();
153
+ if (pending) surrenderTo(pending);
154
+
155
+ startLogin();
156
+ surrenderTo(await waitForCode());