@energy8platform/golem 0.6.0 → 0.7.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/README.md +14 -0
- package/dist/editor.css +3 -4
- package/dist/editor.js +108 -61
- package/dist/lib/cli.js +101 -70
- package/dist/lib/cli.js.map +4 -4
- package/dist/lib/e8/agent.d.ts +35 -0
- package/dist/lib/e8/host.d.ts +22 -0
- package/dist/lib/e8/node.d.ts +66 -0
- package/dist/lib/e8/runtime.d.ts +10 -0
- package/dist/lib/e8/schema.d.ts +3 -0
- package/dist/lib/e8/types.d.ts +80 -0
- package/dist/lib/e8-agent.js +6781 -0
- package/dist/lib/e8-agent.js.map +7 -0
- package/dist/lib/e8-client.js +116 -0
- package/dist/lib/e8-host.js +6929 -0
- package/dist/lib/e8-host.js.map +7 -0
- package/dist/lib/e8-runtime.js +2024 -0
- package/dist/lib/e8-runtime.js.map +7 -0
- package/dist/lib/e8-schema.js +7 -0
- package/dist/lib/e8-schema.js.map +7 -0
- package/dist/lib/editor/api.d.ts +1 -0
- package/dist/lib/editor/embed.d.ts +9 -0
- package/dist/lib/editor/server.d.ts +23 -1
- package/dist/lib/editor/store.d.ts +2 -0
- package/dist/lib/editor/timeline.d.ts +15 -0
- package/dist/lib/editor-entry.d.ts +1 -1
- package/dist/lib/editor-entry.js +102 -70
- package/dist/lib/editor-entry.js.map +4 -4
- package/dist/lib/rig-import-layers.d.ts +2 -0
- package/dist/lib/rig-version.d.ts +12 -0
- package/dist/lib/tool-schema.d.ts +3 -0
- package/dist/lib/tools.d.ts +2 -0
- package/dist/lib/tools.js +49 -10
- package/dist/lib/tools.js.map +4 -4
- package/editor.html +2 -2
- package/package.json +38 -3
- package/skills/e8-golem/SKILL.md +71 -0
- package/skills/golem-symbol-animation/SKILL.md +87 -0
- package/skills/golem-symbol-animation/agents/openai.yaml +4 -0
- package/skills/golem-symbol-animation/references/facial-controls.md +48 -0
- package/skills/golem-symbol-animation/references/game-engine-integration.md +133 -0
- package/skills/golem-symbol-animation/references/golem-authoring.md +138 -0
- package/skills/golem-symbol-animation/references/interactive-editor.md +17 -0
- package/skills/golem-symbol-animation/references/motion-craft.md +113 -0
- package/skills/golem-symbol-animation/references/packed-delivery.md +88 -0
- package/skills/golem-symbol-animation/references/quantitative-qa.md +106 -0
- package/skills/golem-symbol-animation/references/spine-rive-study.md +87 -0
- package/skills/golem-symbol-animation/scripts/atlas_parts.py +87 -0
- package/skills/golem-symbol-animation/scripts/check_package.py +71 -0
- package/skills/golem-symbol-animation/scripts/image_gates.py +143 -0
- package/skills/golem-symbol-cutting/SKILL.md +70 -0
- package/skills/golem-symbol-cutting/agents/openai.yaml +4 -0
- package/skills/golem-symbol-cutting/assets/h1/h1.atlas.png +0 -0
- package/skills/golem-symbol-cutting/assets/h1/h1.png +0 -0
- package/skills/golem-symbol-cutting/references/cutting-workflow.md +92 -0
- package/skills/golem-symbol-cutting/references/facial-layers.md +44 -0
- package/skills/golem-symbol-cutting/references/generation-workflow.md +109 -0
- package/skills/golem-symbol-cutting/references/golem-handoff.md +62 -0
- package/skills/golem-symbol-cutting/references/h1-example.md +46 -0
- package/skills/golem-symbol-cutting/references/hybrid-workflow.md +119 -0
- package/skills/golem-symbol-cutting/references/registration-and-motion.md +89 -0
- package/skills/golem-symbol-cutting/references/spine-rive-construction.md +73 -0
- package/skills/golem-symbol-cutting/scripts/atlas_parts.py +87 -0
- package/skills/golem-symbol-cutting/scripts/hybrid_parts.py +279 -0
package/dist/lib/editor-entry.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/editor/server.ts
|
|
2
2
|
import { createServer } from "node:http";
|
|
3
|
-
import { readFile as readFile10, stat as
|
|
3
|
+
import { readFile as readFile10, stat as stat4, readdir as readdir5, mkdir as mkdir7, open } from "node:fs/promises";
|
|
4
4
|
import { watch as fsWatch } from "node:fs";
|
|
5
|
-
import { resolve as resolve2, dirname as dirname9, basename as basename5, extname as extname2, sep, join as join10, relative as
|
|
5
|
+
import { resolve as resolve2, dirname as dirname9, basename as basename5, extname as extname2, sep as sep2, join as join10, relative as relative4 } from "node:path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
7
7
|
|
|
8
8
|
// src/rig-format.ts
|
|
@@ -568,7 +568,7 @@ function parse(input) {
|
|
|
568
568
|
// src/rig-api.ts
|
|
569
569
|
import { z as z3 } from "zod";
|
|
570
570
|
import { readFile as readFile9, writeFile as writeFile7, stat as stat2, mkdir as mkdir6, readdir as readdir4 } from "node:fs/promises";
|
|
571
|
-
import { dirname as dirname8, join as join9, relative as
|
|
571
|
+
import { dirname as dirname8, join as join9, relative as relative3, isAbsolute as isAbsolute2 } from "node:path";
|
|
572
572
|
|
|
573
573
|
// src/rig-io.ts
|
|
574
574
|
import { mkdir, rename, rm, writeFile } from "node:fs/promises";
|
|
@@ -591,8 +591,10 @@ var saveJson = (path, value) => saveBytes(path, JSON.stringify(value, null, 2));
|
|
|
591
591
|
// src/rig-queue.ts
|
|
592
592
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
593
593
|
import { resolve } from "node:path";
|
|
594
|
-
var pending
|
|
595
|
-
|
|
594
|
+
var { pending, active } = globalThis[Symbol.for("golem.rigQueue")] ??= {
|
|
595
|
+
pending: /* @__PURE__ */ new Map(),
|
|
596
|
+
active: new AsyncLocalStorage()
|
|
597
|
+
};
|
|
596
598
|
async function withRigQueue(path, op) {
|
|
597
599
|
const key = resolve(path);
|
|
598
600
|
const held = active.getStore();
|
|
@@ -2008,8 +2010,9 @@ function createDoc(name, width, height, notes) {
|
|
|
2008
2010
|
function createSymbol(name, asset, margin = 0.15, notes) {
|
|
2009
2011
|
const mx = Math.ceil(asset.width * margin), my = Math.ceil(asset.height * margin);
|
|
2010
2012
|
const width = asset.width + 2 * mx, height = asset.height + 2 * my;
|
|
2011
|
-
const
|
|
2012
|
-
const
|
|
2013
|
+
const stem = asset.src.replace(/^.*\//, "").replace(/\.[^.]+$/, "").toLowerCase().replace(/[^a-z0-9_]/g, "_");
|
|
2014
|
+
const fromFile = stem.replace(/^[^a-z]+/, "") || (stem ? `symbol_${stem}` : "");
|
|
2015
|
+
const id = asset.id ?? (ID_RE.test(fromFile) ? fromFile : ID_RE.test(name) ? name : "symbol");
|
|
2013
2016
|
let doc = createDoc(name, width, height, notes);
|
|
2014
2017
|
doc = { ...doc, bones: [{ ...doc.bones[0], transform: { ...doc.bones[0].transform, x: width / 2, y: height - my } }] };
|
|
2015
2018
|
doc = { ...doc, assets: [RigSchema.shape.assets.element.parse({ id, src: asset.src, width: asset.width, height: asset.height })] };
|
|
@@ -2765,7 +2768,7 @@ function setGoal(doc, bone, x, y, opts) {
|
|
|
2765
2768
|
|
|
2766
2769
|
// src/rig-import-layers.ts
|
|
2767
2770
|
import { readdir, readFile } from "node:fs/promises";
|
|
2768
|
-
import { join as join2, basename as basename2 } from "node:path";
|
|
2771
|
+
import { join as join2, basename as basename2, isAbsolute, relative, sep } from "node:path";
|
|
2769
2772
|
var DEFAULT_PARENT = {
|
|
2770
2773
|
head: "neck",
|
|
2771
2774
|
neck: "torso",
|
|
@@ -2791,6 +2794,10 @@ function pngSize(buf) {
|
|
|
2791
2794
|
if (buf.readUInt32BE(12) !== 1229472850) throw new Error("not a PNG (IHDR missing)");
|
|
2792
2795
|
return [buf.readUInt32BE(16), buf.readUInt32BE(20)];
|
|
2793
2796
|
}
|
|
2797
|
+
function outsideDir(dir, file) {
|
|
2798
|
+
const r = relative(dir, join2(dir, file));
|
|
2799
|
+
return isAbsolute(file) || r === ".." || r.startsWith(".." + sep);
|
|
2800
|
+
}
|
|
2794
2801
|
async function importLayers(doc, dir, relPrefix = "layers") {
|
|
2795
2802
|
let specs;
|
|
2796
2803
|
try {
|
|
@@ -2803,6 +2810,7 @@ async function importLayers(doc, dir, relPrefix = "layers") {
|
|
|
2803
2810
|
let next = doc;
|
|
2804
2811
|
const parsed = [];
|
|
2805
2812
|
for (const s of specs) {
|
|
2813
|
+
if (outsideDir(dir, s.file)) throw new Error(`layers.json file "${s.file}" is outside the layers folder (${dir})`);
|
|
2806
2814
|
const buf = await readFile(join2(dir, s.file));
|
|
2807
2815
|
const [w, h] = pngSize(buf);
|
|
2808
2816
|
const m = basename2(s.file, ".png").match(/^(?:(\d+)_)?(.+)$/);
|
|
@@ -4052,7 +4060,7 @@ async function buildAtlas(doc, assetsDir, outDir, opts = {}) {
|
|
|
4052
4060
|
|
|
4053
4061
|
// src/spine-import.ts
|
|
4054
4062
|
import { readFile as readFile7, stat, writeFile as writeFile5 } from "node:fs/promises";
|
|
4055
|
-
import { basename as basename4, dirname as dirname6, extname, join as join8, relative } from "node:path";
|
|
4063
|
+
import { basename as basename4, dirname as dirname6, extname, join as join8, relative as relative2 } from "node:path";
|
|
4056
4064
|
|
|
4057
4065
|
// src/ktx2.ts
|
|
4058
4066
|
import { readFile as readFile6 } from "node:fs/promises";
|
|
@@ -4201,13 +4209,13 @@ var Ids = class {
|
|
|
4201
4209
|
var spineToDoc = (x, y, originX, originY) => [x + originX, originY - y];
|
|
4202
4210
|
var r4 = (v) => Math.round(v * 1e6) / 1e6 + 0;
|
|
4203
4211
|
var hex8 = (c) => `#${(c ?? "ffffffff").toLowerCase().padEnd(8, "f").slice(0, 8)}`;
|
|
4204
|
-
function importSpine(
|
|
4212
|
+
function importSpine(json2, atlasText, opts = {}) {
|
|
4205
4213
|
const warnings = [];
|
|
4206
4214
|
const atlas = parseAtlas(atlasText);
|
|
4207
|
-
const version = String(
|
|
4208
|
-
if (
|
|
4215
|
+
const version = String(json2.skeleton?.spine ?? "4.0");
|
|
4216
|
+
if (json2.physics?.length) warnings.push(`${json2.physics.length} Spine physics constraints are not imported; bake their motion before delivery`);
|
|
4209
4217
|
const major = parseFloat(version);
|
|
4210
|
-
const bounds = { x:
|
|
4218
|
+
const bounds = { x: json2.skeleton?.x ?? 0, y: json2.skeleton?.y ?? 0, w: json2.skeleton?.width ?? 0, h: json2.skeleton?.height ?? 0 };
|
|
4211
4219
|
const margin = opts.margin ?? 0;
|
|
4212
4220
|
let originX = -bounds.x + margin, originY = bounds.y + bounds.h + margin;
|
|
4213
4221
|
const toDoc = (x, y) => spineToDoc(x, y, originX, originY);
|
|
@@ -4224,7 +4232,7 @@ function importSpine(json, atlasText, opts = {}) {
|
|
|
4224
4232
|
}
|
|
4225
4233
|
return id;
|
|
4226
4234
|
};
|
|
4227
|
-
const spineBones =
|
|
4235
|
+
const spineBones = json2.bones ?? [];
|
|
4228
4236
|
const boneIndexByName = new Map(spineBones.map((b, i) => [b.name, i]));
|
|
4229
4237
|
const bones = [{ id: ROOT, transform: { x: r4(originX), y: r4(originY) }, role: "root" }];
|
|
4230
4238
|
for (const b of spineBones) {
|
|
@@ -4237,7 +4245,7 @@ function importSpine(json, atlasText, opts = {}) {
|
|
|
4237
4245
|
...b.color ? { color: b.color } : {}
|
|
4238
4246
|
});
|
|
4239
4247
|
}
|
|
4240
|
-
const skins = Array.isArray(
|
|
4248
|
+
const skins = Array.isArray(json2.skins) ? json2.skins : Object.entries(json2.skins ?? {}).map(([name, attachments2]) => ({ name, attachments: attachments2 }));
|
|
4241
4249
|
const selectedSkin = opts.skin ? skins.find((s) => s.name === opts.skin) : skins.find((s) => s.name === "default") ?? skins[0];
|
|
4242
4250
|
if (opts.skin && !selectedSkin) throw new Error(`Spine skin "${opts.skin}" not found; available: ${skins.map((s) => s.name).join(", ")}`);
|
|
4243
4251
|
const lookupSkins = [selectedSkin, skins.find((s) => s.name === "default")].filter((s, i, list) => s && list.indexOf(s) === i);
|
|
@@ -4261,7 +4269,7 @@ function importSpine(json, atlasText, opts = {}) {
|
|
|
4261
4269
|
const resolved = skinnedName.get(`${slot} ${name}`);
|
|
4262
4270
|
return resolved === void 0 ? null : attachmentId(slot, resolved);
|
|
4263
4271
|
};
|
|
4264
|
-
const spineSlots =
|
|
4272
|
+
const spineSlots = json2.slots ?? [];
|
|
4265
4273
|
const slotIndexByName = new Map(spineSlots.map((s, i) => [s.name, i]));
|
|
4266
4274
|
const blendOf = (b) => b === "additive" ? "add" : b === "multiply" ? "multiply" : b === "screen" ? "screen" : "normal";
|
|
4267
4275
|
const slots = spineSlots.map((s, i) => ({
|
|
@@ -4427,7 +4435,7 @@ function importSpine(json, atlasText, opts = {}) {
|
|
|
4427
4435
|
}
|
|
4428
4436
|
}
|
|
4429
4437
|
const activeConstraint = (kind, c) => !c.skin || lookupSkins.some((s) => s[kind]?.includes(c.name));
|
|
4430
|
-
const ik = (
|
|
4438
|
+
const ik = (json2.ik ?? []).filter((c) => activeConstraint("ik", c)).map((c) => ({
|
|
4431
4439
|
id: ikIds.get(c.name),
|
|
4432
4440
|
order: c.order ?? 0,
|
|
4433
4441
|
bones: c.bones.map((b) => boneIds.get(b)),
|
|
@@ -4440,7 +4448,7 @@ function importSpine(json, atlasText, opts = {}) {
|
|
|
4440
4448
|
stretch: !!c.stretch,
|
|
4441
4449
|
uniform: !!c.uniform
|
|
4442
4450
|
}));
|
|
4443
|
-
const transform = (
|
|
4451
|
+
const transform = (json2.transform ?? []).filter((c) => activeConstraint("transform", c)).map((c) => ({
|
|
4444
4452
|
id: tcIds.get(c.name),
|
|
4445
4453
|
order: c.order ?? 0,
|
|
4446
4454
|
bones: c.bones.map((b) => boneIds.get(b)),
|
|
@@ -4460,7 +4468,7 @@ function importSpine(json, atlasText, opts = {}) {
|
|
|
4460
4468
|
mixScaleY: c.mixScaleY ?? c.mixScaleX ?? c.scaleMix ?? 1,
|
|
4461
4469
|
mixShearY: c.mixShearY ?? c.shearMix ?? 1
|
|
4462
4470
|
}));
|
|
4463
|
-
const path = (
|
|
4471
|
+
const path = (json2.path ?? []).filter((c) => activeConstraint("path", c)).map((c) => ({
|
|
4464
4472
|
id: pcIds.get(c.name),
|
|
4465
4473
|
order: c.order ?? 0,
|
|
4466
4474
|
bones: c.bones.map((b) => boneIds.get(b)),
|
|
@@ -4514,7 +4522,7 @@ function importSpine(json, atlasText, opts = {}) {
|
|
|
4514
4522
|
return keys;
|
|
4515
4523
|
};
|
|
4516
4524
|
const animations = [];
|
|
4517
|
-
for (const [animName, a] of Object.entries(
|
|
4525
|
+
for (const [animName, a] of Object.entries(json2.animations ?? {})) {
|
|
4518
4526
|
const tracks = [], events = [];
|
|
4519
4527
|
let duration = 0;
|
|
4520
4528
|
const seen = (frames) => {
|
|
@@ -4795,7 +4803,7 @@ function importSpine(json, atlasText, opts = {}) {
|
|
|
4795
4803
|
const build = () => ({
|
|
4796
4804
|
format: "energy8.rig",
|
|
4797
4805
|
version: 2,
|
|
4798
|
-
meta: { name: opts.name ??
|
|
4806
|
+
meta: { name: opts.name ?? json2.skeleton?.hash ?? "spine", width, height, notes: opts.notes ?? `imported from Spine ${version}` },
|
|
4799
4807
|
assets: [...assets.values()],
|
|
4800
4808
|
bones,
|
|
4801
4809
|
slots,
|
|
@@ -4848,7 +4856,7 @@ async function firstExisting(candidates, what) {
|
|
|
4848
4856
|
throw new Error(`${what} not found, tried: ${candidates.join(", ")}`);
|
|
4849
4857
|
}
|
|
4850
4858
|
async function importSpineProject(opts) {
|
|
4851
|
-
const
|
|
4859
|
+
const json2 = JSON.parse(await readFile7(opts.json, "utf8"));
|
|
4852
4860
|
const stem = opts.json.replace(/\.json$/i, "");
|
|
4853
4861
|
const atlasPath = opts.atlas ?? await firstExisting([stem + ".atlas", stem + ".atlas.txt"], "atlas");
|
|
4854
4862
|
const atlasText = await readFile7(atlasPath, "utf8");
|
|
@@ -4858,6 +4866,7 @@ async function importSpineProject(opts) {
|
|
|
4858
4866
|
const rigDir = dirname6(opts.rigPath);
|
|
4859
4867
|
const pagePaths = /* @__PURE__ */ new Map();
|
|
4860
4868
|
for (const page2 of pages) {
|
|
4869
|
+
if (!opts.image && outsideDir(dirname6(atlasPath), page2.name)) throw new Error(`atlas page "${page2.name}" is outside the atlas folder (${dirname6(atlasPath)})`);
|
|
4861
4870
|
const pageStem = join8(dirname6(atlasPath), page2.name.replace(/\.[a-z0-9]+$/i, ""));
|
|
4862
4871
|
const imagePath = opts.image ?? await firstExisting([join8(dirname6(atlasPath), page2.name), pageStem + ".png", pageStem + ".ktx2", ...pages.length === 1 ? [stem + ".png", stem + ".ktx2"] : []], `atlas page image ${page2.name}`);
|
|
4863
4872
|
let pngPath = imagePath;
|
|
@@ -4870,10 +4879,10 @@ async function importSpineProject(opts) {
|
|
|
4870
4879
|
}
|
|
4871
4880
|
pagePaths.set(page2.name, pngPath);
|
|
4872
4881
|
}
|
|
4873
|
-
const pageImage = (page2) =>
|
|
4882
|
+
const pageImage = (page2) => relative2(rigDir, pagePaths.get(page2)).split("\\").join("/");
|
|
4874
4883
|
const name = opts.name ?? basename4(stem);
|
|
4875
4884
|
const imagePaths = [...pagePaths.values()];
|
|
4876
|
-
return { ...importSpine(
|
|
4885
|
+
return { ...importSpine(json2, atlasText, { name, skin: opts.skin, pageImage, margin: opts.margin, notes: opts.notes }), imagePath: imagePaths[0], imagePaths };
|
|
4877
4886
|
}
|
|
4878
4887
|
|
|
4879
4888
|
// src/rig-templates.ts
|
|
@@ -5562,8 +5571,8 @@ async function load(path) {
|
|
|
5562
5571
|
}
|
|
5563
5572
|
var save = (path, doc) => saveJson(path, doc);
|
|
5564
5573
|
var exists2 = (p) => stat2(p).then(() => true, () => false);
|
|
5565
|
-
var resolveFrom = (rigPath, p) =>
|
|
5566
|
-
var srcFor = (rigPath, file) =>
|
|
5574
|
+
var resolveFrom = (rigPath, p) => isAbsolute2(p) ? p : join9(dirname8(rigPath), p);
|
|
5575
|
+
var srcFor = (rigPath, file) => relative3(dirname8(rigPath), file).split("\\").join("/");
|
|
5567
5576
|
var libraryFor = (rigPath, lib) => lib ? resolveFrom(rigPath, lib) : join9(dirname8(rigPath), "presets.json");
|
|
5568
5577
|
async function pngInfo(rigPath, file) {
|
|
5569
5578
|
const abs = resolveFrom(rigPath, file);
|
|
@@ -5814,9 +5823,9 @@ tool(
|
|
|
5814
5823
|
margin: z3.number().optional().describe("canvas margin around the skeleton bounds, px"),
|
|
5815
5824
|
notes: z3.string().optional()
|
|
5816
5825
|
},
|
|
5817
|
-
async ({ path, json, atlas, image, name, skin: skin2, margin, notes }) => {
|
|
5826
|
+
async ({ path, json: json2, atlas, image, name, skin: skin2, margin, notes }) => {
|
|
5818
5827
|
try {
|
|
5819
|
-
const r = await importSpineProject({ json: resolveFrom(path,
|
|
5828
|
+
const r = await importSpineProject({ json: resolveFrom(path, json2), atlas: atlas && resolveFrom(path, atlas), image: image && resolveFrom(path, image), rigPath: path, name, skin: skin2, margin, notes });
|
|
5820
5829
|
const res = await create(path, parse(r.doc));
|
|
5821
5830
|
if (r.warnings.length) res.text += `
|
|
5822
5831
|
warnings:
|
|
@@ -6588,40 +6597,41 @@ tool(
|
|
|
6588
6597
|
}
|
|
6589
6598
|
);
|
|
6590
6599
|
|
|
6600
|
+
// src/rig-version.ts
|
|
6601
|
+
import { stat as stat3 } from "node:fs/promises";
|
|
6602
|
+
async function rigVersion(abs) {
|
|
6603
|
+
try {
|
|
6604
|
+
const s = await stat3(abs);
|
|
6605
|
+
return `${s.mtimeMs}:${s.size}`;
|
|
6606
|
+
} catch {
|
|
6607
|
+
return "missing";
|
|
6608
|
+
}
|
|
6609
|
+
}
|
|
6610
|
+
|
|
6591
6611
|
// src/editor/server.ts
|
|
6592
6612
|
var TYPES = { ".html": "text/html; charset=utf-8", ".js": "text/javascript", ".css": "text/css", ".map": "application/json", ".json": "application/json", ".png": "image/png", ".jpg": "image/jpeg", ".webp": "image/webp" };
|
|
6593
|
-
|
|
6613
|
+
var json = (res, status, body) => {
|
|
6614
|
+
res.writeHead(status, { "content-type": "application/json" });
|
|
6615
|
+
res.end(JSON.stringify(body));
|
|
6616
|
+
};
|
|
6617
|
+
function createEditorHandler(opts) {
|
|
6594
6618
|
const root2 = resolve2(opts.root);
|
|
6595
6619
|
const assets = resolve2(opts.assets ?? fileURLToPath3(new URL("../..", import.meta.url)));
|
|
6596
6620
|
const inside = (rel) => {
|
|
6597
6621
|
const abs = resolve2(root2, rel);
|
|
6598
|
-
return abs === root2 || abs.startsWith(root2 +
|
|
6599
|
-
};
|
|
6600
|
-
const version = async (abs) => {
|
|
6601
|
-
try {
|
|
6602
|
-
const s = await stat3(abs);
|
|
6603
|
-
return `${s.mtimeMs}:${s.size}`;
|
|
6604
|
-
} catch {
|
|
6605
|
-
return "missing";
|
|
6606
|
-
}
|
|
6622
|
+
return abs === root2 || abs.startsWith(root2 + sep2) ? abs : void 0;
|
|
6607
6623
|
};
|
|
6608
6624
|
const readDoc = async (abs) => RigDocument.parse(JSON.parse(await readFile10(abs, "utf8")));
|
|
6609
6625
|
const readDocAt = async (abs) => {
|
|
6610
|
-
let v = await
|
|
6626
|
+
let v = await rigVersion(abs);
|
|
6611
6627
|
for (let i = 0; i < 3; i++) {
|
|
6612
6628
|
const doc = await readDoc(abs);
|
|
6613
|
-
const after = await
|
|
6629
|
+
const after = await rigVersion(abs);
|
|
6614
6630
|
if (after === v) return { doc, version: v };
|
|
6615
6631
|
v = after;
|
|
6616
6632
|
}
|
|
6617
6633
|
return { doc: await readDoc(abs), version: v };
|
|
6618
6634
|
};
|
|
6619
|
-
const json = (res, status, body2) => {
|
|
6620
|
-
res.writeHead(status, { "content-type": "application/json" });
|
|
6621
|
-
res.end(JSON.stringify(body2));
|
|
6622
|
-
};
|
|
6623
|
-
let allowedHosts = /* @__PURE__ */ new Set();
|
|
6624
|
-
const hostOk = (req) => allowedHosts.has(String(req.headers.host ?? "").toLowerCase());
|
|
6625
6635
|
const clients = /* @__PURE__ */ new Map();
|
|
6626
6636
|
const watchers = /* @__PURE__ */ new Map();
|
|
6627
6637
|
function ensureWatch(abs) {
|
|
@@ -6633,7 +6643,7 @@ async function startEditorServer(opts) {
|
|
|
6633
6643
|
const schedule = () => {
|
|
6634
6644
|
clearTimeout(entry.timer);
|
|
6635
6645
|
entry.timer = setTimeout(async () => {
|
|
6636
|
-
const v = await
|
|
6646
|
+
const v = await rigVersion(abs);
|
|
6637
6647
|
if (v === entry.last) return;
|
|
6638
6648
|
entry.last = v;
|
|
6639
6649
|
for (const res of clients.get(abs) ?? []) res.write(`event: changed
|
|
@@ -6642,7 +6652,7 @@ data: ${JSON.stringify({ version: v })}
|
|
|
6642
6652
|
`);
|
|
6643
6653
|
}, 50);
|
|
6644
6654
|
};
|
|
6645
|
-
|
|
6655
|
+
rigVersion(abs).then((v) => {
|
|
6646
6656
|
entry.last = v;
|
|
6647
6657
|
});
|
|
6648
6658
|
watchers.set(abs, entry);
|
|
@@ -6668,7 +6678,7 @@ data: ${JSON.stringify({ version: v })}
|
|
|
6668
6678
|
name = String(JSON.parse(await readFile10(abs, "utf8")).meta?.name ?? "?");
|
|
6669
6679
|
} catch {
|
|
6670
6680
|
}
|
|
6671
|
-
out.push({ rig:
|
|
6681
|
+
out.push({ rig: relative4(root2, abs).split(sep2).join("/"), name, mtimeMs: (await stat4(abs)).mtimeMs });
|
|
6672
6682
|
}
|
|
6673
6683
|
};
|
|
6674
6684
|
await walk(root2);
|
|
@@ -6702,20 +6712,22 @@ data: ${JSON.stringify({ version: v })}
|
|
|
6702
6712
|
}
|
|
6703
6713
|
return Buffer.concat(chunks);
|
|
6704
6714
|
}
|
|
6705
|
-
|
|
6715
|
+
async function handle(req, res, rawUrl = req.url ?? "/") {
|
|
6706
6716
|
try {
|
|
6707
|
-
|
|
6708
|
-
const url = new URL(req.url ?? "/", "http://x");
|
|
6717
|
+
const url = new URL(rawUrl, "http://x");
|
|
6709
6718
|
if (url.pathname === "/") {
|
|
6710
|
-
res.writeHead(302, { location: "
|
|
6711
|
-
return res.end();
|
|
6719
|
+
res.writeHead(302, { location: "editor.html" + url.search });
|
|
6720
|
+
return void res.end();
|
|
6721
|
+
}
|
|
6722
|
+
if (url.pathname.startsWith("/api/")) {
|
|
6723
|
+
await api(url, req, res);
|
|
6724
|
+
return;
|
|
6712
6725
|
}
|
|
6713
|
-
if (url.pathname.startsWith("/api/")) return await api(url, req, res);
|
|
6714
6726
|
const STATIC = /* @__PURE__ */ new Set(["/editor.html", "/dist/editor.js", "/dist/editor.js.map", "/dist/editor.css", "/dist/editor.css.map"]);
|
|
6715
6727
|
const abs = url.pathname.startsWith("/rig/") ? inside(decodeURIComponent(url.pathname.slice(5))) : STATIC.has(url.pathname) ? join10(assets, url.pathname) : void 0;
|
|
6716
6728
|
if (!abs) {
|
|
6717
6729
|
res.writeHead(403);
|
|
6718
|
-
return res.end("forbidden");
|
|
6730
|
+
return void res.end("forbidden");
|
|
6719
6731
|
}
|
|
6720
6732
|
try {
|
|
6721
6733
|
const data = await readFile10(abs);
|
|
@@ -6733,7 +6745,7 @@ data: ${JSON.stringify({ version: v })}
|
|
|
6733
6745
|
}
|
|
6734
6746
|
json(res, 500, { isError: true, text: `ERROR: ${e.message}` });
|
|
6735
6747
|
}
|
|
6736
|
-
}
|
|
6748
|
+
}
|
|
6737
6749
|
async function api(url, req, res) {
|
|
6738
6750
|
const rigOf = (rel) => {
|
|
6739
6751
|
if (!rel) return void 0;
|
|
@@ -6750,7 +6762,7 @@ data: ${JSON.stringify({ version: v })}
|
|
|
6750
6762
|
res.writeHead(403);
|
|
6751
6763
|
return res.end();
|
|
6752
6764
|
}
|
|
6753
|
-
if (!await
|
|
6765
|
+
if (!await stat4(abs).then(() => true, () => false)) return json(res, 404, { isError: true, text: `ERROR: ${url.searchParams.get("rig")} does not exist under ${root2}` });
|
|
6754
6766
|
ensureWatch(abs);
|
|
6755
6767
|
res.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
|
|
6756
6768
|
res.write(`: connected
|
|
@@ -6772,7 +6784,7 @@ data: ${JSON.stringify({ version: v })}
|
|
|
6772
6784
|
if (!abs) return json(res, 403, { isError: true, text: "rig outside root" });
|
|
6773
6785
|
ensureWatch(abs);
|
|
6774
6786
|
return await withRigQueue(abs, async () => {
|
|
6775
|
-
if (baseVersion !== void 0 && await
|
|
6787
|
+
if (baseVersion !== void 0 && await rigVersion(abs) !== baseVersion) {
|
|
6776
6788
|
const at = await readDocAt(abs);
|
|
6777
6789
|
return json(res, 409, {
|
|
6778
6790
|
isError: true,
|
|
@@ -6781,8 +6793,15 @@ data: ${JSON.stringify({ version: v })}
|
|
|
6781
6793
|
text: `ERROR: the rig changed on disk (expected ${baseVersion}, found ${at.version}); nothing was written`
|
|
6782
6794
|
});
|
|
6783
6795
|
}
|
|
6784
|
-
const
|
|
6785
|
-
|
|
6796
|
+
const tool2 = url.pathname.slice(10);
|
|
6797
|
+
let given = { ...args ?? {}, path: abs };
|
|
6798
|
+
try {
|
|
6799
|
+
if (opts.args) given = opts.args(tool2, given);
|
|
6800
|
+
} catch (e) {
|
|
6801
|
+
return json(res, 400, { isError: true, text: `ERROR: ${e instanceof Error ? e.message : String(e)}` });
|
|
6802
|
+
}
|
|
6803
|
+
const r = await callTool(tool2, given);
|
|
6804
|
+
return json(res, 200, { text: r.text, doc: r.doc, image: r.image?.toString("base64"), isError: r.isError, version: await rigVersion(abs) });
|
|
6786
6805
|
});
|
|
6787
6806
|
}
|
|
6788
6807
|
if (url.pathname === "/api/rigs" && req.method === "GET") return json(res, 200, await listRigs());
|
|
@@ -6793,11 +6812,11 @@ data: ${JSON.stringify({ version: v })}
|
|
|
6793
6812
|
if (typeof rig !== "string" || !/(^|\/)rig\.json$/.test(rig)) return json(res, 400, { isError: true, text: "ERROR: a project is a directory with a rig.json \u2014 the path must end with /rig.json" });
|
|
6794
6813
|
const abs = inside(rig);
|
|
6795
6814
|
if (!abs) return json(res, 403, { isError: true, text: "rig outside root" });
|
|
6796
|
-
if (await
|
|
6815
|
+
if (await stat4(abs).then(() => true, () => false)) return json(res, 409, { isError: true, text: `ERROR: ${rig} already exists \u2014 open it, or pick another directory` });
|
|
6797
6816
|
await mkdir7(dirname9(abs), { recursive: true });
|
|
6798
6817
|
const r = await callTool("rig_create", { path: abs, name: name ?? basename5(dirname9(abs)), width: width ?? 640, height: height ?? 700 });
|
|
6799
6818
|
ensureWatch(abs);
|
|
6800
|
-
return json(res, r.isError ? 400 : 200, { text: r.text, doc: r.doc, isError: r.isError, version: await
|
|
6819
|
+
return json(res, r.isError ? 400 : 200, { text: r.text, doc: r.doc, isError: r.isError, version: await rigVersion(abs) });
|
|
6801
6820
|
}
|
|
6802
6821
|
if (url.pathname === "/api/files" && req.method === "GET") {
|
|
6803
6822
|
const abs = rigOf(url.searchParams.get("rig"));
|
|
@@ -6811,8 +6830,8 @@ data: ${JSON.stringify({ version: v })}
|
|
|
6811
6830
|
if (!/^[A-Za-z0-9._-]+\.png$/i.test(name) || name.startsWith(".")) return json(res, 400, { isError: true, text: `ERROR: bad file name "${name}" \u2014 letters, digits, . _ - and a .png extension` });
|
|
6812
6831
|
if (!String(req.headers["content-type"] ?? "").toLowerCase().startsWith("image/png")) return json(res, 415, { isError: true, text: "ERROR: upload needs content-type: image/png" });
|
|
6813
6832
|
const target = join10(dirname9(abs), name);
|
|
6814
|
-
if (!inside(
|
|
6815
|
-
if (await
|
|
6833
|
+
if (!inside(relative4(root2, target))) return json(res, 403, { isError: true, text: "file outside root" });
|
|
6834
|
+
if (await stat4(target).then(() => true, () => false)) return json(res, 409, { isError: true, text: `ERROR: ${name} already exists next to the rig \u2014 rename the file` });
|
|
6816
6835
|
const data = await bytes(req, UPLOAD_MAX);
|
|
6817
6836
|
if (!data) return json(res, 413, { isError: true, text: `ERROR: ${name} is larger than ${UPLOAD_MAX / 1048576} MB` });
|
|
6818
6837
|
let size;
|
|
@@ -6826,6 +6845,22 @@ data: ${JSON.stringify({ version: v })}
|
|
|
6826
6845
|
}
|
|
6827
6846
|
json(res, 404, { isError: true, text: "unknown api route" });
|
|
6828
6847
|
}
|
|
6848
|
+
return { handle, close() {
|
|
6849
|
+
for (const w of watchers.values()) {
|
|
6850
|
+
clearTimeout(w.timer);
|
|
6851
|
+
w.w.close();
|
|
6852
|
+
}
|
|
6853
|
+
for (const set of clients.values()) for (const c of set) c.end();
|
|
6854
|
+
} };
|
|
6855
|
+
}
|
|
6856
|
+
async function startEditorServer(opts) {
|
|
6857
|
+
const editor = createEditorHandler(opts);
|
|
6858
|
+
let allowedHosts = /* @__PURE__ */ new Set();
|
|
6859
|
+
const server = createServer((req, res) => {
|
|
6860
|
+
if (!allowedHosts.has(String(req.headers.host ?? "").toLowerCase()))
|
|
6861
|
+
return json(res, 403, { isError: true, text: `ERROR: forbidden Host "${req.headers.host ?? ""}" \u2014 this server answers only on 127.0.0.1 / localhost` });
|
|
6862
|
+
void editor.handle(req, res);
|
|
6863
|
+
});
|
|
6829
6864
|
await new Promise((ok, fail2) => {
|
|
6830
6865
|
server.once("error", fail2);
|
|
6831
6866
|
server.listen(opts.port ?? 8765, "127.0.0.1", () => {
|
|
@@ -6836,15 +6871,12 @@ data: ${JSON.stringify({ version: v })}
|
|
|
6836
6871
|
const port = server.address().port;
|
|
6837
6872
|
allowedHosts = /* @__PURE__ */ new Set([`127.0.0.1:${port}`, `localhost:${port}`, `[::1]:${port}`]);
|
|
6838
6873
|
return { port, url: `http://127.0.0.1:${port}`, close: async () => {
|
|
6839
|
-
|
|
6840
|
-
clearTimeout(w.timer);
|
|
6841
|
-
w.w.close();
|
|
6842
|
-
}
|
|
6843
|
-
for (const set of clients.values()) for (const c of set) c.end();
|
|
6874
|
+
editor.close();
|
|
6844
6875
|
await new Promise((r) => server.close(() => r()));
|
|
6845
6876
|
} };
|
|
6846
6877
|
}
|
|
6847
6878
|
export {
|
|
6879
|
+
createEditorHandler,
|
|
6848
6880
|
startEditorServer
|
|
6849
6881
|
};
|
|
6850
6882
|
//# sourceMappingURL=editor-entry.js.map
|