frontpage-host 0.1.23 → 0.1.25

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.
@@ -0,0 +1,137 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { assertNoUnknownFlags, takeFlag } from "../argv.js";
3
+ import { CliHttpError, EXIT_OK, EXIT_USAGE, v1Post } from "../http/client.js";
4
+ import { emitOk, isJson } from "../runtime.js";
5
+ const AREAS = [
6
+ "groups",
7
+ "stages",
8
+ "membership",
9
+ "rules",
10
+ "import",
11
+ "attachments",
12
+ "person",
13
+ "company",
14
+ ];
15
+ export function isPeopleWorkspaceCommand(sub) {
16
+ return (!!sub &&
17
+ (AREAS.includes(sub) ||
18
+ ["overview", "sources", "audience", "workspace"].includes(sub)));
19
+ }
20
+ export async function runPeopleWorkspace(slug, args) {
21
+ const work = [...args], area = work.shift();
22
+ const operation = area === "workspace"
23
+ ? work.shift()
24
+ : area === "overview" || area === "sources"
25
+ ? area
26
+ : `${area}.${work.shift() ?? (area === "company" ? "get" : area === "audience" ? "preview" : "list")}`;
27
+ const file = takeFlag(work, "--file"), json = takeFlag(work, "--data");
28
+ if (file && json)
29
+ throw new CliHttpError("Use --file or --data, not both.", EXIT_USAGE, 0, "bad_request");
30
+ let payload = {};
31
+ if (file || json) {
32
+ try {
33
+ const parsed = JSON.parse(file ? await readFile(file, "utf8") : json);
34
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
35
+ throw Error();
36
+ payload = parsed;
37
+ }
38
+ catch {
39
+ throw new CliHttpError("Provide a JSON object through --file or --data.", EXIT_USAGE, 0, "bad_request");
40
+ }
41
+ }
42
+ for (const [flag, key] of Object.entries({
43
+ "--id": "id",
44
+ "--name": "name",
45
+ "--label": "label",
46
+ "--group": "groupId",
47
+ "--stage": "stageId",
48
+ "--person": "personId",
49
+ "--from-group": "fromGroupId",
50
+ "--mode": "mode",
51
+ "--color": "color",
52
+ "--run": "runId",
53
+ "--rule": "ruleId",
54
+ "--email-id": "emailId",
55
+ "--email-kind": "emailKind",
56
+ "--override": "override",
57
+ "--cursor": "cursor",
58
+ "--request-key": "requestKey",
59
+ })) {
60
+ const v = takeFlag(work, flag);
61
+ if (v !== undefined)
62
+ payload[key] = key === "override" && v === "auto" ? null : v;
63
+ }
64
+ for (const [flag, key] of Object.entries({
65
+ "--position": "position",
66
+ "--limit": "limit",
67
+ })) {
68
+ const v = takeFlag(work, flag);
69
+ if (v !== undefined) {
70
+ const n = Number(v);
71
+ if (!Number.isInteger(n))
72
+ throw new CliHttpError(`${flag} requires an integer.`, EXIT_USAGE, 0, "bad_request");
73
+ payload[key] = n;
74
+ }
75
+ }
76
+ for (const [flag, key] of Object.entries({
77
+ "--enabled": "enabled",
78
+ "--hold": "hold",
79
+ "--terminal": "terminal",
80
+ "--allowed": "allowed",
81
+ "--archived": "archived",
82
+ "--reset": "reset",
83
+ })) {
84
+ const v = takeFlag(work, flag);
85
+ if (v !== undefined) {
86
+ if (v !== "true" && v !== "false")
87
+ throw new CliHttpError(`${flag} is true or false.`, EXIT_USAGE, 0, "bad_request");
88
+ payload[key] = v === "true";
89
+ }
90
+ }
91
+ const csv = takeFlag(work, "--csv");
92
+ if (csv)
93
+ payload.csv = await readFile(csv, "utf8");
94
+ if (operation === "import.start" && !payload.requestKey)
95
+ throw new CliHttpError("Provide --request-key with a stable UUID so retries reuse this import.", EXIT_USAGE, 0, "bad_request");
96
+ assertNoUnknownFlags(work, "people");
97
+ if (work.length || !operation)
98
+ throw new CliHttpError("Unexpected arguments. Run frontpage help people.", EXIT_USAGE, 0, "bad_request");
99
+ const result = await v1Post(`/api/v1/sites/${encodeURIComponent(slug)}/people/workspace`, { ...payload, operation });
100
+ if (isJson())
101
+ emitOk("people", result);
102
+ else
103
+ console.log(JSON.stringify(result, null, 2));
104
+ return EXIT_OK;
105
+ }
106
+ export async function runCampaign(slug, args) {
107
+ const work = [...args], operation = work.shift() ?? "get", file = takeFlag(work, "--file"), data = takeFlag(work, "--data"), documentId = takeFlag(work, "--id"), requestKey = takeFlag(work, "--request-key");
108
+ if (file && data)
109
+ throw new CliHttpError("Use --file or --data.", EXIT_USAGE, 0, "bad_request");
110
+ let payload = {};
111
+ if (file || data) {
112
+ try {
113
+ payload = JSON.parse(file ? await readFile(file, "utf8") : data);
114
+ if (!payload || Array.isArray(payload) || typeof payload !== "object")
115
+ throw Error();
116
+ }
117
+ catch {
118
+ throw new CliHttpError("Provide a JSON object.", EXIT_USAGE, 0, "bad_request");
119
+ }
120
+ }
121
+ assertNoUnknownFlags(work, "mail");
122
+ if (work.length)
123
+ throw new CliHttpError("Unexpected campaign arguments.", EXIT_USAGE, 0, "bad_request");
124
+ const result = await v1Post(`/api/v1/sites/${encodeURIComponent(slug)}/mail/campaign`, {
125
+ ...payload,
126
+ operation,
127
+ ...(documentId ? { documentId } : {}),
128
+ ...(operation === "send"
129
+ ? { requestKey: requestKey ?? payload.requestKey }
130
+ : {}),
131
+ });
132
+ if (isJson())
133
+ emitOk("mail", result);
134
+ else
135
+ console.log(JSON.stringify(result, null, 2));
136
+ return EXIT_OK;
137
+ }
@@ -1,3 +1,4 @@
1
+ import { isPeopleWorkspaceCommand, runPeopleWorkspace } from "./people-workspace.js";
1
2
  import { writeFile } from "node:fs/promises";
2
3
  import { resolveSlug } from "../site.js";
3
4
  import { CliHttpError, EXIT_OK, EXIT_USAGE, apiBase, exitCodeForError, getBearerAuthHeader, httpErrorFromV1, v1Delete, v1Get, v1Patch, v1Post, } from "../http/client.js";
@@ -73,11 +74,15 @@ export async function runPeople(args) {
73
74
  try {
74
75
  const { slug, rest } = await resolveSlug(args);
75
76
  const sub = rest[0];
77
+ if (isPeopleWorkspaceCommand(sub))
78
+ return await runPeopleWorkspace(slug, rest);
76
79
  const base = `/api/v1/sites/${encodeURIComponent(slug)}/people`;
77
80
  if (!sub || sub === "list") {
78
81
  const work = sub === "list" ? rest.slice(1) : [...rest];
79
82
  const q = takeFlag(work, "--q") ?? takeFlag(work, "--query");
80
83
  const status = takeFlag(work, "--status");
84
+ const groupId = takeFlag(work, "--group");
85
+ const stageId = takeFlag(work, "--stage");
81
86
  const source = takeFlag(work, "--source");
82
87
  const formSlug = takeFlag(work, "--form-slug");
83
88
  const followUp = takeFlag(work, "--follow-up");
@@ -91,6 +96,10 @@ export async function runPeople(args) {
91
96
  const params = new URLSearchParams();
92
97
  if (q)
93
98
  params.set("q", q);
99
+ if (groupId)
100
+ params.set("groupId", groupId);
101
+ if (stageId)
102
+ params.set("stageId", stageId);
94
103
  if (status)
95
104
  params.set("status", status);
96
105
  if (source)
@@ -313,6 +322,8 @@ export async function runPeople(args) {
313
322
  const work = rest.slice(1);
314
323
  const q = takeFlag(work, "--q") ?? takeFlag(work, "--query");
315
324
  const status = takeFlag(work, "--status");
325
+ const groupId = takeFlag(work, "--group");
326
+ const stageId = takeFlag(work, "--stage");
316
327
  const source = takeFlag(work, "--source");
317
328
  const formSlug = takeFlag(work, "--form-slug");
318
329
  const followUp = takeFlag(work, "--follow-up");
@@ -325,6 +336,10 @@ export async function runPeople(args) {
325
336
  const params = new URLSearchParams();
326
337
  if (q)
327
338
  params.set("q", q);
339
+ if (groupId)
340
+ params.set("groupId", groupId);
341
+ if (stageId)
342
+ params.set("stageId", stageId);
328
343
  if (status)
329
344
  params.set("status", status);
330
345
  if (source)
@@ -1,7 +1,8 @@
1
1
  import { readdir, readFile, stat } from "node:fs/promises";
2
2
  import { join, relative, sep } from "node:path";
3
3
  import { resolveSlug } from "../site.js";
4
- import { EXIT_OK, EXIT_USAGE, v1Get, v1Put } from "../http/client.js";
4
+ import { CliHttpError, EXIT_CONFLICT, EXIT_OK, EXIT_USAGE, v1Get, v1Put, } from "../http/client.js";
5
+ import { gitBlobSha } from "../git-blob-sha.js";
5
6
  import { readState, writeState } from "../state/frontpage-state.js";
6
7
  import { emitErr, emitOk, emitUsage, isJson } from "../runtime.js";
7
8
  import { waitForAgentThenCompile } from "./wait-site-compile.js";
@@ -47,7 +48,9 @@ async function walkAllowlisted(cwd) {
47
48
  for (const ent of entries) {
48
49
  const child = join(abs, ent.name);
49
50
  if (ent.isDirectory()) {
50
- if (ent.name === "node_modules" || ent.name === ".git" || ent.name === ".frontpage")
51
+ if (ent.name === "node_modules" ||
52
+ ent.name === ".git" ||
53
+ ent.name === ".frontpage")
51
54
  continue;
52
55
  await walk(child);
53
56
  }
@@ -83,7 +86,9 @@ export async function runPush(args) {
83
86
  const messageIdx = work.indexOf("--message");
84
87
  const message = messageIdx >= 0
85
88
  ? work[messageIdx + 1]
86
- : work.find((a) => a.startsWith("--message="))?.slice("--message=".length);
89
+ : work
90
+ .find((a) => a.startsWith("--message="))
91
+ ?.slice("--message=".length);
87
92
  const cwd = process.cwd();
88
93
  const state = await readState(cwd);
89
94
  if (!state.baseCommitSha) {
@@ -94,6 +99,7 @@ export async function runPush(args) {
94
99
  return emitUsage("push", "No allowlisted files (src/, public/, content/) found in this folder.", EXIT_USAGE);
95
100
  }
96
101
  const listing = await v1Get(`/api/v1/sites/${encodeURIComponent(slug)}/files`);
102
+ const remoteByPath = new Map(listing.files.map((file) => [file.path, file.blobSha]));
97
103
  const files = [
98
104
  ...pushDeletesForMissingLocal(paths, listing.files.map((f) => f.path)),
99
105
  ];
@@ -102,21 +108,38 @@ export async function runPush(args) {
102
108
  const st = await stat(abs);
103
109
  if (!st.isFile())
104
110
  continue;
111
+ const buf = await readFile(abs);
112
+ if (gitBlobSha(buf) === remoteByPath.get(path))
113
+ continue;
105
114
  if (isTextPath(path)) {
106
- const content = await readFile(abs, "utf8");
115
+ const content = buf.toString("utf8");
107
116
  files.push({ path, encoding: "utf-8", content });
108
117
  }
109
118
  else {
110
- const buf = await readFile(abs);
111
- files.push({ path, encoding: "base64", content: buf.toString("base64") });
119
+ files.push({
120
+ path,
121
+ encoding: "base64",
122
+ content: buf.toString("base64"),
123
+ });
112
124
  }
113
125
  }
114
- const data = await v1Put(`/api/v1/sites/${encodeURIComponent(slug)}/files`, {
115
- baseCommitSha: state.baseCommitSha,
116
- expectedHeadSha: state.baseCommitSha,
117
- message: message || undefined,
118
- files,
119
- });
126
+ if (!files.length && listing.baseCommitSha !== state.baseCommitSha) {
127
+ throw new CliHttpError("Remote tip moved since your last pull. Run frontpage pull and retry.", EXIT_CONFLICT, 409, "conflict");
128
+ }
129
+ const data = files.length
130
+ ? await v1Put(`/api/v1/sites/${encodeURIComponent(slug)}/files`, {
131
+ baseCommitSha: state.baseCommitSha,
132
+ expectedHeadSha: state.baseCommitSha,
133
+ message: message || undefined,
134
+ files,
135
+ })
136
+ : {
137
+ changed: false,
138
+ commitSha: state.baseCommitSha,
139
+ baseCommitSha: state.baseCommitSha,
140
+ filesChanged: [],
141
+ warnings: [],
142
+ };
120
143
  if (data.commitSha) {
121
144
  await writeState({ ...state, slug, baseCommitSha: data.commitSha }, cwd);
122
145
  }
@@ -32,7 +32,7 @@ export async function runSettings(args) {
32
32
  }
33
33
  const buildModel = takeFlag(work, "--build-model");
34
34
  if (buildModel !== undefined)
35
- body.buildModel = buildModel;
35
+ body.buildModel = buildModel === "default" ? null : buildModel;
36
36
  const brandName = takeFlag(work, "--brand-name");
37
37
  const brandLogo = takeFlag(work, "--brand-logo");
38
38
  if (work.includes("--brand-reset")) {
@@ -164,7 +164,7 @@ export async function runShop(args) {
164
164
  const group = rest[0];
165
165
  const base = `/api/v1/sites/${encodeURIComponent(slug)}/shop`;
166
166
  if (!group)
167
- return emitUsage("shop", USAGE);
167
+ return emitUsage("store", USAGE);
168
168
  if (group === "items") {
169
169
  const sub = rest[1];
170
170
  const work = rest.slice(2);
@@ -181,7 +181,7 @@ export async function runShop(args) {
181
181
  const suffix = qs.toString() ? `?${qs.toString()}` : "";
182
182
  const data = await v1Get(`${base}/items${suffix}`);
183
183
  if (isJson()) {
184
- emitOk("shop", data);
184
+ emitOk("store", data);
185
185
  return EXIT_OK;
186
186
  }
187
187
  const rows = data.items ?? [];
@@ -198,7 +198,7 @@ export async function runShop(args) {
198
198
  assertNoUnknownFlags(work, "store");
199
199
  const data = await v1Get(`${base}/items/${encodeURIComponent(itemId)}`);
200
200
  if (isJson()) {
201
- emitOk("shop", data);
201
+ emitOk("store", data);
202
202
  return EXIT_OK;
203
203
  }
204
204
  printItem(data.item);
@@ -271,7 +271,7 @@ export async function runShop(args) {
271
271
  }
272
272
  const data = await v1Post(`${base}/items`, body);
273
273
  if (isJson()) {
274
- emitOk("shop", data);
274
+ emitOk("store", data);
275
275
  return EXIT_OK;
276
276
  }
277
277
  printItem(data.item);
@@ -313,7 +313,7 @@ export async function runShop(args) {
313
313
  }
314
314
  const data = await v1Patch(`${base}/items/${encodeURIComponent(itemId)}`, body);
315
315
  if (isJson()) {
316
- emitOk("shop", data);
316
+ emitOk("store", data);
317
317
  return EXIT_OK;
318
318
  }
319
319
  printItem(data.item);
@@ -324,7 +324,7 @@ export async function runShop(args) {
324
324
  assertNoUnknownFlags(work, "store");
325
325
  const data = await v1Post(`${base}/items/${encodeURIComponent(itemId)}/archive`, {});
326
326
  if (isJson()) {
327
- emitOk("shop", data);
327
+ emitOk("store", data);
328
328
  return EXIT_OK;
329
329
  }
330
330
  printItem(data.item);
@@ -337,7 +337,7 @@ export async function runShop(args) {
337
337
  assertNoUnknownFlags(work, "store");
338
338
  const data = await v1Post(`${base}/items/${encodeURIComponent(itemId)}/activate`, { active: active ?? true });
339
339
  if (isJson()) {
340
- emitOk("shop", data);
340
+ emitOk("store", data);
341
341
  return EXIT_OK;
342
342
  }
343
343
  printItem(data.item);
@@ -349,7 +349,7 @@ export async function runShop(args) {
349
349
  assertNoUnknownFlags(work, "store");
350
350
  const data = await v1Put(`${base}/items/${encodeURIComponent(itemId)}/images`, { images: urls });
351
351
  if (isJson()) {
352
- emitOk("shop", data);
352
+ emitOk("store", data);
353
353
  return EXIT_OK;
354
354
  }
355
355
  printItem(data.item);
@@ -396,13 +396,13 @@ export async function runShop(args) {
396
396
  }
397
397
  const data = await v1Post(`${base}/items/${encodeURIComponent(itemId)}/stock`, body);
398
398
  if (isJson()) {
399
- emitOk("shop", data);
399
+ emitOk("store", data);
400
400
  return EXIT_OK;
401
401
  }
402
402
  printStock(data);
403
403
  return EXIT_OK;
404
404
  }
405
- return emitUsage("shop", USAGE);
405
+ return emitUsage("store", USAGE);
406
406
  }
407
407
  if (group === "variants") {
408
408
  const sub = rest[1];
@@ -429,7 +429,7 @@ export async function runShop(args) {
429
429
  active,
430
430
  });
431
431
  if (isJson()) {
432
- emitOk("shop", data);
432
+ emitOk("store", data);
433
433
  return EXIT_OK;
434
434
  }
435
435
  printItem(data.item);
@@ -460,7 +460,7 @@ export async function runShop(args) {
460
460
  }
461
461
  const data = await v1Patch(`${base}/items/${encodeURIComponent(itemId)}/variants/${encodeURIComponent(variantId)}`, body);
462
462
  if (isJson()) {
463
- emitOk("shop", data);
463
+ emitOk("store", data);
464
464
  return EXIT_OK;
465
465
  }
466
466
  printItem(data.item);
@@ -472,13 +472,13 @@ export async function runShop(args) {
472
472
  assertNoUnknownFlags(work, "store");
473
473
  const data = await v1Post(`${base}/items/${encodeURIComponent(itemId)}/variants/${encodeURIComponent(variantId)}/archive`, {});
474
474
  if (isJson()) {
475
- emitOk("shop", data);
475
+ emitOk("store", data);
476
476
  return EXIT_OK;
477
477
  }
478
478
  printItem(data.item);
479
479
  return EXIT_OK;
480
480
  }
481
- return emitUsage("shop", USAGE);
481
+ return emitUsage("store", USAGE);
482
482
  }
483
483
  if (group === "categories") {
484
484
  const sub = rest[1];
@@ -490,7 +490,7 @@ export async function runShop(args) {
490
490
  const qs = archived ? "?archived=1" : "";
491
491
  const data = await v1Get(`${base}/categories${qs}`);
492
492
  if (isJson()) {
493
- emitOk("shop", data);
493
+ emitOk("store", data);
494
494
  return EXIT_OK;
495
495
  }
496
496
  const rows = data.categories ?? [];
@@ -517,7 +517,7 @@ export async function runShop(args) {
517
517
  label,
518
518
  });
519
519
  if (isJson()) {
520
- emitOk("shop", data);
520
+ emitOk("store", data);
521
521
  return EXIT_OK;
522
522
  }
523
523
  console.log(`${data.category.slug} ${data.category.label}`);
@@ -532,7 +532,7 @@ export async function runShop(args) {
532
532
  }
533
533
  const data = await v1Patch(`${base}/categories/${encodeURIComponent(slug)}`, { label });
534
534
  if (isJson()) {
535
- emitOk("shop", data);
535
+ emitOk("store", data);
536
536
  return EXIT_OK;
537
537
  }
538
538
  console.log(`${data.category.slug} ${data.category.label}`);
@@ -543,13 +543,13 @@ export async function runShop(args) {
543
543
  assertNoUnknownFlags(work, "store");
544
544
  const data = await v1Post(`${base}/categories/${encodeURIComponent(slug)}/archive`, {});
545
545
  if (isJson()) {
546
- emitOk("shop", data);
546
+ emitOk("store", data);
547
547
  return EXIT_OK;
548
548
  }
549
549
  console.log(`${data.category.slug} archived`);
550
550
  return EXIT_OK;
551
551
  }
552
- return emitUsage("shop", USAGE);
552
+ return emitUsage("store", USAGE);
553
553
  }
554
554
  if (group === "settings") {
555
555
  const sub = rest[1] ?? "get";
@@ -558,7 +558,7 @@ export async function runShop(args) {
558
558
  assertNoUnknownFlags(work, "store");
559
559
  const data = await v1Get(`${base}/settings`);
560
560
  if (isJson()) {
561
- emitOk("shop", data);
561
+ emitOk("store", data);
562
562
  return EXIT_OK;
563
563
  }
564
564
  const s = data.settings ?? {};
@@ -614,7 +614,7 @@ export async function runShop(args) {
614
614
  freeShippingOverCents: freeCents,
615
615
  });
616
616
  if (isJson())
617
- emitOk("shop", data);
617
+ emitOk("store", data);
618
618
  else
619
619
  console.log("Fulfillment saved.");
620
620
  return EXIT_OK;
@@ -627,7 +627,7 @@ export async function runShop(args) {
627
627
  }
628
628
  const data = await v1Post(`${base}/settings/currency`, { currency });
629
629
  if (isJson())
630
- emitOk("shop", data);
630
+ emitOk("store", data);
631
631
  else
632
632
  console.log("Currency saved.");
633
633
  return EXIT_OK;
@@ -640,7 +640,7 @@ export async function runShop(args) {
640
640
  }
641
641
  const data = await v1Post(`${base}/settings/checkout`, { checkout });
642
642
  if (isJson())
643
- emitOk("shop", data);
643
+ emitOk("store", data);
644
644
  else
645
645
  console.log(checkout === "instant" ? "Checkout is Pay now." : "Checkout is add to cart.");
646
646
  return EXIT_OK;
@@ -654,12 +654,12 @@ export async function runShop(args) {
654
654
  cents,
655
655
  });
656
656
  if (isJson())
657
- emitOk("shop", data);
657
+ emitOk("store", data);
658
658
  else
659
659
  console.log(off ? "Service fee off." : "Service fee saved.");
660
660
  return EXIT_OK;
661
661
  }
662
- return emitUsage("shop", USAGE);
662
+ return emitUsage("store", USAGE);
663
663
  }
664
664
  if (group === "connect") {
665
665
  const work = rest.slice(1);
@@ -668,7 +668,7 @@ export async function runShop(args) {
668
668
  const data = await v1Post(`${base}/connect`, { action: "connect" });
669
669
  const already = data.alreadyConnected === true;
670
670
  if (isJson()) {
671
- emitOk("shop", data);
671
+ emitOk("store", data);
672
672
  }
673
673
  else {
674
674
  const url = resolveOpenUrl(data.connectUrl);
@@ -688,7 +688,7 @@ export async function runShop(args) {
688
688
  assertNoUnknownFlags(work, "store");
689
689
  const data = await v1Get(`${base}/connect`);
690
690
  if (isJson()) {
691
- emitOk("shop", data);
691
+ emitOk("store", data);
692
692
  return EXIT_OK;
693
693
  }
694
694
  const connection = (data.connection ?? {});
@@ -707,10 +707,10 @@ export async function runShop(args) {
707
707
  assertNoUnknownFlags(work, "store");
708
708
  const refuse = requireYesOrTty("disconnect the store (shared Stripe account with payments)");
709
709
  if (refuse)
710
- return emitUsage("shop", refuse);
710
+ return emitUsage("store", refuse);
711
711
  const data = await v1Post(`${base}/connect`, { action: "disconnect" });
712
712
  if (isJson()) {
713
- emitOk("shop", data);
713
+ emitOk("store", data);
714
714
  return EXIT_OK;
715
715
  }
716
716
  console.log(typeof data.message === "string" ? data.message : "Disconnected.");
@@ -721,7 +721,7 @@ export async function runShop(args) {
721
721
  assertNoUnknownFlags(work, "store");
722
722
  const data = await v1Post(`${base}/import`, {});
723
723
  if (isJson()) {
724
- emitOk("shop", data);
724
+ emitOk("store", data);
725
725
  return EXIT_OK;
726
726
  }
727
727
  const imported = data.imported ?? [];
@@ -748,7 +748,7 @@ export async function runShop(args) {
748
748
  assertNoUnknownFlags(work, "store");
749
749
  const data = await v1Get(`${base}/${noun}`);
750
750
  if (isJson()) {
751
- emitOk("shop", data);
751
+ emitOk("store", data);
752
752
  return EXIT_OK;
753
753
  }
754
754
  const rows = data[noun] ?? [];
@@ -766,17 +766,17 @@ export async function runShop(args) {
766
766
  assertNoUnknownFlags(work, "store");
767
767
  const data = await v1Get(`${base}/${noun}/${encodeURIComponent(id)}`);
768
768
  if (isJson()) {
769
- emitOk("shop", data);
769
+ emitOk("store", data);
770
770
  return EXIT_OK;
771
771
  }
772
772
  console.log(JSON.stringify(data[noun === "orders" ? "order" : "payment"], null, 2));
773
773
  return EXIT_OK;
774
774
  }
775
- return emitUsage("shop", USAGE);
775
+ return emitUsage("store", USAGE);
776
776
  }
777
- return emitUsage("shop", USAGE);
777
+ return emitUsage("store", USAGE);
778
778
  }
779
779
  catch (err) {
780
- return emitErr("shop", err);
780
+ return emitErr("store", err);
781
781
  }
782
782
  }
@@ -34,17 +34,18 @@ export const MODULE_COMMANDS = [
34
34
  {
35
35
  key: "callbar",
36
36
  name: "Call bar",
37
- summary: "Phone / SMS bar. Typed get/set for phone, sms, and label.",
37
+ summary: "Phone / SMS bar. Typed get/set for phone, sms, call, and label.",
38
38
  genericActions: GENERIC_ACTIONS,
39
39
  extraActions: [
40
40
  "get",
41
- "set --phone +1… [--sms yes|no] [--label \"Call\"]",
41
+ "set --phone +1… [--sms yes|no] [--call yes|no] [--label \"Call\"]",
42
42
  ],
43
43
  notYet: [],
44
44
  examples: [
45
45
  "frontpage modules callbar",
46
46
  "frontpage modules callbar get",
47
47
  "frontpage modules callbar set --phone +14085551234 --sms no",
48
+ "frontpage modules callbar set --call no --sms yes",
48
49
  "frontpage modules callbar docs",
49
50
  `frontpage modules config callbar --config '{"phone":"+1…"}'`,
50
51
  ],
@@ -29,6 +29,7 @@ function printLine(label, value) {
29
29
  }
30
30
  function printCallbar(config) {
31
31
  printLine("phone", dash(config.phone));
32
+ printLine("call", yesNo(config.call));
32
33
  printLine("sms", yesNo(config.sms));
33
34
  printLine("label", dash(config.label));
34
35
  }
@@ -111,6 +112,7 @@ export async function runTypedConfig(key, slug, action, flags) {
111
112
  const instanceId = takeFlag(work, "--instance");
112
113
  const phone = takeFlag(work, "--phone");
113
114
  const sms = takeFlag(work, "--sms");
115
+ const call = takeFlag(work, "--call");
114
116
  const label = takeFlag(work, "--label");
115
117
  const notifyEmail = takeFlag(work, "--notify-email");
116
118
  const hours = takeFlag(work, "--hours");
@@ -131,6 +133,8 @@ export async function runTypedConfig(key, slug, action, flags) {
131
133
  body.phone = phone;
132
134
  if (sms !== undefined)
133
135
  body.sms = sms;
136
+ if (call !== undefined)
137
+ body.call = call;
134
138
  if (label !== undefined)
135
139
  body.label = label;
136
140
  if (notifyEmail !== undefined)