andesine 0.1.0 → 0.4.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.
Files changed (81) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +27 -8
  3. package/dist/cli.js +1611 -121
  4. package/dist/cli.js.map +1 -1
  5. package/dist/config.schema.json +20 -3
  6. package/dist/templates/pages/.env.example +5 -0
  7. package/dist/templates/pages/AGENTS.md +79 -0
  8. package/dist/templates/pages/LICENSE +21 -0
  9. package/dist/templates/pages/README.md +72 -0
  10. package/dist/templates/pages/andesine.config.ts +39 -0
  11. package/dist/templates/pages/astro.config.ts +23 -0
  12. package/dist/templates/pages/gitignore +13 -0
  13. package/dist/templates/pages/package.template.json +45 -0
  14. package/dist/templates/pages/src/api/openapi.yaml +246 -0
  15. package/dist/templates/pages/src/assets/andesine.svg +1 -0
  16. package/dist/templates/pages/src/assets/andesine.ts +6 -0
  17. package/dist/templates/pages/src/assets/favicon.svg +13 -0
  18. package/dist/templates/pages/src/assets/social-dots.svg +14 -0
  19. package/dist/templates/pages/src/components/content/callout.astro +26 -0
  20. package/dist/templates/pages/src/components/content/card-grid.astro +1 -0
  21. package/dist/templates/pages/src/components/content/card.astro +31 -0
  22. package/dist/templates/pages/src/components/content/code-block.astro +34 -0
  23. package/dist/templates/pages/src/components/content/code-group.astro +13 -0
  24. package/dist/templates/pages/src/components/content/copy-button.tsx +53 -0
  25. package/dist/templates/pages/src/components/content/disclosure.astro +26 -0
  26. package/dist/templates/pages/src/components/content/figure.astro +10 -0
  27. package/dist/templates/pages/src/components/content/file-tree.astro +47 -0
  28. package/dist/templates/pages/src/components/content/index.ts +30 -0
  29. package/dist/templates/pages/src/components/content/steps.astro +16 -0
  30. package/dist/templates/pages/src/components/content/tab.astro +7 -0
  31. package/dist/templates/pages/src/components/content/tabs.astro +15 -0
  32. package/dist/templates/pages/src/components/content/tabs.tsx +99 -0
  33. package/dist/templates/pages/src/components/effects/page-scroll-shadow.tsx +27 -0
  34. package/dist/templates/pages/src/components/effects/page-scrollbar.tsx +35 -0
  35. package/dist/templates/pages/src/components/layout/call-to-action.tsx +28 -0
  36. package/dist/templates/pages/src/components/layout/footer.astro +44 -0
  37. package/dist/templates/pages/src/components/layout/group-label.tsx +34 -0
  38. package/dist/templates/pages/src/components/layout/header.astro +53 -0
  39. package/dist/templates/pages/src/components/layout/mobile-navigation.tsx +51 -0
  40. package/dist/templates/pages/src/components/layout/navigation-links.tsx +57 -0
  41. package/dist/templates/pages/src/components/layout/navigation-tree.tsx +138 -0
  42. package/dist/templates/pages/src/components/layout/outline.tsx +162 -0
  43. package/dist/templates/pages/src/components/layout/page-actions.tsx +87 -0
  44. package/dist/templates/pages/src/components/layout/powered-by.tsx +29 -0
  45. package/dist/templates/pages/src/components/layout/section-tabs.tsx +83 -0
  46. package/dist/templates/pages/src/components/layout/sidebar.tsx +65 -0
  47. package/dist/templates/pages/src/components/primitives/button.tsx +121 -0
  48. package/dist/templates/pages/src/components/primitives/card.tsx +25 -0
  49. package/dist/templates/pages/src/components/primitives/dialog.tsx +47 -0
  50. package/dist/templates/pages/src/components/primitives/input.tsx +22 -0
  51. package/dist/templates/pages/src/components/primitives/logo.tsx +41 -0
  52. package/dist/templates/pages/src/components/primitives/menu.tsx +121 -0
  53. package/dist/templates/pages/src/components/primitives/scroll-area.tsx +103 -0
  54. package/dist/templates/pages/src/components/primitives/shortcut.tsx +60 -0
  55. package/dist/templates/pages/src/components/primitives/tooltip.tsx +39 -0
  56. package/dist/templates/pages/src/components/reference/facts.astro +27 -0
  57. package/dist/templates/pages/src/components/reference/fields.astro +102 -0
  58. package/dist/templates/pages/src/components/reference/method-badge.astro +18 -0
  59. package/dist/templates/pages/src/components/reference/methods.ts +17 -0
  60. package/dist/templates/pages/src/components/reference/operation.astro +252 -0
  61. package/dist/templates/pages/src/components/reference/styles.ts +14 -0
  62. package/dist/templates/pages/src/components/search/highlighted-text.tsx +22 -0
  63. package/dist/templates/pages/src/components/search/search-answer.tsx +123 -0
  64. package/dist/templates/pages/src/components/search/search-palette.tsx +174 -0
  65. package/dist/templates/pages/src/components/search/search-results.tsx +229 -0
  66. package/dist/templates/pages/src/components/social/card.ts +77 -0
  67. package/dist/templates/pages/src/content/docs/customization/index.mdx +11 -0
  68. package/dist/templates/pages/src/content/docs/customization/styles.mdx +18 -0
  69. package/dist/templates/pages/src/content/docs/getting-started/api-reference.mdx +100 -0
  70. package/dist/templates/pages/src/content/docs/getting-started/components.mdx +156 -0
  71. package/dist/templates/pages/src/content/docs/getting-started/configuration.mdx +194 -0
  72. package/dist/templates/pages/src/content/docs/getting-started/deployment.mdx +142 -0
  73. package/dist/templates/pages/src/content/docs/getting-started/index.mdx +22 -0
  74. package/dist/templates/pages/src/content/docs/getting-started/writing-pages.mdx +35 -0
  75. package/dist/templates/pages/src/content/docs/index.mdx +21 -0
  76. package/dist/templates/pages/src/content.config.ts +1 -0
  77. package/dist/templates/pages/src/layouts/page.astro +195 -0
  78. package/dist/templates/pages/src/styles/global.css +95 -0
  79. package/dist/templates/pages/tsconfig.json +10 -0
  80. package/dist/templates/pages/uno.config.ts +168 -0
  81. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command, Option } from "@commander-js/extra-typings";
3
- import { chmod, link, lstat, mkdir, open, readFile, rename, unlink, writeFile } from "node:fs/promises";
3
+ import { chmod, cp, link, lstat, mkdir, open, readFile, readdir, rename, rm, unlink, writeFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import * as z from "zod";
6
6
  import { homedir } from "node:os";
@@ -13,6 +13,7 @@ import { setTimeout } from "node:timers/promises";
13
13
  import lockfile from "proper-lockfile";
14
14
  import { AndesineAPIError, AndesineStreamProtocolError, createClient, paginate, paginatePages } from "@andesine/sdk";
15
15
  import openBrowser from "open";
16
+ import { fileURLToPath } from "node:url";
16
17
  import { Readable } from "node:stream";
17
18
  import { pipeline } from "node:stream/promises";
18
19
  import { CommanderError } from "commander";
@@ -41,7 +42,7 @@ var CancelledError = class extends CLIError {
41
42
  };
42
43
  //#endregion
43
44
  //#region package.json
44
- var version = "0.1.0";
45
+ var version = "0.4.0";
45
46
  //#endregion
46
47
  //#region src/config/files.ts
47
48
  /** Read declarative JSON only. Missing optional files are distinct from invalid files. */
@@ -107,7 +108,7 @@ const typesConfigSchema = z.strictObject({
107
108
  kind: "published",
108
109
  channel: "published"
109
110
  }),
110
- collections: z.array(nonemptyString).default([]).describe("Collection IDs or paths; empty selects all accessible collections."),
111
+ collections: z.array(z.union([nonemptyString, z.strictObject({ collectionSlugPath: nonemptyString })])).default([]).describe("Collection IDs, name paths, or objects with collectionSlugPath; empty selects all accessible collections."),
111
112
  output: nonemptyString.default("src/andesine.generated.ts").describe("Output path relative to andesine.json, or the working directory when no config is present."),
112
113
  includeEntryIDs: z.boolean().default(false),
113
114
  includeEntryPaths: z.boolean().default(false),
@@ -951,7 +952,7 @@ const listCollections = async (client, source, signal) => {
951
952
  const validateCollections = async (client, source, selectors, signal) => {
952
953
  let snapshotID = source.kind === "published" && "snapshotID" in source ? source.snapshotID : void 0;
953
954
  for (const selector of selectors.length ? selectors : [void 0]) {
954
- const scope = selector === void 0 ? {} : /^coll_[A-Za-z\d]+$/.test(selector) ? { collectionID: selector } : { collectionPath: selector };
955
+ const scope = selector === void 0 ? {} : typeof selector !== "string" ? selector : /^coll_[A-Za-z\d]+$/.test(selector) ? { collectionID: selector } : { collectionPath: selector };
955
956
  if (source.kind === "current") await client.collections.list({
956
957
  ...scope,
957
958
  limit: 1
@@ -967,6 +968,53 @@ const validateCollections = async (client, source, selectors, signal) => {
967
968
  }
968
969
  };
969
970
  //#endregion
971
+ //#region src/types/options.ts
972
+ const validateTypeOptions = (options) => {
973
+ if (options.channel !== void 0 && options.snapshot !== void 0 || options.source === "current" && (options.channel !== void 0 || options.snapshot !== void 0)) throw new CLIError("Use --channel or --snapshot for published content; neither can be used with --source current.", exitCodes.usage);
974
+ if (options.allCollections && (options.collection || options.collectionSlugPath)) throw new CLIError("Use collection selectors or --all-collections, not both.", exitCodes.usage);
975
+ };
976
+ /** Apply command overrides without changing the saved configuration. */
977
+ const collectionSelectors = (options, initial) => {
978
+ if (options.allCollections) return [];
979
+ if (options.collection === void 0 && options.collectionSlugPath === void 0) return initial;
980
+ return [...options.collection ?? [], ...(options.collectionSlugPath ?? []).map((collectionSlugPath) => ({ collectionSlugPath }))];
981
+ };
982
+ const resolveTypes = (config, options) => {
983
+ const initial = config.types;
984
+ const directory = config.configPath ? path.dirname(config.configPath) : process.cwd();
985
+ let source = initial.source;
986
+ validateTypeOptions(options);
987
+ if (options.source === "current") source = { kind: "current" };
988
+ else if (options.snapshot !== void 0) source = {
989
+ kind: "published",
990
+ snapshotID: options.snapshot
991
+ };
992
+ else if (options.source === "published" || options.channel !== void 0) {
993
+ const previousChannel = initial.source.kind === "published" && "channel" in initial.source ? initial.source.channel : "published";
994
+ source = {
995
+ kind: "published",
996
+ channel: options.channel ?? previousChannel
997
+ };
998
+ }
999
+ const parsed = typesConfigSchema.safeParse({
1000
+ ...initial,
1001
+ source,
1002
+ collections: collectionSelectors(options, initial.collections),
1003
+ output: options.output ?? initial.output,
1004
+ includeEntryIDs: options.includeEntryIds ?? initial.includeEntryIDs,
1005
+ includeEntryPaths: options.includeEntryPaths ?? initial.includeEntryPaths,
1006
+ includeTree: options.includeTree ?? initial.includeTree
1007
+ });
1008
+ if (!parsed.success) throw new CLIError("Invalid type options. Check the source, collection selectors, and output path.", exitCodes.usage);
1009
+ const output = path.resolve(directory, parsed.data.output);
1010
+ if (!/\.(?:ts|mts|cts)$/.test(output) || output === config.configPath) throw new CLIError("The types output must be a TypeScript file, separate from the configuration.", exitCodes.usage);
1011
+ return {
1012
+ ...parsed.data,
1013
+ collections: [...new Set(parsed.data.collections)],
1014
+ output
1015
+ };
1016
+ };
1017
+ //#endregion
970
1018
  //#region src/init/choices.ts
971
1019
  const selectSource = async (context, client, options, initial) => {
972
1020
  const interactive = canPrompt(options.interactive);
@@ -1032,12 +1080,12 @@ const selectSource = async (context, client, options, initial) => {
1032
1080
  const selectTypes = async (context, client, options, initial) => {
1033
1081
  const interactive = canPrompt(options.interactive);
1034
1082
  const source = await selectSource(context, client, options, initial.source);
1035
- let collections = options.allCollections ? [] : options.collection ?? initial.collections;
1083
+ let collections = collectionSelectors(options, initial.collections);
1036
1084
  let output = options.output ?? initial.output;
1037
1085
  let includeEntryIDs = options.includeEntryIds ?? initial.includeEntryIDs;
1038
1086
  let includeEntryPaths = options.includeEntryPaths ?? initial.includeEntryPaths;
1039
1087
  let includeTree = options.includeTree ?? initial.includeTree;
1040
- if (interactive && options.collection === void 0 && !options.allCollections) {
1088
+ if (interactive && options.collection === void 0 && options.collectionSlugPath === void 0 && !options.allCollections) {
1041
1089
  if (!collections.length || await context.prompt((prompt) => confirm({
1042
1090
  ...prompt,
1043
1091
  message: "Change the saved collection selection?",
@@ -1048,28 +1096,36 @@ const selectTypes = async (context, client, options, initial) => {
1048
1096
  const format = await context.prompt((prompt) => select({
1049
1097
  ...prompt,
1050
1098
  message: "How should collections be saved?",
1051
- options: [{
1052
- value: "id",
1053
- label: "IDs",
1054
- hint: "Stable when collections are renamed"
1055
- }, {
1056
- value: "path",
1057
- label: "Paths",
1058
- hint: "Readable names from the explorer"
1059
- }]
1099
+ options: [
1100
+ {
1101
+ value: "id",
1102
+ label: "IDs",
1103
+ hint: "Stable when collections are renamed"
1104
+ },
1105
+ {
1106
+ value: "path",
1107
+ label: "Paths",
1108
+ hint: "Readable names from the explorer"
1109
+ },
1110
+ {
1111
+ value: "slug",
1112
+ label: "Slug paths",
1113
+ hint: "URL paths derived from names"
1114
+ }
1115
+ ]
1060
1116
  }));
1061
1117
  const selected = await context.prompt((prompt) => multiselect({
1062
1118
  ...prompt,
1063
1119
  message: "Collections (leave empty for all)",
1064
1120
  required: false,
1065
- initialValues: available.filter((item) => collections.includes(item.id) || collections.includes(item.path)).map((item) => item.id),
1121
+ initialValues: available.filter((item) => collections.some((selector) => typeof selector === "string" ? selector === item.id || selector === item.path : selector.collectionSlugPath === item.slugPath)).map((item) => item.id),
1066
1122
  options: available.map((item) => ({
1067
1123
  value: item.id,
1068
1124
  label: item.path,
1069
1125
  hint: item.id
1070
1126
  }))
1071
1127
  }));
1072
- collections = available.filter((item) => selected.includes(item.id)).map((item) => format === "id" ? item.id : item.path);
1128
+ collections = available.filter((item) => selected.includes(item.id)).map((item) => format === "id" ? item.id : format === "slug" ? { collectionSlugPath: item.slugPath } : item.path);
1073
1129
  } else {
1074
1130
  collections = [];
1075
1131
  await context.output.diagnostic("No collections yet. The configuration will include all collections as they become available.");
@@ -1174,48 +1230,6 @@ const writeProjectConfig = async (file, config, expected, signal) => {
1174
1230
  }
1175
1231
  };
1176
1232
  //#endregion
1177
- //#region src/types/options.ts
1178
- const validateTypeOptions = (options) => {
1179
- if (options.channel !== void 0 && options.snapshot !== void 0 || options.source === "current" && (options.channel !== void 0 || options.snapshot !== void 0)) throw new CLIError("Use --channel or --snapshot for published content; neither can be used with --source current.", exitCodes.usage);
1180
- if (options.allCollections && options.collection) throw new CLIError("Use --collection or --all-collections, not both.", exitCodes.usage);
1181
- };
1182
- /** Apply command overrides without changing the saved configuration. */
1183
- const resolveTypes = (config, options) => {
1184
- const initial = config.types;
1185
- const directory = config.configPath ? path.dirname(config.configPath) : process.cwd();
1186
- let source = initial.source;
1187
- validateTypeOptions(options);
1188
- if (options.source === "current") source = { kind: "current" };
1189
- else if (options.snapshot !== void 0) source = {
1190
- kind: "published",
1191
- snapshotID: options.snapshot
1192
- };
1193
- else if (options.source === "published" || options.channel !== void 0) {
1194
- const previousChannel = initial.source.kind === "published" && "channel" in initial.source ? initial.source.channel : "published";
1195
- source = {
1196
- kind: "published",
1197
- channel: options.channel ?? previousChannel
1198
- };
1199
- }
1200
- const parsed = typesConfigSchema.safeParse({
1201
- ...initial,
1202
- source,
1203
- collections: options.allCollections ? [] : options.collection ?? initial.collections,
1204
- output: options.output ?? initial.output,
1205
- includeEntryIDs: options.includeEntryIds ?? initial.includeEntryIDs,
1206
- includeEntryPaths: options.includeEntryPaths ?? initial.includeEntryPaths,
1207
- includeTree: options.includeTree ?? initial.includeTree
1208
- });
1209
- if (!parsed.success) throw new CLIError("Invalid type options. Check the source, collection selectors, and output path.", exitCodes.usage);
1210
- const output = path.resolve(directory, parsed.data.output);
1211
- if (!/\.(?:ts|mts|cts)$/.test(output) || output === config.configPath) throw new CLIError("The types output must be a TypeScript file, separate from the configuration.", exitCodes.usage);
1212
- return {
1213
- ...parsed.data,
1214
- collections: [...new Set(parsed.data.collections)],
1215
- output
1216
- };
1217
- };
1218
- //#endregion
1219
1233
  //#region src/api/errors.ts
1220
1234
  /** Keep structured API failures on stderr without exposing response bodies or credentials. */
1221
1235
  const reportAPIError = async (context, error) => {
@@ -1374,7 +1388,7 @@ const treeTypes = (metadata, collectionNames) => {
1374
1388
  const childEntries = row.entryIDs.map((id) => {
1375
1389
  const entry = entries.get(id);
1376
1390
  if (!entry || entry.collectionID !== collection.id) throw new Error(`Invalid tree entry reference ${literal(id)}.`);
1377
- return `Omit<SDK.PublishedEntry, "id" | "name" | "path"> & { id: ${literal(id)}; name: ${literal(entry.name)}; path: ${literal(entry.path)} }`;
1391
+ return `Omit<SDK.PublishedEntry, "id" | "name" | "path" | "slugPath"> & { id: ${literal(id)}; name: ${literal(entry.name)}; path: ${literal(entry.path)}; slugPath: ${literal(entry.slugPath)} }`;
1378
1392
  });
1379
1393
  if (metadata.source.kind === "current") return `export interface ${name} {
1380
1394
  collectionID: ${literal(collection.id)};
@@ -1385,6 +1399,7 @@ const treeTypes = (metadata, collectionNames) => {
1385
1399
  id: ${literal(collection.id)};
1386
1400
  name: ${literal(collection.name)};
1387
1401
  path: ${literal(collection.path)};
1402
+ slugPath: ${literal(collection.slugPath)};
1388
1403
  entries: ${tuple(childEntries)};
1389
1404
  collections: ${tuple(childTrees)};
1390
1405
  }`;
@@ -1443,12 +1458,13 @@ const generateTypes = (metadata, options = {}) => {
1443
1458
  const treeBindings = [];
1444
1459
  const contentBase = `Omit<${metadata.source.kind === "published" ? "SDK.PublishedEntryContent" : "SDK.Entry"}, "schema" | "properties" | "fragments">`;
1445
1460
  const bind = (map, key, value) => {
1446
- if (map.has(key)) throw new Error(`Duplicate metadata selector ${literal(key)}.`);
1461
+ if (map.has(key) && map.get(key) !== value) throw new Error(`Duplicate metadata selector ${literal(key)}.`);
1447
1462
  map.set(key, value);
1448
1463
  };
1449
1464
  for (const collection of collections) {
1450
1465
  const name = collectionNames.get(collection.id ?? "/");
1451
1466
  bind(collectionBindings, collection.path, name);
1467
+ if (collection.slugPath !== collection.path) bind(collectionBindings, collection.slugPath, name);
1452
1468
  if (collection.id !== null) bind(collectionBindings, collection.id, name);
1453
1469
  }
1454
1470
  for (const entry of entries) {
@@ -1456,7 +1472,10 @@ const generateTypes = (metadata, options = {}) => {
1456
1472
  if (entry.schemaRevisionID !== null) usedRevisions.add(entry.schemaRevisionID);
1457
1473
  const binding = `{ collection: ${literal(entry.collectionID ?? "/")}; schemaRevisionID: ${literal(entry.schemaRevisionID)} }`;
1458
1474
  if (options.includeEntryIDs) bind(entryBindings, entry.id, binding);
1459
- if (options.includeEntryPaths) bind(entryBindings, entry.path, binding);
1475
+ if (options.includeEntryPaths) {
1476
+ bind(entryBindings, entry.path, binding);
1477
+ if (entry.slugPath !== entry.path) bind(entryBindings, entry.slugPath, binding);
1478
+ }
1460
1479
  }
1461
1480
  const revisionNames = names([...usedRevisions].map((id) => ({
1462
1481
  key: id,
@@ -1483,6 +1502,7 @@ const generateTypes = (metadata, options = {}) => {
1483
1502
  interfaces.push(`export interface ${name} extends SDK.WorkspaceCollection {
1484
1503
  id: ${literal(collection.id)};
1485
1504
  path: ${literal(collection.path)};
1505
+ slugPath: ${literal(collection.slugPath)};
1486
1506
  schemaRevisionIDs: readonly ${tuple(ids.map(literal))};
1487
1507
  descendants: readonly ${tuple(childIDs.map(literal))};
1488
1508
  }`);
@@ -1490,9 +1510,10 @@ const generateTypes = (metadata, options = {}) => {
1490
1510
  types.push(`export type ${name}StructuredContent = SDK.StructuredContent<${name}Content>;`);
1491
1511
  const collectionEntries = entries.filter((entry) => entry.collectionID === collection.id);
1492
1512
  if (options.includeEntryIDs) types.push(`export type ${name}EntryID = ${union(collectionEntries.map((entry) => literal(entry.id)))};`);
1493
- if (options.includeEntryPaths) types.push(`export type ${name}EntryPath = ${union(collectionEntries.map((entry) => literal(entry.path)))};`);
1513
+ if (options.includeEntryPaths) types.push(`export type ${name}EntryPath = ${union(collectionEntries.map((entry) => literal(entry.path)))};`, `export type ${name}EntrySlugPath = ${union(collectionEntries.map((entry) => literal(entry.slugPath)))};`);
1494
1514
  if (options.includeTree) {
1495
1515
  treeBindings.push(` ${literal(collection.path)}: ${name}Tree;`);
1516
+ if (collection.slugPath !== collection.path) treeBindings.push(` ${literal(collection.slugPath)}: ${name}Tree;`);
1496
1517
  if (collection.id !== null) treeBindings.push(` ${literal(collection.id)}: ${name}Tree;`);
1497
1518
  }
1498
1519
  }
@@ -1515,7 +1536,7 @@ const generateTypes = (metadata, options = {}) => {
1515
1536
  [
1516
1537
  GENERATED_TYPES_MARKER,
1517
1538
  `// Workspace: ${literal(metadata.workspaceID)}; source: ${json(source)}.`,
1518
- `// Collections: ${json([...new Set(options.collections ?? [])].sort(compare))}; entry IDs: ${Boolean(options.includeEntryIDs)}; entry paths: ${Boolean(options.includeEntryPaths)}; tree: ${Boolean(options.includeTree)}.`,
1539
+ `// Collections: ${json([...options.collections ?? []].sort((a, b) => compare(json(a), json(b))))}; entry IDs: ${Boolean(options.includeEntryIDs)}; entry paths: ${Boolean(options.includeEntryPaths)}; tree: ${Boolean(options.includeTree)}.`,
1519
1540
  "// Types only. Configure the runtime client for the same workspace and source."
1520
1541
  ].join("\n"),
1521
1542
  "import type * as SDK from \"@andesine/sdk\";",
@@ -1670,7 +1691,7 @@ const executeTypes = async (context, options = {}, emitResult = true) => {
1670
1691
  await output.heading(`Andesine · ${options.check ? "Check" : options.watch ? "Watch" : "Generate"} content types`);
1671
1692
  await output.note("Type generation", {
1672
1693
  Source: JSON.stringify(types.source),
1673
- Collections: types.collections.join(", ") || "All accessible collections",
1694
+ Collections: types.collections.map((selector) => typeof selector === "string" ? selector : selector.collectionSlugPath).join(", ") || "All accessible collections",
1674
1695
  Output: types.output
1675
1696
  });
1676
1697
  if (options.watch) await output.diagnostic(`Watching every ${interval / 1e3}s. Press Ctrl+C to stop.`);
@@ -1753,7 +1774,7 @@ const describeConfig = (config) => ({
1753
1774
  "Workspace": config.workspaceID,
1754
1775
  "Profile": config.profile,
1755
1776
  "Source": config.types ? JSON.stringify(config.types.source) : void 0,
1756
- "Collections": config.types ? config.types.collections.join(", ") || "All accessible collections" : void 0,
1777
+ "Collections": config.types ? config.types.collections.map((selector) => typeof selector === "string" ? selector : selector.collectionSlugPath).join(", ") || "All accessible collections" : void 0,
1757
1778
  "Output": config.types?.output,
1758
1779
  "Entry IDs": config.types ? String(config.types.includeEntryIDs) : void 0,
1759
1780
  "Entry paths": config.types ? String(config.types.includeEntryPaths) : void 0,
@@ -1847,6 +1868,42 @@ const init = async (options, signal) => {
1847
1868
  });
1848
1869
  };
1849
1870
  //#endregion
1871
+ //#region src/pages/init.ts
1872
+ const templateDirectory = fileURLToPath(new URL("./templates/pages/", import.meta.url));
1873
+ const isEmptyDirectory = async (directory) => {
1874
+ try {
1875
+ return (await readdir(directory)).length === 0;
1876
+ } catch (error) {
1877
+ if (error.code === "ENOENT") return true;
1878
+ throw error;
1879
+ }
1880
+ };
1881
+ /** Package names allow lowercase letters, digits, and `-._~` only. */
1882
+ const toPackageName = (directory) => {
1883
+ return path.basename(directory).toLowerCase().replace(/[^a-z\d._~-]+/g, "-").replace(/^[._-]+|[-]+$/g, "") || "andesine-pages-site";
1884
+ };
1885
+ /** Creates an Andesine Pages site from the template in an empty directory. */
1886
+ const initPages = async (directory, output) => {
1887
+ const target = path.resolve(directory);
1888
+ const manifestPath = path.join(target, "package.json");
1889
+ if (!await isEmptyDirectory(target)) throw new CLIError(`${directory} is not empty. Choose a new or empty directory.`, exitCodes.usage);
1890
+ await output.progress("Copying the template", async () => {
1891
+ const manifest = JSON.parse(await readFile(path.join(templateDirectory, "package.template.json"), "utf8"));
1892
+ await cp(templateDirectory, target, { recursive: true });
1893
+ await rename(path.join(target, "gitignore"), path.join(target, ".gitignore"));
1894
+ await rm(path.join(target, "package.template.json"));
1895
+ await writeFile(manifestPath, `${JSON.stringify({
1896
+ ...manifest,
1897
+ name: toPackageName(target)
1898
+ }, null, 2)}\n`);
1899
+ }, "Template copied");
1900
+ await output.note("Next steps", {
1901
+ "Open the site": `cd ${path.relative(process.cwd(), target) || "."}`,
1902
+ "Install": "npm install",
1903
+ "Start": "npm run dev"
1904
+ });
1905
+ };
1906
+ //#endregion
1850
1907
  //#region src/api/manifest.ts
1851
1908
  const commands = [
1852
1909
  {
@@ -2141,7 +2198,7 @@ const commands = [
2141
2198
  method: "get",
2142
2199
  path: "/assets/search",
2143
2200
  summary: "Search assets",
2144
- description: "Finds workspace images by text or SHA-256 checksum. Semantic search defaults to true. Returns up to 50 results.\n\nRequired API key permissions: read:entries, read:collections. Write permissions also grant read access for the same resource.",
2201
+ description: "Finds workspace images by text or SHA-256 checksum. Semantic search defaults to true and uses 1 AI credit for a text query. Returns up to 50 results.\n\nRequired API key permissions: read:entries, read:collections. Write permissions also grant read access for the same resource.",
2145
2202
  example: {
2146
2203
  query: "mountains",
2147
2204
  limit: 10
@@ -2331,7 +2388,7 @@ const commands = [
2331
2388
  method: "post",
2332
2389
  path: "/collections",
2333
2390
  summary: "Create a collection",
2334
- description: "Creates a child collection. Without parentID, uses the workspace root. The name defaults to Untitled. Creation selects an available sibling name with a numeric suffix when needed. Names are trimmed, NFC-normalized, case-sensitive, and shared by sibling entries and collections. Names cannot contain a slash or equal a single dot or two dots. Restricted collections require a session with restricted_collections permission and the Pro plan; API keys cannot create them.\n\nRequired API key permissions: collections. Write permissions also grant read access for the same resource.",
2391
+ description: "Creates a child collection. Without parentID, uses the workspace root. The name defaults to Untitled. Creation selects an available sibling name with a numeric suffix when needed. Names are trimmed and NFC-normalized. Derived URL slugs must be unique across sibling entries and collections, including case and punctuation variants. Names cannot contain a slash or equal a single dot or two dots. Restricted collections require a session with restricted_collections permission and the Pro plan; API keys cannot create them.\n\nRequired API key permissions: collections. Write permissions also grant read access for the same resource.",
2335
2392
  example: {
2336
2393
  name: "Documentation",
2337
2394
  parentID: "coll_example"
@@ -2481,6 +2538,18 @@ const commands = [
2481
2538
  kind: "string",
2482
2539
  nullable: false
2483
2540
  },
2541
+ {
2542
+ name: "collectionSlugPath",
2543
+ location: "query",
2544
+ required: false,
2545
+ schema: {
2546
+ type: "string",
2547
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
2548
+ },
2549
+ flag: "collection-slug-path",
2550
+ kind: "string",
2551
+ nullable: false
2552
+ },
2484
2553
  {
2485
2554
  name: "cursor",
2486
2555
  location: "query",
@@ -2620,6 +2689,18 @@ const commands = [
2620
2689
  kind: "string",
2621
2690
  nullable: false
2622
2691
  },
2692
+ {
2693
+ name: "slugPath",
2694
+ location: "query",
2695
+ required: false,
2696
+ schema: {
2697
+ type: "string",
2698
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
2699
+ },
2700
+ flag: "slug-path",
2701
+ kind: "string",
2702
+ nullable: false
2703
+ },
2623
2704
  {
2624
2705
  name: "expectedSchemaHash",
2625
2706
  location: "query",
@@ -2795,6 +2876,18 @@ const commands = [
2795
2876
  kind: "string",
2796
2877
  nullable: false
2797
2878
  },
2879
+ {
2880
+ name: "slugPath",
2881
+ location: "query",
2882
+ required: false,
2883
+ schema: {
2884
+ type: "string",
2885
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
2886
+ },
2887
+ flag: "slug-path",
2888
+ kind: "string",
2889
+ nullable: false
2890
+ },
2798
2891
  {
2799
2892
  name: "channel",
2800
2893
  location: "query",
@@ -2874,6 +2967,18 @@ const commands = [
2874
2967
  kind: "string",
2875
2968
  nullable: false
2876
2969
  },
2970
+ {
2971
+ name: "collectionSlugPath",
2972
+ location: "query",
2973
+ required: false,
2974
+ schema: {
2975
+ type: "string",
2976
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
2977
+ },
2978
+ flag: "collection-slug-path",
2979
+ kind: "string",
2980
+ nullable: false
2981
+ },
2877
2982
  {
2878
2983
  name: "channel",
2879
2984
  location: "query",
@@ -2924,7 +3029,7 @@ const commands = [
2924
3029
  method: "get",
2925
3030
  path: "/content/collections",
2926
3031
  summary: "List published collections",
2927
- description: "Returns a flat page of collections from a publication snapshot. The default channel is published. On later pages, pass the returned snapshotID and pagination.nextCursor, and omit channel. Optionally select direct children using collectionID or collectionPath. Results are ordered by ID, not display order.\n\nRequired API key permissions: read:publishing. Write permissions also grant read access for the same resource.",
3032
+ description: "Returns a flat page of collections from a publication snapshot. The default channel is published. On later pages, pass the returned snapshotID and pagination.nextCursor, and omit channel. Optionally select direct children using collectionID, collectionPath, or collectionSlugPath. Results are ordered by ID, not display order.\n\nRequired API key permissions: read:publishing. Write permissions also grant read access for the same resource.",
2928
3033
  example: {
2929
3034
  channel: "published",
2930
3035
  limit: 20
@@ -2982,6 +3087,18 @@ const commands = [
2982
3087
  kind: "string",
2983
3088
  nullable: false
2984
3089
  },
3090
+ {
3091
+ name: "collectionSlugPath",
3092
+ location: "query",
3093
+ required: false,
3094
+ schema: {
3095
+ type: "string",
3096
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
3097
+ },
3098
+ flag: "collection-slug-path",
3099
+ kind: "string",
3100
+ nullable: false
3101
+ },
2985
3102
  {
2986
3103
  name: "channel",
2987
3104
  location: "query",
@@ -3031,7 +3148,7 @@ const commands = [
3031
3148
  method: "get",
3032
3149
  path: "/content/entries",
3033
3150
  summary: "List published entries",
3034
- description: "Returns a flat page of entries from a publication snapshot. The default channel is published. On later pages, pass the returned snapshotID and pagination.nextCursor, and omit channel. Select direct children using collectionID or collectionPath, or include nested entries with descendants: true. descendants requires a collection scope. All property filters must match the assigned version. includeContent: true returns validated full content, properties, fragments, assets, and recorded schema metadata. An invalid full item fails the page. Keep the same scope and filters across pages. Results are ordered by ID, not display order.\n\nRequired API key permissions: read:publishing. Write permissions also grant read access for the same resource.",
3151
+ description: "Returns a flat page of entries from a publication snapshot. The default channel is published. On later pages, pass the returned snapshotID and pagination.nextCursor, and omit channel. Select direct children using collectionID, collectionPath, or collectionSlugPath, or include nested entries with descendants: true. descendants requires a collection scope. All property filters must match the assigned version. includeContent: true returns validated full content, properties, fragments, assets, and recorded schema metadata. An invalid full item fails the page. Keep the same scope and filters across pages. Results are ordered by ID, not display order.\n\nRequired API key permissions: read:publishing. Write permissions also grant read access for the same resource.",
3035
3152
  example: {
3036
3153
  channel: "published",
3037
3154
  limit: 20
@@ -3089,6 +3206,18 @@ const commands = [
3089
3206
  kind: "string",
3090
3207
  nullable: false
3091
3208
  },
3209
+ {
3210
+ name: "collectionSlugPath",
3211
+ location: "query",
3212
+ required: false,
3213
+ schema: {
3214
+ type: "string",
3215
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
3216
+ },
3217
+ flag: "collection-slug-path",
3218
+ kind: "string",
3219
+ nullable: false
3220
+ },
3092
3221
  {
3093
3222
  name: "channel",
3094
3223
  location: "query",
@@ -3218,7 +3347,7 @@ const commands = [
3218
3347
  method: "post",
3219
3348
  path: "/entries",
3220
3349
  summary: "Create an entry",
3221
- description: "Creates an entry and its initial document. The name defaults to Untitled. Creation selects an available sibling name with a numeric suffix when needed. Names are trimmed, NFC-normalized, case-sensitive, and shared by sibling entries and collections. Names cannot contain a slash or equal a single dot or two dots. A collection schema can set the initial content.\n\nRequired API key permissions: entries. Write permissions also grant read access for the same resource.",
3350
+ description: "Creates an entry and its initial document. The name defaults to Untitled. Creation selects an available sibling name with a numeric suffix when needed. Names are trimmed and NFC-normalized. Derived URL slugs must be unique across sibling entries and collections, including case and punctuation variants. Names cannot contain a slash or equal a single dot or two dots. A collection schema can set the initial content.\n\nRequired API key permissions: entries. Write permissions also grant read access for the same resource.",
3222
3351
  example: {
3223
3352
  name: "Getting started",
3224
3353
  collectionID: "coll_example"
@@ -3352,6 +3481,18 @@ const commands = [
3352
3481
  kind: "string",
3353
3482
  nullable: false
3354
3483
  },
3484
+ {
3485
+ name: "slugPath",
3486
+ location: "query",
3487
+ required: false,
3488
+ schema: {
3489
+ type: "string",
3490
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
3491
+ },
3492
+ flag: "slug-path",
3493
+ kind: "string",
3494
+ nullable: false
3495
+ },
3355
3496
  {
3356
3497
  name: "expectedSchemaHash",
3357
3498
  location: "query",
@@ -3419,6 +3560,18 @@ const commands = [
3419
3560
  kind: "string",
3420
3561
  nullable: false
3421
3562
  },
3563
+ {
3564
+ name: "collectionSlugPath",
3565
+ location: "query",
3566
+ required: false,
3567
+ schema: {
3568
+ type: "string",
3569
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
3570
+ },
3571
+ flag: "collection-slug-path",
3572
+ kind: "string",
3573
+ nullable: false
3574
+ },
3422
3575
  {
3423
3576
  name: "cursor",
3424
3577
  location: "query",
@@ -5627,6 +5780,18 @@ const commands = [
5627
5780
  kind: "string",
5628
5781
  nullable: false
5629
5782
  },
5783
+ {
5784
+ name: "collectionSlugPath",
5785
+ location: "query",
5786
+ required: false,
5787
+ schema: {
5788
+ type: "string",
5789
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
5790
+ },
5791
+ flag: "collection-slug-path",
5792
+ kind: "string",
5793
+ nullable: false
5794
+ },
5630
5795
  {
5631
5796
  name: "x-workspace-id",
5632
5797
  location: "header",
@@ -5922,7 +6087,7 @@ const commands = [
5922
6087
  method: "post",
5923
6088
  path: "/search/current/ask",
5924
6089
  summary: "Ask AI about current content",
5925
- description: "Returns a complete answer with numbered sources. Requires explicit ai-answers permission in addition to content read permissions. Accepts up to 1,000 question characters, 10 history messages of up to 4,000 characters each, and 20 property filters. Uses the existing Ask AI rate limit. Keep API keys on your server.\n\nRequired API key permissions: ai-answers, read:entries, read:collections. Write permissions also grant read access for the same resource.",
6090
+ description: "Returns a complete answer with numbered sources. Requires explicit ai-answers permission in addition to content read permissions. Accepts up to 1,000 question characters, 10 history messages of up to 4,000 characters each, and 20 property filters. Uses 3 AI credits and the Ask AI rate limit. Use a secret key on your server, or a publishable key for published content.\n\nRequired API key permissions: ai-answers, read:entries, read:collections. Write permissions also grant read access for the same resource.",
5926
6091
  example: { question: "How do I install Andesine?" },
5927
6092
  security: [{ apiKey: [] }, { oauth: [] }],
5928
6093
  fields: [
@@ -5975,6 +6140,18 @@ const commands = [
5975
6140
  kind: "string",
5976
6141
  nullable: false
5977
6142
  },
6143
+ {
6144
+ name: "collectionSlugPath",
6145
+ location: "body",
6146
+ required: false,
6147
+ schema: {
6148
+ type: "string",
6149
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
6150
+ },
6151
+ flag: "collection-slug-path",
6152
+ kind: "string",
6153
+ nullable: false
6154
+ },
5978
6155
  {
5979
6156
  name: "filters",
5980
6157
  location: "body",
@@ -6014,7 +6191,7 @@ const commands = [
6014
6191
  method: "post",
6015
6192
  path: "/search/current/ask/stream",
6016
6193
  summary: "Stream an AI answer about current content",
6017
- description: "Emits sources, textDelta, and completed events over SSE. Uses the same inputs, permissions, and rate limit as complete answers. Stream errors use SSE error frames. Never reconnect automatically.\n\nRequired API key permissions: ai-answers, read:entries, read:collections. Write permissions also grant read access for the same resource.",
6194
+ description: "Emits sources, textDelta, and completed events over SSE. Uses the same inputs, permissions, AI credits, and rate limit as complete answers. Stream errors use SSE error frames. Never reconnect automatically.\n\nRequired API key permissions: ai-answers, read:entries, read:collections. Write permissions also grant read access for the same resource.",
6018
6195
  security: [{ apiKey: [] }, { oauth: [] }],
6019
6196
  fields: [
6020
6197
  {
@@ -6066,6 +6243,18 @@ const commands = [
6066
6243
  kind: "string",
6067
6244
  nullable: false
6068
6245
  },
6246
+ {
6247
+ name: "collectionSlugPath",
6248
+ location: "body",
6249
+ required: false,
6250
+ schema: {
6251
+ type: "string",
6252
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
6253
+ },
6254
+ flag: "collection-slug-path",
6255
+ kind: "string",
6256
+ nullable: false
6257
+ },
6069
6258
  {
6070
6259
  name: "filters",
6071
6260
  location: "body",
@@ -6105,7 +6294,7 @@ const commands = [
6105
6294
  method: "post",
6106
6295
  path: "/search/published/ask",
6107
6296
  summary: "Ask AI about published content",
6108
- description: "Returns a complete answer with numbered sources. Requires explicit ai-answers permission in addition to content read permissions. Accepts up to 1,000 question characters, 10 history messages of up to 4,000 characters each, and 20 property filters. Uses the existing Ask AI rate limit. Keep API keys on your server.\n\nRequired API key permissions: ai-answers, read:publishing. Write permissions also grant read access for the same resource.",
6297
+ description: "Returns a complete answer with numbered sources. Requires explicit ai-answers permission in addition to content read permissions. Accepts up to 1,000 question characters, 10 history messages of up to 4,000 characters each, and 20 property filters. Uses 3 AI credits and the Ask AI rate limit. Use a secret key on your server, or a publishable key for published content.\n\nRequired API key permissions: ai-answers, read:publishing. Write permissions also grant read access for the same resource.",
6109
6298
  example: {
6110
6299
  question: "How do I install Andesine?",
6111
6300
  channel: "published"
@@ -6161,6 +6350,18 @@ const commands = [
6161
6350
  kind: "string",
6162
6351
  nullable: false
6163
6352
  },
6353
+ {
6354
+ name: "collectionSlugPath",
6355
+ location: "body",
6356
+ required: false,
6357
+ schema: {
6358
+ type: "string",
6359
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
6360
+ },
6361
+ flag: "collection-slug-path",
6362
+ kind: "string",
6363
+ nullable: false
6364
+ },
6164
6365
  {
6165
6366
  name: "filters",
6166
6367
  location: "body",
@@ -6214,7 +6415,7 @@ const commands = [
6214
6415
  method: "post",
6215
6416
  path: "/search/published/ask/stream",
6216
6417
  summary: "Stream an AI answer about published content",
6217
- description: "Emits sources, textDelta, and completed events over SSE. Uses the same inputs, permissions, and rate limit as complete answers. Stream errors use SSE error frames. Never reconnect automatically.\n\nRequired API key permissions: ai-answers, read:publishing. Write permissions also grant read access for the same resource.",
6418
+ description: "Emits sources, textDelta, and completed events over SSE. Uses the same inputs, permissions, AI credits, and rate limit as complete answers. Stream errors use SSE error frames. Never reconnect automatically.\n\nRequired API key permissions: ai-answers, read:publishing. Write permissions also grant read access for the same resource.",
6218
6419
  security: [{ apiKey: [] }, { oauth: [] }],
6219
6420
  fields: [
6220
6421
  {
@@ -6266,6 +6467,18 @@ const commands = [
6266
6467
  kind: "string",
6267
6468
  nullable: false
6268
6469
  },
6470
+ {
6471
+ name: "collectionSlugPath",
6472
+ location: "body",
6473
+ required: false,
6474
+ schema: {
6475
+ type: "string",
6476
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
6477
+ },
6478
+ flag: "collection-slug-path",
6479
+ kind: "string",
6480
+ nullable: false
6481
+ },
6269
6482
  {
6270
6483
  name: "filters",
6271
6484
  location: "body",
@@ -6319,7 +6532,7 @@ const commands = [
6319
6532
  method: "post",
6320
6533
  path: "/search/current",
6321
6534
  summary: "Search current content",
6322
- description: "Searches current entry content by text and optional property filters. Up to 20 filters and 50 results are allowed. Semantic search is optional and has a separate rate limit.\n\nRequired API key permissions: read:entries, read:collections. Write permissions also grant read access for the same resource.",
6535
+ description: "Searches current entry content by text and optional property filters. Up to 20 filters and 50 results are allowed. Semantic search is optional, uses 1 AI credit, and has a separate rate limit.\n\nRequired API key permissions: read:entries, read:collections. Write permissions also grant read access for the same resource.",
6323
6536
  example: {
6324
6537
  query: "installation",
6325
6538
  limit: 10
@@ -6374,6 +6587,18 @@ const commands = [
6374
6587
  kind: "string",
6375
6588
  nullable: false
6376
6589
  },
6590
+ {
6591
+ name: "collectionSlugPath",
6592
+ location: "body",
6593
+ required: false,
6594
+ schema: {
6595
+ type: "string",
6596
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
6597
+ },
6598
+ flag: "collection-slug-path",
6599
+ kind: "string",
6600
+ nullable: false
6601
+ },
6377
6602
  {
6378
6603
  name: "filters",
6379
6604
  location: "body",
@@ -6426,7 +6651,7 @@ const commands = [
6426
6651
  method: "post",
6427
6652
  path: "/search/published",
6428
6653
  summary: "Search published content",
6429
- description: "Searches content published to the required channel. Up to 20 filters and 50 results are allowed. Semantic search is optional and has a separate rate limit.\n\nRequired API key permissions: read:publishing. Write permissions also grant read access for the same resource.",
6654
+ description: "Searches content published to the required channel. Up to 20 filters and 50 results are allowed. Semantic search is optional, uses 1 AI credit, and has a separate rate limit.\n\nRequired API key permissions: read:publishing. Write permissions also grant read access for the same resource.",
6430
6655
  example: {
6431
6656
  query: "installation",
6432
6657
  channel: "published",
@@ -6482,6 +6707,18 @@ const commands = [
6482
6707
  kind: "string",
6483
6708
  nullable: false
6484
6709
  },
6710
+ {
6711
+ name: "collectionSlugPath",
6712
+ location: "body",
6713
+ required: false,
6714
+ schema: {
6715
+ type: "string",
6716
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
6717
+ },
6718
+ flag: "collection-slug-path",
6719
+ kind: "string",
6720
+ nullable: false
6721
+ },
6485
6722
  {
6486
6723
  name: "filters",
6487
6724
  location: "body",
@@ -6574,15 +6811,26 @@ const commands = [
6574
6811
  schema: {
6575
6812
  type: "array",
6576
6813
  maxItems: 100,
6577
- items: { anyOf: [{
6578
- type: "string",
6579
- pattern: "^coll_[A-Za-z\\d]{1,22}$"
6580
- }, {
6581
- type: "string",
6582
- description: "Decoded content path, such as /Docs/Page or coll_ID/Page. Names are case-sensitive. Do not URL-encode names before passing them to the SDK."
6583
- }] },
6814
+ items: { anyOf: [
6815
+ {
6816
+ type: "string",
6817
+ pattern: "^coll_[A-Za-z\\d]{1,22}$"
6818
+ },
6819
+ {
6820
+ type: "string",
6821
+ description: "Decoded content path, such as /Docs/Page or coll_ID/Page. Names are case-sensitive. Do not URL-encode names before passing them to the SDK."
6822
+ },
6823
+ {
6824
+ type: "object",
6825
+ properties: { collectionSlugPath: {
6826
+ type: "string",
6827
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
6828
+ } },
6829
+ required: ["collectionSlugPath"]
6830
+ }
6831
+ ] },
6584
6832
  default: [],
6585
- description: "Collection IDs or paths. Includes selected subtrees; empty selects all accessible collections."
6833
+ description: "Collection IDs, name paths, or objects with collectionSlugPath. Includes selected subtrees; empty selects all accessible collections."
6586
6834
  },
6587
6835
  flag: "collections",
6588
6836
  kind: "json",
@@ -6651,15 +6899,26 @@ const commands = [
6651
6899
  schema: {
6652
6900
  type: "array",
6653
6901
  maxItems: 100,
6654
- items: { anyOf: [{
6655
- type: "string",
6656
- pattern: "^coll_[A-Za-z\\d]{1,22}$"
6657
- }, {
6658
- type: "string",
6659
- description: "Decoded content path, such as /Docs/Page or coll_ID/Page. Names are case-sensitive. Do not URL-encode names before passing them to the SDK."
6660
- }] },
6902
+ items: { anyOf: [
6903
+ {
6904
+ type: "string",
6905
+ pattern: "^coll_[A-Za-z\\d]{1,22}$"
6906
+ },
6907
+ {
6908
+ type: "string",
6909
+ description: "Decoded content path, such as /Docs/Page or coll_ID/Page. Names are case-sensitive. Do not URL-encode names before passing them to the SDK."
6910
+ },
6911
+ {
6912
+ type: "object",
6913
+ properties: { collectionSlugPath: {
6914
+ type: "string",
6915
+ description: "Decoded derived slug path, such as /docs/getting-started or coll_ID/getting-started. Use the returned slugPath without URL encoding."
6916
+ } },
6917
+ required: ["collectionSlugPath"]
6918
+ }
6919
+ ] },
6661
6920
  default: [],
6662
- description: "Collection IDs or paths. Includes selected subtrees; empty selects all accessible collections."
6921
+ description: "Collection IDs, name paths, or objects with collectionSlugPath. Includes selected subtrees; empty selects all accessible collections."
6663
6922
  },
6664
6923
  flag: "collections",
6665
6924
  kind: "json",
@@ -6998,32 +7257,1224 @@ const commands = [
6998
7257
  response: "empty"
6999
7258
  },
7000
7259
  {
7001
- id: "workspaces.list",
7002
- group: "workspaces",
7003
- command: "list",
7004
- method: "get",
7005
- path: "/workspaces",
7006
- summary: "List available workspaces",
7007
- description: "Lists workspaces available to the OAuth user, with current role permissions and plan access. Browser sessions can list workspaces across signed-in accounts. Does not require workspace selection and does not count toward API usage.",
7008
- security: [{ oauth: [] }],
7009
- fields: [],
7010
- conditional: false,
7011
- response: "json"
7012
- }
7013
- ];
7014
- const schemas = {
7015
- PropertyFilter: { anyOf: [
7016
- { $ref: "#/components/schemas/TextPropertyFilter" },
7017
- { $ref: "#/components/schemas/NumberPropertyFilter" },
7018
- { $ref: "#/components/schemas/BooleanPropertyFilter" },
7019
- { $ref: "#/components/schemas/DatePropertyFilter" }
7020
- ] },
7021
- TextPropertyFilter: {
7022
- type: "object",
7023
- properties: {
7024
- kind: { const: "text" },
7025
- key: {
7026
- type: "string",
7260
+ id: "webhooks.bulkDelete",
7261
+ group: "webhooks",
7262
+ command: "bulk-delete",
7263
+ method: "post",
7264
+ path: "/webhooks/bulk/delete",
7265
+ summary: "Delete webhooks",
7266
+ description: "Deletes every listed webhook in one transaction, checking each expectedRevision. Each deletion cancels pending deliveries and destroys signing keys; history stays until its original expiry.\n\nRequired API key permissions: webhooks. Write permissions also grant read access for the same resource.",
7267
+ security: [{ apiKey: [] }, { oauth: [] }],
7268
+ fields: [{
7269
+ name: "x-workspace-id",
7270
+ location: "header",
7271
+ required: false,
7272
+ schema: {
7273
+ type: "string",
7274
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
7275
+ },
7276
+ flag: "x-workspace-id",
7277
+ kind: "string",
7278
+ nullable: false
7279
+ }, {
7280
+ name: "webhooks",
7281
+ location: "body",
7282
+ required: true,
7283
+ schema: {
7284
+ type: "array",
7285
+ minItems: 1,
7286
+ maxItems: 10,
7287
+ items: {
7288
+ type: "object",
7289
+ properties: {
7290
+ id: {
7291
+ type: "string",
7292
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
7293
+ },
7294
+ expectedRevision: {
7295
+ type: "integer",
7296
+ minimum: -9007199254740991,
7297
+ maximum: 9007199254740991,
7298
+ exclusiveMinimum: 0
7299
+ }
7300
+ },
7301
+ required: ["id", "expectedRevision"],
7302
+ additionalProperties: false
7303
+ }
7304
+ },
7305
+ flag: "webhooks",
7306
+ kind: "json",
7307
+ nullable: false
7308
+ }],
7309
+ conditional: false,
7310
+ response: "empty"
7311
+ },
7312
+ {
7313
+ id: "webhooks.bulkRedeliver",
7314
+ group: "webhooks",
7315
+ command: "bulk-redeliver",
7316
+ method: "post",
7317
+ path: "/webhooks/{id}/deliveries/bulk/redeliver",
7318
+ summary: "Redeliver webhook events",
7319
+ description: "Replays every listed delivery in one transaction with the same checks as a single redelivery. Any ineligible delivery fails the whole request. Counts as one request for rate limiting.\n\nRequired API key permissions: webhooks. Write permissions also grant read access for the same resource.",
7320
+ security: [{ apiKey: [] }, { oauth: [] }],
7321
+ fields: [
7322
+ {
7323
+ name: "id",
7324
+ location: "path",
7325
+ required: true,
7326
+ schema: {
7327
+ type: "string",
7328
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
7329
+ },
7330
+ flag: "id",
7331
+ kind: "string",
7332
+ nullable: false
7333
+ },
7334
+ {
7335
+ name: "x-workspace-id",
7336
+ location: "header",
7337
+ required: false,
7338
+ schema: {
7339
+ type: "string",
7340
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
7341
+ },
7342
+ flag: "x-workspace-id",
7343
+ kind: "string",
7344
+ nullable: false
7345
+ },
7346
+ {
7347
+ name: "expectedDestinationRevision",
7348
+ location: "body",
7349
+ required: true,
7350
+ schema: {
7351
+ type: "integer",
7352
+ minimum: -9007199254740991,
7353
+ maximum: 9007199254740991,
7354
+ exclusiveMinimum: 0
7355
+ },
7356
+ flag: "expected-destination-revision",
7357
+ kind: "number",
7358
+ nullable: false
7359
+ },
7360
+ {
7361
+ name: "deliveryIDs",
7362
+ location: "body",
7363
+ required: true,
7364
+ schema: {
7365
+ type: "array",
7366
+ minItems: 1,
7367
+ maxItems: 25,
7368
+ items: {
7369
+ type: "string",
7370
+ pattern: "^whdel_[A-Za-z\\d]{1,22}$"
7371
+ }
7372
+ },
7373
+ flag: "delivery-ids",
7374
+ kind: "json",
7375
+ nullable: false
7376
+ }
7377
+ ],
7378
+ conditional: false,
7379
+ response: "json"
7380
+ },
7381
+ {
7382
+ id: "webhooks.bulkSetEnabled",
7383
+ group: "webhooks",
7384
+ command: "bulk-set-enabled",
7385
+ method: "post",
7386
+ path: "/webhooks/bulk/set-enabled",
7387
+ summary: "Enable or disable webhooks",
7388
+ description: "Applies the state to every listed webhook in one transaction, checking each expectedRevision. Enabling checks the scope authority and destination of each webhook. Webhooks already in the state are unchanged.\n\nRequired API key permissions: webhooks. Write permissions also grant read access for the same resource.",
7389
+ security: [{ apiKey: [] }, { oauth: [] }],
7390
+ fields: [
7391
+ {
7392
+ name: "x-workspace-id",
7393
+ location: "header",
7394
+ required: false,
7395
+ schema: {
7396
+ type: "string",
7397
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
7398
+ },
7399
+ flag: "x-workspace-id",
7400
+ kind: "string",
7401
+ nullable: false
7402
+ },
7403
+ {
7404
+ name: "webhooks",
7405
+ location: "body",
7406
+ required: true,
7407
+ schema: {
7408
+ type: "array",
7409
+ minItems: 1,
7410
+ maxItems: 10,
7411
+ items: {
7412
+ type: "object",
7413
+ properties: {
7414
+ id: {
7415
+ type: "string",
7416
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
7417
+ },
7418
+ expectedRevision: {
7419
+ type: "integer",
7420
+ minimum: -9007199254740991,
7421
+ maximum: 9007199254740991,
7422
+ exclusiveMinimum: 0
7423
+ }
7424
+ },
7425
+ required: ["id", "expectedRevision"],
7426
+ additionalProperties: false
7427
+ }
7428
+ },
7429
+ flag: "webhooks",
7430
+ kind: "json",
7431
+ nullable: false
7432
+ },
7433
+ {
7434
+ name: "enabled",
7435
+ location: "body",
7436
+ required: true,
7437
+ schema: { type: "boolean" },
7438
+ flag: "enabled",
7439
+ kind: "boolean",
7440
+ nullable: false
7441
+ }
7442
+ ],
7443
+ conditional: false,
7444
+ response: "json"
7445
+ },
7446
+ {
7447
+ id: "webhooks.create",
7448
+ group: "webhooks",
7449
+ command: "create",
7450
+ method: "post",
7451
+ path: "/webhooks",
7452
+ summary: "Create a webhook",
7453
+ description: "Creates an enabled webhook unless `enabled` is false and returns the signing secret once. The caller must be able to read every resource type its events send, and restricted content when included.\n\nRequired API key permissions: webhooks. Write permissions also grant read access for the same resource.",
7454
+ security: [{ apiKey: [] }, { oauth: [] }],
7455
+ fields: [
7456
+ {
7457
+ name: "x-workspace-id",
7458
+ location: "header",
7459
+ required: false,
7460
+ schema: {
7461
+ type: "string",
7462
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
7463
+ },
7464
+ flag: "x-workspace-id",
7465
+ kind: "string",
7466
+ nullable: false
7467
+ },
7468
+ {
7469
+ name: "name",
7470
+ location: "body",
7471
+ required: true,
7472
+ schema: {
7473
+ type: "string",
7474
+ minLength: 1,
7475
+ maxLength: 100
7476
+ },
7477
+ flag: "name",
7478
+ kind: "string",
7479
+ nullable: false
7480
+ },
7481
+ {
7482
+ name: "url",
7483
+ location: "body",
7484
+ required: true,
7485
+ schema: {
7486
+ type: "string",
7487
+ maxLength: 2048,
7488
+ format: "uri"
7489
+ },
7490
+ flag: "url",
7491
+ kind: "string",
7492
+ nullable: false
7493
+ },
7494
+ {
7495
+ name: "enabled",
7496
+ location: "body",
7497
+ required: false,
7498
+ schema: {
7499
+ type: "boolean",
7500
+ default: true
7501
+ },
7502
+ flag: "enabled",
7503
+ kind: "boolean",
7504
+ nullable: false
7505
+ },
7506
+ {
7507
+ name: "eventTypes",
7508
+ location: "body",
7509
+ required: true,
7510
+ schema: {
7511
+ type: "array",
7512
+ minItems: 1,
7513
+ maxItems: 14,
7514
+ items: { $ref: "#/components/schemas/WebhookEventName" }
7515
+ },
7516
+ flag: "event-types",
7517
+ kind: "json",
7518
+ nullable: false
7519
+ },
7520
+ {
7521
+ name: "schemaVersion",
7522
+ location: "body",
7523
+ required: true,
7524
+ schema: { const: 1 },
7525
+ flag: "schema-version",
7526
+ kind: "number",
7527
+ nullable: false
7528
+ },
7529
+ {
7530
+ name: "collections",
7531
+ location: "body",
7532
+ required: true,
7533
+ schema: { anyOf: [{
7534
+ type: "object",
7535
+ properties: { mode: { const: "all" } },
7536
+ required: ["mode"],
7537
+ additionalProperties: false
7538
+ }, {
7539
+ type: "object",
7540
+ properties: {
7541
+ mode: { const: "selected" },
7542
+ roots: {
7543
+ type: "array",
7544
+ minItems: 1,
7545
+ maxItems: 100,
7546
+ items: {
7547
+ type: "string",
7548
+ pattern: "^coll_[A-Za-z\\d]{1,22}$"
7549
+ }
7550
+ }
7551
+ },
7552
+ required: ["mode", "roots"],
7553
+ additionalProperties: false
7554
+ }] },
7555
+ flag: "collections",
7556
+ kind: "json",
7557
+ nullable: false
7558
+ },
7559
+ {
7560
+ name: "channels",
7561
+ location: "body",
7562
+ required: true,
7563
+ schema: { anyOf: [{
7564
+ type: "object",
7565
+ properties: { mode: { const: "all" } },
7566
+ required: ["mode"],
7567
+ additionalProperties: false
7568
+ }, {
7569
+ type: "object",
7570
+ properties: {
7571
+ mode: { const: "selected" },
7572
+ codes: {
7573
+ type: "array",
7574
+ minItems: 1,
7575
+ maxItems: 100,
7576
+ items: {
7577
+ type: "string",
7578
+ minLength: 1,
7579
+ maxLength: 50
7580
+ }
7581
+ }
7582
+ },
7583
+ required: ["mode", "codes"],
7584
+ additionalProperties: false
7585
+ }] },
7586
+ flag: "channels",
7587
+ kind: "json",
7588
+ nullable: false
7589
+ },
7590
+ {
7591
+ name: "restrictedContent",
7592
+ location: "body",
7593
+ required: true,
7594
+ schema: {
7595
+ type: "boolean",
7596
+ description: "Include restricted collections, including ones restricted later"
7597
+ },
7598
+ flag: "restricted-content",
7599
+ kind: "boolean",
7600
+ nullable: false
7601
+ }
7602
+ ],
7603
+ conditional: false,
7604
+ response: "json"
7605
+ },
7606
+ {
7607
+ id: "webhooks.delete",
7608
+ group: "webhooks",
7609
+ command: "delete",
7610
+ method: "delete",
7611
+ path: "/webhooks/{id}",
7612
+ summary: "Delete a webhook",
7613
+ description: "Cancels pending deliveries and destroys signing keys. Retains history until its original expiry; a deleted endpoint cannot be replayed.\n\nRequired API key permissions: webhooks. Write permissions also grant read access for the same resource.",
7614
+ security: [{ apiKey: [] }, { oauth: [] }],
7615
+ fields: [
7616
+ {
7617
+ name: "id",
7618
+ location: "path",
7619
+ required: true,
7620
+ schema: {
7621
+ type: "string",
7622
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
7623
+ },
7624
+ flag: "id",
7625
+ kind: "string",
7626
+ nullable: false
7627
+ },
7628
+ {
7629
+ name: "x-workspace-id",
7630
+ location: "header",
7631
+ required: false,
7632
+ schema: {
7633
+ type: "string",
7634
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
7635
+ },
7636
+ flag: "x-workspace-id",
7637
+ kind: "string",
7638
+ nullable: false
7639
+ },
7640
+ {
7641
+ name: "expectedRevision",
7642
+ location: "body",
7643
+ required: true,
7644
+ schema: {
7645
+ type: "integer",
7646
+ minimum: -9007199254740991,
7647
+ maximum: 9007199254740991,
7648
+ exclusiveMinimum: 0
7649
+ },
7650
+ flag: "expected-revision",
7651
+ kind: "number",
7652
+ nullable: false
7653
+ }
7654
+ ],
7655
+ conditional: false,
7656
+ response: "empty"
7657
+ },
7658
+ {
7659
+ id: "webhooks.get",
7660
+ group: "webhooks",
7661
+ command: "get",
7662
+ method: "get",
7663
+ path: "/webhooks/{id}",
7664
+ summary: "Get a webhook",
7665
+ description: "Required API key permissions: read:webhooks. Write permissions also grant read access for the same resource.",
7666
+ security: [{ apiKey: [] }, { oauth: [] }],
7667
+ fields: [{
7668
+ name: "id",
7669
+ location: "path",
7670
+ required: true,
7671
+ schema: {
7672
+ type: "string",
7673
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
7674
+ },
7675
+ flag: "id",
7676
+ kind: "string",
7677
+ nullable: false
7678
+ }, {
7679
+ name: "x-workspace-id",
7680
+ location: "header",
7681
+ required: false,
7682
+ schema: {
7683
+ type: "string",
7684
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
7685
+ },
7686
+ flag: "x-workspace-id",
7687
+ kind: "string",
7688
+ nullable: false
7689
+ }],
7690
+ conditional: false,
7691
+ response: "json"
7692
+ },
7693
+ {
7694
+ id: "webhooks.getDelivery",
7695
+ group: "webhooks",
7696
+ command: "get-delivery",
7697
+ method: "get",
7698
+ path: "/webhooks/{id}/deliveries/{deliveryID}",
7699
+ summary: "Get a webhook delivery",
7700
+ description: "Returns retained delivery metadata and, with content authority, the immutable event payload. Expired deliveries return not found even before physical cleanup.\n\nRequired API key permissions: read:webhooks. Write permissions also grant read access for the same resource.",
7701
+ security: [{ apiKey: [] }, { oauth: [] }],
7702
+ fields: [
7703
+ {
7704
+ name: "id",
7705
+ location: "path",
7706
+ required: true,
7707
+ schema: {
7708
+ type: "string",
7709
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
7710
+ },
7711
+ flag: "id",
7712
+ kind: "string",
7713
+ nullable: false
7714
+ },
7715
+ {
7716
+ name: "deliveryID",
7717
+ location: "path",
7718
+ required: true,
7719
+ schema: {
7720
+ type: "string",
7721
+ pattern: "^whdel_[A-Za-z\\d]{1,22}$"
7722
+ },
7723
+ flag: "delivery-id",
7724
+ kind: "string",
7725
+ nullable: false
7726
+ },
7727
+ {
7728
+ name: "x-workspace-id",
7729
+ location: "header",
7730
+ required: false,
7731
+ schema: {
7732
+ type: "string",
7733
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
7734
+ },
7735
+ flag: "x-workspace-id",
7736
+ kind: "string",
7737
+ nullable: false
7738
+ }
7739
+ ],
7740
+ conditional: false,
7741
+ response: "json"
7742
+ },
7743
+ {
7744
+ id: "webhooks.list",
7745
+ group: "webhooks",
7746
+ command: "list",
7747
+ method: "get",
7748
+ path: "/webhooks",
7749
+ summary: "List webhooks",
7750
+ description: "Required API key permissions: read:webhooks. Write permissions also grant read access for the same resource.",
7751
+ security: [{ apiKey: [] }, { oauth: [] }],
7752
+ fields: [
7753
+ {
7754
+ name: "cursor",
7755
+ location: "query",
7756
+ required: false,
7757
+ schema: {
7758
+ type: "string",
7759
+ pattern: "^(?:\\w+?_[A-Za-z\\d]{1,22})$",
7760
+ description: "Cursor from the previous page; keep the same filters"
7761
+ },
7762
+ flag: "cursor",
7763
+ kind: "string",
7764
+ nullable: false
7765
+ },
7766
+ {
7767
+ name: "limit",
7768
+ location: "query",
7769
+ required: false,
7770
+ schema: {
7771
+ type: "integer",
7772
+ minimum: 1,
7773
+ maximum: 100,
7774
+ default: 50
7775
+ },
7776
+ flag: "limit",
7777
+ kind: "number",
7778
+ nullable: false
7779
+ },
7780
+ {
7781
+ name: "x-workspace-id",
7782
+ location: "header",
7783
+ required: false,
7784
+ schema: {
7785
+ type: "string",
7786
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
7787
+ },
7788
+ flag: "x-workspace-id",
7789
+ kind: "string",
7790
+ nullable: false
7791
+ }
7792
+ ],
7793
+ conditional: false,
7794
+ response: "json",
7795
+ pagination: "cursor"
7796
+ },
7797
+ {
7798
+ id: "webhooks.listAttempts",
7799
+ group: "webhooks",
7800
+ command: "list-attempts",
7801
+ method: "get",
7802
+ path: "/webhooks/{id}/deliveries/{deliveryID}/runs/{runID}/attempts",
7803
+ summary: "List delivery attempts",
7804
+ description: "Required API key permissions: read:webhooks. Write permissions also grant read access for the same resource.",
7805
+ security: [{ apiKey: [] }, { oauth: [] }],
7806
+ fields: [
7807
+ {
7808
+ name: "id",
7809
+ location: "path",
7810
+ required: true,
7811
+ schema: {
7812
+ type: "string",
7813
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
7814
+ },
7815
+ flag: "id",
7816
+ kind: "string",
7817
+ nullable: false
7818
+ },
7819
+ {
7820
+ name: "deliveryID",
7821
+ location: "path",
7822
+ required: true,
7823
+ schema: {
7824
+ type: "string",
7825
+ pattern: "^whdel_[A-Za-z\\d]{1,22}$"
7826
+ },
7827
+ flag: "delivery-id",
7828
+ kind: "string",
7829
+ nullable: false
7830
+ },
7831
+ {
7832
+ name: "runID",
7833
+ location: "path",
7834
+ required: true,
7835
+ schema: {
7836
+ type: "string",
7837
+ pattern: "^whrun_[A-Za-z\\d]{1,22}$"
7838
+ },
7839
+ flag: "run-id",
7840
+ kind: "string",
7841
+ nullable: false
7842
+ },
7843
+ {
7844
+ name: "cursor",
7845
+ location: "query",
7846
+ required: false,
7847
+ schema: {
7848
+ type: "string",
7849
+ pattern: "^whatt_[A-Za-z\\d]{1,22}$"
7850
+ },
7851
+ flag: "cursor",
7852
+ kind: "string",
7853
+ nullable: false
7854
+ },
7855
+ {
7856
+ name: "limit",
7857
+ location: "query",
7858
+ required: false,
7859
+ schema: {
7860
+ type: "integer",
7861
+ minimum: 1,
7862
+ maximum: 100,
7863
+ default: 50
7864
+ },
7865
+ flag: "limit",
7866
+ kind: "number",
7867
+ nullable: false
7868
+ },
7869
+ {
7870
+ name: "x-workspace-id",
7871
+ location: "header",
7872
+ required: false,
7873
+ schema: {
7874
+ type: "string",
7875
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
7876
+ },
7877
+ flag: "x-workspace-id",
7878
+ kind: "string",
7879
+ nullable: false
7880
+ }
7881
+ ],
7882
+ conditional: false,
7883
+ response: "json",
7884
+ pagination: "cursor"
7885
+ },
7886
+ {
7887
+ id: "webhooks.listDeliveries",
7888
+ group: "webhooks",
7889
+ command: "list-deliveries",
7890
+ method: "get",
7891
+ path: "/webhooks/{id}/deliveries",
7892
+ summary: "List webhook deliveries",
7893
+ description: "Returns unexpired history, including for a deleted endpoint. Metadata does not include event payloads.\n\nRequired API key permissions: read:webhooks. Write permissions also grant read access for the same resource.",
7894
+ security: [{ apiKey: [] }, { oauth: [] }],
7895
+ fields: [
7896
+ {
7897
+ name: "id",
7898
+ location: "path",
7899
+ required: true,
7900
+ schema: {
7901
+ type: "string",
7902
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
7903
+ },
7904
+ flag: "id",
7905
+ kind: "string",
7906
+ nullable: false
7907
+ },
7908
+ {
7909
+ name: "cursor",
7910
+ location: "query",
7911
+ required: false,
7912
+ schema: {
7913
+ type: "string",
7914
+ pattern: "^whdel_[A-Za-z\\d]{1,22}$"
7915
+ },
7916
+ flag: "cursor",
7917
+ kind: "string",
7918
+ nullable: false
7919
+ },
7920
+ {
7921
+ name: "limit",
7922
+ location: "query",
7923
+ required: false,
7924
+ schema: {
7925
+ type: "integer",
7926
+ minimum: 1,
7927
+ maximum: 100,
7928
+ default: 50
7929
+ },
7930
+ flag: "limit",
7931
+ kind: "number",
7932
+ nullable: false
7933
+ },
7934
+ {
7935
+ name: "type",
7936
+ location: "query",
7937
+ required: false,
7938
+ schema: { $ref: "#/components/schemas/WebhookEventName" },
7939
+ flag: "type",
7940
+ kind: "string",
7941
+ nullable: false
7942
+ },
7943
+ {
7944
+ name: "state",
7945
+ location: "query",
7946
+ required: false,
7947
+ schema: {
7948
+ enum: [
7949
+ "pending",
7950
+ "in_flight",
7951
+ "succeeded",
7952
+ "failed",
7953
+ "cancelled"
7954
+ ],
7955
+ type: "string"
7956
+ },
7957
+ flag: "state",
7958
+ kind: "string",
7959
+ nullable: false
7960
+ },
7961
+ {
7962
+ name: "createdAfter",
7963
+ location: "query",
7964
+ required: false,
7965
+ schema: {
7966
+ type: "string",
7967
+ format: "date-time"
7968
+ },
7969
+ flag: "created-after",
7970
+ kind: "string",
7971
+ nullable: false
7972
+ },
7973
+ {
7974
+ name: "createdBefore",
7975
+ location: "query",
7976
+ required: false,
7977
+ schema: {
7978
+ type: "string",
7979
+ format: "date-time"
7980
+ },
7981
+ flag: "created-before",
7982
+ kind: "string",
7983
+ nullable: false
7984
+ },
7985
+ {
7986
+ name: "x-workspace-id",
7987
+ location: "header",
7988
+ required: false,
7989
+ schema: {
7990
+ type: "string",
7991
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
7992
+ },
7993
+ flag: "x-workspace-id",
7994
+ kind: "string",
7995
+ nullable: false
7996
+ }
7997
+ ],
7998
+ conditional: false,
7999
+ response: "json",
8000
+ pagination: "cursor"
8001
+ },
8002
+ {
8003
+ id: "webhooks.listRuns",
8004
+ group: "webhooks",
8005
+ command: "list-runs",
8006
+ method: "get",
8007
+ path: "/webhooks/{id}/deliveries/{deliveryID}/runs",
8008
+ summary: "List delivery runs",
8009
+ description: "Required API key permissions: read:webhooks. Write permissions also grant read access for the same resource.",
8010
+ security: [{ apiKey: [] }, { oauth: [] }],
8011
+ fields: [
8012
+ {
8013
+ name: "id",
8014
+ location: "path",
8015
+ required: true,
8016
+ schema: {
8017
+ type: "string",
8018
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
8019
+ },
8020
+ flag: "id",
8021
+ kind: "string",
8022
+ nullable: false
8023
+ },
8024
+ {
8025
+ name: "deliveryID",
8026
+ location: "path",
8027
+ required: true,
8028
+ schema: {
8029
+ type: "string",
8030
+ pattern: "^whdel_[A-Za-z\\d]{1,22}$"
8031
+ },
8032
+ flag: "delivery-id",
8033
+ kind: "string",
8034
+ nullable: false
8035
+ },
8036
+ {
8037
+ name: "cursor",
8038
+ location: "query",
8039
+ required: false,
8040
+ schema: {
8041
+ type: "string",
8042
+ pattern: "^whrun_[A-Za-z\\d]{1,22}$"
8043
+ },
8044
+ flag: "cursor",
8045
+ kind: "string",
8046
+ nullable: false
8047
+ },
8048
+ {
8049
+ name: "limit",
8050
+ location: "query",
8051
+ required: false,
8052
+ schema: {
8053
+ type: "integer",
8054
+ minimum: 1,
8055
+ maximum: 100,
8056
+ default: 50
8057
+ },
8058
+ flag: "limit",
8059
+ kind: "number",
8060
+ nullable: false
8061
+ },
8062
+ {
8063
+ name: "x-workspace-id",
8064
+ location: "header",
8065
+ required: false,
8066
+ schema: {
8067
+ type: "string",
8068
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
8069
+ },
8070
+ flag: "x-workspace-id",
8071
+ kind: "string",
8072
+ nullable: false
8073
+ }
8074
+ ],
8075
+ conditional: false,
8076
+ response: "json",
8077
+ pagination: "cursor"
8078
+ },
8079
+ {
8080
+ id: "webhooks.redeliver",
8081
+ group: "webhooks",
8082
+ command: "redeliver",
8083
+ method: "post",
8084
+ path: "/webhooks/{id}/deliveries/{deliveryID}/redeliver",
8085
+ summary: "Redeliver a webhook event",
8086
+ description: "Checks the reviewed destination revision and current scope authority. Reuses the original event ID, payload, and expiry. Returns a conflict if a run is active, or the endpoint is disabled or deleted.\n\nRequired API key permissions: webhooks. Write permissions also grant read access for the same resource.",
8087
+ security: [{ apiKey: [] }, { oauth: [] }],
8088
+ fields: [
8089
+ {
8090
+ name: "id",
8091
+ location: "path",
8092
+ required: true,
8093
+ schema: {
8094
+ type: "string",
8095
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
8096
+ },
8097
+ flag: "id",
8098
+ kind: "string",
8099
+ nullable: false
8100
+ },
8101
+ {
8102
+ name: "deliveryID",
8103
+ location: "path",
8104
+ required: true,
8105
+ schema: {
8106
+ type: "string",
8107
+ pattern: "^whdel_[A-Za-z\\d]{1,22}$"
8108
+ },
8109
+ flag: "delivery-id",
8110
+ kind: "string",
8111
+ nullable: false
8112
+ },
8113
+ {
8114
+ name: "x-workspace-id",
8115
+ location: "header",
8116
+ required: false,
8117
+ schema: {
8118
+ type: "string",
8119
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
8120
+ },
8121
+ flag: "x-workspace-id",
8122
+ kind: "string",
8123
+ nullable: false
8124
+ },
8125
+ {
8126
+ name: "expectedDestinationRevision",
8127
+ location: "body",
8128
+ required: true,
8129
+ schema: {
8130
+ type: "integer",
8131
+ minimum: -9007199254740991,
8132
+ maximum: 9007199254740991,
8133
+ exclusiveMinimum: 0
8134
+ },
8135
+ flag: "expected-destination-revision",
8136
+ kind: "number",
8137
+ nullable: false
8138
+ }
8139
+ ],
8140
+ conditional: false,
8141
+ response: "json"
8142
+ },
8143
+ {
8144
+ id: "webhooks.rotateSecret",
8145
+ group: "webhooks",
8146
+ command: "rotate-secret",
8147
+ method: "post",
8148
+ path: "/webhooks/{id}/rotate-secret",
8149
+ summary: "Rotate a webhook secret",
8150
+ description: "Returns a new secret once. Overlap keeps the old secret valid for 24 hours. Immediate rotation retires it at once. Normal rotation conflicts while a previous overlap remains active.\n\nRequired API key permissions: webhooks. Write permissions also grant read access for the same resource.",
8151
+ security: [{ apiKey: [] }, { oauth: [] }],
8152
+ fields: [
8153
+ {
8154
+ name: "id",
8155
+ location: "path",
8156
+ required: true,
8157
+ schema: {
8158
+ type: "string",
8159
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
8160
+ },
8161
+ flag: "id",
8162
+ kind: "string",
8163
+ nullable: false
8164
+ },
8165
+ {
8166
+ name: "x-workspace-id",
8167
+ location: "header",
8168
+ required: false,
8169
+ schema: {
8170
+ type: "string",
8171
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
8172
+ },
8173
+ flag: "x-workspace-id",
8174
+ kind: "string",
8175
+ nullable: false
8176
+ },
8177
+ {
8178
+ name: "expectedRevision",
8179
+ location: "body",
8180
+ required: true,
8181
+ schema: {
8182
+ type: "integer",
8183
+ minimum: -9007199254740991,
8184
+ maximum: 9007199254740991,
8185
+ exclusiveMinimum: 0
8186
+ },
8187
+ flag: "expected-revision",
8188
+ kind: "number",
8189
+ nullable: false
8190
+ },
8191
+ {
8192
+ name: "mode",
8193
+ location: "body",
8194
+ required: true,
8195
+ schema: {
8196
+ enum: ["overlap", "immediate"],
8197
+ type: "string"
8198
+ },
8199
+ flag: "mode",
8200
+ kind: "string",
8201
+ nullable: false
8202
+ }
8203
+ ],
8204
+ conditional: false,
8205
+ response: "json"
8206
+ },
8207
+ {
8208
+ id: "webhooks.sendTest",
8209
+ group: "webhooks",
8210
+ command: "send-test",
8211
+ method: "post",
8212
+ path: "/webhooks/{id}/test",
8213
+ summary: "Send a webhook test",
8214
+ description: "Schedules one signed synthetic attempt for this endpoint, including when disabled. The event must be selected by the endpoint. It does not enable the endpoint or change content.\n\nRequired API key permissions: webhooks. Write permissions also grant read access for the same resource.",
8215
+ security: [{ apiKey: [] }, { oauth: [] }],
8216
+ fields: [
8217
+ {
8218
+ name: "id",
8219
+ location: "path",
8220
+ required: true,
8221
+ schema: {
8222
+ type: "string",
8223
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
8224
+ },
8225
+ flag: "id",
8226
+ kind: "string",
8227
+ nullable: false
8228
+ },
8229
+ {
8230
+ name: "x-workspace-id",
8231
+ location: "header",
8232
+ required: false,
8233
+ schema: {
8234
+ type: "string",
8235
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
8236
+ },
8237
+ flag: "x-workspace-id",
8238
+ kind: "string",
8239
+ nullable: false
8240
+ },
8241
+ {
8242
+ name: "expectedRevision",
8243
+ location: "body",
8244
+ required: true,
8245
+ schema: {
8246
+ type: "integer",
8247
+ minimum: -9007199254740991,
8248
+ maximum: 9007199254740991,
8249
+ exclusiveMinimum: 0
8250
+ },
8251
+ flag: "expected-revision",
8252
+ kind: "number",
8253
+ nullable: false
8254
+ },
8255
+ {
8256
+ name: "type",
8257
+ location: "body",
8258
+ required: true,
8259
+ schema: { $ref: "#/components/schemas/WebhookEventName" },
8260
+ flag: "type",
8261
+ kind: "string",
8262
+ nullable: false
8263
+ }
8264
+ ],
8265
+ conditional: false,
8266
+ response: "json"
8267
+ },
8268
+ {
8269
+ id: "webhooks.update",
8270
+ group: "webhooks",
8271
+ command: "update",
8272
+ method: "put",
8273
+ path: "/webhooks/{id}",
8274
+ summary: "Update a webhook",
8275
+ description: "Checks expectedRevision and validates the merged configuration. A URL change cancels pending deliveries and returns a replacement secret once. Scope changes require authority over the previous and new scope.\n\nRequired API key permissions: webhooks. Write permissions also grant read access for the same resource.",
8276
+ security: [{ apiKey: [] }, { oauth: [] }],
8277
+ fields: [
8278
+ {
8279
+ name: "id",
8280
+ location: "path",
8281
+ required: true,
8282
+ schema: {
8283
+ type: "string",
8284
+ pattern: "^wh_[A-Za-z\\d]{1,22}$"
8285
+ },
8286
+ flag: "id",
8287
+ kind: "string",
8288
+ nullable: false
8289
+ },
8290
+ {
8291
+ name: "x-workspace-id",
8292
+ location: "header",
8293
+ required: false,
8294
+ schema: {
8295
+ type: "string",
8296
+ description: "Required for OAuth: ID of the workspace to access. API keys use their own workspace."
8297
+ },
8298
+ flag: "x-workspace-id",
8299
+ kind: "string",
8300
+ nullable: false
8301
+ },
8302
+ {
8303
+ name: "name",
8304
+ location: "body",
8305
+ required: false,
8306
+ schema: {
8307
+ type: "string",
8308
+ minLength: 1,
8309
+ maxLength: 100
8310
+ },
8311
+ flag: "name",
8312
+ kind: "string",
8313
+ nullable: false
8314
+ },
8315
+ {
8316
+ name: "url",
8317
+ location: "body",
8318
+ required: false,
8319
+ schema: {
8320
+ type: "string",
8321
+ maxLength: 2048,
8322
+ format: "uri"
8323
+ },
8324
+ flag: "url",
8325
+ kind: "string",
8326
+ nullable: false
8327
+ },
8328
+ {
8329
+ name: "enabled",
8330
+ location: "body",
8331
+ required: false,
8332
+ schema: { type: "boolean" },
8333
+ flag: "enabled",
8334
+ kind: "boolean",
8335
+ nullable: false
8336
+ },
8337
+ {
8338
+ name: "eventTypes",
8339
+ location: "body",
8340
+ required: false,
8341
+ schema: {
8342
+ type: "array",
8343
+ minItems: 1,
8344
+ maxItems: 14,
8345
+ items: { $ref: "#/components/schemas/WebhookEventName" }
8346
+ },
8347
+ flag: "event-types",
8348
+ kind: "json",
8349
+ nullable: false
8350
+ },
8351
+ {
8352
+ name: "schemaVersion",
8353
+ location: "body",
8354
+ required: false,
8355
+ schema: { const: 1 },
8356
+ flag: "schema-version",
8357
+ kind: "number",
8358
+ nullable: false
8359
+ },
8360
+ {
8361
+ name: "collections",
8362
+ location: "body",
8363
+ required: false,
8364
+ schema: { anyOf: [{
8365
+ type: "object",
8366
+ properties: { mode: { const: "all" } },
8367
+ required: ["mode"],
8368
+ additionalProperties: false
8369
+ }, {
8370
+ type: "object",
8371
+ properties: {
8372
+ mode: { const: "selected" },
8373
+ roots: {
8374
+ type: "array",
8375
+ minItems: 1,
8376
+ maxItems: 100,
8377
+ items: {
8378
+ type: "string",
8379
+ pattern: "^coll_[A-Za-z\\d]{1,22}$"
8380
+ }
8381
+ }
8382
+ },
8383
+ required: ["mode", "roots"],
8384
+ additionalProperties: false
8385
+ }] },
8386
+ flag: "collections",
8387
+ kind: "json",
8388
+ nullable: false
8389
+ },
8390
+ {
8391
+ name: "channels",
8392
+ location: "body",
8393
+ required: false,
8394
+ schema: { anyOf: [{
8395
+ type: "object",
8396
+ properties: { mode: { const: "all" } },
8397
+ required: ["mode"],
8398
+ additionalProperties: false
8399
+ }, {
8400
+ type: "object",
8401
+ properties: {
8402
+ mode: { const: "selected" },
8403
+ codes: {
8404
+ type: "array",
8405
+ minItems: 1,
8406
+ maxItems: 100,
8407
+ items: {
8408
+ type: "string",
8409
+ minLength: 1,
8410
+ maxLength: 50
8411
+ }
8412
+ }
8413
+ },
8414
+ required: ["mode", "codes"],
8415
+ additionalProperties: false
8416
+ }] },
8417
+ flag: "channels",
8418
+ kind: "json",
8419
+ nullable: false
8420
+ },
8421
+ {
8422
+ name: "restrictedContent",
8423
+ location: "body",
8424
+ required: false,
8425
+ schema: {
8426
+ type: "boolean",
8427
+ description: "Include restricted collections, including ones restricted later"
8428
+ },
8429
+ flag: "restricted-content",
8430
+ kind: "boolean",
8431
+ nullable: false
8432
+ },
8433
+ {
8434
+ name: "expectedRevision",
8435
+ location: "body",
8436
+ required: true,
8437
+ schema: {
8438
+ type: "integer",
8439
+ minimum: -9007199254740991,
8440
+ maximum: 9007199254740991,
8441
+ exclusiveMinimum: 0
8442
+ },
8443
+ flag: "expected-revision",
8444
+ kind: "number",
8445
+ nullable: false
8446
+ }
8447
+ ],
8448
+ conditional: false,
8449
+ response: "json"
8450
+ },
8451
+ {
8452
+ id: "workspaces.list",
8453
+ group: "workspaces",
8454
+ command: "list",
8455
+ method: "get",
8456
+ path: "/workspaces",
8457
+ summary: "List available workspaces",
8458
+ description: "Lists workspaces available to the OAuth user, with current role permissions and plan access. Browser sessions can list workspaces across signed-in accounts. Does not require workspace selection and does not count toward API usage.",
8459
+ security: [{ oauth: [] }],
8460
+ fields: [],
8461
+ conditional: false,
8462
+ response: "json"
8463
+ }
8464
+ ];
8465
+ const schemas = {
8466
+ PropertyFilter: { anyOf: [
8467
+ { $ref: "#/components/schemas/TextPropertyFilter" },
8468
+ { $ref: "#/components/schemas/NumberPropertyFilter" },
8469
+ { $ref: "#/components/schemas/BooleanPropertyFilter" },
8470
+ { $ref: "#/components/schemas/DatePropertyFilter" }
8471
+ ] },
8472
+ TextPropertyFilter: {
8473
+ type: "object",
8474
+ properties: {
8475
+ kind: { const: "text" },
8476
+ key: {
8477
+ type: "string",
7027
8478
  minLength: 1,
7028
8479
  maxLength: 100,
7029
8480
  description: "Property identifier"
@@ -7122,6 +8573,25 @@ const schemas = {
7122
8573
  "value"
7123
8574
  ]
7124
8575
  },
8576
+ WebhookEventName: {
8577
+ enum: [
8578
+ "entry.created",
8579
+ "entry.updated",
8580
+ "entry.content_saved",
8581
+ "entry.moved",
8582
+ "entry.deleted",
8583
+ "entry.restored",
8584
+ "collection.created",
8585
+ "collection.updated",
8586
+ "collection.moved",
8587
+ "collection.deleted",
8588
+ "collection.restored",
8589
+ "publishing.channel_advanced",
8590
+ "publishing.channel_created",
8591
+ "publishing.channel_deleted"
8592
+ ],
8593
+ type: "string"
8594
+ },
7125
8595
  Permission: {
7126
8596
  enum: [
7127
8597
  "content",
@@ -7134,6 +8604,8 @@ const schemas = {
7134
8604
  "read:restricted_collections",
7135
8605
  "memberships",
7136
8606
  "roles",
8607
+ "webhooks",
8608
+ "read:webhooks",
7137
8609
  "workspace"
7138
8610
  ],
7139
8611
  type: "string"
@@ -7321,6 +8793,21 @@ const bindings = {
7321
8793
  "versions.list": (client, input, options) => client.versions.list(input, options),
7322
8794
  "versions.revert": (client, input, options) => client.versions.revert(input, options),
7323
8795
  "versions.update": (client, input, options) => client.versions.update(input, options),
8796
+ "webhooks.bulkDelete": (client, input, options) => client.webhooks.bulkDelete(input, options),
8797
+ "webhooks.bulkRedeliver": (client, input, options) => client.webhooks.bulkRedeliver(input, options),
8798
+ "webhooks.bulkSetEnabled": (client, input, options) => client.webhooks.bulkSetEnabled(input, options),
8799
+ "webhooks.create": (client, input, options) => client.webhooks.create(input, options),
8800
+ "webhooks.delete": (client, input, options) => client.webhooks.delete(input, options),
8801
+ "webhooks.get": (client, input, options) => client.webhooks.get(input, options),
8802
+ "webhooks.getDelivery": (client, input, options) => client.webhooks.getDelivery(input, options),
8803
+ "webhooks.list": (client, input, options) => client.webhooks.list(input, options),
8804
+ "webhooks.listAttempts": (client, input, options) => client.webhooks.listAttempts(input, options),
8805
+ "webhooks.listDeliveries": (client, input, options) => client.webhooks.listDeliveries(input, options),
8806
+ "webhooks.listRuns": (client, input, options) => client.webhooks.listRuns(input, options),
8807
+ "webhooks.redeliver": (client, input, options) => client.webhooks.redeliver(input, options),
8808
+ "webhooks.rotateSecret": (client, input, options) => client.webhooks.rotateSecret(input, options),
8809
+ "webhooks.sendTest": (client, input, options) => client.webhooks.sendTest(input, options),
8810
+ "webhooks.update": (client, input, options) => client.webhooks.update(input, options),
7324
8811
  "workspaces.list": (client, input, options) => client.workspaces.list(input, options)
7325
8812
  };
7326
8813
  //#endregion
@@ -7532,7 +9019,7 @@ const registerAPI = (program, signal) => {
7532
9019
  //#endregion
7533
9020
  //#region src/types/register.ts
7534
9021
  const registerTypes = (program, signal) => {
7535
- program.command("types").description("Generate TypeScript types for workspace content").command("generate").description("Generate a types file, check it for CI, or watch for metadata changes").addOption(new Option("--source <source>", "Content source (default: saved source or published)").choices(["published", "current"])).option("--channel <code>", "Follow a publishing channel").option("--snapshot <id>", "Use a fixed published snapshot").option("--collection <selectors...>", "Collection IDs or decoded paths, including descendants").option("--all-collections", "Include all accessible collections").option("--output <file>", "Types file relative to config, or the working directory without config").option("--include-entry-ids", "Include entry ID types").option("--no-include-entry-ids", "Omit entry ID types").option("--include-entry-paths", "Include entry path types").option("--no-include-entry-paths", "Omit entry path types").option("--include-tree", "Include exact tree types").option("--no-include-tree", "Omit exact tree types").option("--check", "Compare against live metadata without writes; exit 3 if missing or stale").option("--watch", "Poll in the foreground; no daemon or overlapping requests").option("--interval <seconds>", "Watch polling interval, 1–300 seconds (default: 10)").option("--force", "Replace an unrelated output file; with watch, applies only until the first successful generation").addHelpText("after", "\nCommit the generated file and use --check in CI. Channel/current sources follow live changes;\n--snapshot fixes the source while that snapshot is retained. Options override saved config.\nWatch holds configuration fixed; restart it after config changes. Nothing runs at install time.").action(async (options, command) => {
9022
+ program.command("types").description("Generate TypeScript types for workspace content").command("generate").description("Generate a types file, check it for CI, or watch for metadata changes").addOption(new Option("--source <source>", "Content source (default: saved source or published)").choices(["published", "current"])).option("--channel <code>", "Follow a publishing channel").option("--snapshot <id>", "Use a fixed published snapshot").option("--collection <selectors...>", "Collection IDs or decoded paths, including descendants").option("--collection-slug-path <paths...>", "Decoded collection slug paths, including descendants").option("--all-collections", "Include all accessible collections").option("--output <file>", "Types file relative to config, or the working directory without config").option("--include-entry-ids", "Include entry ID types").option("--no-include-entry-ids", "Omit entry ID types").option("--include-entry-paths", "Include entry path types").option("--no-include-entry-paths", "Omit entry path types").option("--include-tree", "Include exact tree types").option("--no-include-tree", "Omit exact tree types").option("--check", "Compare against live metadata without writes; exit 3 if missing or stale").option("--watch", "Poll in the foreground; no daemon or overlapping requests").option("--interval <seconds>", "Watch polling interval, 1–300 seconds (default: 10)").option("--force", "Replace an unrelated output file; with watch, applies only until the first successful generation").addHelpText("after", "\nCommit the generated file and use --check in CI. Channel/current sources follow live changes;\n--snapshot fixes the source while that snapshot is retained. Options override saved config.\nWatch holds configuration fixed; restart it after config changes. Nothing runs at install time.").action(async (options, command) => {
7536
9023
  const globals = command.optsWithGlobals();
7537
9024
  await executeTypes(await createCommandContext(globals, signal), options);
7538
9025
  });
@@ -7543,9 +9030,12 @@ const registerTypes = (program, signal) => {
7543
9030
  const createProgram = (signal) => {
7544
9031
  const program = new Command().name("andesine").description("Command-line tools for Andesine").version(version).option("--config <file>", "Use a project configuration file (ANDESINE_CONFIG)").option("--profile <name>", "Select a saved user profile (ANDESINE_PROFILE)").option("--base-url <url>", "Set the API root (ANDESINE_BASE_URL)").option("--workspace <id>", "Select a workspace (ANDESINE_WORKSPACE_ID)").option("--no-interactive", "Disable interactive prompts").showHelpAfterError().exitOverride();
7545
9032
  const auth = program.command("auth").description("Manage CLI authentication");
7546
- program.command("init").description("Configure this project for Andesine content and type generation").addOption(new Option("--source <source>", "Type source: published (default) or current").choices(["published", "current"])).option("--channel <code>", "Publishing channel (default: published)").option("--snapshot <id>", "Use a fixed published snapshot instead of a channel").option("--collection <selectors...>", "Collection IDs or decoded paths, including collection-ID anchors").option("--all-collections", "Include all accessible collections").option("--output <file>", "Types file relative to config (default: src/andesine.generated.ts)").option("--include-entry-ids", "Include entry ID types").option("--no-include-entry-ids", "Omit entry ID types").option("--include-entry-paths", "Include entry path types").option("--no-include-entry-paths", "Omit entry path types").option("--include-tree", "Include exact tree types").option("--no-include-tree", "Omit exact tree types").option("--yes", "Save without confirmation, including updates to an existing config").option("--generate", "Generate the configured types file after saving").option("--no-generate", "Save configuration without generating types").option("--no-browser", "Do not open a browser if login is needed").addOption(new Option("--credential-store <store>", "Credential store for a new login").choices(["keyring", "file"])).action(async (_options, command) => {
9033
+ program.command("init").description("Configure this project for Andesine content and type generation").addOption(new Option("--source <source>", "Type source: published (default) or current").choices(["published", "current"])).option("--channel <code>", "Publishing channel (default: published)").option("--snapshot <id>", "Use a fixed published snapshot instead of a channel").option("--collection <selectors...>", "Collection IDs or decoded paths, including collection-ID anchors").option("--collection-slug-path <paths...>", "Decoded collection slug paths, including descendants").option("--all-collections", "Include all accessible collections").option("--output <file>", "Types file relative to config (default: src/andesine.generated.ts)").option("--include-entry-ids", "Include entry ID types").option("--no-include-entry-ids", "Omit entry ID types").option("--include-entry-paths", "Include entry path types").option("--no-include-entry-paths", "Omit entry path types").option("--include-tree", "Include exact tree types").option("--no-include-tree", "Omit exact tree types").option("--yes", "Save without confirmation, including updates to an existing config").option("--generate", "Generate the configured types file after saving").option("--no-generate", "Save configuration without generating types").option("--no-browser", "Do not open a browser if login is needed").addOption(new Option("--credential-store <store>", "Credential store for a new login").choices(["keyring", "file"])).action(async (_options, command) => {
7547
9034
  await init(command.optsWithGlobals(), signal);
7548
9035
  });
9036
+ program.command("pages").description("Create Andesine Pages documentation sites").command("init").description("Create an Andesine Pages site from the template in a new or empty directory").argument("[directory]", "Directory of the new site", "docs").action(async (directory, _options, command) => {
9037
+ await initPages(directory, createOutput(command.optsWithGlobals().interactive, signal));
9038
+ });
7549
9039
  auth.command("login").description("Sign in through browser device approval").option("--no-browser", "Display the URL and code without opening a browser").addOption(new Option("--credential-store <store>", "Store credentials in the OS keyring or an unencrypted private file").choices(["keyring", "file"])).action(async (options, command) => {
7550
9040
  const context = await createCommandContext(command.optsWithGlobals(), signal, { allowNewProfile: true });
7551
9041
  await login(context, options);