frontpage-host 0.1.28 → 0.2.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.
@@ -3,10 +3,8 @@ import { resolveSlug } from "../site.js";
3
3
  import { CliHttpError, EXIT_OK, EXIT_USAGE, v1Get, v1Patch, v1Post, v1Put, } from "../http/client.js";
4
4
  import { assertNoUnknownFlags, hasFlag, takeFlag } from "../argv.js";
5
5
  import { emitErr, emitOk, emitUsage, isJson, requireYesOrTty } from "../runtime.js";
6
- import { openBrowser, resolveOpenUrl } from "../modules/oauth.js";
7
6
  import { SHOP_ID_RE, VARIANT_ID_RE } from "./shop-ids.js";
8
- const USAGE = "Usage: frontpage store items|variants|categories|settings|orders|payments|connect|status|disconnect|import … [--site <slug>] [--json]";
9
- const CONNECT_FINISH = "Open the Frontpage URL while logged in, finish Stripe in the browser, then re-run status. This CLI cannot complete OAuth.";
7
+ const USAGE = "Usage: frontpage store items|variants|categories|settings|orders|import … [--site <slug>] [--json]";
10
8
  const CENTS_HINT = "$49.99 is 4999";
11
9
  function takeAll(work, name) {
12
10
  const out = [];
@@ -142,7 +140,7 @@ async function readJsonFile(path) {
142
140
  function printItem(row) {
143
141
  const live = (row.variants ?? []).filter((v) => !v.archivedAt);
144
142
  const prices = live.map((v) => v.discountPriceCents != null ? `${v.discountPriceCents}/${v.priceCents}` : String(v.priceCents));
145
- const stocks = live.map((v) => (v.stock == null ? "unlimited" : String(v.stock)));
143
+ const stocks = live.map((v) => v.stock == null ? "unlimited" : String(v.stock));
146
144
  const bits = [
147
145
  row.checkout ?? "",
148
146
  row.amount ?? "",
@@ -218,7 +216,6 @@ export async function runShop(args) {
218
216
  work.splice(work.indexOf(itemId), 1);
219
217
  const title = takeFlag(work, "--title");
220
218
  const description = takeFlag(work, "--description");
221
- const checkout = takeFlag(work, "--checkout");
222
219
  const amount = takeFlag(work, "--amount");
223
220
  const minCents = parseCentsFlag(takeFlag(work, "--min-cents"), "minCents");
224
221
  const requiresShipping = parseBoolFlag(takeFlag(work, "--requires-shipping"), "requiresShipping");
@@ -238,8 +235,6 @@ export async function runShop(args) {
238
235
  body.title = title;
239
236
  if (description !== undefined)
240
237
  body.description = description;
241
- if (checkout)
242
- body.checkout = checkout;
243
238
  if (amount)
244
239
  body.amount = amount;
245
240
  if (minCents !== undefined)
@@ -254,13 +249,14 @@ export async function runShop(args) {
254
249
  body.images = images;
255
250
  if (body.amount == null)
256
251
  body.amount = "fixed";
257
- if (!Array.isArray(body.variants) && priceCents !== undefined) {
252
+ const initialPrice = priceCents ?? (body.amount === "open" && typeof body.minCents === "number" ? body.minCents : undefined);
253
+ if (!Array.isArray(body.variants) && initialPrice !== undefined) {
258
254
  const stock = stockRaw === undefined ? undefined : stockRaw === "unlimited" ? null : parseCountFlag(stockRaw, "stock");
259
255
  body.variants = [
260
256
  {
261
257
  variantId: variantId ?? "default",
262
258
  ...(options ? { options } : {}),
263
- priceCents,
259
+ priceCents: initialPrice,
264
260
  ...(discountCents !== undefined ? { discountPriceCents: discountCents } : {}),
265
261
  ...(stock !== undefined ? { stock } : {}),
266
262
  },
@@ -281,7 +277,6 @@ export async function runShop(args) {
281
277
  const itemId = requireShopId(work);
282
278
  const title = takeFlag(work, "--title");
283
279
  const description = takeFlag(work, "--description");
284
- const checkout = takeFlag(work, "--checkout");
285
280
  const amount = takeFlag(work, "--amount");
286
281
  const minCents = parseCentsFlag(takeFlag(work, "--min-cents"), "minCents");
287
282
  const requiresShipping = parseBoolFlag(takeFlag(work, "--requires-shipping"), "requiresShipping");
@@ -294,8 +289,6 @@ export async function runShop(args) {
294
289
  body.title = title;
295
290
  if (description !== undefined)
296
291
  body.description = description;
297
- if (checkout !== undefined)
298
- body.checkout = checkout;
299
292
  if (amount !== undefined)
300
293
  body.amount = amount;
301
294
  if (minCents !== undefined)
@@ -319,10 +312,10 @@ export async function runShop(args) {
319
312
  printItem(data.item);
320
313
  return EXIT_OK;
321
314
  }
322
- if (sub === "archive") {
315
+ if (sub === "archive" || sub === "restore") {
323
316
  const itemId = requireShopId(work);
324
317
  assertNoUnknownFlags(work, "store");
325
- const data = await v1Post(`${base}/items/${encodeURIComponent(itemId)}/archive`, {});
318
+ const data = await v1Post(`${base}/items/${encodeURIComponent(itemId)}/${sub}`, {});
326
319
  if (isJson()) {
327
320
  emitOk("store", data);
328
321
  return EXIT_OK;
@@ -466,11 +459,11 @@ export async function runShop(args) {
466
459
  printItem(data.item);
467
460
  return EXIT_OK;
468
461
  }
469
- if (sub === "archive") {
462
+ if (sub === "archive" || sub === "restore") {
470
463
  const itemId = requireShopId(work, "Item id", { allowIdFlag: false });
471
464
  const variantId = requireVariantId(takeFlag(work, "--id") ?? takeFlag(work, "--variant"));
472
465
  assertNoUnknownFlags(work, "store");
473
- const data = await v1Post(`${base}/items/${encodeURIComponent(itemId)}/variants/${encodeURIComponent(variantId)}/archive`, {});
466
+ const data = await v1Post(`${base}/items/${encodeURIComponent(itemId)}/variants/${encodeURIComponent(variantId)}/${sub}`, {});
474
467
  if (isJson()) {
475
468
  emitOk("store", data);
476
469
  return EXIT_OK;
@@ -568,7 +561,9 @@ export async function runShop(args) {
568
561
  fulfill.pickupEnabled ? "pickup" : null,
569
562
  fulfill.shippingEnabled ? "delivery" : null,
570
563
  ].filter(Boolean);
571
- const fulfillText = methods.length > 0 ? methods.join("+") : fulfill.shipping ?? "(no fulfillment)";
564
+ const fulfillText = methods.length > 0
565
+ ? methods.join("+")
566
+ : (fulfill.shipping ?? "(no fulfillment)");
572
567
  const checkoutText = s.storeCheckout === "instant" ? "pay-now" : "cart";
573
568
  const feeText = fee.enabled ? `service-fee ${fee.cents ?? 0}` : "service-fee off";
574
569
  console.log(`${s.currency ?? "(no currency)"} ${fulfillText} ${checkoutText} ${feeText}`);
@@ -619,39 +614,54 @@ export async function runShop(args) {
619
614
  console.log("Fulfillment saved.");
620
615
  return EXIT_OK;
621
616
  }
622
- if (sub === "set-currency") {
623
- const currency = takeFlag(work, "--currency");
617
+ if (sub === "set-receipt") {
618
+ const templateId = takeFlag(work, "--template");
619
+ const stock = hasFlag(work, "--stock");
624
620
  assertNoUnknownFlags(work, "store");
625
- if (!currency) {
626
- throw new CliHttpError("Usage: frontpage store settings set-currency --currency usd (any Stripe presentment code such as usd or jpy)", EXIT_USAGE, 0, "bad_request");
627
- }
628
- const data = await v1Post(`${base}/settings/currency`, { currency });
621
+ if ((templateId === undefined) === !stock)
622
+ throw new CliHttpError("Use --template <id> or --stock, never both.", EXIT_USAGE, 0, "bad_request");
623
+ const data = await v1Post(`${base}/settings/receipt`, {
624
+ templateId: stock ? "" : templateId,
625
+ });
629
626
  if (isJson())
630
627
  emitOk("store", data);
631
628
  else
632
- console.log("Currency saved.");
629
+ console.log("Order receipt saved. No email was sent.");
633
630
  return EXIT_OK;
634
631
  }
635
- if (sub === "set-checkout") {
636
- const checkout = takeFlag(work, "--mode") ?? takeFlag(work, "--checkout");
632
+ if (sub === "availability") {
633
+ const mode = work.shift() ?? "get";
634
+ if (!["get", "on", "off"].includes(mode))
635
+ throw new CliHttpError("Use store settings availability get|on|off.", EXIT_USAGE, 0, "bad_request");
637
636
  assertNoUnknownFlags(work, "store");
638
- if (checkout !== "cart" && checkout !== "instant") {
639
- throw new CliHttpError("Usage: frontpage store settings set-checkout --mode cart|instant", EXIT_USAGE, 0, "bad_request");
640
- }
641
- const data = await v1Post(`${base}/settings/checkout`, { checkout });
637
+ if (mode !== "get") {
638
+ const problem = requireYesOrTty("Change Store availability");
639
+ if (problem)
640
+ throw new CliHttpError(problem, EXIT_USAGE, 0, "bad_request");
641
+ }
642
+ const data = mode === "get"
643
+ ? await v1Get(`${base}/settings/availability`)
644
+ : await v1Post(`${base}/settings/availability`, {
645
+ enabled: mode === "on",
646
+ confirm: true,
647
+ });
642
648
  if (isJson())
643
649
  emitOk("store", data);
644
650
  else
645
- console.log(checkout === "instant" ? "Checkout is Pay now." : "Checkout is add to cart.");
651
+ console.log(JSON.stringify(data, null, 2));
646
652
  return EXIT_OK;
647
653
  }
648
654
  if (sub === "set-checkout-fee") {
649
655
  const off = hasFlag(work, "--off");
650
656
  const cents = parseCentsFlag(takeFlag(work, "--cents"), "checkoutFeeCents");
657
+ const mode = takeFlag(work, "--mode");
658
+ const bps = parseCountFlag(takeFlag(work, "--bps"), "checkoutFeeBps");
651
659
  assertNoUnknownFlags(work, "store");
652
660
  const data = await v1Post(`${base}/settings/checkout-fee`, {
653
661
  enabled: off ? false : true,
654
662
  cents,
663
+ mode,
664
+ bps,
655
665
  });
656
666
  if (isJson())
657
667
  emitOk("store", data);
@@ -661,61 +671,6 @@ export async function runShop(args) {
661
671
  }
662
672
  return emitUsage("store", USAGE);
663
673
  }
664
- if (group === "connect") {
665
- const work = rest.slice(1);
666
- const noOpen = hasFlag(work, "--no-open");
667
- assertNoUnknownFlags(work, "store");
668
- const data = await v1Post(`${base}/connect`, { action: "connect" });
669
- const already = data.alreadyConnected === true;
670
- if (isJson()) {
671
- emitOk("store", data);
672
- }
673
- else {
674
- const url = resolveOpenUrl(data.connectUrl);
675
- if (url)
676
- console.log(url);
677
- console.log(typeof data.message === "string" ? data.message : already ? "Already connected." : CONNECT_FINISH);
678
- if (typeof data.warning === "string")
679
- console.log(data.warning);
680
- }
681
- const url = resolveOpenUrl(data.connectUrl);
682
- if (url && !noOpen && !already)
683
- openBrowser(url);
684
- return EXIT_OK;
685
- }
686
- if (group === "status") {
687
- const work = rest.slice(1);
688
- assertNoUnknownFlags(work, "store");
689
- const data = await v1Get(`${base}/connect`);
690
- if (isJson()) {
691
- emitOk("store", data);
692
- return EXIT_OK;
693
- }
694
- const connection = (data.connection ?? {});
695
- const display = connection.display ?? {};
696
- console.log(`connected ${connection.ready ? "yes" : "no"}`);
697
- console.log(`charges ${display.chargesEnabled == null ? "—" : display.chargesEnabled ? "yes" : "no"}`);
698
- console.log(`account ${display.accountPresent ? "present" : "none"}`);
699
- if (typeof data.sharedStripe === "string")
700
- console.log(`note ${data.sharedStripe}`);
701
- if (typeof data.connectUrl === "string")
702
- console.log(`connect ${data.connectUrl}`);
703
- return EXIT_OK;
704
- }
705
- if (group === "disconnect") {
706
- const work = rest.slice(1);
707
- assertNoUnknownFlags(work, "store");
708
- const refuse = requireYesOrTty("disconnect the store (shared Stripe account with payments)");
709
- if (refuse)
710
- return emitUsage("store", refuse);
711
- const data = await v1Post(`${base}/connect`, { action: "disconnect" });
712
- if (isJson()) {
713
- emitOk("store", data);
714
- return EXIT_OK;
715
- }
716
- console.log(typeof data.message === "string" ? data.message : "Disconnected.");
717
- return EXIT_OK;
718
- }
719
674
  if (group === "import") {
720
675
  const work = rest.slice(1);
721
676
  assertNoUnknownFlags(work, "store");
@@ -740,7 +695,7 @@ export async function runShop(args) {
740
695
  console.log(`${imported.length} imported ${skipped.length} skipped ${failed.length} failed`);
741
696
  return EXIT_OK;
742
697
  }
743
- if (group === "orders" || group === "payments") {
698
+ if (group === "orders") {
744
699
  const noun = group;
745
700
  const sub = rest[1] ?? "list";
746
701
  const work = rest.slice(2);
@@ -27,7 +27,8 @@ _frontpage_completions() {
27
27
  analytics) COMPREPLY=( $(compgen -W "dashboard summary query funnel timeseries compare sql" -- "\${cur}") ) ;;
28
28
  people) COMPREPLY=( $(compgen -W "list get add set note rm-note follow-up hide unhide" -- "\${cur}") ) ;;
29
29
  routines) COMPREPLY=( $(compgen -W "list get add set pause resume rm run runs" -- "\${cur}") ) ;;
30
- store) COMPREPLY=( $(compgen -W "items variants categories settings orders payments connect status disconnect import" -- "\${cur}") ) ;;
30
+ payments) COMPREPLY=( $(compgen -W "status settings connect availability activity buttons tax invoices recurring customers --file --operation-key --preview-hash --person --email --description --amount-cents --cadence --first-date --time-zone --due-days --no-open --site --json --yes" -- "\${cur}") ) ;;
31
+ store) COMPREPLY=( $(compgen -W "items variants categories settings orders import" -- "\${cur}") ) ;;
31
32
  modules)
32
33
  if [[ \${COMP_CWORD} -ge 3 && \${COMP_WORDS[2]} == google_reviews ]]; then
33
34
  COMPREPLY=( $(compgen -W "docs request-setup enable disable archive config connect resolve status list-reviews hide show set-stars set-sort set-weight reset-weights write-review-url refresh disconnect" -- "\${cur}") )
@@ -36,7 +37,7 @@ _frontpage_completions() {
36
37
  elif [[ \${COMP_CWORD} -ge 3 && (\${COMP_WORDS[2]} == callbar || \${COMP_WORDS[2]} == booking || \${COMP_WORDS[2]} == signups) ]]; then
37
38
  COMPREPLY=( $(compgen -W "docs request-setup enable disable archive config get set" -- "\${cur}") )
38
39
  elif [[ \${COMP_CWORD} -ge 3 && \${COMP_WORDS[2]} == payments ]]; then
39
- COMPREPLY=( $(compgen -W "docs request-setup enable disable archive config status connect disconnect" -- "\${cur}") )
40
+ COMPREPLY=( $(compgen -W "docs" -- "\${cur}") )
40
41
  else
41
42
  COMPREPLY=( $(compgen -W "list docs request-setup enable disable archive config callbar forms booking signups payments articles google_reviews" -- "\${cur}") )
42
43
  fi
@@ -72,7 +73,8 @@ _frontpage() {
72
73
  analytics) _values 'subcommand' dashboard summary query funnel timeseries compare sql ;;
73
74
  people) _values 'subcommand' list get add set note rm-note follow-up hide unhide ;;
74
75
  routines) _values 'subcommand' list get add set pause resume rm run runs ;;
75
- store) _values 'subcommand' items variants categories settings orders payments connect status disconnect import ;;
76
+ payments) _values 'subcommand' status settings connect availability activity buttons tax invoices recurring customers ;;
77
+ store) _values 'subcommand' items variants categories settings orders import ;;
76
78
  modules)
77
79
  if [[ $words[2] == google_reviews ]]; then
78
80
  _values 'subcommand' docs request-setup enable disable archive config connect resolve status list-reviews hide show set-stars set-sort set-weight reset-weights write-review-url refresh disconnect
@@ -81,7 +83,7 @@ _frontpage() {
81
83
  elif [[ $words[2] == callbar || $words[2] == booking || $words[2] == signups ]]; then
82
84
  _values 'subcommand' docs request-setup enable disable archive config get set
83
85
  elif [[ $words[2] == payments ]]; then
84
- _values 'subcommand' docs request-setup enable disable archive config status connect disconnect
86
+ _values 'subcommand' docs
85
87
  else
86
88
  _values 'subcommand' list docs request-setup enable disable archive config callbar forms booking signups payments articles google_reviews
87
89
  fi
@@ -110,12 +112,13 @@ export function generateFishCompletion() {
110
112
  `complete -c frontpage -n "__fish_seen_subcommand_from analytics" -a "dashboard summary query funnel timeseries compare sql"`,
111
113
  `complete -c frontpage -n "__fish_seen_subcommand_from people" -a "list get add set note rm-note follow-up hide unhide"`,
112
114
  `complete -c frontpage -n "__fish_seen_subcommand_from routines" -a "list get add set pause resume rm run runs"`,
113
- `complete -c frontpage -n "__fish_seen_subcommand_from store" -a "items variants categories settings orders payments connect status disconnect import"`,
115
+ `complete -c frontpage -n "__fish_seen_subcommand_from payments" -a "status settings connect availability activity buttons tax invoices recurring customers"
116
+ complete -c frontpage -n "__fish_seen_subcommand_from store" -a "items variants categories settings orders import"`,
114
117
  `complete -c frontpage -n "__fish_seen_subcommand_from modules" -a "list docs request-setup enable disable archive config callbar forms booking signups payments articles google_reviews"`,
115
118
  `complete -c frontpage -n "__fish_seen_subcommand_from modules; and __fish_seen_subcommand_from google_reviews" -a "connect resolve status list-reviews hide show set-stars set-sort set-weight reset-weights write-review-url refresh disconnect"`,
116
119
  `complete -c frontpage -n "__fish_seen_subcommand_from modules; and __fish_seen_subcommand_from forms" -a "get set destinations connect-destination remove-destination disconnect-destination attach-destination detach-destination request-destination-setup"`,
117
120
  `complete -c frontpage -n "__fish_seen_subcommand_from modules; and __fish_seen_subcommand_from callbar booking signups" -a "get set"`,
118
- `complete -c frontpage -n "__fish_seen_subcommand_from modules; and __fish_seen_subcommand_from payments" -a "status connect disconnect"`,
121
+ `complete -c frontpage -n "__fish_seen_subcommand_from modules; and __fish_seen_subcommand_from payments" -a "docs"`,
119
122
  `complete -c frontpage -n "__fish_seen_subcommand_from extensions" -a "list docs enable disable config"`,
120
123
  `complete -c frontpage -l site -d "Site subdomain slug"`,
121
124
  ];
package/dist/registry.js CHANGED
@@ -39,7 +39,9 @@ export const COMMANDS = [
39
39
  usage: "frontpage login [api-key]",
40
40
  topics: ["auth"],
41
41
  examples: ['frontpage login', 'frontpage login "$FRONTPAGE_API_KEY"'],
42
- flags: ["FRONTPAGE_API_KEY env overrides keychain after login for that process"],
42
+ flags: [
43
+ "FRONTPAGE_API_KEY env overrides keychain after login for that process"
44
+ ],
43
45
  seeAlso: ["whoami", "doctor", "logout"],
44
46
  },
45
47
  {
@@ -68,7 +70,9 @@ export const COMMANDS = [
68
70
  usage: "frontpage use <subdomain>",
69
71
  topics: ["auth", "sync"],
70
72
  examples: ["frontpage use arbiter"],
71
- dangers: ["Clears baseCommitSha when switching sites — run pull again before push"],
73
+ dangers: [
74
+ "Clears baseCommitSha when switching sites — run pull again before push"
75
+ ],
72
76
  seeAlso: ["link", "pull", "status"],
73
77
  },
74
78
  {
@@ -200,7 +204,11 @@ export const COMMANDS = [
200
204
  summary: "Open the site subdomain in a browser",
201
205
  usage: "frontpage open [/path|--path /path] [--site <slug>] [--prod]",
202
206
  topics: ["sync"],
203
- examples: ["frontpage open", "frontpage open /updates", "frontpage open --path /pricing"],
207
+ examples: [
208
+ "frontpage open",
209
+ "frontpage open /updates",
210
+ "frontpage open --path /pricing"
211
+ ],
204
212
  flags: [
205
213
  "Optional site-relative path appended to https://<slug>.frontpage.host",
206
214
  "--prod is a no-op stub today — still opens the *.frontpage.host subdomain",
@@ -337,12 +345,12 @@ export const COMMANDS = [
337
345
  "frontpage modules forms get",
338
346
  "frontpage modules forms set --instance lead-gen --notify-email hi@example.com",
339
347
  "frontpage modules forms set --instance lead-gen --confirm-template <template-id>",
340
- "frontpage store connect --no-open",
348
+ "frontpage payments connect --no-open",
341
349
  "frontpage modules docs forms",
342
350
  "frontpage modules docs google_reviews",
343
351
  "frontpage modules docs signups",
344
352
  "frontpage modules list",
345
- 'frontpage store connect --no-open',
353
+ "frontpage payments connect --no-open",
346
354
  'frontpage modules request-setup google_reviews --reason "You asked for Google reviews — pick your listing so we show the right ones"',
347
355
  ],
348
356
  flags: [
@@ -350,7 +358,7 @@ export const COMMANDS = [
350
358
  "Key-first and verb-first both work: `modules google_reviews enable` = `modules enable google_reviews`",
351
359
  "google_reviews extras: connect / resolve / status / list-reviews / hide / show / set-stars / set-sort / set-weight / reset-weights / refresh / disconnect / write-review-url",
352
360
  "callbar/forms/booking/signups: typed get / set (same whitelist as `modules config`)",
353
- "Store Stripe connect is frontpage store connect. request-setup still lights the editor amber bar.",
361
+ "Stripe setup is frontpage payments connect. request-setup still lights the editor amber bar.",
354
362
  "See https://frontpage.host/developers/modules and /developers/modules/google-reviews",
355
363
  ],
356
364
  dangers: [
@@ -407,6 +415,26 @@ export const COMMANDS = [
407
415
  ],
408
416
  seeAlso: ["prompt", "help content", "help site-building"],
409
417
  },
418
+ {
419
+ name: "customer-inbox",
420
+ summary: "Manage internal Customer Inbox settings, knowledge and human conversation handling",
421
+ usage: "frontpage customer-inbox get|put|post <resource> [--file <json>] [--request-key <key>] [--site <slug>] [--json]; export|download <id> --out <new-file>",
422
+ topics: ["content"],
423
+ examples: [
424
+ "frontpage customer-inbox get settings",
425
+ "frontpage customer-inbox put settings --file ./inbox-settings.json --request-key settings_revision_1",
426
+ "frontpage customer-inbox get conversations",
427
+ "frontpage customer-inbox get knowledge"
428
+ ],
429
+ flags: [
430
+ "Internal plans and the site pilot apply",
431
+ "Writes require a stable --request-key and a JSON file; reuse both on retries",
432
+ "Human CLI access can guide, take over and reply; website-agent tools have management scope only",
433
+ "Luna and 10× AI billing are fixed",
434
+ "Full request examples: CUSTOMER-INBOX.md"
435
+ ],
436
+ seeAlso: ["people", "usage"],
437
+ },
410
438
  {
411
439
  name: "inbox",
412
440
  summary: "List leads / subscribers / orders",
@@ -461,7 +489,7 @@ export const COMMANDS = [
461
489
  "frontpage people export",
462
490
  ],
463
491
  flags: [
464
- "Workspace: groups list|get|create|update|archive|restore|preview; stages create|update|reorder|archive|restore; membership get|add|remove|move|stage|advance|override|hold; rules list|get|create|update|enable|pause|archive|validate|preview|runs|retry; attachments create; import preview|start|get|retry; person permission; company get|set; audience preview; overview; sources",
492
+ "Workspace: groups list|get|create|update|archive|restore|preview; stages create|update|reorder|archive|restore; membership get|add|remove|move|stage|advance|override|hold; rules list|get|create|update|enable|pause|archive|validate|preview|runs|retry; attachments create; import preview|start|get|retry; person permission; audience preview; overview; sources",
465
493
  "All workspace operations accept --file JSON or --data JSON. Named flags: --id --label --name --group --stage --person --from-group --mode --color --position --terminal --enabled --hold --allowed --archived --reset --override include|exclude|auto --email-kind template|series --email-id --rule --run --cursor --limit --request-key. Boolean flags take true|false. Complex conditions/actions/mappings use JSON.",
466
494
  "CSV: --csv PATH plus --file mapping.json. import start requires a stable --request-key. Retry the same import with the same key and contents.",
467
495
  "list/export: --group GROUP_ID --stage STAGE_ID. See package PEOPLE.md for full examples and every JSON shape.",
@@ -476,41 +504,68 @@ export const COMMANDS = [
476
504
  },
477
505
  {
478
506
  name: "store",
479
- summary: "Store catalog: items, variants, stock, and store settings (not inbox)",
480
- usage: "frontpage store items|variants|categories|settings|orders|payments|connect|status|disconnect|import … [--site <slug>] [--json]",
507
+ summary: "Cart-only Store: products, variants, stock, categories, fulfillment, fees, receipts and orders",
508
+ usage: "frontpage store items|variants|categories|settings|orders|import … [--site <slug>] [--json]",
481
509
  topics: ["content"],
482
510
  examples: [
483
- "frontpage store items list",
484
- "frontpage store categories create hats --label Hats",
485
- "frontpage store items create camp-hat --title \"Camp hat\" --price-cents 4999 --categories hats --stock 20",
486
- "frontpage store settings set-checkout --mode cart",
487
- "frontpage store items list --category hats",
488
- "frontpage store variants add camp-hat --id m --option Size=M --price-cents 4999 --stock 8",
489
- "frontpage store items stock camp-hat --variant m --add 5",
490
- "frontpage store items stock camp-hat --variant m --remove 5",
491
- "frontpage store items stock camp-hat --variant m --set 20",
492
- "frontpage store settings set-fulfillment --mode pickup",
493
- "frontpage store settings set-fulfillment --pickup --delivery --mode flat-rate --flat-cents 500 --pickup-location \"The shop\"",
494
- "frontpage store settings set-currency --currency usd",
495
- "frontpage store settings set-currency --currency jpy",
496
- "frontpage store connect --no-open",
497
- "frontpage store status",
498
- "frontpage store import",
511
+ 'frontpage store items create camp-hat --title "Camp hat" --price-cents 4999 --stock 20',
512
+ "frontpage store items restore camp-hat",
513
+ "frontpage store settings set-fulfillment --pickup --delivery --mode flat-rate --flat-cents 500",
514
+ "frontpage store settings set-checkout-fee --mode percent --bps 250",
515
+ "frontpage store settings set-receipt --stock",
516
+ "frontpage store settings availability off --yes",
499
517
  ],
500
518
  flags: [
501
- "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.",
502
- "--add / --remove become signed delta. 5 adds 5. --remove 5 removes 5. Result copy is added 5 / removed 5 / set to 20 / stockNow.",
503
- "Pass either --add, --remove, or --set. Never two together. --set unlimited returns counted stock to unlimited.",
504
- "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",
505
- "categories: list|create|rename|archive. Create the category first. Assigning an unknown slug fails.",
506
- "variants: add|update|archive <itemId> --id m --price-cents … [--option Size=M] [--stock]",
507
- "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.",
508
- "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",
509
- "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.",
510
- "orders / payments list|get read the same ledgers as frontpage inbox orders|payments",
511
- "Unknown leftover flags fail closed (exit 2)",
519
+ "items list|get|create|update|archive|restore|activate|images|stock; use --file for nested variants, options, customizations and images. Existing fixed/open pricing is retained. Store always uses Add to cart.",
520
+ "Descriptions use a small markdown subset. Bind data-fp-store-bind=description for the full product story; do not bake titles, prices or images into the page. Store is cart-only; use Payments for explicit Pay now.",
521
+ "items stock uses --add/--remove as a signed delta, or --set/--unlimited. Never combine these verbs. Product pages use data-fp-store-product and the platform cart templates. Store prices use the account currency.",
522
+ "variants add|update|archive|restore; categories list|create|rename|archive. Create categories before assigning their slugs.",
523
+ "settings get|set-fulfillment|set-checkout-fee|set-receipt|availability get|on|off. Fee mode flat uses --cents; percent uses --bps (250 is 2.5%). Receipt takes --template <id> or --stock. Availability requires admin and --yes in scripts.",
524
+ "orders list|get; import explicitly reads the existing site product files. No fleet import or data conversion occurs.",
525
+ "Money fields use integer minor units: $49.99 is 4999 minor units in USD; JPY 4999 is ¥4999. Currency is read-only from Stripe. No --checkout, set-checkout or set-currency command.",
526
+ "Store off preserves catalog/orders and leaves Pay now/invoices/recurring billing running. Payments, Stripe setup, tax, activity and Pay now management use frontpage payments.",
527
+ "Full Store reference: packaged README.md and https://frontpage.host/developers/store. List bounds are source-specific; manage_store list_items is at most 25 compact rows.",
528
+ ],
529
+ seeAlso: ["payments", "people", "https://frontpage.host/developers/store"],
530
+ },
531
+ {
532
+ name: "payments",
533
+ summary: "Stripe, tax, payment activity, Pay now buttons, invoices and native recurring billing",
534
+ usage: "frontpage payments status|settings|connect|availability|activity|tax|buttons|invoices|recurring|customers … [--site <slug>] [--json]",
535
+ topics: ["content"],
536
+ examples: [
537
+ "frontpage payments status",
538
+ "frontpage payments connect --no-open",
539
+ "frontpage payments invoices preview --file invoice.json --json",
540
+ "frontpage payments invoices send --file invoice.json --preview-hash <hash> --operation-key <stable-uuid> --yes --json",
541
+ "frontpage payments recurring preview --file recurring.json --json",
542
+ "frontpage payments recurring create --file recurring.json --preview-hash <hash> --operation-key <stable-uuid> --yes",
543
+ "frontpage payments customers send-billing-link <customerMapId> --operation-key <stable-uuid> --yes",
544
+ "frontpage payments availability off --yes",
545
+ ],
546
+ flags: [
547
+ "status; settings; connect [--no-open]. Connect returns an existing signed-in admin browser handoff; it never accepts Stripe terms or completes onboarding.",
548
+ "availability preview|on|off. off reads affected counts before confirmed pause/void; a partial response stays off and requires another off request. on never resumes stopped billing or restores void invoices.",
549
+ "activity list|get <id> --source order|payment. invoices and recurring have separate get commands. Source IDs never collide.",
550
+ "buttons list [--limit 1..100 --offset N --archived true]; get|create|update|archive. create uses --id, --title, --price-cents, optional --amount open --min-cents; update takes an id and --file. Change price separately from details. Archive requires --yes. Only explicit Pay now references/registrations qualify; an old instant Store setting does not.",
551
+ "tax get; tax settings --file <file> --yes; tax registrations list|add --country US --state CA --yes|end <id> --yes. Supply the business's actual tax registrations. No automatic tax advice or raw Account Session secrets.",
552
+ "invoices preview|send --file <file>; list [--limit 1..100 --offset N --arrangement <id>]; get|refresh|resend|void <id>. send/resend need --operation-key; send needs --preview-hash; financial writes require --yes in scripts.",
553
+ "recurring preview|create --file <file>; list|get|refresh|cancel|resume|resend-invitation. Supported cadence: weekly, fortnightly, monthly, quarterly, yearly; firstDate YYYY-MM-DD, explicit IANA timeZone, 09:00 start. create/resume/invitation need stable operation keys. Resume requires first-date, preview-hash and --yes; no missed-period backfill.",
554
+ "customers billing-link|send-billing-link <customerMapId> --operation-key <key>. Send requires --yes. A returned link is private and expires after 30 minutes; do not put it in logs.",
555
+ "Common invoice flags: --person <CRM-id> OR --email <email> [--name <name>], --description, --amount-cents, --kind once|recurring, --collection manual|autopay, --cadence, --first-date, --time-zone, --due-days 7|14|30, --preview-hash, --operation-key. Use --file for nested address (line1,line2,city,region,postal,country).",
556
+ "Full REST methods, JSON examples, permissions, retries and financial semantics are in packaged PAYMENTS.md. All normal global auth/site/JSON/quiet/error/exit rules still apply.",
557
+ ],
558
+ dangers: [
559
+ "Reuse the same operation key and reviewed input after a timeout. Do not create a new key to force a send. Accepted email is not paid or proof of arrival; Stripe test mode may suppress invoice emails.",
560
+ "Autopay create sends an invitation. The customer must agree and finish Stripe setup. Saved cards, owner flags and browser returns are not consent or payment.",
561
+ "CLI requires Scale/Enterprise/Internal. New invoices require Growth+ separately. Financial writes require admin; existing tax writes and private reads permit editors; viewers receive no private ledger.",
562
+ ],
563
+ seeAlso: [
564
+ "store",
565
+ "people",
566
+ "modules",
567
+ "https://frontpage.host/developers/payments",
512
568
  ],
513
- seeAlso: ["inbox", "https://frontpage.host/developers/store"],
514
569
  },
515
570
  {
516
571
  name: "mail",
@@ -561,7 +616,9 @@ export const COMMANDS = [
561
616
  "frontpage seo get --path /updates",
562
617
  "frontpage seo set --path / --title \"Home\"",
563
618
  ],
564
- flags: ["get accepts positional path or --path (flag wins); unknown leftover flags fail closed"],
619
+ flags: [
620
+ "get accepts positional path or --path (flag wins); unknown leftover flags fail closed"
621
+ ],
565
622
  },
566
623
  {
567
624
  name: "articles",
@@ -733,7 +790,7 @@ export const TOPICS = {
733
790
  - Forms: <form data-fp-form="slug"> with named inputs; no action/onsubmit/honeypot.
734
791
  - Articles: frontpage articles …; migrate folders via frontpage prompt (don't invent mass delete+write).
735
792
  - 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.
736
- - 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
793
+ - Store: frontpage store for catalog, settings set-fulfillment / set-checkout-fee / set-receipt; frontpage payments owns Stripe and invoices. 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
737
794
 
738
795
  Full guide: https://frontpage.host/developers/site-building
739
796
  See also: frontpage help sync | content | AGENTS.md`,
@@ -773,7 +830,8 @@ export const TOPICS = {
773
830
  frontpage routines list|get|add|set|pause|resume|rm|run|runs
774
831
  # Routines guide: https://frontpage.host/developers/routines
775
832
  # 8am,12pm,4pm and tue,thu,fri store hours [8, 12, 16] and weekdays [2, 4, 5] on one routine. Timezone is IANA.
776
- frontpage store items|variants|categories|settings|orders|payments|connect|status|disconnect
833
+ frontpage store items|variants|categories|settings|orders|import
834
+ frontpage payments status|connect|availability|activity|buttons|tax|invoices|recurring|customers
777
835
  # Store catalog + live storefront: https://frontpage.host/developers/store
778
836
  # Integer cents only. $49.99 is 4999. stock --add/--remove are signed delta.
779
837
  # Markup: data-fp-store-grid / data-fp-store-product / data-fp-cart. One item.astro, not one file per product.
@@ -785,7 +843,7 @@ export const TOPICS = {
785
843
  frontpage tokens set --group … --key … --value …
786
844
  frontpage modules list|docs|request-setup|enable|disable|archive|config <key>
787
845
  frontpage modules <key> # that key's CLI usage
788
- frontpage store connect [--no-open] # Store Stripe connect
846
+ frontpage payments connect [--no-open] # Payments Stripe connect
789
847
  # mailing list key: signups. Index: https://frontpage.host/developers/modules
790
848
  frontpage extensions list|docs|enable|disable|config <key>
791
849
  frontpage settings get|set
@@ -795,7 +853,7 @@ export const TOPICS = {
795
853
  Do not fake payments/checkout. LOCKED extensions → do not fake; say plan required.
796
854
  Migrate pages → articles: prefer frontpage prompt "migrate …" over mass delete+write.
797
855
  edit uses click-edit (exact single occurrence), not push / gated-write.
798
- Connect opens Frontpage (logged-in), then Stripe; never accepts or returns OAuth secrets. Store connect is frontpage store connect.
856
+ Connect opens Frontpage (logged-in), then Stripe; never accepts or returns OAuth secrets. Stripe setup is frontpage payments connect.
799
857
  Site contract: frontpage help site-building · https://frontpage.host/developers/site-building
800
858
  Domains, billing, members, and site delete are out of v1.`,
801
859
  history: `History
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: 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.
12
+ - Modules setup: Store Stripe connect is frontpage payments connect. Payments availability owns pause/restore and retains 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)
@@ -18,7 +18,7 @@
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
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
+ - Store catalog: frontpage store items|variants|categories|settings|orders|import. 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 payments connect [--no-open] opens Frontpage then Stripe. Store is cart-only; currency comes from Stripe and cannot be set. Use frontpage payments for standalone Pay now and invoices. 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
22
22
  - Analytics funnel: --steps JSON array or comma paths (/, /pricing); bad JSON → Invalid JSON for --steps
23
23
  - Modules: unknown key errors list Known: …; mailing list module key is signups (not subscribe)
24
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
@@ -35,3 +35,8 @@
35
35
  ## People groups, stages, rules, and imports
36
36
 
37
37
  Use `frontpage people overview`, `groups`, `stages`, `membership`, `rules`, `attachments`, `import`, and `company`. Use `frontpage mail campaign` for one-off group/stage/list audiences in Emails. Every editor action is available through these commands or `frontpage people workspace OPERATION --file payload.json`. Full operations and examples: [PEOPLE.md](./PEOPLE.md); public guide: https://frontpage.host/developers/people. Automated emails select a saved template or series. Campaigns use their own drafts.
38
+
39
+ Customer Inbox (internal sites): CUSTOMER-INBOX.md. Settings, widget, knowledge, human guidance/relay, lifecycle, export/download and recovery. Website agents configure through manage_customer_inbox and must not use human credentials for conversations.
40
+
41
+
42
+ Payments, invoices and recurring billing: see packaged [PAYMENTS.md](./PAYMENTS.md), `frontpage help payments`, and https://frontpage.host/developers/payments. Version 0.2.0 removes writable currency, Store checkout mode and Store-owned money commands; Store stays cart-only. Preview/review before sends; keep stable operation keys on retries. Autopay requires the customer’s own verified authorization.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "frontpage-host",
3
- "version": "0.1.28",
4
- "description": "Frontpage CLI — manage your site from the terminal",
3
+ "version": "0.2.0",
4
+ "description": "Frontpage CLI \u2014 manage your site from the terminal",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "frontpage": "dist/bin.js"
@@ -11,7 +11,9 @@
11
11
  "README.md",
12
12
  "AGENTS.md",
13
13
  "llms.txt",
14
- "PEOPLE.md"
14
+ "PEOPLE.md",
15
+ "CUSTOMER-INBOX.md",
16
+ "PAYMENTS.md"
15
17
  ],
16
18
  "scripts": {
17
19
  "build": "tsc -p tsconfig.json",