@instruktlabs/kiln 1.2.0 → 1.2.1
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/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +16 -0
- package/dist/agent-run.mjs +5 -2
- package/dist/build.json +22 -22
- package/dist/cli.mjs +5 -2
- package/dist/mcp-engine.mjs +3 -4
- package/dist/mcp-server.mjs +6 -6
- package/dist/viewer/app.js +180 -180
- package/dist/viewer/chat.html +183 -183
- package/lib/asset-widget-uri.d.ts +1 -1
- package/lib/asset-widget-uri.js +1 -1
- package/lib/engine-identity.d.ts +1 -1
- package/lib/engine-identity.js +1 -1
- package/lib/mcp-core.d.ts +1 -1
- package/lib/tools/registry.d.ts +1 -2
- package/lib/tools/registry.js +2 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/plugins/kiln-engine/.claude-plugin/plugin.json +1 -1
- package/plugins/kiln-engine/README.md +3 -3
- package/plugins/kiln-engine/package-provenance.json +6 -6
- package/plugins/kiln-engine/plugin.json +1 -1
- package/plugins/kiln-engine/runtime.json +1 -1
- package/src/asset-widget-uri.ts +1 -1
- package/src/engine-identity.ts +1 -1
- package/src/generated/mcp-manifest.json +4 -4
- package/src/tools/registry.ts +2 -1
- package/src/viewer/chat-app.ts +6 -2
- package/src/viewer/glb-loader.ts +78 -0
- package/src/viewer/scene.ts +9 -26
package/lib/asset-widget-uri.js
CHANGED
package/lib/engine-identity.d.ts
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* `src/__tests__/package-contents.test.ts` fails if it disagrees with the manifest
|
|
27
27
|
* or with any plugin manifest, which is what keeps the literal honest.
|
|
28
28
|
*/
|
|
29
|
-
export declare const ENGINE_VERSION = "1.2.
|
|
29
|
+
export declare const ENGINE_VERSION = "1.2.1";
|
|
30
30
|
/**
|
|
31
31
|
* Where this engine was loaded from, as a `file://` URL of the package root.
|
|
32
32
|
*
|
package/lib/engine-identity.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* `src/__tests__/package-contents.test.ts` fails if it disagrees with the manifest
|
|
27
27
|
* or with any plugin manifest, which is what keeps the literal honest.
|
|
28
28
|
*/
|
|
29
|
-
export const ENGINE_VERSION = '1.2.
|
|
29
|
+
export const ENGINE_VERSION = '1.2.1';
|
|
30
30
|
/**
|
|
31
31
|
* Where this engine was loaded from, as a `file://` URL of the package root.
|
|
32
32
|
*
|
package/lib/mcp-core.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ import { type ServeStdioOptions, type StdioServerHandle } from '@modelcontextpro
|
|
|
23
23
|
import type { AssetLink } from "./assets-resources.js";
|
|
24
24
|
/** Server identity reported in the handshake and stamped into every 2026-07-28 result. */
|
|
25
25
|
export declare const MCP_SERVER_NAME = "kiln";
|
|
26
|
-
export declare const MCP_SERVER_VERSION = "1.2.
|
|
26
|
+
export declare const MCP_SERVER_VERSION = "1.2.1";
|
|
27
27
|
/**
|
|
28
28
|
* The spec's optional `instructions` field, returned by `initialize` and
|
|
29
29
|
* `server/discover`.
|
package/lib/tools/registry.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { type ViewRenderTimeoutContextProvider, type ViewRenderTimeoutResolver }
|
|
|
17
17
|
import { ViewEvidenceHistoryStore } from "../views/evidence-history.js";
|
|
18
18
|
import { type BackdropId } from "../views/background.js";
|
|
19
19
|
import type { TextureUsage } from "../textures.js";
|
|
20
|
-
export
|
|
20
|
+
export { KILN_ASSET_WIDGET_URI } from "../asset-widget-uri.js";
|
|
21
21
|
export interface KilnToolDef {
|
|
22
22
|
/** Shared action ownership and requirements; never hand-maintained by transports. */
|
|
23
23
|
actionContract?: Readonly<Record<string, import("./operation-contract.js").KilnActionContract>>;
|
|
@@ -793,4 +793,3 @@ export declare function newestRevision(target: {
|
|
|
793
793
|
export declare function buildProgramAssetDraft(code: string, context: KilnToolContext, backdrop?: BackdropId): Promise<Pick<import("../assets.js").AssetDraft, 'code' | 'glb' | 'preview' | 'previewInfo' | 'build'>>;
|
|
794
794
|
/** All asset operation schemas live here, alongside the existing tool definitions. */
|
|
795
795
|
export declare function createKilnAssetDefs(context: KilnToolContext, presentation?: 'grouped' | 'operations'): KilnToolDef[];
|
|
796
|
-
export {};
|
package/lib/tools/registry.js
CHANGED
|
@@ -35,7 +35,8 @@ import { persistedPreviewFidelity } from "./preview-fidelity.js";
|
|
|
35
35
|
// =============================================================================
|
|
36
36
|
// Tool definition contract
|
|
37
37
|
// =============================================================================
|
|
38
|
-
export
|
|
38
|
+
export { KILN_ASSET_WIDGET_URI } from "../asset-widget-uri.js";
|
|
39
|
+
import { KILN_ASSET_WIDGET_URI } from "../asset-widget-uri.js";
|
|
39
40
|
function proceduralTextureMaterialContract(rendered, context) {
|
|
40
41
|
const required = [...new Set(context.requiredProceduralTextureUsages ?? [])];
|
|
41
42
|
if (required.length === 0)
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "kiln",
|
|
3
3
|
"displayName": "Kiln",
|
|
4
4
|
"description": "Build and revise 3D assets with your coding agent. Write JavaScript, inspect selected views, edit saved source revisions, and export GLB files locally.",
|
|
5
|
-
"version": "1.2.
|
|
5
|
+
"version": "1.2.1",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://github.com/instruktlabs/kiln",
|
|
8
8
|
"keywords": [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Kiln local plugin
|
|
2
2
|
|
|
3
|
-
Plugin version 1.2.
|
|
4
|
-
This plugin pins engine 1.2.
|
|
3
|
+
Plugin version 1.2.1, maintained by Instrukt Labs under the MIT license.
|
|
4
|
+
This plugin pins engine 1.2.1. Registry setup requires that exact version
|
|
5
5
|
to be published. Use a published release tag for normal installation or a reviewed
|
|
6
6
|
local archive for development qualification. The installation ID remains
|
|
7
7
|
`kiln-engine@instruktlabs`.
|
|
@@ -31,7 +31,7 @@ codex plugin add kiln-engine@instruktlabs
|
|
|
31
31
|
Restart or reload your coding agent's plugins as its installation message directs.
|
|
32
32
|
Then ask it to set up a Kiln workspace. The maintained setup skill runs this
|
|
33
33
|
plugin's `bin/kiln-setup-workspace.mjs` with Node, installs
|
|
34
|
-
`@instruktlabs/kiln@1.2.
|
|
34
|
+
`@instruktlabs/kiln@1.2.1` outside the project and configures your chosen
|
|
35
35
|
workspace. The same initializer supports existing projects and new workspaces.
|
|
36
36
|
|
|
37
37
|
The plugin registers setup only. The workspace supplies authoring skills and one
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"kind": "kiln-local-plugin",
|
|
4
|
-
"pluginVersion": "1.2.
|
|
5
|
-
"engineVersion": "1.2.
|
|
4
|
+
"pluginVersion": "1.2.1",
|
|
5
|
+
"engineVersion": "1.2.1",
|
|
6
6
|
"executables": [
|
|
7
7
|
"bin/kiln-setup-workspace.mjs"
|
|
8
8
|
],
|
|
9
9
|
"files": {
|
|
10
10
|
".claude-plugin/icon.png": "sha256:b4b651c402793b7e27cab780f04b14f80daec6d0cd5b7e4e90313930581a54e9",
|
|
11
|
-
".claude-plugin/plugin.json": "sha256:
|
|
11
|
+
".claude-plugin/plugin.json": "sha256:bef717ea5e0c96411c2db93d54e298dc3798512df9909ba74c950a6c1c03f6cb",
|
|
12
12
|
"bin/kiln-setup-workspace.mjs": "sha256:3d9a30236919a6ca91acb6b26f808cec442e6edc3dd88fe010645b010d76e838",
|
|
13
13
|
"LICENSE": "sha256:cdba278ebf1d6de618869936226bf03be4887179c128efc7aaa13d22034826a5",
|
|
14
|
-
"plugin.json": "sha256:
|
|
14
|
+
"plugin.json": "sha256:9ef79b862a25ae3efe86c03a37e09cd622912b97a0165a08fdf55cff8b71a010",
|
|
15
15
|
"PRIVACY.md": "sha256:5f58d981667e40441e21c2dec411f269a9a67a6141cec61976d27088fb12573b",
|
|
16
|
-
"README.md": "sha256:
|
|
17
|
-
"runtime.json": "sha256:
|
|
16
|
+
"README.md": "sha256:029732d7bb9e04561105727f8e46118d3106f513b4575a8d74d57cbd1bb68e7a",
|
|
17
|
+
"runtime.json": "sha256:b6d078f1223cb5ed3a522b0ef5a3cb778e051afc0115237cfd9cfe04c1f42025",
|
|
18
18
|
"skills/kiln-setup-workspace/references/plugin-install.md": "sha256:c338fb792e7fad0364c3e1a13c94c506afa1395489ec1b6e83b36d04728935fb",
|
|
19
19
|
"skills/kiln-setup-workspace/references/renderer-setup.md": "sha256:b743b1b2827fa814385990e1e5b849391cb427413e9c016d273aa19cebd46031",
|
|
20
20
|
"skills/kiln-setup-workspace/SKILL.md": "sha256:65ed926b3503a33f4fe793e84e4a045ce36e3967f0e6b340e456a15dd010ce54"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "kiln-engine",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.1",
|
|
5
5
|
"description": "Set up a local Kiln workspace to create, inspect and revise editable 3D assets with Claude Code or Codex.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Instrukt Labs",
|
package/src/asset-widget-uri.ts
CHANGED
package/src/engine-identity.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* `src/__tests__/package-contents.test.ts` fails if it disagrees with the manifest
|
|
28
28
|
* or with any plugin manifest, which is what keeps the literal honest.
|
|
29
29
|
*/
|
|
30
|
-
export const ENGINE_VERSION = '1.2.
|
|
30
|
+
export const ENGINE_VERSION = '1.2.1';
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Where this engine was loaded from, as a `file://` URL of the package root.
|
|
@@ -1733,13 +1733,13 @@
|
|
|
1733
1733
|
},
|
|
1734
1734
|
"_meta": {
|
|
1735
1735
|
"ui": {
|
|
1736
|
-
"resourceUri": "ui://kiln/asset-
|
|
1736
|
+
"resourceUri": "ui://kiln/asset-v6.html",
|
|
1737
1737
|
"visibility": [
|
|
1738
1738
|
"model"
|
|
1739
1739
|
]
|
|
1740
1740
|
},
|
|
1741
|
-
"openai/outputTemplate": "ui://kiln/asset-
|
|
1742
|
-
"ui/resourceUri": "ui://kiln/asset-
|
|
1741
|
+
"openai/outputTemplate": "ui://kiln/asset-v6.html",
|
|
1742
|
+
"ui/resourceUri": "ui://kiln/asset-v6.html"
|
|
1743
1743
|
},
|
|
1744
1744
|
"outputSchema": {
|
|
1745
1745
|
"type": "object",
|
|
@@ -2018,7 +2018,7 @@
|
|
|
2018
2018
|
],
|
|
2019
2019
|
"resources": [
|
|
2020
2020
|
{
|
|
2021
|
-
"uri": "ui://kiln/asset-
|
|
2021
|
+
"uri": "ui://kiln/asset-v6.html",
|
|
2022
2022
|
"name": "kiln-asset-viewer",
|
|
2023
2023
|
"mimeType": "text/html;profile=mcp-app",
|
|
2024
2024
|
"description": "Interactive Kiln asset viewer and downloads",
|
package/src/tools/registry.ts
CHANGED
|
@@ -94,7 +94,8 @@ import type { TextureUsage } from '../textures';
|
|
|
94
94
|
// Tool definition contract
|
|
95
95
|
// =============================================================================
|
|
96
96
|
|
|
97
|
-
export
|
|
97
|
+
export { KILN_ASSET_WIDGET_URI } from '../asset-widget-uri';
|
|
98
|
+
import { KILN_ASSET_WIDGET_URI } from '../asset-widget-uri';
|
|
98
99
|
export interface KilnToolDef {
|
|
99
100
|
/** Shared action ownership and requirements; never hand-maintained by transports. */
|
|
100
101
|
actionContract?: Readonly<Record<string, import('./operation-contract').KilnActionContract>>;
|
package/src/viewer/chat-app.ts
CHANGED
|
@@ -51,6 +51,8 @@ function encode(bytes: Uint8Array) {
|
|
|
51
51
|
async function show(input: unknown) {
|
|
52
52
|
const revision = ++presentationRevision;
|
|
53
53
|
record = undefined;
|
|
54
|
+
element('stats').textContent = '';
|
|
55
|
+
element<HTMLSelectElement>('clips').hidden = true;
|
|
54
56
|
for (const button of document.querySelectorAll<HTMLButtonElement>('button[data-download]'))
|
|
55
57
|
button.disabled = true;
|
|
56
58
|
try {
|
|
@@ -116,14 +118,16 @@ async function showResult(
|
|
|
116
118
|
status.textContent = 'Drag to orbit · Scroll to zoom';
|
|
117
119
|
} catch (error) {
|
|
118
120
|
if (revision !== presentationRevision) return;
|
|
121
|
+
stage?.dispose();
|
|
122
|
+
stage = undefined;
|
|
119
123
|
const preview = record.files['preview.png'];
|
|
120
124
|
if (preview) {
|
|
121
125
|
const image = new Image();
|
|
122
|
-
image.alt = record.manifest.name
|
|
126
|
+
image.alt = `${record.manifest.name} · saved preview image`;
|
|
123
127
|
image.src = `data:image/png;base64,${encode(preview)}`;
|
|
124
128
|
element('stage').replaceChildren(image);
|
|
125
129
|
}
|
|
126
|
-
status.textContent =
|
|
130
|
+
status.textContent = `${preview ? 'Showing saved preview. ' : ''}3D preview unavailable: ${String(error)}`;
|
|
127
131
|
}
|
|
128
132
|
for (const button of document.querySelectorAll<HTMLButtonElement>('button[data-download]'))
|
|
129
133
|
button.disabled = false;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LoadingManager,
|
|
3
|
+
TextureLoader,
|
|
4
|
+
Texture,
|
|
5
|
+
type Object3D,
|
|
6
|
+
type Mesh,
|
|
7
|
+
type BufferGeometry,
|
|
8
|
+
type Material,
|
|
9
|
+
} from 'three';
|
|
10
|
+
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
|
11
|
+
import { validateAssetGlb } from '../assets';
|
|
12
|
+
|
|
13
|
+
export function disposeAssetModel(model: Object3D) {
|
|
14
|
+
const geometries = new Set<BufferGeometry>();
|
|
15
|
+
const materials = new Set<Material>();
|
|
16
|
+
const textures = new Set<Texture>();
|
|
17
|
+
model.traverse((object) => {
|
|
18
|
+
const mesh = object as Mesh;
|
|
19
|
+
if (!mesh.isMesh) return;
|
|
20
|
+
geometries.add(mesh.geometry);
|
|
21
|
+
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
22
|
+
materials.add(material);
|
|
23
|
+
for (const value of Object.values(material))
|
|
24
|
+
if (value instanceof Texture) textures.add(value);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
for (const value of [...geometries, ...materials, ...textures]) value.dispose();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Parse self-contained GLBs under MCP Apps' img-src data: / connect-src none policy.
|
|
32
|
+
* ImageBitmapLoader fetches even blob/data URLs. Use the documented image handler
|
|
33
|
+
* instead, preserving GLTFLoader's sampler, UV, color-space and extension logic.
|
|
34
|
+
* Only the parser's private JSON copy changes; downloaded GLB bytes remain exact.
|
|
35
|
+
*/
|
|
36
|
+
export async function loadAssetGlb(bytes: Uint8Array) {
|
|
37
|
+
validateAssetGlb(bytes);
|
|
38
|
+
const manager = new LoadingManager();
|
|
39
|
+
let imageFailed = false;
|
|
40
|
+
manager.onError = () => {
|
|
41
|
+
imageFailed = true;
|
|
42
|
+
};
|
|
43
|
+
manager.addHandler(/^data:image\//, new TextureLoader(manager));
|
|
44
|
+
manager.setURLModifier((url) => {
|
|
45
|
+
if (!url.startsWith('blob:') && !url.startsWith('data:'))
|
|
46
|
+
throw new Error('External model resources are not loaded');
|
|
47
|
+
return url;
|
|
48
|
+
});
|
|
49
|
+
const loader = new GLTFLoader(manager);
|
|
50
|
+
loader.register((parser) => ({
|
|
51
|
+
name: 'KILN_embedded_images',
|
|
52
|
+
async beforeRoot() {
|
|
53
|
+
for (const image of parser.json.images ?? []) {
|
|
54
|
+
if (image.bufferView === undefined) continue;
|
|
55
|
+
const buffer = new Uint8Array(await parser.getDependency('bufferView', image.bufferView));
|
|
56
|
+
let binary = '';
|
|
57
|
+
for (let i = 0; i < buffer.length; i += 8192)
|
|
58
|
+
binary += String.fromCharCode(...buffer.subarray(i, i + 8192));
|
|
59
|
+
if (!/^image\/(png|jpeg|webp|avif)$/.test(image.mimeType ?? ''))
|
|
60
|
+
throw new Error('Unsupported embedded image type');
|
|
61
|
+
image.uri = `data:${image.mimeType};base64,${btoa(binary)}`;
|
|
62
|
+
delete image.bufferView;
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
}));
|
|
66
|
+
const gltf = await loader.parseAsync(Uint8Array.from(bytes).buffer, '');
|
|
67
|
+
if (imageFailed) {
|
|
68
|
+
for (const scene of gltf.scenes) disposeAssetModel(scene);
|
|
69
|
+
throw new Error('An embedded texture could not be decoded.');
|
|
70
|
+
}
|
|
71
|
+
// GLTFLoader deliberately converts failed images to null maps. Never commit a
|
|
72
|
+
// material-degraded scene, including resources loaded later for detached LODs.
|
|
73
|
+
return Object.assign(gltf, {
|
|
74
|
+
assertTexturesLoaded() {
|
|
75
|
+
if (imageFailed) throw new Error('An embedded texture could not be decoded.');
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
package/src/viewer/scene.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
import * as THREE from 'three';
|
|
3
|
-
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
|
4
3
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
5
4
|
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
|
|
6
|
-
import {
|
|
5
|
+
import { loadAssetGlb, disposeAssetModel as disposeModel } from './glb-loader';
|
|
7
6
|
import { frameAssetBounds } from './framing';
|
|
8
7
|
import {
|
|
9
8
|
captureCameraView,
|
|
@@ -156,22 +155,6 @@ export function createAssetStage(container: HTMLElement) {
|
|
|
156
155
|
],
|
|
157
156
|
};
|
|
158
157
|
};
|
|
159
|
-
const disposeModel = (model: THREE.Object3D) => {
|
|
160
|
-
const geometries = new Set<THREE.BufferGeometry>();
|
|
161
|
-
const materials = new Set<THREE.Material>();
|
|
162
|
-
const textures = new Set<THREE.Texture>();
|
|
163
|
-
model.traverse((object) => {
|
|
164
|
-
const mesh = object as THREE.Mesh;
|
|
165
|
-
if (!mesh.isMesh) return;
|
|
166
|
-
geometries.add(mesh.geometry);
|
|
167
|
-
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
168
|
-
materials.add(material);
|
|
169
|
-
for (const value of Object.values(material))
|
|
170
|
-
if (value instanceof THREE.Texture) textures.add(value);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
for (const value of [...geometries, ...materials, ...textures]) value.dispose();
|
|
174
|
-
};
|
|
175
158
|
const reset = () => {
|
|
176
159
|
if (!root) return;
|
|
177
160
|
const box = drawnBounds(root);
|
|
@@ -263,17 +246,17 @@ export function createAssetStage(container: HTMLElement) {
|
|
|
263
246
|
const loadStarted = performance.now();
|
|
264
247
|
const current = ++generation;
|
|
265
248
|
cancelMeasurement('The artifact changed during measurement. Measure the new revision again.');
|
|
266
|
-
|
|
267
|
-
const manager = new THREE.LoadingManager();
|
|
268
|
-
manager.setURLModifier((url) => {
|
|
269
|
-
if (!url.startsWith('blob:') && !url.startsWith('data:'))
|
|
270
|
-
throw new Error('External model resources are not loaded');
|
|
271
|
-
return url;
|
|
272
|
-
});
|
|
273
|
-
const parsed = await new GLTFLoader(manager).parseAsync(Uint8Array.from(bytes).buffer, '');
|
|
249
|
+
const parsed = await loadAssetGlb(bytes);
|
|
274
250
|
// three's loader draws every node; hide what KHR_node_visibility hides before counting.
|
|
275
251
|
applyNodeVisibility(parsed, [parsed.scene]);
|
|
276
252
|
const parsedLevels = await loadViewerLevels(parsed);
|
|
253
|
+
try {
|
|
254
|
+
parsed.assertTexturesLoaded();
|
|
255
|
+
} catch (error) {
|
|
256
|
+
disposeModel(parsed.scene);
|
|
257
|
+
for (const level of detachedLevels(parsedLevels)) disposeModel(level);
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
277
260
|
const gltf = acceptLoadedResource(
|
|
278
261
|
parsed,
|
|
279
262
|
() => !disposed && current === generation && options.isCurrent?.() !== false,
|