frontpage-host 0.1.21 → 0.1.23

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
  ];
@@ -62,7 +59,7 @@ export const MODULE_COMMANDS = [
62
59
  genericActions: GENERIC_ACTIONS,
63
60
  extraActions: [
64
61
  "get [--instance <slug>]",
65
- "set --instance <slug> [--notify-email] [--confirm yes|no]",
62
+ "set --instance <slug> [--notify-email] [--confirm yes|no] [--confirm-template <id>]",
66
63
  "destinations [--instance <slug>]",
67
64
  "connect-destination <privyr|hubspot|pipedrive> --token \"…\" [--lead-source \"…\"]",
68
65
  "attach-destination --instance <slug> --provider <id>",
@@ -77,6 +74,7 @@ export const MODULE_COMMANDS = [
77
74
  "frontpage modules forms get",
78
75
  "frontpage modules forms get --instance lead-gen",
79
76
  "frontpage modules forms set --instance lead-gen --notify-email hi@example.com",
77
+ "frontpage modules forms set --instance lead-gen --confirm-template <template-id>",
80
78
  "frontpage modules forms destinations",
81
79
  "frontpage modules forms connect-destination privyr --token \"…\"",
82
80
  "frontpage modules forms attach-destination --instance lead-gen --provider privyr",
@@ -86,6 +84,7 @@ export const MODULE_COMMANDS = [
86
84
  notes: [
87
85
  "Without --instance, get lists each form (id, status, notifyEmail). Do not target `default`.",
88
86
  "Confirmation emails require a paid plan (same as Modules).",
87
+ "--confirm-template sets that form's saved template id. Every token must be one of the form's input names. An empty value clears it. A series is not attached to a form.",
89
88
  "Destinations copy leads to HubSpot, Pipedrive, or Privyr. Connect once, then attach each form. Token write is admin (Privyr: paste the Incoming Webhook URL, not a token). request-destination-setup lights the editor amber bar (editor key). remove-destination requires --yes. Locations: https://frontpage.host/developers/modules#forms-destinations",
90
89
  ],
91
90
  },
@@ -130,85 +129,6 @@ export const MODULE_COMMANDS = [
130
129
  "Typed flags write the same whitelist as `modules config`. Generic JSON config still works.",
131
130
  ],
132
131
  },
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
132
  {
213
133
  key: "articles",
214
134
  name: "Articles",
@@ -281,7 +201,6 @@ export const GOOGLE_REVIEWS_CLI_ACTIONS = [
281
201
  ];
282
202
  export const TYPED_CONFIG_CLI_ACTIONS = ["get", "set"];
283
203
  export const STRIPE_OAUTH_CLI_ACTIONS = ["status", "connect", "disconnect"];
284
- export const ORDERING_OAUTH_CLI_ACTIONS = ["status", "connect", "disconnect", "sync"];
285
204
  export function isCliModuleAction(key, action) {
286
205
  if (key === "google_reviews") {
287
206
  return GOOGLE_REVIEWS_CLI_ACTIONS.includes(action);
@@ -301,12 +220,6 @@ export function isCliModuleAction(key, action) {
301
220
  if (key === "callbar" || key === "booking" || key === "signups") {
302
221
  return TYPED_CONFIG_CLI_ACTIONS.includes(action);
303
222
  }
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
223
  return false;
311
224
  }
312
225
  export function isDocumentedModuleKey(raw) {
@@ -318,6 +231,26 @@ export function isGenericModuleVerb(raw) {
318
231
  export function moduleCommand(key) {
319
232
  return MODULE_COMMANDS.find((c) => c.key === key);
320
233
  }
234
+ export function formatShopIsNotAModule() {
235
+ return [
236
+ "shop is not a module. Use frontpage store.",
237
+ "",
238
+ "Catalog, fulfillment, pay buttons, and Stripe connect:",
239
+ " frontpage store items|variants|categories|settings|orders|payments|connect|status|disconnect",
240
+ "",
241
+ "See also: https://frontpage.host/developers/store",
242
+ ].join("\n");
243
+ }
244
+ export function formatPaymentsIsNotAModule() {
245
+ return [
246
+ "payments is not a module. Use frontpage store.",
247
+ "",
248
+ "Stripe connect lives on Store:",
249
+ " frontpage store connect|status|disconnect|payments",
250
+ "",
251
+ "See also: https://frontpage.host/developers/store",
252
+ ].join("\n");
253
+ }
321
254
  export function formatModuleUsage(key) {
322
255
  const def = moduleCommand(key);
323
256
  const lines = [
@@ -353,6 +286,7 @@ export function formatModulesIndex() {
353
286
  ...rows,
354
287
  "",
355
288
  "Generic: frontpage modules list|docs|request-setup|enable|disable|archive|config …",
289
+ "Store: frontpage store …",
356
290
  "Articles content: frontpage articles …",
357
291
  "Index: https://frontpage.host/developers/modules",
358
292
  ].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
  }
@@ -45,6 +45,9 @@ function printConfirmable(config, confirmations) {
45
45
  printLine("confirm", `${yesNo(on)} (paid plan required)`);
46
46
  else
47
47
  printLine("confirm", yesNo(on));
48
+ if (typeof config.confirmTemplateId === "string") {
49
+ printLine("template", config.confirmTemplateId.trim() || "—");
50
+ }
48
51
  }
49
52
  function printFormsList(instances, destinations) {
50
53
  if (!instances.length) {
@@ -112,6 +115,7 @@ export async function runTypedConfig(key, slug, action, flags) {
112
115
  const notifyEmail = takeFlag(work, "--notify-email");
113
116
  const hours = takeFlag(work, "--hours");
114
117
  const confirmRaw = takeFlag(work, "--confirm");
118
+ const confirmTemplate = takeFlag(work, "--confirm-template");
115
119
  assertNoUnknownFlags(work, "modules");
116
120
  let confirmEnabled;
117
121
  try {
@@ -135,8 +139,14 @@ export async function runTypedConfig(key, slug, action, flags) {
135
139
  body.hours = hours;
136
140
  if (confirmEnabled !== undefined)
137
141
  body.confirmEnabled = confirmEnabled;
142
+ if (confirmTemplate !== undefined) {
143
+ if (key !== "forms") {
144
+ return emitUsage("modules", `--confirm-template is only for forms.\n\n${usage}`);
145
+ }
146
+ body.confirmTemplateId = confirmTemplate.trim();
147
+ }
138
148
  if (key === "forms" && !instanceId) {
139
- return emitUsage("modules", `Usage: frontpage modules forms set --instance <slug> [--notify-email] [--confirm yes|no]\n\n${usage}`);
149
+ return emitUsage("modules", `Usage: frontpage modules forms set --instance <slug> [--notify-email] [--confirm yes|no] [--confirm-template <id>]\n\n${usage}`);
140
150
  }
141
151
  const data = await v1Post(pathFor(slug, key), body);
142
152
  if (isJson()) {
@@ -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,13 @@ 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 modules forms set --instance lead-gen --confirm-template <template-id>",
339
+ "frontpage store connect --no-open",
340
340
  "frontpage modules docs forms",
341
341
  "frontpage modules docs google_reviews",
342
342
  "frontpage modules docs signups",
343
343
  "frontpage modules list",
344
- 'frontpage modules request-setup payments --reason "You asked for a $50 deposit button — Stripe setup makes it live"',
344
+ 'frontpage store connect --no-open',
345
345
  'frontpage modules request-setup google_reviews --reason "You asked for Google reviews — pick your listing so we show the right ones"',
346
346
  ],
347
347
  flags: [
@@ -349,13 +349,13 @@ export const COMMANDS = [
349
349
  "Key-first and verb-first both work: `modules google_reviews enable` = `modules enable google_reviews`",
350
350
  "google_reviews extras: connect / resolve / status / list-reviews / hide / show / set-stars / set-sort / set-weight / reset-weights / refresh / disconnect / write-review-url",
351
351
  "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.",
352
+ "Store Stripe connect is frontpage store connect. request-setup still lights the editor amber bar.",
353
353
  "See https://frontpage.host/developers/modules and /developers/modules/google-reviews",
354
354
  ],
355
355
  dangers: [
356
356
  "Secret values are never returned — masks only",
357
357
  "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",
358
+ "payments and the store share one Stripe account — payments disconnect 409s if the store still uses it",
359
359
  "OAuth cannot finish in the terminal; connect only opens the vendor URL",
360
360
  "Do not write placeId through generic config — that skips listing resolve + seed",
361
361
  ],
@@ -413,6 +413,29 @@ export const COMMANDS = [
413
413
  topics: ["content"],
414
414
  dangers: ["--mark-read is side-effecting with no confirm"],
415
415
  },
416
+ {
417
+ name: "routines",
418
+ summary: "Schedule a prompt for the site agent (not a module)",
419
+ usage: "frontpage routines list|get|add|set|pause|resume|rm|run|runs [--site <slug>] [--json]",
420
+ topics: ["content"],
421
+ examples: [
422
+ "frontpage routines list",
423
+ "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.\"",
424
+ "frontpage routines pause <id>",
425
+ "frontpage routines resume <id>",
426
+ "frontpage routines run <id>",
427
+ ],
428
+ flags: [
429
+ "add requires --name, --prompt, --cadence hourly|daily|weekly|monthly, and --timezone (IANA, such as America/Chicago)",
430
+ "--hours accepts 8, 8am, 12pm, 4pm. Several hours: 8am,12pm,4pm stores [8, 12, 16]",
431
+ "--weekdays accepts tue,thu,fri or 2,4,5. Sunday is 0. Weekly only",
432
+ "--month-days accepts 1,15. Days are 1 through 28. Monthly only",
433
+ "add starts the clock. pause stops it. run fires once and does not move the next time",
434
+ "That Tuesday example stores hours [8, 12, 16] and weekdays [2, 4, 5] on one routine",
435
+ "Unknown leftover flags fail closed (exit 2)",
436
+ ],
437
+ seeAlso: ["people", "https://frontpage.host/developers/routines"],
438
+ },
416
439
  {
417
440
  name: "people",
418
441
  summary: "List and manage site People (not inbox, not a module)",
@@ -422,6 +445,7 @@ export const COMMANDS = [
422
445
  "frontpage people list",
423
446
  "frontpage people add --email alex@example.com --name Alex",
424
447
  "frontpage people set <id> --status working",
448
+ "frontpage people set <id> --line1 \"12 Main\" --city Austin --region TX --postal 78701 --country US",
425
449
  "frontpage people note <id> --body \"Called them\"",
426
450
  "frontpage people follow-up <id> --at 2026-09-12T00:00:00Z",
427
451
  "frontpage people hide <id>",
@@ -440,36 +464,42 @@ export const COMMANDS = [
440
464
  seeAlso: ["inbox", "https://frontpage.host/developers/people"],
441
465
  },
442
466
  {
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]",
467
+ name: "store",
468
+ summary: "Store catalog: items, variants, stock, and store settings (not inbox)",
469
+ usage: "frontpage store items|variants|categories|settings|orders|payments|connect|status|disconnect|import … [--site <slug>] [--json]",
446
470
  topics: ["content"],
447
471
  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",
472
+ "frontpage store items list",
473
+ "frontpage store categories create hats --label Hats",
474
+ "frontpage store items create camp-hat --title \"Camp hat\" --price-cents 4999 --categories hats --stock 20",
475
+ "frontpage store settings set-checkout --mode cart",
476
+ "frontpage store items list --category hats",
477
+ "frontpage store variants add camp-hat --id m --option Size=M --price-cents 4999 --stock 8",
478
+ "frontpage store items stock camp-hat --variant m --add 5",
479
+ "frontpage store items stock camp-hat --variant m --remove 5",
480
+ "frontpage store items stock camp-hat --variant m --set 20",
481
+ "frontpage store settings set-fulfillment --mode pickup",
482
+ "frontpage store settings set-fulfillment --pickup --delivery --mode flat-rate --flat-cents 500 --pickup-location \"The shop\"",
483
+ "frontpage store settings set-currency --currency usd",
484
+ "frontpage store settings set-currency --currency jpy",
485
+ "frontpage store connect --no-open",
486
+ "frontpage store status",
487
+ "frontpage store import",
460
488
  ],
461
489
  flags: [
462
- "Integer cents only. $49.99 is 4999. Do not send 49.99 or a dollar string.",
490
+ "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
491
  "--add / --remove become signed delta. 5 adds 5. --remove 5 removes 5. Result copy is added 5 / removed 5 / set to 20 / stockNow.",
464
492
  "Pass either --add, --remove, or --set. Never two together. --set unlimited returns counted stock to unlimited.",
465
- "items: list|get|create|update|archive|activate [--off] | images --urls … | stock [--variant m] --add|--remove|--set. list --category hats. create/update --categories hats,summer",
493
+ "items: list|get|create|update|archive|activate [--off] | images --urls … | stock [--variant m] --add|--remove|--set. list --category hats. items list --json returns every product and every variant — do not paste that into a model. The editor manage_store list_items call is one page of 25 slim rows plus total; get_item is one product. create/update --categories hats,summer",
466
494
  "categories: list|create|rename|archive. Create the category first. Assigning an unknown slug fails.",
467
495
  "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.",
496
+ "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.",
497
+ "Page markup is yours: data-fp-store-grid + data-fp-cart (cart starts hidden), every store control data-fp-requires=store. The public catalog pages with offset, limit, total, and truncated (default 24). list_items is the same shape with a max of 25. 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",
498
+ "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
499
  "orders / payments list|get read the same ledgers as frontpage inbox orders|payments",
470
500
  "Unknown leftover flags fail closed (exit 2)",
471
501
  ],
472
- seeAlso: ["inbox", "https://frontpage.host/developers/shop"],
502
+ seeAlso: ["inbox", "https://frontpage.host/developers/store"],
473
503
  },
474
504
  {
475
505
  name: "mail",
@@ -691,6 +721,7 @@ export const TOPICS = {
691
721
  - Forms: <form data-fp-form="slug"> with named inputs; no action/onsubmit/honeypot.
692
722
  - Articles: frontpage articles …; migrate folders via frontpage prompt (don't invent mass delete+write).
693
723
  - 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.
724
+ - 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
725
 
695
726
  Full guide: https://frontpage.host/developers/site-building
696
727
  See also: frontpage help sync | content | AGENTS.md`,
@@ -727,9 +758,13 @@ export const TOPICS = {
727
758
  frontpage inbox [leads|subscribers|orders|google_reviews|…] [--mark-read] [--csv]
728
759
  frontpage people list|get|add|set|note|rm-note|follow-up|hide|unhide|tag|export
729
760
  # 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
761
+ frontpage routines list|get|add|set|pause|resume|rm|run|runs
762
+ # Routines guide: https://frontpage.host/developers/routines
763
+ # 8am,12pm,4pm and tue,thu,fri store hours [8, 12, 16] and weekdays [2, 4, 5] on one routine. Timezone is IANA.
764
+ frontpage store items|variants|categories|settings|orders|payments|connect|status|disconnect
765
+ # Store catalog + live storefront: https://frontpage.host/developers/store
732
766
  # Integer cents only. $49.99 is 4999. stock --add/--remove are signed delta.
767
+ # Markup: data-fp-store-grid / data-fp-store-product / data-fp-cart. One item.astro, not one file per product.
733
768
  frontpage mail settings|email|template|series|send|sends|enrolments|suppress
734
769
  frontpage seo get [/path|--path /path] | seo set --path /
735
770
  frontpage redirects list|set|rm --from /old --to /new
@@ -738,8 +773,7 @@ export const TOPICS = {
738
773
  frontpage tokens set --group … --key … --value …
739
774
  frontpage modules list|docs|request-setup|enable|disable|archive|config <key>
740
775
  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
776
+ frontpage store connect [--no-open] # Store Stripe connect
743
777
  # mailing list key: signups. Index: https://frontpage.host/developers/modules
744
778
  frontpage extensions list|docs|enable|disable|config <key>
745
779
  frontpage settings get|set
@@ -749,7 +783,7 @@ export const TOPICS = {
749
783
  Do not fake payments/checkout. LOCKED extensions → do not fake; say plan required.
750
784
  Migrate pages → articles: prefer frontpage prompt "migrate …" over mass delete+write.
751
785
  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.
786
+ Connect opens Frontpage (logged-in), then Stripe; never accepts or returns OAuth secrets. Store connect is frontpage store connect.
753
787
  Site contract: frontpage help site-building · https://frontpage.host/developers/site-building
754
788
  Domains, billing, members, and site delete are out of v1.`,
755
789
  history: `History
@@ -787,5 +821,5 @@ export const TOPICS = {
787
821
  Prefer requestId from API error envelopes when filing support issues.`,
788
822
  };
789
823
  export function findCommand(name) {
790
- return COMMANDS.find((c) => c.name === name);
824
+ return COMMANDS.find((c) => c.name === name || c.aliases?.includes(name));
791
825
  }
@@ -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
+ }