@myna-sh/cli 0.1.3 → 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.
package/dist/main.js CHANGED
@@ -223,7 +223,9 @@ function renderField(field, depth) {
223
223
  const optional = field.required ? "" : "?";
224
224
  const doc = field.description ? `${indent}/** ${escapeComment(field.description)} */
225
225
  ` : "";
226
- return `${doc}${indent}${safeKey(field.key)}${optional}: ${fieldType(field, depth)};`;
226
+ const base = fieldType(field, depth);
227
+ const type = field.localized ? `{ [locale: string]: ${base} }` : base;
228
+ return `${doc}${indent}${safeKey(field.key)}${optional}: ${type};`;
227
229
  }
228
230
  function fieldType(field, depth) {
229
231
  switch (field.type) {
@@ -250,6 +252,21 @@ function fieldType(field, depth) {
250
252
  return renderObject(field, depth);
251
253
  case "list":
252
254
  return `${listItemType(field.item, depth)}[]`;
255
+ case "richText":
256
+ return "RichTextDoc";
257
+ case "blocks": {
258
+ const inner = " ".repeat(depth + 1);
259
+ const options = field.blocks.map((b) => {
260
+ const lines = b.fields.map((f) => renderField(f, depth + 2));
261
+ return `{ type: ${JSON.stringify(b.key)}; fields: {
262
+ ${lines.join("\n")}
263
+ ${inner}} }`;
264
+ });
265
+ return `Array<
266
+ ${inner}${options.join(`
267
+ ${inner}| `)}
268
+ ${" ".repeat(depth)}>`;
269
+ }
253
270
  default:
254
271
  return "unknown";
255
272
  }
@@ -291,7 +308,12 @@ ${closingIndent}}`;
291
308
  return "unknown";
292
309
  }
293
310
  }
294
- var JSON_VALUE = "export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };";
311
+ var JSON_VALUE = [
312
+ "export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };",
313
+ "",
314
+ "/** Portable rich-text document tree (never rendered HTML). */",
315
+ 'export interface RichTextDoc { type: "doc"; content: Array<{ type: string; [key: string]: unknown }>; }'
316
+ ].join("\n");
295
317
  function generateTypesModule(collections) {
296
318
  return `${JSON_VALUE}
297
319
 
@@ -368,7 +390,12 @@ var ManagementClient = class {
368
390
  create: (organization, body) => this.mutate("POST", `/organizations/${enc(organization)}/projects`, body),
369
391
  get: (project, signal) => this.get(`/projects/${enc(project)}`, void 0, signal),
370
392
  update: (project, body) => this.mutate("PATCH", `/projects/${enc(project)}`, body),
371
- archive: (project) => this.mutate("POST", `/projects/${enc(project)}/archive`)
393
+ archive: (project) => this.mutate("POST", `/projects/${enc(project)}/archive`),
394
+ /** Full project export: schemas, entries (draft + published), asset metadata. */
395
+ export: (project, signal) => this.get(`/projects/${enc(project)}/export`, void 0, signal),
396
+ views: (project, signal) => this.get(`/projects/${enc(project)}/views`, void 0, signal),
397
+ createView: (project, body) => this.mutate("POST", `/projects/${enc(project)}/views`, body),
398
+ deleteView: (project, view) => this.mutate("DELETE", `/projects/${enc(project)}/views/${enc(view)}`)
372
399
  };
373
400
  // --- Schema ---------------------------------------------------------------
374
401
  schema = {
@@ -380,6 +407,13 @@ var ManagementClient = class {
380
407
  collections,
381
408
  allowDestructive: opts.allowDestructive ?? false,
382
409
  changeSummary: opts.changeSummary
410
+ }),
411
+ // Environments as projects
412
+ drift: (project, against, signal) => this.get(`/projects/${enc(project)}/schema/drift`, { against }, signal),
413
+ promote: (project, fromProject, opts = {}) => this.mutate("POST", `/projects/${enc(project)}/schema/promote`, {
414
+ fromProject,
415
+ allowDestructive: opts.allowDestructive ?? false,
416
+ confirm: true
383
417
  })
384
418
  };
385
419
  // --- Entries & revisions --------------------------------------------------
@@ -394,6 +428,12 @@ var ManagementClient = class {
394
428
  delete: (project, entry, changeSetId) => this.mutate("DELETE", `/projects/${enc(project)}/entries/${enc(entry)}`, void 0, { changeSetId }),
395
429
  unpublish: (project, entry, changeSetId) => this.mutate("POST", `/projects/${enc(project)}/entries/${enc(entry)}/unpublish`, void 0, { changeSetId }),
396
430
  restore: (project, entry) => this.mutate("POST", `/projects/${enc(project)}/entries/${enc(entry)}/restore`),
431
+ duplicate: (project, entry, changeSetId) => this.mutate("POST", `/projects/${enc(project)}/entries/${enc(entry)}/duplicate`, {
432
+ changeSetId
433
+ }),
434
+ bulk: (project, body) => this.mutate("POST", `/projects/${enc(project)}/entries/bulk`, body),
435
+ import: (project, body) => this.mutate("POST", `/projects/${enc(project)}/entries/import`, body),
436
+ references: (project, entry, signal) => this.get(`/projects/${enc(project)}/entries/${enc(entry)}/references`, void 0, signal),
397
437
  revisions: (project, entry, signal) => this.get(`/projects/${enc(project)}/entries/${enc(entry)}/revisions`, void 0, signal),
398
438
  revision: (project, entry, revision, signal) => this.get(`/projects/${enc(project)}/entries/${enc(entry)}/revisions/${enc(revision)}`, void 0, signal),
399
439
  restoreRevision: (project, entry, revision) => this.mutate(
@@ -409,7 +449,48 @@ var ManagementClient = class {
409
449
  update: (project, changeSet, body) => this.mutate("PATCH", `/projects/${enc(project)}/change-sets/${enc(changeSet)}`, body),
410
450
  validate: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/validate`),
411
451
  publish: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/publish`, { confirm: true }),
412
- close: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/close`)
452
+ close: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/close`),
453
+ // Scheduled publishing
454
+ schedule: (project, changeSet, publishAt) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/schedule`, {
455
+ publishAt
456
+ }),
457
+ cancelSchedule: (project, changeSet) => this.mutate("DELETE", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/schedule`),
458
+ // Reviews & approvals
459
+ reviews: (project, changeSet, signal) => this.get(
460
+ `/projects/${enc(project)}/change-sets/${enc(changeSet)}/reviews`,
461
+ void 0,
462
+ signal
463
+ ),
464
+ requestReview: (project, changeSet, body) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/reviews`, body),
465
+ removeReviewer: (project, changeSet, reviewer) => this.mutate(
466
+ "DELETE",
467
+ `/projects/${enc(project)}/change-sets/${enc(changeSet)}/reviews/${enc(reviewer)}`
468
+ ),
469
+ approve: (project, changeSet, body = {}) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/approve`, body),
470
+ requestChanges: (project, changeSet, body = {}) => this.mutate(
471
+ "POST",
472
+ `/projects/${enc(project)}/change-sets/${enc(changeSet)}/request-changes`,
473
+ body
474
+ ),
475
+ // Comments
476
+ comments: (project, changeSet, signal) => this.get(
477
+ `/projects/${enc(project)}/change-sets/${enc(changeSet)}/comments`,
478
+ void 0,
479
+ signal
480
+ ),
481
+ comment: (project, changeSet, body) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/comments`, body),
482
+ resolveComment: (project, changeSet, comment, resolved = true) => this.mutate(
483
+ "PATCH",
484
+ `/projects/${enc(project)}/change-sets/${enc(changeSet)}/comments/${enc(comment)}`,
485
+ { resolved }
486
+ ),
487
+ // Automated checks
488
+ checks: (project, changeSet, signal) => this.get(
489
+ `/projects/${enc(project)}/change-sets/${enc(changeSet)}/checks`,
490
+ void 0,
491
+ signal
492
+ ),
493
+ runChecks: (project, changeSet) => this.mutate("POST", `/projects/${enc(project)}/change-sets/${enc(changeSet)}/checks/run`)
413
494
  };
414
495
  // --- Previews -------------------------------------------------------------
415
496
  previews = {
@@ -427,6 +508,7 @@ var ManagementClient = class {
427
508
  get: (project, asset, signal) => this.get(`/projects/${enc(project)}/assets/${enc(asset)}`, void 0, signal),
428
509
  usage: (project, asset, signal) => this.get(`/projects/${enc(project)}/assets/${enc(asset)}`, { usage: "true" }, signal),
429
510
  update: (project, asset, body) => this.mutate("PATCH", `/projects/${enc(project)}/assets/${enc(asset)}`, body),
511
+ replace: (project, asset, body) => this.mutate("POST", `/projects/${enc(project)}/assets/${enc(asset)}/replace`, body),
430
512
  delete: (project, asset) => this.mutate("DELETE", `/projects/${enc(project)}/assets/${enc(asset)}`),
431
513
  /** Full presigned upload flow: create → PUT bytes → complete. */
432
514
  upload: (project, input, meta = {}) => this.uploadAsset(project, input, meta)
@@ -1407,6 +1489,35 @@ function registerSchema(program) {
1407
1489
  emit(result, () => renderDiff(result));
1408
1490
  })
1409
1491
  );
1492
+ schema.command("drift").description("Show schema drift between this project and another (e.g. staging)").requiredOption("--against <project>", "project id or slug to compare with").action(
1493
+ handle(async (ctx, _args, opts) => {
1494
+ const project = ctx.requireProject();
1495
+ const result = await ctx.management().schema.drift(project, opts.against);
1496
+ emit(result, () => {
1497
+ if (result.inSync) diag("Schemas are in sync.");
1498
+ else {
1499
+ diag(`${result.ops.length} op(s) of drift (${result.classification}):`);
1500
+ for (const op of result.ops) {
1501
+ process.stderr.write(` ${op.kind} ${op.collection}${op.field ? `.${op.field}` : ""} \u2014 ${op.detail}
1502
+ `);
1503
+ }
1504
+ }
1505
+ });
1506
+ if (!result.inSync) process.exitCode = 1;
1507
+ })
1508
+ );
1509
+ schema.command("promote").description("Promote another project's deployed schemas into this project").requiredOption("--from <project>", "source project id or slug").option("--allow-destructive", "permit destructive schema changes", false).action(
1510
+ handle(async (ctx, _args, opts) => {
1511
+ const project = ctx.requireProject();
1512
+ const result = await ctx.management().schema.promote(project, opts.from, {
1513
+ allowDestructive: Boolean(opts.allowDestructive)
1514
+ });
1515
+ emit(result, () => {
1516
+ renderDiff(result.diff);
1517
+ diag(result.applied ? `Promoted ${result.versions.length} version(s).` : "Nothing to promote.");
1518
+ });
1519
+ })
1520
+ );
1410
1521
  schema.command("push").description("Apply local schemas, creating immutable versions").option("--schema-dir <dir>", "schema directory").option("--allow-destructive", "permit destructive schema changes", false).option("--summary <text>", "change summary").action(
1411
1522
  handle(async (ctx, _args, opts) => {
1412
1523
  const project = ctx.requireProject();
@@ -1480,6 +1591,7 @@ function renderDiff(diff) {
1480
1591
  }
1481
1592
 
1482
1593
  // src/commands/entries.ts
1594
+ import { readFile as readFile2 } from "fs/promises";
1483
1595
  function registerEntries(program) {
1484
1596
  const entries = program.command("entries").description("Create, read, update, and manage entries");
1485
1597
  entries.command("list").description("List entries in a collection").argument("<collection>", "collection key").option("--status <status>", "filter by status").option("--limit <n>", "page size", "25").option("--cursor <cursor>", "pagination cursor").action(
@@ -1578,6 +1690,81 @@ function registerEntries(program) {
1578
1690
  );
1579
1691
  })
1580
1692
  );
1693
+ entries.command("duplicate").description("Duplicate an entry into a new draft").argument("<ref>", "collection/slug or entry id").option("--change-set <id>", "stage on an explicit change set").action(
1694
+ handle(async (ctx, args, opts) => {
1695
+ const project = ctx.requireProject();
1696
+ const id = await resolveEntryId(ctx.management(), project, args[0]);
1697
+ const entry = await ctx.management().entries.duplicate(project, id, opts.changeSet);
1698
+ emit(entry, () => diag(`Duplicated into ${entry.id} (${entry.slug ?? "no slug"}).`));
1699
+ })
1700
+ );
1701
+ entries.command("bulk").description("Stage a delete or unpublish for many entries at once").requiredOption("--action <action>", "delete or unpublish").requiredOption("--ids <ids>", "comma-separated entry ids").option("--change-set <id>", "stage on an explicit change set").action(
1702
+ handle(async (ctx, _args, opts) => {
1703
+ const action = opts.action;
1704
+ if (action !== "delete" && action !== "unpublish") {
1705
+ throw new UsageError("--action must be delete or unpublish.");
1706
+ }
1707
+ const project = ctx.requireProject();
1708
+ const result = await ctx.management().entries.bulk(project, {
1709
+ action,
1710
+ entryIds: opts.ids.split(",").map((s) => s.trim()).filter(Boolean),
1711
+ changeSetId: opts.changeSet
1712
+ });
1713
+ emit(
1714
+ result,
1715
+ () => table(result.results, [
1716
+ { header: "ENTRY", value: (r) => r.entryId },
1717
+ { header: "OK", value: (r) => r.ok ? "yes" : "no" },
1718
+ { header: "CHANGE SET", value: (r) => r.changeSetId ?? "\u2014" },
1719
+ { header: "ERROR", value: (r) => r.error ?? "" }
1720
+ ])
1721
+ );
1722
+ if (result.results.some((r) => !r.ok)) process.exitCode = 1;
1723
+ })
1724
+ );
1725
+ entries.command("import").description("Import entries from a JSON file, with dry-run validation").argument("<file>", 'JSON file: { "collection": "posts", "entries": [{ "slug"?, "data" }] }').option("--dry-run", "validate without creating anything", false).action(
1726
+ handle(async (ctx, args, opts) => {
1727
+ const project = ctx.requireProject();
1728
+ const raw = JSON.parse(await readFile2(args[0], "utf8"));
1729
+ const result = await ctx.management().entries.import(project, {
1730
+ collection: raw.collection,
1731
+ entries: raw.entries,
1732
+ dryRun: Boolean(opts.dryRun)
1733
+ });
1734
+ emit(result, () => {
1735
+ diag(
1736
+ result.dryRun ? `Dry run: ${result.valid ? "all rows valid" : "validation failed"}.` : `Imported ${result.results.filter((r) => r.ok).length}/${result.results.length} into change set ${result.changeSetId}.`
1737
+ );
1738
+ for (const row of result.results.filter((r) => !r.ok)) {
1739
+ for (const e of row.errors ?? []) {
1740
+ process.stderr.write(` row ${row.index}${row.slug ? ` (${row.slug})` : ""}: ${e.path} ${e.message}
1741
+ `);
1742
+ }
1743
+ }
1744
+ });
1745
+ if (!result.valid) process.exitCode = 1;
1746
+ })
1747
+ );
1748
+ entries.command("references").description("Show which entries reference this one and what it references").argument("<ref>", "collection/slug or entry id").action(
1749
+ handle(async (ctx, args) => {
1750
+ const project = ctx.requireProject();
1751
+ const id = await resolveEntryId(ctx.management(), project, args[0]);
1752
+ const refs = await ctx.management().entries.references(project, id);
1753
+ emit(refs, () => {
1754
+ diag(`Referenced by ${refs.referencedBy.length} entr(y/ies):`);
1755
+ table(refs.referencedBy, [
1756
+ { header: "ENTRY", value: (r) => `${r.collectionKey}/${r.slug ?? r.id}` },
1757
+ { header: "IN DRAFT", value: (r) => r.inDraft ? "yes" : "no" },
1758
+ { header: "IN PUBLISHED", value: (r) => r.inPublished ? "yes" : "no" }
1759
+ ]);
1760
+ diag(`References ${refs.references.length} entr(y/ies):`);
1761
+ table(refs.references, [
1762
+ { header: "ENTRY", value: (r) => r.collectionKey ? `${r.collectionKey}/${r.slug ?? r.id}` : r.id },
1763
+ { header: "STATE", value: (r) => !r.exists ? "missing" : r.published ? "published" : "unpublished" }
1764
+ ]);
1765
+ });
1766
+ })
1767
+ );
1581
1768
  entries.command("restore").description("Restore a historical revision into a new draft").argument("<ref>", "collection/slug or entry id").requiredOption("--revision <id>", "revision id to restore").action(
1582
1769
  handle(async (ctx, args, opts) => {
1583
1770
  const project = ctx.requireProject();
@@ -1682,6 +1869,107 @@ function registerChanges(program) {
1682
1869
  emit(cs, () => diag(`Closed ${cs.id}.`));
1683
1870
  })
1684
1871
  );
1872
+ changes.command("schedule").description("Schedule, reschedule, or cancel a change set's publish").argument("<id>", "change set id").option("--at <datetime>", "ISO 8601 datetime to publish at").option("--cancel", "cancel the scheduled publish", false).action(
1873
+ handle(async (ctx, args, opts) => {
1874
+ const project = ctx.requireProject();
1875
+ if (Boolean(opts.at) === Boolean(opts.cancel)) {
1876
+ throw new UsageError("Provide exactly one of --at <datetime> or --cancel.");
1877
+ }
1878
+ const client = ctx.management();
1879
+ const cs = opts.cancel ? await client.changeSets.cancelSchedule(project, args[0]) : await client.changeSets.schedule(project, args[0], new Date(opts.at).toISOString());
1880
+ emit(
1881
+ cs,
1882
+ () => diag(cs.scheduledAt ? `Scheduled to publish at ${cs.scheduledAt}.` : "Schedule cancelled.")
1883
+ );
1884
+ })
1885
+ );
1886
+ changes.command("reviews").description("List reviews and approvals for a change set").argument("<id>", "change set id").action(
1887
+ handle(async (ctx, args) => {
1888
+ const project = ctx.requireProject();
1889
+ const reviews = await ctx.management().changeSets.reviews(project, args[0]);
1890
+ emit(
1891
+ reviews,
1892
+ () => table(reviews, [
1893
+ { header: "REVIEWER", value: (r) => `${r.reviewerType}:${r.reviewerId}` },
1894
+ { header: "STATUS", value: (r) => r.status },
1895
+ { header: "STALE", value: (r) => r.stale ? "yes" : "no" },
1896
+ { header: "DECIDED", value: (r) => r.decidedAt ?? "\u2014" }
1897
+ ])
1898
+ );
1899
+ })
1900
+ );
1901
+ changes.command("request-review").description("Assign a reviewer to a change set").argument("<id>", "change set id").requiredOption("--reviewer <id>", "reviewer id (user, api key, or agent id)").option("--reviewer-type <type>", "reviewer actor type", "user").action(
1902
+ handle(async (ctx, args, opts) => {
1903
+ const project = ctx.requireProject();
1904
+ const reviews = await ctx.management().changeSets.requestReview(project, args[0], {
1905
+ reviewerType: opts.reviewerType,
1906
+ reviewerId: opts.reviewer
1907
+ });
1908
+ emit(reviews, () => diag(`Requested review from ${opts.reviewer}.`));
1909
+ })
1910
+ );
1911
+ changes.command("approve").description("Approve a change set").argument("<id>", "change set id").option("--comment <text>", "optional review comment").action(
1912
+ handle(async (ctx, args, opts) => {
1913
+ const project = ctx.requireProject();
1914
+ const reviews = await ctx.management().changeSets.approve(project, args[0], {
1915
+ comment: opts.comment
1916
+ });
1917
+ emit(reviews, () => diag("Approved."));
1918
+ })
1919
+ );
1920
+ changes.command("request-changes").description("Request changes on a change set").argument("<id>", "change set id").option("--comment <text>", "optional review comment").action(
1921
+ handle(async (ctx, args, opts) => {
1922
+ const project = ctx.requireProject();
1923
+ const reviews = await ctx.management().changeSets.requestChanges(project, args[0], {
1924
+ comment: opts.comment
1925
+ });
1926
+ emit(reviews, () => diag("Requested changes."));
1927
+ })
1928
+ );
1929
+ changes.command("comments").description("List comments on a change set").argument("<id>", "change set id").action(
1930
+ handle(async (ctx, args) => {
1931
+ const project = ctx.requireProject();
1932
+ const comments = await ctx.management().changeSets.comments(project, args[0]);
1933
+ emit(
1934
+ comments,
1935
+ () => table(comments, [
1936
+ { header: "AUTHOR", value: (c) => `${c.authorType}:${c.authorId ?? "\u2014"}` },
1937
+ { header: "ANCHOR", value: (c) => c.resourceId ? `${c.resourceId}${c.fieldPath ? `#${c.fieldPath}` : ""}` : "\u2014" },
1938
+ { header: "RESOLVED", value: (c) => c.resolvedAt ? "yes" : "no" },
1939
+ { header: "BODY", value: (c) => c.body.length > 60 ? `${c.body.slice(0, 57)}...` : c.body }
1940
+ ])
1941
+ );
1942
+ })
1943
+ );
1944
+ changes.command("comment").description("Comment on a change set (optionally anchored to a field)").argument("<id>", "change set id").argument("<body>", "comment body").option("--entry <id>", "anchor to an entry in the change set").option("--field <path>", "anchor to a field path, e.g. fields.title").action(
1945
+ handle(async (ctx, args, opts) => {
1946
+ const project = ctx.requireProject();
1947
+ const comment = await ctx.management().changeSets.comment(project, args[0], {
1948
+ body: args[1],
1949
+ resourceType: opts.entry ? "entry" : void 0,
1950
+ resourceId: opts.entry,
1951
+ fieldPath: opts.field
1952
+ });
1953
+ emit(comment, () => diag(`Commented ${comment.id}.`));
1954
+ })
1955
+ );
1956
+ changes.command("checks").description("Show the latest check run for a change set").argument("<id>", "change set id").option("--run", "run checks before showing results", false).action(
1957
+ handle(async (ctx, args, opts) => {
1958
+ const project = ctx.requireProject();
1959
+ const client = ctx.management();
1960
+ const checks = opts.run ? (await client.changeSets.runChecks(project, args[0])).checks : await client.changeSets.checks(project, args[0]);
1961
+ emit(
1962
+ checks,
1963
+ () => table(checks, [
1964
+ { header: "CHECK", value: (c) => c.name },
1965
+ { header: "STATUS", value: (c) => c.status },
1966
+ { header: "STALE", value: (c) => c.stale ? "yes" : "no" },
1967
+ { header: "ISSUES", value: (c) => String(c.details?.length ?? 0) }
1968
+ ])
1969
+ );
1970
+ if (checks.some((c) => c.status === "failed")) process.exitCode = 1;
1971
+ })
1972
+ );
1685
1973
  }
1686
1974
 
1687
1975
  // src/commands/previews.ts
@@ -1893,6 +2181,46 @@ function registerProjects(program) {
1893
2181
  );
1894
2182
  })
1895
2183
  );
2184
+ projects.command("export").description("Export the full project (schemas, entries, asset metadata) as JSON").argument("[project]", "project id or slug").action(
2185
+ handle(async (ctx, args) => {
2186
+ const ref = args[0] ?? ctx.requireProject();
2187
+ const data = await ctx.management().projects.export(ref);
2188
+ process.stdout.write(`${JSON.stringify(data, null, 2)}
2189
+ `);
2190
+ })
2191
+ );
2192
+ projects.command("update").description("Update project settings").argument("[project]", "project id or slug").option("--name <name>", "project name").option("--timezone <timezone>", "IANA timezone").option("--public-api <state>", "public API state (on|off)").option("--default-preview-template <template>", "default preview URL template").option("--clear-default-preview-template", "remove the default preview URL template").option("--origins <urls>", "comma-separated allowed origins").option("--clear-origins", "remove all allowed origins").action(
2193
+ handle(async (ctx, args, opts) => {
2194
+ const ref = args[0] ?? ctx.requireProject();
2195
+ const patch = {};
2196
+ if (opts.name !== void 0) patch.name = opts.name;
2197
+ if (opts.timezone !== void 0) patch.timezone = opts.timezone;
2198
+ if (opts.publicApi !== void 0) {
2199
+ const state = String(opts.publicApi).toLowerCase();
2200
+ if (!["on", "off", "true", "false"].includes(state)) {
2201
+ throw new UsageError("--public-api must be on or off.");
2202
+ }
2203
+ patch.publicApiEnabled = state === "on" || state === "true";
2204
+ }
2205
+ if (opts.defaultPreviewTemplate !== void 0 && opts.clearDefaultPreviewTemplate) {
2206
+ throw new UsageError("Use either --default-preview-template or --clear-default-preview-template, not both.");
2207
+ }
2208
+ if (opts.defaultPreviewTemplate !== void 0) {
2209
+ patch.defaultPreviewTemplate = opts.defaultPreviewTemplate;
2210
+ }
2211
+ if (opts.clearDefaultPreviewTemplate) patch.defaultPreviewTemplate = null;
2212
+ if (opts.origins !== void 0 && opts.clearOrigins) {
2213
+ throw new UsageError("Use either --origins or --clear-origins, not both.");
2214
+ }
2215
+ if (opts.origins !== void 0) patch.origins = csv(opts.origins);
2216
+ if (opts.clearOrigins) patch.origins = [];
2217
+ if (Object.keys(patch).length === 0) {
2218
+ throw new UsageError("Specify at least one project setting to update.");
2219
+ }
2220
+ const project = await ctx.management().projects.update(ref, patch);
2221
+ emit(project, () => diag(`Updated ${project.slug}.`));
2222
+ })
2223
+ );
1896
2224
  projects.command("archive").description("Archive a project").argument("[project]", "project id or slug").action(
1897
2225
  handle(async (ctx, args) => {
1898
2226
  const ref = args[0] ?? ctx.requireProject();
@@ -2135,7 +2463,7 @@ function registerBilling(program) {
2135
2463
  }
2136
2464
 
2137
2465
  // src/main.ts
2138
- var VERSION = "0.1.3";
2466
+ var VERSION = "0.2.0";
2139
2467
  var GLOBAL_VALUE_FLAGS = /* @__PURE__ */ new Set(["--project", "--organization", "--token", "--api-url"]);
2140
2468
  var GLOBAL_BOOL_FLAGS = /* @__PURE__ */ new Set(["--json", "--no-interactive", "--interactive"]);
2141
2469
  function normalizeGlobals(argv) {