@genex-ai/cli-demo 1.26.0-dev.620 → 1.26.2-dev.622
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/{blender-mcp-TTLZ24PD.js → blender-mcp-DPH5PIVX.js} +1 -1
- package/dist/{chunk-57CT5CAN.js → chunk-I77A53C6.js} +15 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/templates/blender-service/ops.py +2 -2
- package/templates/blender-service/server.py +10 -2
- package/templates/skills/genex-blender-scene/SKILL.md +16 -5
- package/templates/skills/genex-game-director/SKILL.md +7 -0
|
@@ -704,6 +704,7 @@ async function touchCliSeat() {
|
|
|
704
704
|
}
|
|
705
705
|
}
|
|
706
706
|
async function blenderCall(base, route, body) {
|
|
707
|
+
const cwd = process.cwd();
|
|
707
708
|
const seat = readSeatGrant();
|
|
708
709
|
const ctl = new AbortController();
|
|
709
710
|
const timer = setTimeout(() => ctl.abort(), BLENDER_TIMEOUT_MS);
|
|
@@ -734,6 +735,15 @@ async function blenderCall(base, route, body) {
|
|
|
734
735
|
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
735
736
|
signal: ctl.signal
|
|
736
737
|
});
|
|
738
|
+
if (res.status === 401 && seat?.token) {
|
|
739
|
+
const current = readSeatGrant(cwd);
|
|
740
|
+
if (current?.url === seat.url && current.token === seat.token) deleteSeatGrant(cwd);
|
|
741
|
+
await res.body?.cancel().catch(() => {
|
|
742
|
+
});
|
|
743
|
+
throw new Error(
|
|
744
|
+
`${route} refused the seat token (401) \u2014 ${seat.kind === "cli" ? 'run "genex blender seat" to acquire a new one' : "run the command again to acquire a new one"}`
|
|
745
|
+
);
|
|
746
|
+
}
|
|
737
747
|
const text = await res.text();
|
|
738
748
|
let json;
|
|
739
749
|
try {
|
|
@@ -748,7 +758,11 @@ async function blenderCall(base, route, body) {
|
|
|
748
758
|
);
|
|
749
759
|
}
|
|
750
760
|
const detail = typeof json.detail === "string" ? ` \u2014 ${json.detail}` : "";
|
|
751
|
-
|
|
761
|
+
const stage = typeof json.stage === "string" ? ` [${json.stage}]` : "";
|
|
762
|
+
const mutationWarning = json.scriptStarted === true ? " (script may have changed the scene; inspect it before retrying)" : "";
|
|
763
|
+
const trace = typeof json.trace === "string" ? `
|
|
764
|
+
${json.trace.slice(-4e3)}` : "";
|
|
765
|
+
throw new Error(`${route} failed (${res.status}): ${json.error ?? text.slice(0, 200)}${stage}${detail}${mutationWarning}${trace}`);
|
|
752
766
|
}
|
|
753
767
|
if (seat?.kind === "cli") void touchCliSeat();
|
|
754
768
|
return json;
|
package/dist/index.js
CHANGED
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
writeProject,
|
|
42
42
|
writeUserToken,
|
|
43
43
|
writeWorkspace
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-I77A53C6.js";
|
|
45
45
|
import {
|
|
46
46
|
CLI_CHANNEL,
|
|
47
47
|
DEFAULT_API_URL,
|
|
@@ -21104,7 +21104,7 @@ async function runBlender(opts) {
|
|
|
21104
21104
|
return serveLocalBlender({ port, log });
|
|
21105
21105
|
}
|
|
21106
21106
|
if (sub === "mcp") {
|
|
21107
|
-
const { runBlenderMcp } = await import("./blender-mcp-
|
|
21107
|
+
const { runBlenderMcp } = await import("./blender-mcp-DPH5PIVX.js");
|
|
21108
21108
|
return runBlenderMcp();
|
|
21109
21109
|
}
|
|
21110
21110
|
if (sub === "seat") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genex-ai/cli-demo",
|
|
3
|
-
"version": "1.26.
|
|
3
|
+
"version": "1.26.2-dev.622",
|
|
4
4
|
"description": "Set up your project's agent workspace (.claude/.codex/.cursor in the game folder), authorize, create a game project, generate AI assets, and publish (genex CLI).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -214,12 +214,12 @@ def scene_digest():
|
|
|
214
214
|
if m.use_nodes and m.node_tree:
|
|
215
215
|
for node in m.node_tree.nodes:
|
|
216
216
|
if node.type == "BSDF_PRINCIPLED":
|
|
217
|
-
h.update(("bsdf:%s\n" % tuple(node.inputs["Base Color"].default_value)).encode())
|
|
217
|
+
h.update(("bsdf:%s\n" % (tuple(node.inputs["Base Color"].default_value),)).encode())
|
|
218
218
|
elif obj.type == "LIGHT" and data is not None:
|
|
219
219
|
h.update(("light:%s:%s:%s\n" % (data.type, data.energy, tuple(data.color))).encode())
|
|
220
220
|
elif obj.type == "CAMERA" and data is not None:
|
|
221
221
|
h.update(("cam:%s:%s\n" % (data.lens, data.type)).encode())
|
|
222
222
|
w = scene.world
|
|
223
223
|
if w is not None:
|
|
224
|
-
h.update(("world:%s\n" % tuple(getattr(w, "color", (0, 0, 0)))).encode())
|
|
224
|
+
h.update(("world:%s\n" % (tuple(getattr(w, "color", (0, 0, 0))),)).encode())
|
|
225
225
|
return h.hexdigest()
|
|
@@ -406,15 +406,21 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
406
406
|
started = time.time()
|
|
407
407
|
route = self.path.split("?")[0]
|
|
408
408
|
_set_deadline(route, _budget_for(route, body))
|
|
409
|
+
stage = route.lstrip("/")
|
|
410
|
+
script_started = False
|
|
409
411
|
try:
|
|
410
412
|
if route == "/exec":
|
|
413
|
+
stage = "script"
|
|
414
|
+
script_started = True
|
|
411
415
|
result = ops.run_script(body.get("script", ""))
|
|
416
|
+
stage = "scene_info"
|
|
412
417
|
result["scene"] = ops.scene_info()
|
|
413
418
|
result["gpu"] = GPU_WITNESS
|
|
414
419
|
# The sheet rides EVERY exec by default. verify:false is for
|
|
415
420
|
# bulk sub-steps only -- an agent that has to ask for the
|
|
416
421
|
# picture is an agent that will forget to.
|
|
417
422
|
if body.get("verify", True):
|
|
423
|
+
stage = "verification"
|
|
418
424
|
_attach_sheet(result, body, body.get("mode") or DEFAULT_MODE)
|
|
419
425
|
result["ms"] = int((time.time() - started) * 1000)
|
|
420
426
|
_checkpoint()
|
|
@@ -492,7 +498,8 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
492
498
|
except Exception as e:
|
|
493
499
|
import traceback
|
|
494
500
|
|
|
495
|
-
self._send(500, {"error": "exec_failed", "detail": str(e), "trace": traceback.format_exc(limit=6)
|
|
501
|
+
self._send(500, {"error": "exec_failed", "detail": str(e), "trace": traceback.format_exc(limit=6),
|
|
502
|
+
"stage": stage, "scriptStarted": script_started})
|
|
496
503
|
finally:
|
|
497
504
|
# MUST run: a stale deadline file makes the supervisor kill a
|
|
498
505
|
# perfectly healthy Blender on its next poll.
|
|
@@ -600,4 +607,5 @@ def main():
|
|
|
600
607
|
srv.server_close()
|
|
601
608
|
|
|
602
609
|
|
|
603
|
-
|
|
610
|
+
if __name__ == "__main__":
|
|
611
|
+
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: genex-blender-scene
|
|
3
|
-
description: Compose a level, arena, interior, building or map — a SPACE of rooms, lanes, walls and sites that must relate — in a live Blender with `npx genex blender`, then export one GLB the game walks through. Use
|
|
3
|
+
description: Compose a level, arena, interior, building or map — a SPACE of rooms, lanes, walls and sites that must relate — in a live Blender with `npx genex blender`, then export one GLB the game walks through. Use for places to move through, floor plans, or explicit Blender authoring including standalone assets. Probe the lane first; code is the fallback only when Blender was not explicitly required.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Genex Blender · Scene
|
|
@@ -12,13 +12,22 @@ level grows the way a level is actually built: shell, then zones, then dressing.
|
|
|
12
12
|
|
|
13
13
|
## When this lane owns the work
|
|
14
14
|
|
|
15
|
+
- An explicit request to author or edit **in Blender**, even one standalone
|
|
16
|
+
asset. This overrides the default single-object route below. Keep Python
|
|
17
|
+
source under `blender/`, export the requested GLB and show it in a simple
|
|
18
|
+
browser preview; add gameplay only when requested. In hosted creation use
|
|
19
|
+
the CLI through Bash and open contact sheets with Read: the Blender MCP
|
|
20
|
+
server is not registered in the hosted harness.
|
|
21
|
+
If Blender is unavailable, report that limitation and keep the authored
|
|
22
|
+
scripts; the code fallback below is for requests that did not require Blender.
|
|
23
|
+
|
|
15
24
|
- A **space**: a level, arena, interior, building, map, dungeon, station — several
|
|
16
25
|
rooms, lanes, walls or sites that must relate to each other, and a player who
|
|
17
26
|
moves through them.
|
|
18
27
|
- A **floor plan or reference** to trace (a sketch, an ASCII plan, a photo of a room).
|
|
19
28
|
- A **blockout**: the playable shape first, dressing later.
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
Default routes when the player did not explicitly request Blender:
|
|
22
31
|
|
|
23
32
|
- One recognisable object (a barrel, a cart, a statue) → `$genex-ai-model`.
|
|
24
33
|
- One parameterized object placed many times with variation, or a stylized shader
|
|
@@ -224,9 +233,11 @@ when one is available — the measurements favour it, and it costs nothing.
|
|
|
224
233
|
- `payload_too_large` on export → fewer cylinder segments, delete faces the player
|
|
225
234
|
never sees, or export two GLBs (`--out public/assets/level-a.glb`).
|
|
226
235
|
- The sheet is grey → `material.diffuse_color` was not set (see above).
|
|
227
|
-
- `refused the seat token (401)` → the
|
|
228
|
-
|
|
229
|
-
|
|
236
|
+
- `refused the seat token (401)` → the rejected cached grant has been removed.
|
|
237
|
+
In hosted creation, run `npx genex blender scene` to acquire a new seat and
|
|
238
|
+
inspect the restored scene before deciding whether to rerun the failed script.
|
|
239
|
+
At a terminal, run `npx genex blender seat` first. Recovery never automatically
|
|
240
|
+
replays scene changes; a replaced pod restores the latest saved checkpoint.
|
|
230
241
|
- `seat_not_ready` → the pod is still booting; run again shortly.
|
|
231
242
|
- The numbers are right but the top view is wrong → coordinates: `y` in Blender is
|
|
232
243
|
depth, and `scale` on a 2-unit cube is the half-size.
|
|
@@ -41,6 +41,13 @@ paths. Building, previewing, promoting and publishing use `genex preview` /
|
|
|
41
41
|
|
|
42
42
|
Use the request in front of you, not the oldest description of the project.
|
|
43
43
|
|
|
44
|
+
- **Explicit Blender authoring**, including a standalone prop, owns the
|
|
45
|
+
`$genex-blender-scene` lane. Keep the requested scope: Python scripts, an
|
|
46
|
+
exported GLB and a browser preview for an asset request. Do not invent
|
|
47
|
+
gameplay, a character controller or a game concept to justify the hosted
|
|
48
|
+
workspace. Blender in this sentence names the authoring tool; a kitchen
|
|
49
|
+
blender requested as a prop follows the ordinary asset rows.
|
|
50
|
+
|
|
44
51
|
- **Clear focused request**—“work on the UI,” “fix movement,” “add one enemy,”
|
|
45
52
|
“make this prop,” “publish this”—read the touched implementation and
|
|
46
53
|
`DESIGN.md`, load only the owning skills, update `Now:`, and start. Do not
|