@bendyline/gezel-catalog 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { CatalogKind, CatalogItemSummary, CatalogItemDetail, CatalogItemVersionInfo, CraftbookTestSpec, ToolsetCategory, ToolsetManifest } from '@bendyline/gezel';
1
+ import { CatalogKind, CatalogItemSummary, CatalogItemDetail, CatalogItemVersionInfo, CraftbookTestSpec, ToolsetCategory, CraftbookDoc, NewCraftbookStep, CraftbookStepWritableOutputMedium, CraftbookStepOutputMedium, ToolsetManifest } from '@bendyline/gezel';
2
+ import { z } from 'zod';
2
3
 
3
4
  /**
4
5
  * ─ CatalogSource ───────────────────────────────────────────────────
@@ -43,7 +44,7 @@ interface CatalogSource {
43
44
  readItemFile(kind: CatalogKind, id: string, relPath: string, version?: string): Promise<Buffer | null>;
44
45
  /**
45
46
  * Optional: every file under an item's folder as item-relative paths. Only
46
- * on-disk sources implement it (used by the `.gzl` exporter); synthetic
47
+ * on-disk sources implement it (used by the `.gezapp` exporter); synthetic
47
48
  * sources (builtin toolsets) omit it.
48
49
  */
49
50
  listItemFiles?(kind: CatalogKind, id: string): Promise<string[]>;
@@ -122,7 +123,7 @@ declare class BundledSource implements CatalogSource {
122
123
  /**
123
124
  * Every file under an item's folder, as item-relative paths (e.g.
124
125
  * `manifest.json`, `versions/1.0.0/pages/gallery/index.html`), sorted. Used
125
- * by the `.gzl` exporter to pack an item verbatim — pages, seeds, and all
126
+ * by the `.gezapp` exporter to pack an item verbatim — pages, seeds, and all
126
127
  * assets, not just the ones the manifest names. Read each back with
127
128
  * `readItemFile(kind, id, relPath)` (no version → item-relative). Empty when
128
129
  * the item folder is missing.
@@ -172,9 +173,11 @@ declare class BundledSource implements CatalogSource {
172
173
  *
173
174
  * 1. BuiltinToolsetsSource — synthetic groups for our in-process MCP
174
175
  * server. Tools we ship and own.
175
- * 2. BundledSourcepre-reviewed third-party catalog shipped with
176
+ * 2. InstalledAiAppsSourceactive `.gezapp` packages mounted as units.
177
+ * 3. LocalCatalogSource — manually authored/imported user content.
178
+ * 4. BundledSource — pre-reviewed third-party catalog shipped with
176
179
  * the app (`data/`). Hand-curated.
177
- * 3. CommunitySource — auto-imported entries from the upstream MCP
180
+ * 5. CommunitySource — auto-imported entries from the upstream MCP
178
181
  * registry (`data/community/`). Permissively-licensed only;
179
182
  * treated as a second tier that the UI can label distinctly.
180
183
  *
@@ -185,9 +188,9 @@ declare class CatalogService {
185
188
  private readonly contentRootProvider;
186
189
  /**
187
190
  * @param sources explicit source list (replaces the defaults entirely).
188
- * @param opts.localRoot a GEZEL_HOME to read user-installed / `.gzl`-imported
189
- * catalog items from (project types + gezel roles). Appended just ahead of
190
- * the bundled tier so a user's imported item shadows a same-id bundled one.
191
+ * @param opts.localRoot a GEZEL_HOME to read user-installed catalog items and
192
+ * mounted `.gezapp` packages from. Both sit ahead of the bundled tier so a
193
+ * user's installed item shadows a same-id bundled one.
191
194
  * @param opts.contentRoot dynamic gilde content root for the default
192
195
  * bundled + community tiers, re-read on every disk access. The live
193
196
  * gilde update mechanism flips it without reconstructing this service
@@ -217,7 +220,7 @@ declare class CatalogService {
217
220
  label: string;
218
221
  }>;
219
222
  /**
220
- * Merged listing across sources. Items from earlier sources (bundled)
223
+ * Merged listing across sources. Items from earlier sources
221
224
  * shadow later sources (remote) on id collision.
222
225
  */
223
226
  list(kind: CatalogKind): Promise<CatalogItemSummary[]>;
@@ -288,6 +291,29 @@ declare class LocalCatalogSource extends BundledSource {
288
291
  listItemFiles(kind: CatalogKind, id: string): Promise<string[]>;
289
292
  }
290
293
 
294
+ /**
295
+ * Dynamic catalog source over the active `.gezapp` registry. Package versions
296
+ * stay mounted as units under `~/.gezel/ai-apps/`; the importer never flattens
297
+ * their files into the generic local catalog, so provenance and uninstall
298
+ * remain tractable.
299
+ */
300
+ declare class InstalledAiAppsSource implements CatalogSource {
301
+ private readonly home;
302
+ private readonly gezelVersion?;
303
+ readonly id = "installed-ai-apps";
304
+ readonly label = "AI Apps";
305
+ constructor(home: string, gezelVersion?: string | undefined);
306
+ listKinds(): Promise<CatalogKind[]>;
307
+ list(kind: CatalogKind): Promise<CatalogItemSummary[]>;
308
+ get(kind: CatalogKind, id: string, version?: string): Promise<CatalogItemDetail | null>;
309
+ listVersions(kind: CatalogKind, id: string): Promise<CatalogItemVersionInfo[]>;
310
+ readItemFile(kind: CatalogKind, id: string, relPath: string, version?: string): Promise<Buffer | null>;
311
+ listItemFiles(kind: CatalogKind, id: string): Promise<string[]>;
312
+ private registry;
313
+ private sources;
314
+ private rescope;
315
+ }
316
+
291
317
  /**
292
318
  * Categorize a toolset by its identity-manifest fields. The order of
293
319
  * checks matches the priority order of `RULES` above.
@@ -304,6 +330,58 @@ declare function categorizeToolset(input: {
304
330
  maintainerName?: string;
305
331
  }): ToolsetCategory;
306
332
 
333
+ /** Resolve the authored blueprint's primary result drawer without prompt inference. */
334
+ declare function outputMediumForCraftbookBlueprint(step: NewCraftbookStep): CraftbookStepOutputMedium;
335
+ /**
336
+ * Every output surface the authored step procedure requires after applying
337
+ * the same inference used to persist generated policies. Runtime consumers
338
+ * use this as a compatibility floor for tasks embedded from an older catalog
339
+ * whose generated `additionalOutputMedia` predates the current detector.
340
+ */
341
+ declare function outputMediaForCraftbookBlueprint(step: NewCraftbookStep): ReadonlySet<CraftbookStepWritableOutputMedium>;
342
+ /**
343
+ * Add deterministic subtractive policies to every top-level and fanout step.
344
+ * Existing authored denials are preserved; generated defaults only add
345
+ * groups for which the procedure carries no positive signal.
346
+ */
347
+ declare function applyDefaultCraftbookStepPolicies(doc: CraftbookDoc): CraftbookDoc;
348
+
349
+ /**
350
+ * The gilde `schemas/` payload, defined once and consumed twice: the
351
+ * `export-gilde-schemas` script writes it into a gilde checkout, and
352
+ * `gilde-schema-freshness.test.ts` diffs it against the gilde this build
353
+ * actually resolves.
354
+ *
355
+ * The two consumers resolve gilde differently on purpose. The script
356
+ * writes to the sibling checkout (`GILDE_DIR`) because that is what you
357
+ * PR; the test reads the RESOLVED package (`gildePackageRoot()`) because
358
+ * that is what gezel ships against. With `pnpm link:gilde` they are the
359
+ * same tree; without it, the test is checking the pinned tarball.
360
+ *
361
+ * Why the test exists: gilde's `build-index` normalizes every manifest
362
+ * through these committed JSON Schemas, and a property the schema does
363
+ * not declare is dropped from the published `index.json` — which is the
364
+ * fast path `BundledSource` reads at runtime. So a core schema field
365
+ * added without re-exporting does not fail anywhere; it is silently
366
+ * erased from shipped content. Three craftbooks were shipping that way
367
+ * (`pull-request-review` lost `corpusCoverage.artifact`,
368
+ * `powerpoint-deck` lost `markdownHeadingsMatch.outlineArtifact`,
369
+ * `invoice-run` lost `spawn.overArtifact`) before anyone noticed.
370
+ */
371
+
372
+ /**
373
+ * `unrepresentable` stays at its default ('throw') so a future transform
374
+ * in a published schema fails loudly here instead of silently weakening
375
+ * gilde validation.
376
+ */
377
+ declare function renderSchema(filename: string, schema: z.ZodType): string;
378
+ /**
379
+ * Every file the gilde `schemas/` directory should contain, as exact
380
+ * file contents. Byte-for-byte what the exporter writes, so a test can
381
+ * compare against the committed copies without reimplementing anything.
382
+ */
383
+ declare function renderGildeSchemaFiles(): Array<[filename: string, content: string]>;
384
+
307
385
  /**
308
386
  * ─ Built-in toolsets ────────────────────────────────────────────────
309
387
  *
@@ -485,10 +563,10 @@ declare function validateGildeContentUpgrade(opts: {
485
563
  /**
486
564
  * Tiny Hugging Face Hub API client used by catalog tooling.
487
565
  *
488
- * Scope: enough to list the files in a model repo and extract their
489
- * sha256 + size for manifest generation. Not a general-purpose HF SDK —
490
- * we don't need authentication, dataset endpoints, model cards, or
491
- * downloads (the install path has its own streaming downloader).
566
+ * Scope: enough to list the files in a model or dataset repo and extract
567
+ * their sha256 + size for manifest generation. Not a general-purpose HF SDK —
568
+ * we don't need authentication, model cards, or downloads (the install path
569
+ * has its own streaming downloader).
492
570
  *
493
571
  * The Hub publishes file metadata at `/api/models/<repo>/tree/<rev>`.
494
572
  * Each entry looks like:
@@ -521,9 +599,13 @@ interface HfFileEntry {
521
599
  /** True when the sha came from `lfs.oid` (a real sha256). */
522
600
  lfsBacked: boolean;
523
601
  }
602
+ /** Hub repository kind; the API path differs (`/api/models/` vs `/api/datasets/`). */
603
+ type HfRepoType = 'model' | 'dataset';
524
604
  interface FetchTreeOptions {
525
605
  /** Branch / tag / commit. Defaults to `main`. */
526
606
  rev?: string;
607
+ /** Defaults to `model`. Knowledge catalogs live in dataset repos. */
608
+ repoType?: HfRepoType;
527
609
  /** Test seam — defaults to global fetch. */
528
610
  fetchImpl?: typeof fetch;
529
611
  }
@@ -545,6 +627,7 @@ declare function fetchHuggingfaceTree(repo: string, opts?: FetchTreeOptions): Pr
545
627
  */
546
628
  declare function fetchHuggingfaceCommit(repo: string, opts?: {
547
629
  rev?: string;
630
+ repoType?: HfRepoType;
548
631
  fetchImpl?: typeof fetch;
549
632
  }): Promise<string>;
550
633
  /**
@@ -560,4 +643,4 @@ declare function totalSize(files: ReadonlyArray<HfFileEntry>): number;
560
643
  */
561
644
  declare function selectMlxInstallFiles(files: ReadonlyArray<HfFileEntry>): HfFileEntry[];
562
645
 
563
- export { BUILTIN_TOOLSETS, BUILTIN_TOOL_TO_GROUP, type BuiltinToolsetGroup, BuiltinToolsetsSource, BundledSource, type BundledSourceOptions, CatalogService, type CatalogSource, CommunitySource, type FetchTreeOptions, GILDE_PACKAGE_NAME, type GildeContentRegression, type GildeContentValidation, type GildeReleaseInfo, type HfFileEntry, LocalCatalogSource, builtinCatalogId, categorizeToolset, compareRelease, extractNpmPackageTarball, fetchGildeReleases, fetchHuggingfaceCommit, fetchHuggingfaceTree, getBuiltinToolset, gildeDataDir, gildePackageRoot, installNpmPackageToolset, isGildeReleaseVersion, pickGildePatchUpdate, publishStagedNpmInstall, recoverInterruptedNpmInstall, selectMlxInstallFiles, stageGildeVersion, totalSize, validateGildeContentUpgrade, validateNpmArchiveEntry, verifyTarballSha256 };
646
+ export { BUILTIN_TOOLSETS, BUILTIN_TOOL_TO_GROUP, type BuiltinToolsetGroup, BuiltinToolsetsSource, BundledSource, type BundledSourceOptions, CatalogService, type CatalogSource, CommunitySource, type FetchTreeOptions, GILDE_PACKAGE_NAME, type GildeContentRegression, type GildeContentValidation, type GildeReleaseInfo, type HfFileEntry, InstalledAiAppsSource, LocalCatalogSource, applyDefaultCraftbookStepPolicies, builtinCatalogId, categorizeToolset, compareRelease, extractNpmPackageTarball, fetchGildeReleases, fetchHuggingfaceCommit, fetchHuggingfaceTree, getBuiltinToolset, gildeDataDir, gildePackageRoot, installNpmPackageToolset, isGildeReleaseVersion, outputMediaForCraftbookBlueprint, outputMediumForCraftbookBlueprint, pickGildePatchUpdate, publishStagedNpmInstall, recoverInterruptedNpmInstall, renderGildeSchemaFiles, renderSchema, selectMlxInstallFiles, stageGildeVersion, totalSize, validateGildeContentUpgrade, validateNpmArchiveEntry, verifyTarballSha256 };
package/dist/index.js CHANGED
@@ -33,6 +33,8 @@ var BUILTIN_TOOLSET_ICONS = {
33
33
  tasks: `<svg ${SVG_ATTRS}><rect x="3" y="4" width="6" height="6" rx="1"/><path d="M4.5 7L6 8.5 8 5.5"/><rect x="3" y="14" width="6" height="6" rx="1"/><path d="M12 7h9M12 17h9"/></svg>`,
34
34
  // Three connected nodes — a small team.
35
35
  "team-management": `<svg ${SVG_ATTRS}><circle cx="12" cy="6" r="2.5"/><circle cx="6" cy="17" r="2.5"/><circle cx="18" cy="17" r="2.5"/><path d="M10.5 8L7.5 14.5M13.5 8l3 6.5M8.5 17h7"/></svg>`,
36
+ // A closed shipping box — a packaged app bundle.
37
+ "ai-apps": `<svg ${SVG_ATTRS}><path d="M21 8L12 3 3 8v8l9 5 9-5V8z"/><path d="M3 8l9 5 9-5"/><path d="M7.5 5.5l9 5"/></svg>`,
36
38
  // Terminal-like frame with a prompt arrow.
37
39
  "code-execution": `<svg ${SVG_ATTRS}><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 10l3 2-3 2M13 14h4"/></svg>`,
38
40
  // Browser window + a tiny pointer.
@@ -61,8 +63,8 @@ var BUILTIN_TOOLSETS = [
61
63
  {
62
64
  id: "memory",
63
65
  name: "Memory",
64
- description: "Persistent notes a gezel can search, recall, and write back. Backed by the gezel's per-day memory files plus a vector index.",
65
- tools: ["search_memory", "save_memory", "list_memories"]
66
+ description: "Search indexed project knowledge through one unified surface, plus persistent notes a gezel can recall and write back. The generic search spans workspace content, artifacts, project/gezel memory, and shared documents.",
67
+ tools: ["search", "search_memory", "save_memory", "list_memories"]
66
68
  },
67
69
  {
68
70
  id: "workspace-fs-read",
@@ -168,6 +170,12 @@ var BUILTIN_TOOLSETS = [
168
170
  description: "Project-scoped read-write outputs (reports, scratch files, scripts a gezel produces, and large outputs auto-saved by tools that exceed the inline cap).",
169
171
  tools: ["list_artifacts", "read_artifact", "write_artifact", "grep_artifact"]
170
172
  },
173
+ {
174
+ id: "data-tables",
175
+ name: "Data Tables",
176
+ description: "Read a project's mirrored data tables with SQL. `list_tables` shows what is there, `describe_table` explains a table's columns and units, and `query_table` runs one read-only query and returns the answer \u2014 never the underlying rows, which is what lets a table be far larger than the context window.",
177
+ tools: ["list_tables", "describe_table", "query_table"]
178
+ },
171
179
  {
172
180
  id: "tasks",
173
181
  name: "Task Management",
@@ -243,8 +251,6 @@ var BUILTIN_TOOLSETS = [
243
251
  "list_project_types",
244
252
  "apply_project_type",
245
253
  "start_project_from_type",
246
- "export_project_type",
247
- "import_project_type",
248
254
  "list_project_gezels",
249
255
  "add_gezel_to_project",
250
256
  "remove_gezel_from_project",
@@ -253,6 +259,12 @@ var BUILTIN_TOOLSETS = [
253
259
  "disable_suggested_work"
254
260
  ]
255
261
  },
262
+ {
263
+ id: "ai-apps",
264
+ name: "AI Apps",
265
+ description: "Package a configured project into a shareable .gezapp bundle and install one back. Deliberately in no role default: this is a distribution chore the user drives from Settings or the `gezel app` CLI, not something a gezel reaches for mid-conversation. Install it on a gezel whose actual job is publishing app bundles.",
266
+ tools: ["export_ai_app", "import_ai_app"]
267
+ },
256
268
  {
257
269
  id: "audio",
258
270
  name: "Audio",
@@ -303,8 +315,14 @@ var BUILTIN_TOOLSETS = [
303
315
  {
304
316
  id: "web",
305
317
  name: "Web Access",
306
- description: "Search the web (when a keyed backend like Brave is configured) or Wikipedia, fetch URL contents, and find interactive elements on a browser-controlled page (after a Playwright navigate / click / type). `web_search` only registers when a real keyed backend is configured; `wikipedia_search` only registers for non-cloud models (cloud models already have Wikipedia in training).",
307
- tools: ["web_search", "wikipedia_search", "fetch_url", "browser_find_page_element"]
318
+ description: "Search the web (when a keyed backend like Brave is configured) or Wikipedia, fetch URL contents, and find interactive elements on a browser-controlled page (after a Playwright navigate / click / type). `web_search` only registers when a real keyed backend is configured; `wikipedia_search` / `wikipedia_read` only register for non-cloud models (cloud models already have Wikipedia in training). Wikipedia search results arrive with article lead text already included, so reading a result does not need a follow-up fetch.",
319
+ tools: [
320
+ "web_search",
321
+ "wikipedia_search",
322
+ "wikipedia_read",
323
+ "fetch_url",
324
+ "browser_find_page_element"
325
+ ]
308
326
  },
309
327
  {
310
328
  id: "images",
@@ -480,6 +498,7 @@ import {
480
498
  GEZEL_CONTENT_COMPAT,
481
499
  GezelTemplateVersionManifestSchema,
482
500
  ImageModelVersionManifestSchema,
501
+ KnowledgeCatalogVersionManifestSchema,
483
502
  ProjectTypeVersionManifestSchema,
484
503
  ToolsetVersionManifestSchema,
485
504
  VideoModelVersionManifestSchema,
@@ -501,7 +520,8 @@ var KINDS = [
501
520
  "connector-type",
502
521
  "chat-model",
503
522
  "image-model",
504
- "video-model"
523
+ "video-model",
524
+ "knowledge-catalog"
505
525
  ];
506
526
  var KIND_DIR = {
507
527
  toolset: "toolsets",
@@ -511,7 +531,8 @@ var KIND_DIR = {
511
531
  "connector-type": "connector-types",
512
532
  "chat-model": "chat-models",
513
533
  "image-model": "image-models",
514
- "video-model": "video-models"
534
+ "video-model": "video-models",
535
+ "knowledge-catalog": "knowledge-catalogs"
515
536
  };
516
537
  function shardPrefix(id) {
517
538
  return id.slice(0, 2).toLowerCase();
@@ -718,7 +739,7 @@ var BundledSource = class {
718
739
  /**
719
740
  * Every file under an item's folder, as item-relative paths (e.g.
720
741
  * `manifest.json`, `versions/1.0.0/pages/gallery/index.html`), sorted. Used
721
- * by the `.gzl` exporter to pack an item verbatim — pages, seeds, and all
742
+ * by the `.gezapp` exporter to pack an item verbatim — pages, seeds, and all
722
743
  * assets, not just the ones the manifest names. Read each back with
723
744
  * `readItemFile(kind, id, relPath)` (no version → item-relative). Empty when
724
745
  * the item folder is missing.
@@ -1005,6 +1026,7 @@ function craftbookManifestFromDoc(identity, doc, version, availableVersions) {
1005
1026
  version,
1006
1027
  releasedAt: doc.releasedAt,
1007
1028
  role: identity.role,
1029
+ ...identity.category ? { category: identity.category } : {},
1008
1030
  ...identity.workflow ? { workflow: identity.workflow } : {},
1009
1031
  // The prose lives inline on the doc (`description`) — there is no
1010
1032
  // separate about.md file to point at. `get()` surfaces it directly.
@@ -1020,11 +1042,15 @@ function craftbookManifestFromDoc(identity, doc, version, availableVersions) {
1020
1042
  ...doc.paramSchema ? { paramSchema: doc.paramSchema } : {},
1021
1043
  ...doc.command ? { command: doc.command } : {},
1022
1044
  ...doc.requirements ? { requirements: doc.requirements } : {},
1045
+ ...doc.recommends ? { recommends: doc.recommends } : {},
1023
1046
  ...doc.runModes ? { runModes: doc.runModes } : {},
1024
1047
  ...doc.toolsets ? { toolsets: doc.toolsets } : {},
1048
+ ...doc.commands ? { commands: doc.commands } : {},
1025
1049
  ...doc.connectors ? { connectors: doc.connectors } : {},
1026
1050
  ...doc.hooks ? { hooks: doc.hooks } : {},
1027
1051
  ...doc.spawn ? { spawn: doc.spawn } : {},
1052
+ ...doc.diffpackCapable ? { diffpackCapable: true } : {},
1053
+ ...doc.capabilityFloor ? { capabilityFloor: doc.capabilityFloor } : {},
1028
1054
  availableVersions
1029
1055
  };
1030
1056
  }
@@ -1065,6 +1091,10 @@ function parseVersionPayload(kind, raw) {
1065
1091
  const p2 = VideoModelVersionManifestSchema.parse(raw);
1066
1092
  return { ...p2, __kind: "video-model" };
1067
1093
  }
1094
+ if (kind === "knowledge-catalog") {
1095
+ const p2 = KnowledgeCatalogVersionManifestSchema.parse(raw);
1096
+ return { ...p2, __kind: "knowledge-catalog" };
1097
+ }
1068
1098
  const p = ImageModelVersionManifestSchema.parse(raw);
1069
1099
  return { ...p, __kind: "image-model" };
1070
1100
  } catch {
@@ -1111,6 +1141,7 @@ function mergeIdentityAndVersion(kind, identity, version, availableVersions) {
1111
1141
  version: version.version,
1112
1142
  releasedAt: version.releasedAt,
1113
1143
  role: identity.role,
1144
+ ...identity.category ? { category: identity.category } : {},
1114
1145
  ...identity.workflow ? { workflow: identity.workflow } : {},
1115
1146
  about: version.about,
1116
1147
  steps: version.steps,
@@ -1125,6 +1156,7 @@ function mergeIdentityAndVersion(kind, identity, version, availableVersions) {
1125
1156
  ...version.paramSchema ? { paramSchema: version.paramSchema } : {},
1126
1157
  ...version.command ? { command: version.command } : {},
1127
1158
  ...version.requirements ? { requirements: version.requirements } : {},
1159
+ ...version.recommends ? { recommends: version.recommends } : {},
1128
1160
  ...version.runModes ? { runModes: version.runModes } : {},
1129
1161
  ...version.toolsets ? { toolsets: version.toolsets } : {},
1130
1162
  ...version.connectors ? { connectors: version.connectors } : {},
@@ -1304,6 +1336,43 @@ function mergeIdentityAndVersion(kind, identity, version, availableVersions) {
1304
1336
  availableVersions
1305
1337
  };
1306
1338
  }
1339
+ if (kind === "knowledge-catalog" && identity.kind === "knowledge-catalog" && version.__kind === "knowledge-catalog") {
1340
+ return {
1341
+ schemaVersion: 1,
1342
+ kind: "knowledge-catalog",
1343
+ id: identity.id,
1344
+ name: identity.name,
1345
+ description: identity.description,
1346
+ tags: identity.tags,
1347
+ maintainer: identity.maintainer,
1348
+ ...identity.logo !== void 0 ? { logo: identity.logo } : {},
1349
+ ...identity.license !== void 0 ? { license: identity.license } : {},
1350
+ ...identity.licenseClass !== void 0 ? { licenseClass: identity.licenseClass } : {},
1351
+ ...identity.licenseShortName !== void 0 ? { licenseShortName: identity.licenseShortName } : {},
1352
+ ...identity.licenseUrl !== void 0 ? { licenseUrl: identity.licenseUrl } : {},
1353
+ ...identity.recoScore !== void 0 ? { recoScore: identity.recoScore } : {},
1354
+ ...minGezelVersion !== void 0 ? { minGezelVersion } : {},
1355
+ publisherId: identity.publisherId,
1356
+ language: identity.language,
1357
+ ...identity.category ? { category: identity.category } : {},
1358
+ ...identity.upstream !== void 0 ? { upstream: identity.upstream } : {},
1359
+ version: version.version,
1360
+ releasedAt: version.releasedAt,
1361
+ formatVersion: version.formatVersion,
1362
+ huggingface: version.huggingface,
1363
+ sha256: version.sha256,
1364
+ archiveBytes: version.archiveBytes,
1365
+ uncompressedBytes: version.uncompressedBytes,
1366
+ documents: version.documents,
1367
+ chunks: version.chunks,
1368
+ embeddingProfile: version.embeddingProfile,
1369
+ topics: version.topics,
1370
+ ...version.sourceSnapshot ? { sourceSnapshot: version.sourceSnapshot } : {},
1371
+ ...version.parquet ? { parquet: version.parquet } : {},
1372
+ ...version.notes !== void 0 ? { notes: version.notes } : {},
1373
+ availableVersions
1374
+ };
1375
+ }
1307
1376
  if (kind === "video-model" && identity.kind === "video-model" && version.__kind === "video-model") {
1308
1377
  return {
1309
1378
  schemaVersion: 1,
@@ -1364,6 +1433,108 @@ var CommunitySource = class extends BundledSource {
1364
1433
  }
1365
1434
  };
1366
1435
 
1436
+ // src/installed-ai-apps-source.ts
1437
+ import { readFile as readFile2 } from "fs/promises";
1438
+ import { GezappRegistrySchema } from "@bendyline/gezel";
1439
+ import { aiAppItemsDir, aiAppsRegistryFile } from "@bendyline/gezel/paths";
1440
+ var AI_APP_KINDS = /* @__PURE__ */ new Set([
1441
+ "project-type",
1442
+ "gezel-template",
1443
+ "craftbook-template"
1444
+ ]);
1445
+ var InstalledAiAppsSource = class {
1446
+ constructor(home, gezelVersion) {
1447
+ this.home = home;
1448
+ this.gezelVersion = gezelVersion;
1449
+ }
1450
+ home;
1451
+ gezelVersion;
1452
+ id = "installed-ai-apps";
1453
+ label = "AI Apps";
1454
+ async listKinds() {
1455
+ return [...AI_APP_KINDS];
1456
+ }
1457
+ async list(kind) {
1458
+ if (!AI_APP_KINDS.has(kind)) return [];
1459
+ const seen = /* @__PURE__ */ new Set();
1460
+ const out = [];
1461
+ for (const source of await this.sources()) {
1462
+ for (const item of await source.list(kind)) {
1463
+ if (seen.has(item.manifest.id)) continue;
1464
+ seen.add(item.manifest.id);
1465
+ out.push(this.rescope(item));
1466
+ }
1467
+ }
1468
+ out.sort((a, b) => a.manifest.name.localeCompare(b.manifest.name));
1469
+ return out;
1470
+ }
1471
+ async get(kind, id, version) {
1472
+ if (!AI_APP_KINDS.has(kind)) return null;
1473
+ for (const source of await this.sources()) {
1474
+ const item = await source.get(kind, id, version);
1475
+ if (item) return this.rescope(item);
1476
+ }
1477
+ return null;
1478
+ }
1479
+ async listVersions(kind, id) {
1480
+ if (!AI_APP_KINDS.has(kind)) return [];
1481
+ const byVersion = /* @__PURE__ */ new Map();
1482
+ for (const source of await this.sources()) {
1483
+ for (const version of await source.listVersions(kind, id)) {
1484
+ if (!byVersion.has(version.version)) byVersion.set(version.version, version);
1485
+ }
1486
+ }
1487
+ return [...byVersion.values()];
1488
+ }
1489
+ async readItemFile(kind, id, relPath, version) {
1490
+ if (!AI_APP_KINDS.has(kind)) return null;
1491
+ for (const source of await this.sources()) {
1492
+ const content = await source.readItemFile(kind, id, relPath, version);
1493
+ if (content) return content;
1494
+ }
1495
+ return null;
1496
+ }
1497
+ async listItemFiles(kind, id) {
1498
+ if (!AI_APP_KINDS.has(kind)) return [];
1499
+ for (const source of await this.sources()) {
1500
+ const files = await source.listItemFiles(kind, id);
1501
+ if (files.length > 0) return files;
1502
+ }
1503
+ return [];
1504
+ }
1505
+ async registry() {
1506
+ try {
1507
+ return GezappRegistrySchema.parse(
1508
+ JSON.parse(await readFile2(aiAppsRegistryFile(this.home), "utf8"))
1509
+ );
1510
+ } catch {
1511
+ return { schemaVersion: 1, apps: [] };
1512
+ }
1513
+ }
1514
+ async sources() {
1515
+ const registry = await this.registry();
1516
+ return registry.apps.filter((entry) => entry.enabled).map(
1517
+ (entry) => new BundledSource({
1518
+ dataDir: aiAppItemsDir(this.home, entry.appId, entry.version),
1519
+ id: `gezapp:${entry.appId}@${entry.version}`,
1520
+ label: this.label,
1521
+ noIndex: true,
1522
+ ...this.gezelVersion !== void 0 ? { gezelVersion: this.gezelVersion } : {}
1523
+ })
1524
+ );
1525
+ }
1526
+ rescope(item) {
1527
+ const { logoUrl: _ignored, ...rest } = item;
1528
+ const logo = item.manifest.logo;
1529
+ const logoUrl = logo && /^https?:\/\//.test(logo) ? logo : logo ? `/api/catalog/${item.kind}/${encodeURIComponent(item.manifest.id)}/file/${encodeURIComponent(logo)}?source=${encodeURIComponent(this.id)}` : void 0;
1530
+ return {
1531
+ ...rest,
1532
+ sourceId: this.id,
1533
+ ...logoUrl ? { logoUrl } : {}
1534
+ };
1535
+ }
1536
+ };
1537
+
1367
1538
  // src/local-source.ts
1368
1539
  var LOCAL_KINDS = /* @__PURE__ */ new Set([
1369
1540
  "project-type",
@@ -1412,9 +1583,9 @@ var CatalogService = class {
1412
1583
  contentRootProvider;
1413
1584
  /**
1414
1585
  * @param sources explicit source list (replaces the defaults entirely).
1415
- * @param opts.localRoot a GEZEL_HOME to read user-installed / `.gzl`-imported
1416
- * catalog items from (project types + gezel roles). Appended just ahead of
1417
- * the bundled tier so a user's imported item shadows a same-id bundled one.
1586
+ * @param opts.localRoot a GEZEL_HOME to read user-installed catalog items and
1587
+ * mounted `.gezapp` packages from. Both sit ahead of the bundled tier so a
1588
+ * user's installed item shadows a same-id bundled one.
1418
1589
  * @param opts.contentRoot dynamic gilde content root for the default
1419
1590
  * bundled + community tiers, re-read on every disk access. The live
1420
1591
  * gilde update mechanism flips it without reconstructing this service
@@ -1435,8 +1606,10 @@ var CatalogService = class {
1435
1606
  const contentRoot = opts?.contentRoot;
1436
1607
  const gezelVersion = opts?.gezelVersion;
1437
1608
  const local = opts?.localRoot ? [new LocalCatalogSource(opts.localRoot, gezelVersion)] : [];
1609
+ const aiApps = opts?.localRoot ? [new InstalledAiAppsSource(opts.localRoot, gezelVersion)] : [];
1438
1610
  this.sources = [
1439
1611
  new BuiltinToolsetsSource(),
1612
+ ...aiApps,
1440
1613
  ...local,
1441
1614
  new BundledSource({
1442
1615
  ...contentRoot ? { dataDir: contentRoot } : {},
@@ -1462,7 +1635,7 @@ var CatalogService = class {
1462
1635
  return this.sources.map((s) => ({ id: s.id, label: s.label }));
1463
1636
  }
1464
1637
  /**
1465
- * Merged listing across sources. Items from earlier sources (bundled)
1638
+ * Merged listing across sources. Items from earlier sources
1466
1639
  * shadow later sources (remote) on id collision.
1467
1640
  */
1468
1641
  async list(kind) {
@@ -1833,11 +2006,276 @@ function categorizeToolset(input) {
1833
2006
  return "other";
1834
2007
  }
1835
2008
 
2009
+ // src/craftbook-step-policy.ts
2010
+ import { deliverableKindForStep, requiredOutputMediaForGate } from "@bendyline/gezel";
2011
+ var BUILTIN_BY_ID2 = new Map(BUILTIN_TOOLSETS.map((group) => [group.id, group]));
2012
+ var SPECIALIZED_GROUP_SIGNALS = {
2013
+ "security-intel": /\b(?:security|vulnerabilit|threat|attack surface|taint|secret scan)\b/i,
2014
+ "image-intel": /\b(?:image library|photo library|similar images|search_images|describe_folder)\b/i,
2015
+ "entity-intel": /\b(?:find_entity|entity mentions|cross-file entit)\b/i,
2016
+ archives: /\b(?:archive|zip|tar|extract_archive|list_archive)\b/i,
2017
+ "data-tables": /\b(?:sql|query_table|describe_table|list_tables|data table)\b/i,
2018
+ craftbooks: /\b(?:craftbook_(?:read|write|add|remove|reorder|update)|edit (?:the )?craftbook)\b/i,
2019
+ "ai-apps": /\b(?:export_ai_app|import_ai_app|\.gezapp\b|ai app bundle)\b/i,
2020
+ audio: /\b(?:transcribe_audio|synthesize_speech|speech[- ]to[- ]text|text[- ]to[- ]speech)\b/i,
2021
+ videos: /\b(?:generate_video|video generation|generate (?:a )?video)\b/i,
2022
+ images: /\b(?:generate_image|render_image|describe_image|read_image|image generation|generate (?:an? )?image|render (?:a )?(?:chart|diagram))\b/i,
2023
+ "browser-automation": /\b(?:run_playwright_script|playwright|browser automation|headless browser)\b/i,
2024
+ git: /\b(?:run_git|github_|git\b|pull request|\bPR\s*#?\d*)\b/i,
2025
+ web: /\b(?:web_search|wikipedia_|fetch_url|live web|search the web|external source|https?:\/\/)\b/i,
2026
+ "team-management": /\b(?:ensure_gezel|message_gezel|create_gezel|update_gezel|start_project|delegate|hand (?:the )?work)\b/i,
2027
+ "role-delegation": /\b(?:ask_specialist|ask_gezel|delegate_[a-z]|consult_[a-z])\b/i,
2028
+ "role-delegation-escalation": /\b(?:escalat|second opinion|ask_specialist|consult_[a-z])\b/i
2029
+ };
2030
+ var DECLARED_TOOLSET_SIGNALS = {
2031
+ docblocks: /\b(?:docblocks|list_roots|convert_document|preview_document|save_artifact|document artifact uri)\b/i,
2032
+ github: SPECIALIZED_GROUP_SIGNALS.git,
2033
+ "@playwright/mcp": /\b(?:playwright|browser_|browser automation|headless browser)\b/i,
2034
+ "microsoft-playwright-mcp": /\b(?:playwright|browser_|browser automation|headless browser)\b/i
2035
+ };
2036
+ var TASK_NOTE_OUTPUT_SIGNAL = /\bwrite_task_note\b|\b(?:write|record|append|summarize)[^.!?\n]{0,100}\b(?:task\s+)?notes?\b|\bwrite\s+PASS\s*\/\s*FAIL\b/i;
2037
+ function procedureText(step) {
2038
+ return [step.name, step.description, step.prompt, step.suggestedRole].filter(Boolean).join("\n");
2039
+ }
2040
+ function gateChecks(step) {
2041
+ const gate = step.gate;
2042
+ return gate && "checks" in gate && Array.isArray(gate.checks) ? gate.checks : [];
2043
+ }
2044
+ function outputMediumForCraftbookBlueprint(step) {
2045
+ const gateRequiredMedia = [...requiredOutputMediaForGate(step.gate)];
2046
+ if (step.toolPolicy?.outputMedium) {
2047
+ if (step.toolPolicy.outputMedium === "none" && gateRequiredMedia[0]) {
2048
+ return gateRequiredMedia[0];
2049
+ }
2050
+ return step.toolPolicy.outputMedium;
2051
+ }
2052
+ if (step.deliverable?.path) return step.deliverable.artifact ? "artifact" : "workspace";
2053
+ if (step.advanceWhen?.file) return step.advanceWhen.artifact ? "artifact" : "workspace";
2054
+ const fileCheck = gateChecks(step).find(
2055
+ (check) => typeof check.file === "string" && check.file.length > 0
2056
+ );
2057
+ if (fileCheck) return fileCheck.artifact === true ? "artifact" : "workspace";
2058
+ if (gateRequiredMedia[0]) return gateRequiredMedia[0];
2059
+ const text = procedureText(step);
2060
+ if (/\b(?:write_file|append_to_file|replace_in_file|replace_lines|apply_patch|insert_at_marker)\b/i.test(
2061
+ text
2062
+ )) {
2063
+ return "workspace";
2064
+ }
2065
+ if (/\bwrite_artifact\b/i.test(text)) return "artifact";
2066
+ return TASK_NOTE_OUTPUT_SIGNAL.test(text) ? "task-note" : "none";
2067
+ }
2068
+ function outputMediaForCraftbookBlueprint(step) {
2069
+ const primary = outputMediumForCraftbookBlueprint(step);
2070
+ return /* @__PURE__ */ new Set([
2071
+ ...primary === "none" ? [] : [primary],
2072
+ ...additionalOutputMediaForStep(step, primary)
2073
+ ]);
2074
+ }
2075
+ function additionalOutputMediaForStep(step, primary) {
2076
+ if (primary === "none") return [];
2077
+ const text = procedureText(step);
2078
+ const out = new Set(step.toolPolicy?.additionalOutputMedia ?? []);
2079
+ for (const medium of requiredOutputMediaForGate(step.gate)) out.add(medium);
2080
+ if (/\b(?:write_file|append_to_file|replace_in_file|replace_lines|apply_patch|insert_at_marker)\b|\b(?:edit|change|patch|fix)\b[^.!?\n]{0,80}\b(?:actual|workspace|source|project)\s+files?\b/i.test(
2081
+ text
2082
+ )) {
2083
+ out.add("workspace");
2084
+ }
2085
+ if (/\bwrite_artifact\b/i.test(text)) out.add("artifact");
2086
+ if (TASK_NOTE_OUTPUT_SIGNAL.test(text)) out.add("task-note");
2087
+ out.delete(primary);
2088
+ return [...out].sort();
2089
+ }
2090
+ function groupToolMentioned(groupId, text) {
2091
+ const group = BUILTIN_BY_ID2.get(groupId);
2092
+ return group?.tools.some((tool) => new RegExp(`\\b${tool}\\b`, "i").test(text)) ?? false;
2093
+ }
2094
+ function needsCodeExecution(step, text) {
2095
+ const kind = step.deliverable?.kind ?? deliverableKindForStep(step);
2096
+ if (kind === "code-module" || kind === "code-with-tests" || kind === "data-file" || kind === "json" || kind === "audio-file") {
2097
+ return true;
2098
+ }
2099
+ if (gateChecks(step).some((check) => check.kind === "nodeRuns" || check.kind === "commandEvidence")) {
2100
+ return true;
2101
+ }
2102
+ if (step.onExit !== void 0) return true;
2103
+ if (groupToolMentioned("code-execution", text)) return true;
2104
+ return /\b(?:execute|npm|npx|sandbox|compile|transform)\b|\b(?:re-?run|run)[^.!?\n]{0,50}\b(?:test|script|build|command|suite)\b/i.test(
2105
+ text
2106
+ );
2107
+ }
2108
+ function builtinDisallows(step, medium, additionalMedia) {
2109
+ const text = procedureText(step);
2110
+ const out = new Set(step.toolPolicy?.disallowBuiltinToolsets ?? []);
2111
+ const media = /* @__PURE__ */ new Set([medium, ...additionalMedia]);
2112
+ if (!media.has("workspace")) out.add("workspace-fs-write");
2113
+ const consumesArtifact = step.consumes?.some((input) => input.artifact) === true;
2114
+ const mentionsArtifact = /\b(?:read_artifact|list_artifacts|grep_artifact|artifacts drawer)\b/i.test(text);
2115
+ if (!media.has("artifact") && !consumesArtifact && !mentionsArtifact) out.add("artifacts");
2116
+ for (const [groupId, signal] of Object.entries(SPECIALIZED_GROUP_SIGNALS)) {
2117
+ if (groupId === "web" && /research/i.test(step.suggestedRole ?? "")) continue;
2118
+ if (!signal.test(text) && !groupToolMentioned(groupId, text)) out.add(groupId);
2119
+ }
2120
+ if (!needsCodeExecution(step, text)) out.add("code-execution");
2121
+ return [...out].sort();
2122
+ }
2123
+ function explicitlyDeniedExternalToolsets(step, declaredIds) {
2124
+ const text = procedureText(step);
2125
+ const out = new Set(step.toolPolicy?.disallowToolsets ?? []);
2126
+ for (const id of declaredIds) {
2127
+ if (id.startsWith("builtin.")) continue;
2128
+ const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2129
+ const denial = new RegExp(
2130
+ `(?:\\bdo\\s+not\\b|\\bdon't\\b|\\bnever\\b)[^.!?\\n]{0,160}\\b(?:call|use|invoke|run|access|load)\\s+(?:the\\s+)?${escaped}(?=$|[^A-Za-z0-9_-])`,
2131
+ "iu"
2132
+ );
2133
+ const positiveSignal = DECLARED_TOOLSET_SIGNALS[id] ?? new RegExp(escaped, "iu");
2134
+ if (denial.test(text) || !positiveSignal.test(text)) out.add(id);
2135
+ }
2136
+ return [...out].sort();
2137
+ }
2138
+ function withDefaultPolicy(step, declaredIds) {
2139
+ const outputMedium = outputMediumForCraftbookBlueprint(step);
2140
+ const additionalOutputMedia = additionalOutputMediaForStep(step, outputMedium);
2141
+ const disallowBuiltinToolsets = builtinDisallows(step, outputMedium, additionalOutputMedia);
2142
+ const disallowToolsets = explicitlyDeniedExternalToolsets(step, declaredIds);
2143
+ return {
2144
+ ...step,
2145
+ toolPolicy: {
2146
+ ...disallowToolsets.length > 0 ? { disallowToolsets } : {},
2147
+ ...disallowBuiltinToolsets.length > 0 ? { disallowBuiltinToolsets } : {},
2148
+ outputMedium,
2149
+ ...additionalOutputMedia.length > 0 ? { additionalOutputMedia } : {}
2150
+ }
2151
+ };
2152
+ }
2153
+ function applyDefaultCraftbookStepPolicies(doc) {
2154
+ const declaredIds = (doc.toolsets ?? []).map((need) => need.toolsetId);
2155
+ return {
2156
+ ...doc,
2157
+ steps: doc.steps.map((step) => withDefaultPolicy(step, declaredIds)),
2158
+ ...doc.spawn ? {
2159
+ spawn: {
2160
+ ...doc.spawn,
2161
+ steps: doc.spawn.steps.map((step) => withDefaultPolicy(step, declaredIds))
2162
+ }
2163
+ } : {}
2164
+ };
2165
+ }
2166
+
2167
+ // src/gilde-schema-export.ts
2168
+ import {
2169
+ ChatModelIdentitySchema,
2170
+ ChatModelVersionManifestSchema as ChatModelVersionManifestSchema2,
2171
+ ConnectorTypeIdentitySchema,
2172
+ ConnectorTypeVersionManifestSchema as ConnectorTypeVersionManifestSchema2,
2173
+ CraftbookDocSchema,
2174
+ CraftbookTemplateIdentitySchema,
2175
+ CraftbookTemplateVersionManifestSchema as CraftbookTemplateVersionManifestSchema2,
2176
+ CraftbookTestSpecSchema,
2177
+ GezelTemplateIdentitySchema,
2178
+ GezelTemplateVersionManifestSchema as GezelTemplateVersionManifestSchema2,
2179
+ ImageModelIdentitySchema,
2180
+ ImageModelVersionManifestSchema as ImageModelVersionManifestSchema2,
2181
+ KnowledgeCatalogIdentitySchema,
2182
+ KnowledgeCatalogVersionManifestSchema as KnowledgeCatalogVersionManifestSchema2,
2183
+ ProjectTypeIdentitySchema,
2184
+ ProjectTypeVersionManifestSchema as ProjectTypeVersionManifestSchema2,
2185
+ ToolsetIdentitySchema,
2186
+ ToolsetVersionManifestSchema as ToolsetVersionManifestSchema2,
2187
+ VideoModelIdentitySchema,
2188
+ VideoModelVersionManifestSchema as VideoModelVersionManifestSchema2
2189
+ } from "@bendyline/gezel";
2190
+ import { z } from "zod";
2191
+ var GILDE_SCHEMA_EXPORTS = [
2192
+ ["toolset-identity.schema.json", ToolsetIdentitySchema],
2193
+ ["toolset-version.schema.json", ToolsetVersionManifestSchema2],
2194
+ ["gezel-template-identity.schema.json", GezelTemplateIdentitySchema],
2195
+ ["gezel-template-version.schema.json", GezelTemplateVersionManifestSchema2],
2196
+ ["craftbook-template-identity.schema.json", CraftbookTemplateIdentitySchema],
2197
+ ["craftbook-template-version.schema.json", CraftbookTemplateVersionManifestSchema2],
2198
+ ["project-type-identity.schema.json", ProjectTypeIdentitySchema],
2199
+ ["project-type-version.schema.json", ProjectTypeVersionManifestSchema2],
2200
+ ["connector-type-identity.schema.json", ConnectorTypeIdentitySchema],
2201
+ ["connector-type-version.schema.json", ConnectorTypeVersionManifestSchema2],
2202
+ ["chat-model-identity.schema.json", ChatModelIdentitySchema],
2203
+ ["chat-model-version.schema.json", ChatModelVersionManifestSchema2],
2204
+ ["image-model-identity.schema.json", ImageModelIdentitySchema],
2205
+ ["image-model-version.schema.json", ImageModelVersionManifestSchema2],
2206
+ ["video-model-identity.schema.json", VideoModelIdentitySchema],
2207
+ ["video-model-version.schema.json", VideoModelVersionManifestSchema2],
2208
+ ["knowledge-catalog-identity.schema.json", KnowledgeCatalogIdentitySchema],
2209
+ ["knowledge-catalog-version.schema.json", KnowledgeCatalogVersionManifestSchema2],
2210
+ ["craftbook-doc.schema.json", CraftbookDocSchema],
2211
+ ["craftbook-test.schema.json", CraftbookTestSpecSchema]
2212
+ ];
2213
+ var CATALOG_INDEX_SCHEMA = {
2214
+ $schema: "https://json-schema.org/draft/2020-12/schema",
2215
+ $id: "https://gezelgilde.com/schemas/catalog-index.schema.json",
2216
+ type: "object",
2217
+ properties: {
2218
+ schemaVersion: { type: "number", const: 1 },
2219
+ kind: { type: "string" },
2220
+ count: { type: "number", minimum: 0 },
2221
+ entries: {
2222
+ type: "array",
2223
+ items: {
2224
+ type: "object",
2225
+ properties: {
2226
+ manifest: { type: "object" },
2227
+ iconSvg: { type: "string" }
2228
+ },
2229
+ required: ["manifest"]
2230
+ }
2231
+ }
2232
+ },
2233
+ required: ["schemaVersion", "kind", "count", "entries"]
2234
+ };
2235
+ var GILDE_SCHEMAS_README = `# schemas/
2236
+
2237
+ JSON Schemas for every content file in \`data/\`, consumed by
2238
+ \`tools/validate.mjs\`.
2239
+
2240
+ **Generated \u2014 do not edit.** These are exported from the Zod schemas in
2241
+ gezel core (\`packages/core/src/schemas/\`), which remain the source of
2242
+ truth. Regenerate from a gezel checkout with:
2243
+
2244
+ \`\`\`
2245
+ pnpm gilde:export-schemas
2246
+ \`\`\`
2247
+
2248
+ Note: Zod refinements do not survive the export, so validation here is
2249
+ slightly looser than gezel's runtime parse. Layout and cross-reference
2250
+ rules that matter are re-implemented in \`tools/validate.mjs\`.
2251
+ `;
2252
+ function renderSchema(filename, schema) {
2253
+ const json = z.toJSONSchema(schema, { target: "draft-2020-12", io: "input" });
2254
+ const withId = {
2255
+ $schema: "https://json-schema.org/draft/2020-12/schema",
2256
+ $id: `https://gezelgilde.com/schemas/${filename}`,
2257
+ ...json
2258
+ };
2259
+ withId.$schema = "https://json-schema.org/draft/2020-12/schema";
2260
+ return `${JSON.stringify(withId, null, 2)}
2261
+ `;
2262
+ }
2263
+ function renderGildeSchemaFiles() {
2264
+ return [
2265
+ ...GILDE_SCHEMA_EXPORTS.map(
2266
+ ([filename, schema]) => [filename, renderSchema(filename, schema)]
2267
+ ),
2268
+ ["catalog-index.schema.json", `${JSON.stringify(CATALOG_INDEX_SCHEMA, null, 2)}
2269
+ `],
2270
+ ["README.md", GILDE_SCHEMAS_README]
2271
+ ];
2272
+ }
2273
+
1836
2274
  // src/install/npm-package.ts
1837
2275
  import { spawn } from "child_process";
1838
2276
  import { createHash, randomUUID } from "crypto";
1839
2277
  import { createReadStream } from "fs";
1840
- import { mkdir, open, readFile as readFile2, realpath, rename, rm, stat, writeFile } from "fs/promises";
2278
+ import { mkdir, open, readFile as readFile3, realpath, rename, rm, stat, writeFile } from "fs/promises";
1841
2279
  import { dirname as dirname2, isAbsolute, join as join5, relative, resolve } from "path";
1842
2280
  import {
1843
2281
  HttpStatusError,
@@ -2046,7 +2484,7 @@ function validateNpmArchiveEntry(path, type) {
2046
2484
  }
2047
2485
  }
2048
2486
  async function validateExtractedPackage(packageDir, expectedName, expectedVersion) {
2049
- const raw = await readFile2(join5(packageDir, "package.json"), "utf8");
2487
+ const raw = await readFile3(join5(packageDir, "package.json"), "utf8");
2050
2488
  const parsed = JSON.parse(raw);
2051
2489
  if (parsed.name !== expectedName || parsed.version !== expectedVersion) {
2052
2490
  throw new Error("[catalog] extracted package identity does not match the pinned manifest");
@@ -2418,17 +2856,20 @@ async function validateGildeContentUpgrade(opts) {
2418
2856
 
2419
2857
  // src/hf-api.ts
2420
2858
  var HF_HUB_BASE = "https://huggingface.co";
2859
+ function apiPrefix(repoType) {
2860
+ return `${HF_HUB_BASE}/api/${repoType === "dataset" ? "datasets" : "models"}`;
2861
+ }
2421
2862
  async function fetchHuggingfaceTree(repo, opts = {}) {
2422
2863
  const rev = opts.rev ?? "main";
2423
2864
  const fetchImpl = opts.fetchImpl ?? fetch;
2424
2865
  const out = [];
2425
- await walk(repo, rev, "", out, fetchImpl);
2866
+ await walk(repo, rev, "", out, fetchImpl, opts.repoType);
2426
2867
  return out;
2427
2868
  }
2428
2869
  async function fetchHuggingfaceCommit(repo, opts = {}) {
2429
2870
  const rev = opts.rev ?? "main";
2430
2871
  const fetchImpl = opts.fetchImpl ?? fetch;
2431
- const url = `${HF_HUB_BASE}/api/models/${repo}/revision/${encodeURIComponent(rev)}`;
2872
+ const url = `${apiPrefix(opts.repoType)}/${repo}/revision/${encodeURIComponent(rev)}`;
2432
2873
  const res = await fetchImpl(url);
2433
2874
  if (!res.ok) {
2434
2875
  throw new Error(`[hf] commit resolve ${url} failed: ${res.status} ${res.statusText}`);
@@ -2439,8 +2880,8 @@ async function fetchHuggingfaceCommit(repo, opts = {}) {
2439
2880
  }
2440
2881
  return body.sha;
2441
2882
  }
2442
- async function walk(repo, rev, subpath, out, fetchImpl) {
2443
- const url = `${HF_HUB_BASE}/api/models/${repo}/tree/${rev}${subpath ? `/${subpath}` : ""}`;
2883
+ async function walk(repo, rev, subpath, out, fetchImpl, repoType) {
2884
+ const url = `${apiPrefix(repoType)}/${repo}/tree/${rev}${subpath ? `/${subpath}` : ""}`;
2444
2885
  const res = await fetchImpl(url);
2445
2886
  if (!res.ok) {
2446
2887
  throw new Error(`[hf] tree fetch ${url} failed: ${res.status} ${res.statusText}`);
@@ -2448,7 +2889,7 @@ async function walk(repo, rev, subpath, out, fetchImpl) {
2448
2889
  const entries = await res.json();
2449
2890
  for (const e of entries) {
2450
2891
  if (e.type === "directory") {
2451
- await walk(repo, rev, e.path, out, fetchImpl);
2892
+ await walk(repo, rev, e.path, out, fetchImpl, repoType);
2452
2893
  continue;
2453
2894
  }
2454
2895
  if (e.type !== "file") continue;
@@ -2490,7 +2931,9 @@ export {
2490
2931
  CatalogService,
2491
2932
  CommunitySource,
2492
2933
  GILDE_PACKAGE_NAME,
2934
+ InstalledAiAppsSource,
2493
2935
  LocalCatalogSource,
2936
+ applyDefaultCraftbookStepPolicies,
2494
2937
  builtinCatalogId,
2495
2938
  categorizeToolset,
2496
2939
  compareRelease,
@@ -2503,9 +2946,13 @@ export {
2503
2946
  gildePackageRoot,
2504
2947
  installNpmPackageToolset,
2505
2948
  isGildeReleaseVersion,
2949
+ outputMediaForCraftbookBlueprint,
2950
+ outputMediumForCraftbookBlueprint,
2506
2951
  pickGildePatchUpdate,
2507
2952
  publishStagedNpmInstall,
2508
2953
  recoverInterruptedNpmInstall,
2954
+ renderGildeSchemaFiles,
2955
+ renderSchema,
2509
2956
  selectMlxInstallFiles,
2510
2957
  stageGildeVersion,
2511
2958
  totalSize,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/gezel-catalog",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "description": "Gezel catalog loader: sources, install pipeline, and authoring scripts over the external @bendyline/gilde content package.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -47,12 +47,13 @@
47
47
  "!dist/**/*.map"
48
48
  ],
49
49
  "dependencies": {
50
- "@bendyline/gezel": "1.0.6",
51
- "@bendyline/gilde": "0.1.36",
50
+ "@bendyline/gezel": "1.1.0",
51
+ "@bendyline/gilde": "0.1.55",
52
52
  "tar": "7.5.21",
53
53
  "zod": "^4.4.3"
54
54
  },
55
55
  "devDependencies": {
56
+ "@bendyline/gezel-knowledge": "1.1.0",
56
57
  "esbuild": "^0.28.1",
57
58
  "tsup": "^8.5.1",
58
59
  "tsx": "^4.23.1",
@@ -69,7 +70,9 @@
69
70
  "build-index": "node scripts/run-gilde-build-index.mjs",
70
71
  "lint-manifests": "tsx src/chat-model-manifest-lint.ts",
71
72
  "generate-craftbooks": "tsx scripts/generate-craftbooks.ts",
73
+ "migrate-step-policies": "tsx scripts/migrate-craftbook-step-policies.ts",
72
74
  "pin-revisions": "tsx scripts/pin-revisions.ts",
73
- "export-gilde-schemas": "tsx scripts/export-gilde-schemas.ts"
75
+ "export-gilde-schemas": "tsx scripts/export-gilde-schemas.ts",
76
+ "add-knowledge-catalog": "tsx scripts/add-knowledge-catalog.ts"
74
77
  }
75
78
  }