frontpage-host 0.1.21 → 0.1.22

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.
@@ -7,9 +7,6 @@ export const DOCUMENTED_MODULE_KEYS = [
7
7
  "forms",
8
8
  "booking",
9
9
  "signups",
10
- "payments",
11
- "shop",
12
- "ordering",
13
10
  "articles",
14
11
  "google_reviews",
15
12
  ];
@@ -130,85 +127,6 @@ export const MODULE_COMMANDS = [
130
127
  "Typed flags write the same whitelist as `modules config`. Generic JSON config still works.",
131
128
  ],
132
129
  },
133
- {
134
- key: "payments",
135
- name: "Pay buttons",
136
- summary: "One-click Stripe pay buttons. No cart. Connect finishes in the browser, not the terminal.",
137
- genericActions: GENERIC_ACTIONS,
138
- extraActions: [
139
- "status",
140
- "connect [--no-open]",
141
- "disconnect [--yes]",
142
- ],
143
- notYet: [],
144
- examples: [
145
- "frontpage modules payments",
146
- "frontpage modules payments status",
147
- "frontpage modules payments connect --no-open",
148
- "frontpage modules payments disconnect --yes",
149
- "frontpage modules payments docs",
150
- 'frontpage modules payments request-setup --reason "Deposit button needs Stripe"',
151
- ],
152
- notes: [
153
- "Connect needs an admin API key. It opens Frontpage (you must be logged in there), then Stripe. Already-connected sites stay live — disconnect first to switch accounts.",
154
- "Pay buttons and the Online store share one Stripe account. Disconnecting Pay buttons fails if the store still uses that account.",
155
- "request-setup still lights the editor amber bar when you want the owner to finish there.",
156
- ],
157
- },
158
- {
159
- key: "shop",
160
- name: "Online store",
161
- summary: "Catalog, cart, and Stripe checkout. Shares the Pay buttons Stripe account.",
162
- genericActions: GENERIC_ACTIONS,
163
- extraActions: [
164
- "status",
165
- "connect [--no-open]",
166
- "disconnect [--yes]",
167
- ],
168
- notYet: [],
169
- examples: [
170
- "frontpage modules shop",
171
- "frontpage modules shop status",
172
- "frontpage modules shop connect --no-open",
173
- "frontpage modules shop disconnect --yes",
174
- "frontpage modules shop docs",
175
- 'frontpage modules shop request-setup --reason "You asked for a store — Stripe setup makes checkout live"',
176
- ],
177
- notes: [
178
- "Connect needs an admin API key. It opens Frontpage (you must be logged in there), then Stripe. Already-connected sites stay live.",
179
- "payments and shop share one Stripe account. Shop disconnect keeps the account on payments.",
180
- "Shop still needs the shipping step in Modules if you have not set it.",
181
- "request-setup still lights the editor amber bar when you want the owner to finish there.",
182
- ],
183
- },
184
- {
185
- key: "ordering",
186
- name: "Online ordering",
187
- summary: "Square ordering. Connect finishes in the browser, not the terminal.",
188
- genericActions: GENERIC_ACTIONS,
189
- extraActions: [
190
- "status",
191
- "connect [--no-open]",
192
- "disconnect [--yes]",
193
- "sync",
194
- ],
195
- notYet: ["fulfillment get|set"],
196
- examples: [
197
- "frontpage modules ordering",
198
- "frontpage modules ordering status",
199
- "frontpage modules ordering connect --no-open",
200
- "frontpage modules ordering sync",
201
- "frontpage modules ordering disconnect --yes",
202
- "frontpage modules ordering docs",
203
- 'frontpage modules ordering request-setup --reason "You asked for online ordering — Square setup makes it live"',
204
- ],
205
- notes: [
206
- "Connect needs an editor API key. It opens Frontpage (you must be logged in there), then Square. Already-connected sites stay live.",
207
- "sync refreshes the Square catalog (same as the hub Sync button).",
208
- "request-setup still lights the editor amber bar when you want the owner to finish there.",
209
- "Fulfillment (pickup/delivery) is not in this CLI yet — use Modules or the editor.",
210
- ],
211
- },
212
130
  {
213
131
  key: "articles",
214
132
  name: "Articles",
@@ -281,7 +199,6 @@ export const GOOGLE_REVIEWS_CLI_ACTIONS = [
281
199
  ];
282
200
  export const TYPED_CONFIG_CLI_ACTIONS = ["get", "set"];
283
201
  export const STRIPE_OAUTH_CLI_ACTIONS = ["status", "connect", "disconnect"];
284
- export const ORDERING_OAUTH_CLI_ACTIONS = ["status", "connect", "disconnect", "sync"];
285
202
  export function isCliModuleAction(key, action) {
286
203
  if (key === "google_reviews") {
287
204
  return GOOGLE_REVIEWS_CLI_ACTIONS.includes(action);
@@ -301,12 +218,6 @@ export function isCliModuleAction(key, action) {
301
218
  if (key === "callbar" || key === "booking" || key === "signups") {
302
219
  return TYPED_CONFIG_CLI_ACTIONS.includes(action);
303
220
  }
304
- if (key === "payments" || key === "shop") {
305
- return STRIPE_OAUTH_CLI_ACTIONS.includes(action);
306
- }
307
- if (key === "ordering") {
308
- return ORDERING_OAUTH_CLI_ACTIONS.includes(action);
309
- }
310
221
  return false;
311
222
  }
312
223
  export function isDocumentedModuleKey(raw) {
@@ -318,6 +229,26 @@ export function isGenericModuleVerb(raw) {
318
229
  export function moduleCommand(key) {
319
230
  return MODULE_COMMANDS.find((c) => c.key === key);
320
231
  }
232
+ export function formatShopIsNotAModule() {
233
+ return [
234
+ "shop is not a module. Use frontpage store.",
235
+ "",
236
+ "Catalog, fulfillment, pay buttons, and Stripe connect:",
237
+ " frontpage store items|variants|categories|settings|orders|payments|connect|status|disconnect",
238
+ "",
239
+ "See also: https://frontpage.host/developers/store",
240
+ ].join("\n");
241
+ }
242
+ export function formatPaymentsIsNotAModule() {
243
+ return [
244
+ "payments is not a module. Use frontpage store.",
245
+ "",
246
+ "Stripe connect lives on Store:",
247
+ " frontpage store connect|status|disconnect|payments",
248
+ "",
249
+ "See also: https://frontpage.host/developers/store",
250
+ ].join("\n");
251
+ }
321
252
  export function formatModuleUsage(key) {
322
253
  const def = moduleCommand(key);
323
254
  const lines = [
@@ -353,6 +284,7 @@ export function formatModulesIndex() {
353
284
  ...rows,
354
285
  "",
355
286
  "Generic: frontpage modules list|docs|request-setup|enable|disable|archive|config …",
287
+ "Store: frontpage store …",
356
288
  "Articles content: frontpage articles …",
357
289
  "Index: https://frontpage.host/developers/modules",
358
290
  ].join("\n");
@@ -1,4 +1,4 @@
1
- export declare const OAUTH_CLI_KEYS: readonly ["payments", "shop", "ordering"];
1
+ export declare const OAUTH_CLI_KEYS: readonly [];
2
2
  export type OauthCliKey = (typeof OAUTH_CLI_KEYS)[number];
3
3
  export declare function isOauthCliKey(key: string): key is OauthCliKey;
4
4
  export declare function resolveOpenUrl(raw: unknown): string | null;
@@ -9,4 +9,4 @@ export declare function browserOpenCommand(url: string, platform?: NodeJS.Platfo
9
9
  windowsVerbatimArguments?: boolean;
10
10
  };
11
11
  export declare function openBrowser(url: string): void;
12
- export declare function runOauthHandoff(key: OauthCliKey, slug: string, action: string, flags: string[]): Promise<number>;
12
+ export declare function runOauthHandoff(_key: OauthCliKey, _slug: string, _action: string, _flags: string[]): Promise<number>;
@@ -1,19 +1,14 @@
1
1
  /**
2
- * §CLI-MOD-OAUTH / §CLI-MOD-HARD — payments / shop / ordering connect, status, disconnect, sync.
2
+ * Browser helpers for Store Stripe connect (`frontpage store connect`).
3
+ * Leftover-order module OAuth is gone.
3
4
  */
4
5
  import { spawn } from "node:child_process";
5
- import { assertNoUnknownFlags, hasFlag } from "../argv.js";
6
- import { apiBase, EXIT_OK, v1Get, v1Post } from "../http/client.js";
7
- import { emitOk, emitUsage, isJson, requireYesOrTty } from "../runtime.js";
8
- import { formatModuleUsage } from "./commands.js";
9
- export const OAUTH_CLI_KEYS = ["payments", "shop", "ordering"];
6
+ import { apiBase } from "../http/client.js";
7
+ import { emitUsage } from "../runtime.js";
8
+ export const OAUTH_CLI_KEYS = [];
10
9
  export function isOauthCliKey(key) {
11
10
  return OAUTH_CLI_KEYS.includes(key);
12
11
  }
13
- const FINISH = "Open the Frontpage URL while logged in, finish Stripe or Square in the browser, then re-run status. This CLI cannot complete OAuth.";
14
- function pathFor(slug, key) {
15
- return `/api/v1/sites/${encodeURIComponent(slug)}/modules/${encodeURIComponent(key)}`;
16
- }
17
12
  export function resolveOpenUrl(raw) {
18
13
  if (typeof raw !== "string" || !raw.trim())
19
14
  return null;
@@ -44,95 +39,6 @@ export function openBrowser(url) {
44
39
  ...(spec.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}),
45
40
  }).unref();
46
41
  }
47
- function printStatus(key, data) {
48
- const connection = (data.connection ?? {});
49
- const display = connection.display ?? {};
50
- console.log(`connected ${connection.ready ? "yes" : "no"}`);
51
- if (key === "ordering") {
52
- console.log(`merchant ${display.merchantName ?? "—"}`);
53
- if (display.locationCount != null)
54
- console.log(`locations ${display.locationCount}`);
55
- if (display.cardProcessing != null)
56
- console.log(`cards ${display.cardProcessing ? "yes" : "no"}`);
57
- if (display.menuSyncedAt)
58
- console.log(`menu sync ${display.menuSyncedAt}`);
59
- }
60
- else {
61
- console.log(`charges ${display.chargesEnabled == null ? "—" : display.chargesEnabled ? "yes" : "no"}`);
62
- console.log(`account ${display.accountPresent ? "present" : "none"}`);
63
- if (typeof data.sharedStripe === "string")
64
- console.log(`note ${data.sharedStripe}`);
65
- }
66
- if (typeof data.connectUrl === "string")
67
- console.log(`connect ${data.connectUrl}`);
68
- }
69
- export async function runOauthHandoff(key, slug, action, flags) {
70
- const work = [...flags];
71
- const usage = formatModuleUsage(key);
72
- if (action === "status") {
73
- assertNoUnknownFlags(work, "modules");
74
- const data = await v1Get(pathFor(slug, key));
75
- if (isJson()) {
76
- emitOk("modules", data);
77
- return EXIT_OK;
78
- }
79
- printStatus(key, data);
80
- return EXIT_OK;
81
- }
82
- if (action === "connect") {
83
- const noOpen = hasFlag(work, "--no-open");
84
- assertNoUnknownFlags(work, "modules");
85
- const data = await v1Post(pathFor(slug, key), { action: "connect" });
86
- const already = data.alreadyConnected === true;
87
- if (isJson()) {
88
- emitOk("modules", data);
89
- }
90
- else {
91
- const url = resolveOpenUrl(data.connectUrl);
92
- if (url)
93
- console.log(url);
94
- console.log(typeof data.message === "string" ? data.message : already ? "Already connected." : FINISH);
95
- if (typeof data.warning === "string")
96
- console.log(data.warning);
97
- }
98
- const url = resolveOpenUrl(data.connectUrl);
99
- if (url && !noOpen && !already)
100
- openBrowser(url);
101
- return EXIT_OK;
102
- }
103
- if (action === "disconnect") {
104
- assertNoUnknownFlags(work, "modules");
105
- const label = key === "ordering"
106
- ? "disconnect Square"
107
- : key === "payments"
108
- ? "disconnect payments (shared Stripe account with the store)"
109
- : "disconnect the store (shared Stripe account with payments)";
110
- const refuse = requireYesOrTty(label);
111
- if (refuse)
112
- return emitUsage("modules", refuse);
113
- const data = await v1Post(pathFor(slug, key), { action: "disconnect" });
114
- if (isJson()) {
115
- emitOk("modules", data);
116
- return EXIT_OK;
117
- }
118
- console.log(typeof data.message === "string" ? data.message : "Disconnected.");
119
- if (typeof data.warning === "string" && data.warning !== data.message) {
120
- console.log(data.warning);
121
- }
122
- return EXIT_OK;
123
- }
124
- if (action === "sync") {
125
- if (key !== "ordering") {
126
- return emitUsage("modules", `sync is only available for ordering.\n\n${usage}`);
127
- }
128
- assertNoUnknownFlags(work, "modules");
129
- const data = await v1Post(pathFor(slug, key), { action: "sync" });
130
- if (isJson()) {
131
- emitOk("modules", data);
132
- return EXIT_OK;
133
- }
134
- console.log(`Synced — ${data.itemCount ?? 0} items, ${data.locationCount ?? 0} locations.`);
135
- return EXIT_OK;
136
- }
137
- return emitUsage("modules", `${usage}\n\nUnknown action "${action}".`);
42
+ export async function runOauthHandoff(_key, _slug, _action, _flags) {
43
+ return emitUsage("modules", "Unknown module.");
138
44
  }
@@ -6,6 +6,8 @@ export type CommandDef = {
6
6
  name: string;
7
7
  summary: string;
8
8
  usage?: string;
9
+ /** Unpublished leftover names that still dispatch. Help does not list these. */
10
+ aliases?: readonly string[];
9
11
  /** Topic tags for `help <topic>`. */
10
12
  topics?: string[];
11
13
  examples?: string[];
package/dist/registry.js CHANGED
@@ -335,13 +335,12 @@ export const COMMANDS = [
335
335
  "frontpage modules callbar set --phone +14085551234 --sms no",
336
336
  "frontpage modules forms get",
337
337
  "frontpage modules forms set --instance lead-gen --notify-email hi@example.com",
338
- "frontpage modules ordering connect --no-open",
339
- "frontpage modules payments status",
338
+ "frontpage store connect --no-open",
340
339
  "frontpage modules docs forms",
341
340
  "frontpage modules docs google_reviews",
342
341
  "frontpage modules docs signups",
343
342
  "frontpage modules list",
344
- 'frontpage modules request-setup payments --reason "You asked for a $50 deposit button — Stripe setup makes it live"',
343
+ 'frontpage store connect --no-open',
345
344
  'frontpage modules request-setup google_reviews --reason "You asked for Google reviews — pick your listing so we show the right ones"',
346
345
  ],
347
346
  flags: [
@@ -349,13 +348,13 @@ export const COMMANDS = [
349
348
  "Key-first and verb-first both work: `modules google_reviews enable` = `modules enable google_reviews`",
350
349
  "google_reviews extras: connect / resolve / status / list-reviews / hide / show / set-stars / set-sort / set-weight / reset-weights / refresh / disconnect / write-review-url",
351
350
  "callbar/forms/booking/signups: typed get / set (same whitelist as `modules config`)",
352
- "payments/shop/ordering: connect opens the browser; finish there, then status. --no-open for CI. request-setup still lights the editor amber bar.",
351
+ "Store Stripe connect is frontpage store connect. request-setup still lights the editor amber bar.",
353
352
  "See https://frontpage.host/developers/modules and /developers/modules/google-reviews",
354
353
  ],
355
354
  dangers: [
356
355
  "Secret values are never returned — masks only",
357
356
  "request-setup surfaces an owner prompt in the editor status bar (CLI cannot show it)",
358
- "payments and shop share one Stripe account — payments disconnect 409s if the store still uses it",
357
+ "payments and the store share one Stripe account — payments disconnect 409s if the store still uses it",
359
358
  "OAuth cannot finish in the terminal; connect only opens the vendor URL",
360
359
  "Do not write placeId through generic config — that skips listing resolve + seed",
361
360
  ],
@@ -413,6 +412,29 @@ export const COMMANDS = [
413
412
  topics: ["content"],
414
413
  dangers: ["--mark-read is side-effecting with no confirm"],
415
414
  },
415
+ {
416
+ name: "routines",
417
+ summary: "Schedule a prompt for the site agent (not a module)",
418
+ usage: "frontpage routines list|get|add|set|pause|resume|rm|run|runs [--site <slug>] [--json]",
419
+ topics: ["content"],
420
+ examples: [
421
+ "frontpage routines list",
422
+ "frontpage routines add --cadence weekly --weekdays tue,thu,fri --hours 8am,12pm,4pm --timezone America/Chicago --name \"Weekday check\" --prompt \"Review new form submissions and reply to each customer.\"",
423
+ "frontpage routines pause <id>",
424
+ "frontpage routines resume <id>",
425
+ "frontpage routines run <id>",
426
+ ],
427
+ flags: [
428
+ "add requires --name, --prompt, --cadence hourly|daily|weekly|monthly, and --timezone (IANA, such as America/Chicago)",
429
+ "--hours accepts 8, 8am, 12pm, 4pm. Several hours: 8am,12pm,4pm stores [8, 12, 16]",
430
+ "--weekdays accepts tue,thu,fri or 2,4,5. Sunday is 0. Weekly only",
431
+ "--month-days accepts 1,15. Days are 1 through 28. Monthly only",
432
+ "add starts the clock. pause stops it. run fires once and does not move the next time",
433
+ "That Tuesday example stores hours [8, 12, 16] and weekdays [2, 4, 5] on one routine",
434
+ "Unknown leftover flags fail closed (exit 2)",
435
+ ],
436
+ seeAlso: ["people", "https://frontpage.host/developers/routines"],
437
+ },
416
438
  {
417
439
  name: "people",
418
440
  summary: "List and manage site People (not inbox, not a module)",
@@ -422,6 +444,7 @@ export const COMMANDS = [
422
444
  "frontpage people list",
423
445
  "frontpage people add --email alex@example.com --name Alex",
424
446
  "frontpage people set <id> --status working",
447
+ "frontpage people set <id> --line1 \"12 Main\" --city Austin --region TX --postal 78701 --country US",
425
448
  "frontpage people note <id> --body \"Called them\"",
426
449
  "frontpage people follow-up <id> --at 2026-09-12T00:00:00Z",
427
450
  "frontpage people hide <id>",
@@ -440,36 +463,42 @@ export const COMMANDS = [
440
463
  seeAlso: ["inbox", "https://frontpage.host/developers/people"],
441
464
  },
442
465
  {
443
- name: "shop",
444
- summary: "Store catalog: items, variants, stock, and store settings (not Square, not inbox)",
445
- usage: "frontpage shop items|variants|categories|settings|orders|payments|connect|status|disconnect … [--site <slug>] [--json]",
466
+ name: "store",
467
+ summary: "Store catalog: items, variants, stock, and store settings (not inbox)",
468
+ usage: "frontpage store items|variants|categories|settings|orders|payments|connect|status|disconnect|import … [--site <slug>] [--json]",
446
469
  topics: ["content"],
447
470
  examples: [
448
- "frontpage shop items list",
449
- "frontpage shop categories create hats --label Hats",
450
- "frontpage shop items create camp-hat --title \"Camp hat\" --price-cents 4999 --checkout cart --categories hats --stock 20",
451
- "frontpage shop items list --category hats",
452
- "frontpage shop variants add camp-hat --id m --option Size=M --price-cents 4999 --stock 8",
453
- "frontpage shop items stock camp-hat --variant m --add 5",
454
- "frontpage shop items stock camp-hat --variant m --remove 5",
455
- "frontpage shop items stock camp-hat --variant m --set 20",
456
- "frontpage shop settings set-fulfillment --mode pickup",
457
- "frontpage shop settings set-currency --currency usd",
458
- "frontpage shop connect --no-open",
459
- "frontpage shop status",
471
+ "frontpage store items list",
472
+ "frontpage store categories create hats --label Hats",
473
+ "frontpage store items create camp-hat --title \"Camp hat\" --price-cents 4999 --categories hats --stock 20",
474
+ "frontpage store settings set-checkout --mode cart",
475
+ "frontpage store items list --category hats",
476
+ "frontpage store variants add camp-hat --id m --option Size=M --price-cents 4999 --stock 8",
477
+ "frontpage store items stock camp-hat --variant m --add 5",
478
+ "frontpage store items stock camp-hat --variant m --remove 5",
479
+ "frontpage store items stock camp-hat --variant m --set 20",
480
+ "frontpage store settings set-fulfillment --mode pickup",
481
+ "frontpage store settings set-fulfillment --pickup --delivery --mode flat-rate --flat-cents 500 --pickup-location \"The shop\"",
482
+ "frontpage store settings set-currency --currency usd",
483
+ "frontpage store settings set-currency --currency jpy",
484
+ "frontpage store connect --no-open",
485
+ "frontpage store status",
486
+ "frontpage store import",
460
487
  ],
461
488
  flags: [
462
- "Integer cents only. $49.99 is 4999. Do not send 49.99 or a dollar string.",
489
+ "Integer cents only. $49.99 is 4999. Do not send 49.99 or a dollar string. --price-cents is the usual cost. Optional --discount-cents is the sale the buyer pays (strictly less than price). Omit --discount-cents when there is no sale. Checkout charges the sale; the site strikes through the usual price.",
463
490
  "--add / --remove become signed delta. 5 adds 5. --remove 5 removes 5. Result copy is added 5 / removed 5 / set to 20 / stockNow.",
464
491
  "Pass either --add, --remove, or --set. Never two together. --set unlimited returns counted stock to unlimited.",
465
492
  "items: list|get|create|update|archive|activate [--off] | images --urls … | stock [--variant m] --add|--remove|--set. list --category hats. create/update --categories hats,summer",
466
493
  "categories: list|create|rename|archive. Create the category first. Assigning an unknown slug fails.",
467
494
  "variants: add|update|archive <itemId> --id m --price-cents … [--option Size=M] [--stock]",
468
- "settings: get|set-fulfillment|set-currency|set-checkout-fee. Connect: frontpage shop connect|status|disconnect. connect opens Frontpage then Stripe; --no-open for CI. disconnect keeps the shared Stripe account on payments.",
495
+ "settings: get|set-fulfillment|set-currency|set-checkout|set-checkout-fee. Currency is any Stripe presentment code such as usd or jpy. Checkout is store-wide (cart or instant). Do not use leftover items --checkout to pick cart vs Pay now. Gift cards still use --amount open --min-cents. Service fee applies to Add to cart and Pay now. Connect: frontpage store connect|status|disconnect. connect opens Frontpage then Stripe; --no-open for CI. disconnect keeps the shared Stripe account on payments.",
496
+ "Page markup is yours: data-fp-store-grid + data-fp-cart (cart starts hidden), every store control data-fp-requires=store. Product pages are the default: unless they said they do not want product pages, write one src/pages/products/item.astro (or shop) with data-fp-store-product. If they said no, skip that file and do not bind href or url. Description is a small markdown subset. PDP bind is <div data-fp-store-bind=description>. Do not bake titles or prices. Guide: https://frontpage.host/developers/store",
497
+ "import reads src/content/products/*.json from the site store into Store. One default variant each. Skip if that itemId already exists. Does not invent categories or change fulfillment.",
469
498
  "orders / payments list|get read the same ledgers as frontpage inbox orders|payments",
470
499
  "Unknown leftover flags fail closed (exit 2)",
471
500
  ],
472
- seeAlso: ["inbox", "https://frontpage.host/developers/shop"],
501
+ seeAlso: ["inbox", "https://frontpage.host/developers/store"],
473
502
  },
474
503
  {
475
504
  name: "mail",
@@ -691,6 +720,7 @@ export const TOPICS = {
691
720
  - Forms: <form data-fp-form="slug"> with named inputs; no action/onsubmit/honeypot.
692
721
  - Articles: frontpage articles …; migrate folders via frontpage prompt (don't invent mass delete+write).
693
722
  - Media: frontpage media upload (images/PDF ≤25 MB, video ≤50 MB). frontpage images generate --prompt "…" [--count N] for 1–4 AI variations of one invented picture (AI $ + storage). Use sitePath /__media/uploads/<slug>/… in markup (not raw blob URLs). Optional ?w= on rasters only.
723
+ - Store: frontpage store for catalog, settings set-checkout / set-fulfillment / set-checkout-fee, and connect. Write data-fp-store-grid + data-fp-cart (data-fp-requires=store; cart starts hidden). Unless they said they do not want product pages, write one src/pages/products/item.astro with data-fp-store-product. If they said no, skip that file and do not bind href or url. Guide: https://frontpage.host/developers/store
694
724
 
695
725
  Full guide: https://frontpage.host/developers/site-building
696
726
  See also: frontpage help sync | content | AGENTS.md`,
@@ -727,9 +757,13 @@ export const TOPICS = {
727
757
  frontpage inbox [leads|subscribers|orders|google_reviews|…] [--mark-read] [--csv]
728
758
  frontpage people list|get|add|set|note|rm-note|follow-up|hide|unhide|tag|export
729
759
  # People CRM guide: https://frontpage.host/developers/people
730
- frontpage shop items|variants|categories|settings|orders|payments|connect|status|disconnect
731
- # Store catalog: https://frontpage.host/developers/shop
760
+ frontpage routines list|get|add|set|pause|resume|rm|run|runs
761
+ # Routines guide: https://frontpage.host/developers/routines
762
+ # 8am,12pm,4pm and tue,thu,fri store hours [8, 12, 16] and weekdays [2, 4, 5] on one routine. Timezone is IANA.
763
+ frontpage store items|variants|categories|settings|orders|payments|connect|status|disconnect
764
+ # Store catalog + live storefront: https://frontpage.host/developers/store
732
765
  # Integer cents only. $49.99 is 4999. stock --add/--remove are signed delta.
766
+ # Markup: data-fp-store-grid / data-fp-store-product / data-fp-cart. One item.astro, not one file per product.
733
767
  frontpage mail settings|email|template|series|send|sends|enrolments|suppress
734
768
  frontpage seo get [/path|--path /path] | seo set --path /
735
769
  frontpage redirects list|set|rm --from /old --to /new
@@ -738,8 +772,7 @@ export const TOPICS = {
738
772
  frontpage tokens set --group … --key … --value …
739
773
  frontpage modules list|docs|request-setup|enable|disable|archive|config <key>
740
774
  frontpage modules <key> # that key's CLI usage
741
- frontpage modules payments|shop|ordering connect [--no-open] # opens Frontpage then Stripe/Square; then status
742
- frontpage modules ordering sync
775
+ frontpage store connect [--no-open] # Store Stripe connect
743
776
  # mailing list key: signups. Index: https://frontpage.host/developers/modules
744
777
  frontpage extensions list|docs|enable|disable|config <key>
745
778
  frontpage settings get|set
@@ -749,7 +782,7 @@ export const TOPICS = {
749
782
  Do not fake payments/checkout. LOCKED extensions → do not fake; say plan required.
750
783
  Migrate pages → articles: prefer frontpage prompt "migrate …" over mass delete+write.
751
784
  edit uses click-edit (exact single occurrence), not push / gated-write.
752
- Connect opens Frontpage (logged-in), then Stripe/Square; never accepts or returns OAuth secrets. payments/shop connect is admin.
785
+ Connect opens Frontpage (logged-in), then Stripe; never accepts or returns OAuth secrets. Store connect is frontpage store connect.
753
786
  Site contract: frontpage help site-building · https://frontpage.host/developers/site-building
754
787
  Domains, billing, members, and site delete are out of v1.`,
755
788
  history: `History
@@ -787,5 +820,5 @@ export const TOPICS = {
787
820
  Prefer requestId from API error envelopes when filing support issues.`,
788
821
  };
789
822
  export function findCommand(name) {
790
- return COMMANDS.find((c) => c.name === name);
823
+ return COMMANDS.find((c) => c.name === name || c.aliases?.includes(name));
791
824
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * CLI schedule words → the numbers the API stores.
3
+ * tue,thu,fri and 8am,12pm,4pm become weekdays [2, 4, 5] and hours [8, 12, 16].
4
+ */
5
+ export declare function parseHourList(raw: string | undefined): number[];
6
+ export declare function parseWeekdayList(raw: string | undefined): number[];
7
+ export declare function parseMonthDayList(raw: string | undefined): number[];
@@ -0,0 +1,78 @@
1
+ /**
2
+ * CLI schedule words → the numbers the API stores.
3
+ * tue,thu,fri and 8am,12pm,4pm become weekdays [2, 4, 5] and hours [8, 12, 16].
4
+ */
5
+ const WEEKDAYS = {
6
+ sun: 0,
7
+ sunday: 0,
8
+ mon: 1,
9
+ monday: 1,
10
+ tue: 2,
11
+ tues: 2,
12
+ tuesday: 2,
13
+ wed: 3,
14
+ wednesday: 3,
15
+ thu: 4,
16
+ thur: 4,
17
+ thurs: 4,
18
+ thursday: 4,
19
+ fri: 5,
20
+ friday: 5,
21
+ sat: 6,
22
+ saturday: 6,
23
+ };
24
+ export function parseHourList(raw) {
25
+ if (!raw || !raw.trim())
26
+ return [];
27
+ return unique(raw.split(",").map((part) => parseHour(part.trim())));
28
+ }
29
+ export function parseWeekdayList(raw) {
30
+ if (!raw || !raw.trim())
31
+ return [];
32
+ return unique(raw.split(",").map((part) => parseWeekday(part.trim())));
33
+ }
34
+ export function parseMonthDayList(raw) {
35
+ if (!raw || !raw.trim())
36
+ return [];
37
+ return unique(raw.split(",").map((part) => {
38
+ const n = Number(part.trim());
39
+ if (!Number.isInteger(n) || n < 1 || n > 28) {
40
+ throw new Error(`Month day must be 1 through 28. Got "${part.trim()}".`);
41
+ }
42
+ return n;
43
+ }));
44
+ }
45
+ function unique(values) {
46
+ return [...new Set(values)].sort((a, b) => a - b);
47
+ }
48
+ function parseHour(raw) {
49
+ const ampm = raw.toLowerCase().match(/^(\d{1,2})(?::(\d{2}))?\s*(am|pm)$/);
50
+ if (ampm) {
51
+ if (ampm[2] && ampm[2] !== "00") {
52
+ throw new Error(`Routines fire at minute 0. Got "${raw}".`);
53
+ }
54
+ let hour = Number(ampm[1]);
55
+ if (hour < 1 || hour > 12)
56
+ throw new Error(`Hour "${raw}" is not valid.`);
57
+ if (ampm[3] === "am")
58
+ hour = hour === 12 ? 0 : hour;
59
+ else
60
+ hour = hour === 12 ? 12 : hour + 12;
61
+ return hour;
62
+ }
63
+ if (/^\d{1,2}$/.test(raw)) {
64
+ const hour = Number(raw);
65
+ if (hour < 0 || hour > 23)
66
+ throw new Error(`Hour must be 0 through 23. Got "${raw}".`);
67
+ return hour;
68
+ }
69
+ throw new Error(`Hour "${raw}" is not valid. Use 8, 8am, or 4pm.`);
70
+ }
71
+ function parseWeekday(raw) {
72
+ const named = WEEKDAYS[raw.toLowerCase()];
73
+ if (named !== undefined)
74
+ return named;
75
+ if (/^[0-6]$/.test(raw))
76
+ return Number(raw);
77
+ throw new Error(`Weekday "${raw}" is not valid. Use tue or 2. Sunday is 0.`);
78
+ }
package/llms.txt CHANGED
@@ -9,7 +9,7 @@
9
9
  - Fonts: frontpage fonts [--slot] [--query] → exact value → tokens set fonts.<role> → APPLY font-sans|serif|mono|display. Build bundles catalog faces from tokens.ts and/or CSS font-family contexts (not prose). Inline-only without tokens works for catalog faces after rebuild; prefer tokens+utilities for brand roles.
10
10
  - Module/extension docs: frontpage modules docs <key> | frontpage extensions docs <key>
11
11
  - Search flags: --regex --case-sensitive --whole-word --files-only --context-lines --max-results
12
- - Modules setup: wizard modules (payments/shop/ordering): frontpage modules <key> connect [--no-open] opens Frontpage (logged-in browser) then Stripe/Square; already-connected stays live. payments/shop connect = admin key; ordering = editor. payments disconnect 409s if shop still uses the shared Stripe account. request-setup remains valid for the editor amber bar. frontpage modules <key> prints that key's CLI usage. Index: https://frontpage.host/developers/modules. Typed config: frontpage modules callbar|forms|booking|signups get|set (forms set needs --instance; confirm emails paid-plan only). Form destinations: frontpage modules forms destinations|connect-destination|attach-destination|detach-destination|remove-destination --yes|request-destination-setup (token = admin; request-destination-setup lights the editor bar; request-setup forms is a no-op; connect does not attach every form). Privyr: paste Incoming Webhook URL (https://www.privyr.com/api/v1/incoming-leads/…), not a token. HubSpot: Development → Keys → Service keys or Legacy apps → Auth → Show token. Pipedrive: profile → Company settings → Personal preferences → API. Docs: https://frontpage.host/developers/modules#forms-destinations. google_reviews: frontpage modules google_reviews connect --place-id | --maps-url (server resolve + seed; no Autocomplete; do not write placeId via config). Also status, list-reviews, hide/show, set-stars, set-sort, set-weight, reset-weights, refresh, disconnect. Docs: https://frontpage.host/developers/modules/google-reviews. Markup: data-fp-google-reviews / data-fp-google-ask + required data-fp-requires="google_reviews". Ask scores: frontpage inbox google_reviews.
12
+ - Modules setup: Store Stripe connect is frontpage store connect. payments disconnect 409s if the store still uses the shared Stripe account. request-setup remains valid for the editor amber bar. frontpage modules <key> prints that key's CLI usage. Index: https://frontpage.host/developers/modules. Typed config: frontpage modules callbar|forms|booking|signups get|set (forms set needs --instance; confirm emails paid-plan only). Form destinations: frontpage modules forms destinations|connect-destination|attach-destination|detach-destination|remove-destination --yes|request-destination-setup (token = admin; request-destination-setup lights the editor bar; request-setup forms is a no-op; connect does not attach every form). Privyr: paste Incoming Webhook URL (https://www.privyr.com/api/v1/incoming-leads/…), not a token. HubSpot: Development → Keys → Service keys or Legacy apps → Auth → Show token. Pipedrive: profile → Company settings → Personal preferences → API. Docs: https://frontpage.host/developers/modules#forms-destinations. google_reviews: frontpage modules google_reviews connect --place-id | --maps-url (server resolve + seed; no Autocomplete; do not write placeId via config). Also status, list-reviews, hide/show, set-stars, set-sort, set-weight, reset-weights, refresh, disconnect. Docs: https://frontpage.host/developers/modules/google-reviews. Markup: data-fp-google-reviews / data-fp-google-ask + required data-fp-requires="google_reviews". Ask scores: frontpage inbox google_reviews.
13
13
  - Analytics ops: frontpage analytics summary|query|funnel|timeseries|compare|sql (bare analytics = dashboard)
14
14
  - Media: frontpage media upload <file> (images/PDF ≤25 MB, video ≤50 MB, SVG/text ≤4 MB); human prints sitePath first — use /__media/uploads/<slug>/… in markup. Text/CSV is inline only (not media library; --json omits full content).
15
15
  - Prompt attachments: frontpage prompt "…" --attach <file|/__media/…|https://…> [--asset-url https://…] (same media caps as media upload)
@@ -17,7 +17,8 @@
17
17
  - SEO get: frontpage seo get [/path|--path /path]; search path filter: [path|--path <path>]; open [/path|--path /path]
18
18
  - Redirects: frontpage redirects list|set|rm — HTTP 301/308, instant, not a file; revert does not undo; do not fake with Astro.redirect / meta refresh
19
19
  - People CRM: frontpage people list|get|add|set|note|rm-note|follow-up|hide|unhide|tag|export. Site-scoped person graph (email or phone). Inbox stays the raw ledger. Hide is not forget. List export is CSV. Unknown leftover flags fail closed. Guide: https://frontpage.host/developers/people
20
- - Store catalog: frontpage shop items|variants|categories|settings|orders|payments|connect|status|disconnect. Integer cents only ($49.99 is 4999). stock --add/--remove are signed delta (added 5 / removed 5 / set to 20 / stockNow). Create a category before assigning it. frontpage shop connect [--no-open] opens Frontpage then Stripe; disconnect keeps the shared account on payments. frontpage modules shop|payments connect still works. Not Square ordering. Guide: https://frontpage.host/developers/shop
20
+ - Routines: frontpage routines list|get|add|set|pause|resume|rm|run|runs. Several hours and several days are one routine. 8am,12pm,4pm and tue,thu,fri store hours [8, 12, 16] and weekdays [2, 4, 5]. Timezone is IANA. Do not guess UTC. Guide: https://frontpage.host/developers/routines
21
+ - Store catalog: frontpage store items|variants|categories|settings|orders|payments|connect|status|disconnect. Integer cents only ($49.99 is 4999). --price-cents is the usual cost. Optional --discount-cents is the sale the buyer pays (must be lower). Create a category before assigning it. Photos are /__media/uploads/<this-slug>/… or an HTTPS URL already saved on this site. frontpage store connect [--no-open] opens Frontpage then Stripe. Checkout is store-wide (settings set-checkout); do not use leftover items --checkout to pick cart vs Pay now. Service fee is settings set-checkout-fee. The cart starts hidden. Product pages are the default: unless they said they do not want product pages, write one src/pages/products/item.astro (or shop) with data-fp-store-product. If they said no, skip that file and do not bind href or url. Description is a small markdown subset; product-page bind is <div data-fp-store-bind="description">. Every store control carries data-fp-requires="store". Leave data-fp-store-variants empty. Do not bake titles or prices. Guide: https://frontpage.host/developers/store
21
22
  - Analytics funnel: --steps JSON array or comma paths (/, /pricing); bad JSON → Invalid JSON for --steps
22
23
  - Modules: unknown key errors list Known: …; mailing list module key is signups (not subscribe)
23
24
  - Errors: --json API envelopes include requestId when the server returned one; local validation omits requestId/httpStatus; runs wait failed/canceled → ok:false + run.error (exit 2); deploy wait = agent idle + tip compile ready (fail-closed / compile timeout → exit 2); push --wait and publish --wait wait for GET /deploy ready
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontpage-host",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "Frontpage CLI — manage your site from the terminal",
5
5
  "type": "module",
6
6
  "bin": {