@instruktlabs/kiln 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +60 -3
- package/CONTRIBUTING.md +16 -7
- package/README.md +95 -381
- package/dist/agent-run.mjs +220 -150
- package/dist/build.json +40 -28
- package/dist/cli.mjs +242 -163
- package/dist/mcp-engine.mjs +412 -154
- package/dist/mcp-server.mjs +1 -1
- package/docs/architecture.md +6 -3
- package/docs/chatgpt.md +20 -5
- package/docs/examples.md +40 -170
- package/docs/install.md +100 -35
- package/docs/migration.md +20 -0
- package/docs/sdk.md +49 -5
- package/docs/tools-operations.md +1529 -0
- package/lib/asset-widget-uri.d.ts +9 -0
- package/lib/asset-widget-uri.js +9 -0
- package/lib/cli-render-mode.js +10 -3
- package/lib/discovery/index.d.ts +1 -1
- package/lib/discovery/index.js +2 -2
- package/lib/discovery/portable.d.ts +9 -0
- package/lib/discovery/portable.js +6 -0
- package/lib/discovery/service.d.ts +3 -1
- package/lib/discovery/service.js +4 -2
- package/lib/engine-identity.d.ts +1 -1
- package/lib/engine-identity.js +1 -1
- package/lib/evaluator/isolation.js +19 -5
- package/lib/evaluator/probe-invariants.d.ts +11 -0
- package/lib/evaluator/probe-invariants.js +36 -0
- package/lib/evaluator/probe-worker.js +16 -4
- package/lib/loopback.d.ts +6 -0
- package/lib/loopback.js +6 -0
- package/lib/mcp-core.d.ts +210 -0
- package/lib/mcp-core.js +279 -0
- package/lib/mcp-engine.d.ts +59 -0
- package/lib/mcp-engine.js +289 -0
- package/lib/mcp-manifest.d.ts +15 -0
- package/lib/mcp-manifest.js +65 -0
- package/lib/mcp.d.ts +5 -0
- package/lib/mcp.js +2 -0
- package/lib/render-service-host.js +1 -1
- package/lib/tools/discovery.d.ts +1 -1
- package/lib/tools/discovery.js +11 -9
- package/lib/tools/materials.d.ts +1 -1
- package/lib/tools/materials.js +44 -14
- package/lib/tools/operation-contract.d.ts +8 -0
- package/lib/tools/operation-contract.js +37 -0
- package/lib/tools/operations.d.ts +3 -0
- package/lib/tools/operations.js +154 -0
- package/lib/tools/registry.d.ts +6 -2
- package/lib/tools/registry.js +71 -23
- package/package.json +23 -1
- package/plugin.json +1 -1
- package/plugins/kiln-engine/.claude-plugin/icon.png +0 -0
- package/plugins/kiln-engine/.claude-plugin/plugin.json +9 -3
- package/plugins/kiln-engine/PRIVACY.md +100 -0
- package/plugins/kiln-engine/README.md +103 -18
- package/plugins/kiln-engine/{scripts/setup-workspace.mjs → bin/kiln-setup-workspace.mjs} +37 -9
- package/plugins/kiln-engine/package-provenance.json +15 -8
- package/plugins/kiln-engine/plugin.json +10 -4
- package/plugins/kiln-engine/runtime.json +10 -1
- package/plugins/kiln-engine/skills/kiln-setup-workspace/SKILL.md +17 -18
- package/plugins/kiln-engine/skills/kiln-setup-workspace/references/plugin-install.md +28 -8
- package/plugins/kiln-engine/skills/kiln-setup-workspace/references/renderer-setup.md +11 -0
- package/scripts/create-workspace.mjs +71 -13
- package/scripts/workspace-config.mjs +239 -0
- package/scripts/workspace-project.mjs +384 -0
- package/scripts/workspace-transaction.mjs +504 -0
- package/skills/kiln-setup-workspace/SKILL.md +17 -18
- package/skills/kiln-setup-workspace/references/plugin-install.md +28 -8
- package/skills/kiln-setup-workspace/references/renderer-setup.md +11 -0
- package/src/cli-render-mode.ts +9 -9
- package/src/cli.ts +3 -3
- package/src/discovery/index.ts +2 -0
- package/src/discovery/portable.ts +9 -0
- package/src/discovery/service.ts +3 -1
- package/src/engine-identity.ts +1 -1
- package/src/evaluator/isolation.ts +27 -6
- package/src/evaluator/probe-invariants.ts +50 -0
- package/src/evaluator/probe-worker.ts +22 -4
- package/src/mcp-core.ts +3 -0
- package/src/mcp-engine.ts +11 -1
- package/src/mcp.ts +5 -0
- package/src/render-service-host.ts +1 -1
- package/src/tools/discovery.ts +20 -10
- package/src/tools/materials.ts +59 -15
- package/src/tools/operation-contract.ts +51 -0
- package/src/tools/operations.ts +177 -0
- package/src/tools/registry.ts +100 -20
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { KilnToolContext, KilnToolDef } from './registry';
|
|
3
|
+
import { projectActionSchemas } from './operation-contract';
|
|
4
|
+
|
|
5
|
+
/** Adapt domain operations in the engine, never in a transport-specific skin. */
|
|
6
|
+
export function projectOperationDefs(
|
|
7
|
+
defs: readonly KilnToolDef[],
|
|
8
|
+
context: KilnToolContext,
|
|
9
|
+
): KilnToolDef[] {
|
|
10
|
+
return defs.flatMap((def) => {
|
|
11
|
+
if (
|
|
12
|
+
!context.assetLibrary &&
|
|
13
|
+
['kiln_save', 'kiln_assets', 'kiln_present', 'kiln_export', 'kiln_import'].includes(def.name)
|
|
14
|
+
)
|
|
15
|
+
return [];
|
|
16
|
+
if (def.name === 'kiln_renderer') return rendererOperations(def, context);
|
|
17
|
+
if (def.name === 'kiln_discover') return discoveryOperations(def);
|
|
18
|
+
if (def.name === 'kiln_material') return materialOperations(def);
|
|
19
|
+
if (def.name === 'kiln_assets') return assetOperations(def, context);
|
|
20
|
+
return [def];
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function actionTools(def: KilnToolDef) {
|
|
25
|
+
if (!(def.inputSchema instanceof z.ZodObject) || !def.actionContract)
|
|
26
|
+
throw new Error(`${def.name}: missing shared action contract`);
|
|
27
|
+
const schemas = projectActionSchemas(def.name, def.inputSchema, def.actionContract);
|
|
28
|
+
return Object.fromEntries(
|
|
29
|
+
Object.entries(def.actionContract).map(([action, contract]) => [
|
|
30
|
+
action,
|
|
31
|
+
{
|
|
32
|
+
...def,
|
|
33
|
+
actionContract: undefined,
|
|
34
|
+
name: `${def.name}_${action.replaceAll('-', '_')}`,
|
|
35
|
+
description: contract.description,
|
|
36
|
+
inputSchema: schemas[action]!,
|
|
37
|
+
run: async (raw: unknown) => def.run({ ...schemas[action]!.parse(raw), action }),
|
|
38
|
+
} satisfies KilnToolDef,
|
|
39
|
+
]),
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function assetOperations(def: KilnToolDef, context: KilnToolContext): KilnToolDef[] {
|
|
44
|
+
const tools = actionTools(def);
|
|
45
|
+
const list = tools.list!;
|
|
46
|
+
const listSchema = list.inputSchema as z.ZodObject;
|
|
47
|
+
// Remove only the legacy default: omission means all collections in this operation.
|
|
48
|
+
const collection = (listSchema.shape.collection as z.ZodDefault<z.ZodString>)
|
|
49
|
+
.unwrap()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe(
|
|
52
|
+
'Collection ID from kiln_assets_collections; omitted searches all configured collections.',
|
|
53
|
+
);
|
|
54
|
+
const inputSchema = listSchema.extend({ collection });
|
|
55
|
+
const search: KilnToolDef = {
|
|
56
|
+
...list,
|
|
57
|
+
name: 'kiln_assets_search',
|
|
58
|
+
description:
|
|
59
|
+
'Search saved assets with pagination. Omit collection for all configured collections, or select one explicitly. Results identify exact collection, asset and revision; partial read failures are reported.',
|
|
60
|
+
inputSchema,
|
|
61
|
+
run: async (raw) => {
|
|
62
|
+
const input = inputSchema.parse(raw);
|
|
63
|
+
const { collection: _collection, ...filters } = input;
|
|
64
|
+
if (input.collection === undefined) return tools.catalog!.run(filters);
|
|
65
|
+
if (!context.assetLibrary?.collections().some((item) => item.id === input.collection))
|
|
66
|
+
throw new Error(
|
|
67
|
+
'Unknown collection. Use kiln_assets_collections to list available destinations.',
|
|
68
|
+
);
|
|
69
|
+
const result = (await list.run(input)) as {
|
|
70
|
+
total: number;
|
|
71
|
+
nextOffset: number | null;
|
|
72
|
+
assets: Record<string, unknown>[];
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
errors: [],
|
|
76
|
+
total: result.total,
|
|
77
|
+
nextOffset: result.nextOffset,
|
|
78
|
+
assets: result.assets.map((asset) => ({ ...asset, collection: input.collection })),
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
return [tools.collections!, search, tools.get!, tools.restore!];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function materialOperations(def: KilnToolDef): KilnToolDef[] {
|
|
86
|
+
const tools = actionTools(def);
|
|
87
|
+
const presets = tools.presets!;
|
|
88
|
+
const inputSchema = (presets.inputSchema as z.ZodObject).extend({
|
|
89
|
+
scope: z.enum(['all', 'presets', 'saved']).default('all'),
|
|
90
|
+
});
|
|
91
|
+
const search: KilnToolDef = {
|
|
92
|
+
...presets,
|
|
93
|
+
name: 'kiln_material_search',
|
|
94
|
+
description:
|
|
95
|
+
'Browse shipped material recipes and saved material revisions, optionally filtered by exact tag. Recipe IDs and saved material IDs are distinct. Use scope to select presets, saved or both.',
|
|
96
|
+
inputSchema,
|
|
97
|
+
run: async (raw) => {
|
|
98
|
+
const { scope, ...input } = inputSchema.parse(raw);
|
|
99
|
+
const recipes =
|
|
100
|
+
scope === 'saved'
|
|
101
|
+
? { presets: [] }
|
|
102
|
+
: ((await presets.run(input)) as { presets: Record<string, unknown>[] });
|
|
103
|
+
const saved =
|
|
104
|
+
scope === 'presets'
|
|
105
|
+
? { materials: [] }
|
|
106
|
+
: ((await tools.list!.run(input)) as { materials: Record<string, unknown>[] });
|
|
107
|
+
return {
|
|
108
|
+
ok: true,
|
|
109
|
+
presets: recipes.presets.map((item) => ({ ...item, kind: 'preset' })),
|
|
110
|
+
materials: saved.materials.map((item) => ({ ...item, kind: 'saved' })),
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
const preset = tools['create-preset']!;
|
|
115
|
+
const procedural = tools['create-procedural']!;
|
|
116
|
+
const createInput = z.strictObject({
|
|
117
|
+
definition: z.discriminatedUnion('kind', [
|
|
118
|
+
(preset.inputSchema as z.ZodObject).extend({ kind: z.literal('preset') }),
|
|
119
|
+
(procedural.inputSchema as z.ZodObject).extend({ kind: z.literal('procedural') }),
|
|
120
|
+
]),
|
|
121
|
+
});
|
|
122
|
+
const create: KilnToolDef = {
|
|
123
|
+
...preset,
|
|
124
|
+
name: 'kiln_material_create',
|
|
125
|
+
description:
|
|
126
|
+
'Create and retain one immutable material from a preset or procedural definition. Presets require explicit seed, creator and license. Pin the returned materialId/revisionId for rendering. Does not fetch URLs or execute source.',
|
|
127
|
+
inputSchema: createInput,
|
|
128
|
+
run: async (raw) => {
|
|
129
|
+
const { kind, ...input } = createInput.parse(raw).definition;
|
|
130
|
+
return (kind === 'preset' ? preset : procedural).run(input);
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
return [search, tools.get!, create, tools.import!];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function rendererOperations(def: KilnToolDef, context: KilnToolContext): KilnToolDef[] {
|
|
137
|
+
if (!context.renderCapabilities) return [];
|
|
138
|
+
return ['status', ...(context.reprobeRenderer ? ['reprobe'] : [])].map((action) => {
|
|
139
|
+
const inputSchema = z.strictObject({});
|
|
140
|
+
return {
|
|
141
|
+
...def,
|
|
142
|
+
name: `kiln_renderer_${action}`,
|
|
143
|
+
inputSchema,
|
|
144
|
+
run: async (raw: unknown) => {
|
|
145
|
+
inputSchema.parse(raw);
|
|
146
|
+
return def.run({ action });
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function discoveryOperations(def: KilnToolDef): KilnToolDef[] {
|
|
153
|
+
const original = def.inputSchema as z.ZodObject;
|
|
154
|
+
const { capabilities: _capabilities, ...fields } = original.shape;
|
|
155
|
+
const inputSchema = z.strictObject(fields);
|
|
156
|
+
const empty = z.strictObject({});
|
|
157
|
+
return [
|
|
158
|
+
{
|
|
159
|
+
...def,
|
|
160
|
+
description:
|
|
161
|
+
'Discover geometry helpers, assemblies, recipes and complete input shapes. Search with modeling language or fetch up to six exact IDs. Use kiln_capabilities for current host facts.',
|
|
162
|
+
inputSchema,
|
|
163
|
+
run: async (raw) => def.run(inputSchema.parse(raw)),
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
...def,
|
|
167
|
+
name: 'kiln_capabilities',
|
|
168
|
+
description:
|
|
169
|
+
'Inspect declared host capabilities, execution limits and available storage. Observed rendering fidelity is returned by each render.',
|
|
170
|
+
inputSchema: empty,
|
|
171
|
+
run: async (raw) => {
|
|
172
|
+
empty.parse(raw);
|
|
173
|
+
return def.run({ capabilities: true });
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
}
|
package/src/tools/registry.ts
CHANGED
|
@@ -24,6 +24,8 @@ import { createKilnMaterialDef } from './materials';
|
|
|
24
24
|
import { createKilnReviewDef, type ReviewStore } from './review';
|
|
25
25
|
import { withWorkspaceContext } from './workspace';
|
|
26
26
|
import { describeInputError } from './actions';
|
|
27
|
+
import { projectOperationDefs } from './operations';
|
|
28
|
+
import type { KilnActionContract } from './operation-contract';
|
|
27
29
|
import {
|
|
28
30
|
advancedCaptureInput,
|
|
29
31
|
cameraShotInput,
|
|
@@ -94,6 +96,8 @@ import type { TextureUsage } from '../textures';
|
|
|
94
96
|
|
|
95
97
|
export const KILN_ASSET_WIDGET_URI = 'ui://kiln/asset-v5.html';
|
|
96
98
|
export interface KilnToolDef {
|
|
99
|
+
/** Shared action ownership and requirements; never hand-maintained by transports. */
|
|
100
|
+
actionContract?: Readonly<Record<string, import('./operation-contract').KilnActionContract>>;
|
|
97
101
|
outputSchema?: z.ZodType;
|
|
98
102
|
/** Optional MCP App presentation; metadata is hidden from the language model. */
|
|
99
103
|
ui?: {
|
|
@@ -2498,13 +2502,14 @@ async function readSavedRevision(
|
|
|
2498
2502
|
collection: string,
|
|
2499
2503
|
assetId: string,
|
|
2500
2504
|
revisionId: string,
|
|
2505
|
+
listCall = `kiln_assets { action: 'list', collection: '${collection}' }`,
|
|
2501
2506
|
) {
|
|
2502
2507
|
try {
|
|
2503
2508
|
return await target.read(collection, assetId, revisionId);
|
|
2504
2509
|
} catch (error) {
|
|
2505
2510
|
if ((error as NodeJS.ErrnoException).code === 'ENOENT')
|
|
2506
2511
|
throw new Error(
|
|
2507
|
-
`Unknown asset ${assetId} revision ${revisionId} in collection ${collection}.
|
|
2512
|
+
`Unknown asset ${assetId} revision ${revisionId} in collection ${collection}. ${listCall} lists the saved revisions.`,
|
|
2508
2513
|
);
|
|
2509
2514
|
throw error;
|
|
2510
2515
|
}
|
|
@@ -2512,12 +2517,19 @@ async function readSavedRevision(
|
|
|
2512
2517
|
|
|
2513
2518
|
export function createKilnProgramToolRegistry(
|
|
2514
2519
|
suppliedContext: KilnToolContext = {},
|
|
2520
|
+
): KilnToolDef[] {
|
|
2521
|
+
return createProgramDefs(suppliedContext, 'grouped');
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
function createProgramDefs(
|
|
2525
|
+
suppliedContext: KilnToolContext,
|
|
2526
|
+
presentation: 'grouped' | 'operations',
|
|
2515
2527
|
): KilnToolDef[] {
|
|
2516
2528
|
toolRequirements(suppliedContext);
|
|
2517
2529
|
const store = suppliedContext.programStore ?? new MemoryProgramStore();
|
|
2518
2530
|
const context = withCaptureCache(withBuildCache({ ...suppliedContext, programStore: store }));
|
|
2519
2531
|
return [
|
|
2520
|
-
createKilnDiscoveryDef({ ...suppliedContext, programStore: store }),
|
|
2532
|
+
createKilnDiscoveryDef({ ...suppliedContext, programStore: store }, presentation),
|
|
2521
2533
|
{
|
|
2522
2534
|
name: 'kiln_renderer',
|
|
2523
2535
|
description:
|
|
@@ -2564,7 +2576,9 @@ export function createKilnProgramToolRegistry(
|
|
|
2564
2576
|
...(context.projectStore
|
|
2565
2577
|
? [createKilnProjectDef(context.projectStore, context.projectBundleReader)]
|
|
2566
2578
|
: []),
|
|
2567
|
-
...(context.materialLibrary
|
|
2579
|
+
...(context.materialLibrary
|
|
2580
|
+
? [createKilnMaterialDef(context.materialLibrary, presentation)]
|
|
2581
|
+
: []),
|
|
2568
2582
|
...(context.reviewStore && context.assetLibrary
|
|
2569
2583
|
? [
|
|
2570
2584
|
createKilnReviewDef({
|
|
@@ -2574,7 +2588,7 @@ export function createKilnProgramToolRegistry(
|
|
|
2574
2588
|
}),
|
|
2575
2589
|
]
|
|
2576
2590
|
: []),
|
|
2577
|
-
...createKilnAssetDefs({ ...context, programStore: store }),
|
|
2591
|
+
...createKilnAssetDefs({ ...context, programStore: store }, presentation),
|
|
2578
2592
|
]
|
|
2579
2593
|
.map((def) => withWorkspaceContext(def, context))
|
|
2580
2594
|
.map((def) => ({
|
|
@@ -2591,6 +2605,11 @@ export function createKilnProgramToolRegistry(
|
|
|
2591
2605
|
}));
|
|
2592
2606
|
}
|
|
2593
2607
|
|
|
2608
|
+
/** Opt-in named domain operations; grouped local/native contracts remain unchanged. */
|
|
2609
|
+
export function createKilnOperationToolRegistry(context: KilnToolContext = {}): KilnToolDef[] {
|
|
2610
|
+
return projectOperationDefs(createProgramDefs(context, 'operations'), context);
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2594
2613
|
/** Canonical native workflow: shared definitions plus one host-owned terminal. */
|
|
2595
2614
|
export function createKilnNativeToolRegistry(
|
|
2596
2615
|
suppliedContext: KilnToolContext,
|
|
@@ -2686,6 +2705,36 @@ const assetSelector = {
|
|
|
2686
2705
|
revisionId: z.string().regex(/^[a-z][a-z0-9_-]{0,79}$/),
|
|
2687
2706
|
};
|
|
2688
2707
|
|
|
2708
|
+
const ASSET_CONTRACT = {
|
|
2709
|
+
collections: {
|
|
2710
|
+
fields: [],
|
|
2711
|
+
required: [],
|
|
2712
|
+
description: 'List configured asset storage collections. Collection is not project membership.',
|
|
2713
|
+
},
|
|
2714
|
+
catalog: {
|
|
2715
|
+
fields: ['assetId', 'query', 'offset', 'limit'],
|
|
2716
|
+
required: [],
|
|
2717
|
+
description: 'Search saved assets across configured collections with pagination.',
|
|
2718
|
+
},
|
|
2719
|
+
list: {
|
|
2720
|
+
fields: ['collection', 'assetId', 'query', 'offset', 'limit'],
|
|
2721
|
+
required: [],
|
|
2722
|
+
description: 'Search saved assets in one collection with pagination.',
|
|
2723
|
+
},
|
|
2724
|
+
get: {
|
|
2725
|
+
fields: ['collection', 'assetId', 'revisionId'],
|
|
2726
|
+
required: ['assetId'],
|
|
2727
|
+
description:
|
|
2728
|
+
'Read a saved revision and its download descriptors. Omit revisionId for the newest revision.',
|
|
2729
|
+
},
|
|
2730
|
+
restore: {
|
|
2731
|
+
fields: ['collection', 'assetId', 'revisionId'],
|
|
2732
|
+
required: ['assetId'],
|
|
2733
|
+
description:
|
|
2734
|
+
'Restore exact saved source as programRef for editing or rendering. Omit revisionId for the newest revision. Does not overwrite the saved revision. Binary-only assets have no source.',
|
|
2735
|
+
},
|
|
2736
|
+
} satisfies Record<string, KilnActionContract>;
|
|
2737
|
+
|
|
2689
2738
|
/**
|
|
2690
2739
|
* The revision a get or restore without `revisionId` means: the newest, the
|
|
2691
2740
|
* one no later revision names as its parent, ties to the latest `createdAt`
|
|
@@ -2701,12 +2750,11 @@ export async function newestRevision(
|
|
|
2701
2750
|
},
|
|
2702
2751
|
collection: string,
|
|
2703
2752
|
assetId: string,
|
|
2753
|
+
listCall = `kiln_assets { action: 'list', collection: '${collection}' }`,
|
|
2704
2754
|
): Promise<string> {
|
|
2705
2755
|
const revisions = (await target.list(collection)).filter((a) => a.assetId === assetId);
|
|
2706
2756
|
if (!revisions.length)
|
|
2707
|
-
throw new Error(
|
|
2708
|
-
`Unknown asset ${assetId} in ${collection}; kiln_assets { action: 'list', collection: '${collection}' } lists them.`,
|
|
2709
|
-
);
|
|
2757
|
+
throw new Error(`Unknown asset ${assetId} in ${collection}; ${listCall} lists them.`);
|
|
2710
2758
|
const parents = new Set(revisions.map((a) => a.parentRevision).filter(Boolean));
|
|
2711
2759
|
const heads = revisions.filter((a) => !parents.has(a.revisionId));
|
|
2712
2760
|
const newest = (heads.length ? heads : revisions).sort((a, b) =>
|
|
@@ -2797,7 +2845,23 @@ export async function buildProgramAssetDraft(
|
|
|
2797
2845
|
}
|
|
2798
2846
|
|
|
2799
2847
|
/** All asset operation schemas live here, alongside the existing tool definitions. */
|
|
2800
|
-
export function createKilnAssetDefs(
|
|
2848
|
+
export function createKilnAssetDefs(
|
|
2849
|
+
context: KilnToolContext,
|
|
2850
|
+
presentation: 'grouped' | 'operations' = 'grouped',
|
|
2851
|
+
): KilnToolDef[] {
|
|
2852
|
+
const listCall = (collection: string) =>
|
|
2853
|
+
presentation === 'operations'
|
|
2854
|
+
? `kiln_assets_search({ collection: '${collection}' })`
|
|
2855
|
+
: `kiln_assets { action: 'list', collection: '${collection}' }`;
|
|
2856
|
+
const selector =
|
|
2857
|
+
presentation === 'operations'
|
|
2858
|
+
? {
|
|
2859
|
+
...assetSelector,
|
|
2860
|
+
collection: assetSelector.collection.describe(
|
|
2861
|
+
'Collection ID from kiln_assets_collections; default project.',
|
|
2862
|
+
),
|
|
2863
|
+
}
|
|
2864
|
+
: assetSelector;
|
|
2801
2865
|
const library = () => {
|
|
2802
2866
|
if (!context.assetLibrary)
|
|
2803
2867
|
throw new Error(
|
|
@@ -2828,11 +2892,11 @@ export function createKilnAssetDefs(context: KilnToolContext): KilnToolDef[] {
|
|
|
2828
2892
|
downloadUrls: await context.assetDownloadUrls?.(collection, asset.assetId, asset.revisionId),
|
|
2829
2893
|
});
|
|
2830
2894
|
const saveInput = z.object({
|
|
2831
|
-
collection:
|
|
2895
|
+
collection: selector.collection,
|
|
2832
2896
|
programRef: z.string(),
|
|
2833
2897
|
name: z.string().min(1).max(200),
|
|
2834
|
-
assetId:
|
|
2835
|
-
parentRevision:
|
|
2898
|
+
assetId: selector.assetId.optional(),
|
|
2899
|
+
parentRevision: selector.revisionId.optional(),
|
|
2836
2900
|
tags: z.array(z.string().max(80)).max(30).optional(),
|
|
2837
2901
|
brief: z.string().max(8000).optional(),
|
|
2838
2902
|
description: z.string().max(4000).optional(),
|
|
@@ -2850,16 +2914,16 @@ export function createKilnAssetDefs(context: KilnToolContext): KilnToolDef[] {
|
|
|
2850
2914
|
});
|
|
2851
2915
|
const assetsInput = z.object({
|
|
2852
2916
|
action: z.enum(['collections', 'catalog', 'list', 'get', 'restore']).default('list'),
|
|
2853
|
-
collection:
|
|
2854
|
-
assetId:
|
|
2855
|
-
revisionId:
|
|
2917
|
+
collection: selector.collection,
|
|
2918
|
+
assetId: selector.assetId.optional(),
|
|
2919
|
+
revisionId: selector.revisionId
|
|
2856
2920
|
.optional()
|
|
2857
2921
|
.describe('get, restore: the saved revision; omitted, the newest.'),
|
|
2858
2922
|
query: z.string().max(200).optional(),
|
|
2859
2923
|
offset: z.number().int().min(0).default(0),
|
|
2860
2924
|
limit: z.number().int().min(1).max(50).default(20),
|
|
2861
2925
|
});
|
|
2862
|
-
const exportInput = z.object(
|
|
2926
|
+
const exportInput = z.object(selector);
|
|
2863
2927
|
const profileExportInput = exportInput.extend({
|
|
2864
2928
|
profile: z
|
|
2865
2929
|
.enum(['editable', 'runtime'])
|
|
@@ -2869,8 +2933,8 @@ export function createKilnAssetDefs(context: KilnToolContext): KilnToolDef[] {
|
|
|
2869
2933
|
),
|
|
2870
2934
|
});
|
|
2871
2935
|
const importInput = z.object({
|
|
2872
|
-
...
|
|
2873
|
-
sourceCollection:
|
|
2936
|
+
...selector,
|
|
2937
|
+
sourceCollection: selector.collection,
|
|
2874
2938
|
});
|
|
2875
2939
|
return [
|
|
2876
2940
|
{
|
|
@@ -2892,6 +2956,7 @@ export function createKilnAssetDefs(context: KilnToolContext): KilnToolDef[] {
|
|
|
2892
2956
|
input.collection,
|
|
2893
2957
|
input.assetId,
|
|
2894
2958
|
input.parentRevision,
|
|
2959
|
+
listCall(input.collection),
|
|
2895
2960
|
);
|
|
2896
2961
|
assertSavedRequirementsAuthorized(previous.manifest, activeRequirements);
|
|
2897
2962
|
}
|
|
@@ -2908,6 +2973,7 @@ export function createKilnAssetDefs(context: KilnToolContext): KilnToolDef[] {
|
|
|
2908
2973
|
description:
|
|
2909
2974
|
'Browse saved assets: collections discovers storage; catalog searches all configured collections; list searches one. Both searches paginate. get returns a build record/downloads; restore loads exact source for kiln_source/kiln_edit. Collection is not project membership. Binary-only assets cannot restore source.',
|
|
2910
2975
|
inputSchema: assetsInput,
|
|
2976
|
+
actionContract: ASSET_CONTRACT,
|
|
2911
2977
|
run: async (raw) => {
|
|
2912
2978
|
const input = assetsInput.parse(raw);
|
|
2913
2979
|
const activeRequirements = toolRequirements(context);
|
|
@@ -2961,13 +3027,25 @@ export function createKilnAssetDefs(context: KilnToolContext): KilnToolDef[] {
|
|
|
2961
3027
|
})),
|
|
2962
3028
|
};
|
|
2963
3029
|
}
|
|
2964
|
-
if (!input
|
|
3030
|
+
if (ASSET_CONTRACT[input.action].required.some((field) => !input[field as 'assetId']))
|
|
2965
3031
|
throw new Error(
|
|
2966
3032
|
`kiln_assets ${input.action} requires assetId; kiln_assets { action: 'list', collection: '${input.collection}' } lists them.`,
|
|
2967
3033
|
);
|
|
2968
3034
|
const revisionId =
|
|
2969
|
-
input.revisionId ??
|
|
2970
|
-
|
|
3035
|
+
input.revisionId ??
|
|
3036
|
+
(await newestRevision(
|
|
3037
|
+
target,
|
|
3038
|
+
input.collection,
|
|
3039
|
+
input.assetId!,
|
|
3040
|
+
listCall(input.collection),
|
|
3041
|
+
));
|
|
3042
|
+
const record = await readSavedRevision(
|
|
3043
|
+
target,
|
|
3044
|
+
input.collection,
|
|
3045
|
+
input.assetId!,
|
|
3046
|
+
revisionId,
|
|
3047
|
+
listCall(input.collection),
|
|
3048
|
+
);
|
|
2971
3049
|
if (input.action === 'get') return links(input.collection, record.manifest);
|
|
2972
3050
|
const saved = assertSavedRequirementsAuthorized(record.manifest, activeRequirements);
|
|
2973
3051
|
const code = record.files['source.kiln.js'];
|
|
@@ -3059,6 +3137,7 @@ export function createKilnAssetDefs(context: KilnToolContext): KilnToolDef[] {
|
|
|
3059
3137
|
input.collection,
|
|
3060
3138
|
input.assetId,
|
|
3061
3139
|
input.revisionId,
|
|
3140
|
+
listCall(input.collection),
|
|
3062
3141
|
);
|
|
3063
3142
|
const { inspectDrawDiagnostics } = await import('../draw-diagnostics');
|
|
3064
3143
|
if (input.profile === 'runtime') {
|
|
@@ -3102,6 +3181,7 @@ export function createKilnAssetDefs(context: KilnToolContext): KilnToolDef[] {
|
|
|
3102
3181
|
input.sourceCollection,
|
|
3103
3182
|
input.assetId,
|
|
3104
3183
|
input.revisionId,
|
|
3184
|
+
listCall(input.sourceCollection),
|
|
3105
3185
|
);
|
|
3106
3186
|
await target.import(input.collection, [record]);
|
|
3107
3187
|
return links(input.collection, record.manifest);
|