@xiaobuyu/nodesign 0.0.6 → 0.0.8
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 +266 -79
- package/README.zh-CN.md +254 -67
- package/package.json +2 -2
- package/server/api/admin.js +7 -0
- package/server/api/assets/docx-page.js +5 -4
- package/server/api/assets/helpers.js +26 -0
- package/server/api/assets/notes.js +50 -0
- package/server/api/assets.js +31 -38
- package/server/api/board.js +29 -1
- package/server/api/browse.js +3 -2
- package/server/api/chatai.js +9 -8
- package/server/api/exports/cards.js +7 -6
- package/server/api/exports/docx-pdf.js +4 -1
- package/server/api/instruction.js +3 -3
- package/server/api/local.js +10 -8
- package/server/api/projects.js +19 -3
- package/server/api/publish.js +3 -2
- package/server/api/sessions-rewind.js +220 -0
- package/server/api/sessions.js +58 -194
- package/server/api/stage.js +35 -0
- package/server/api/turn-model-switch.js +109 -0
- package/server/api/turn.js +52 -88
- package/server/auth/middleware.js +29 -6
- package/server/auth/tier.js +27 -7
- package/server/auth/users-store.js +14 -1
- package/server/engine/agent/actor-trail.js +110 -0
- package/server/engine/agent/agent-shared.js +22 -52
- package/server/engine/agent/beat-state.js +61 -0
- package/server/engine/agent/cast.js +322 -0
- package/server/engine/agent/events.js +18 -1
- package/server/engine/agent/hooks/beat-gate.js +73 -0
- package/server/engine/agent/hooks/failure.js +11 -1
- package/server/engine/agent/hooks/lifecycle.js +16 -41
- package/server/engine/agent/hooks/post-guidance.js +4 -38
- package/server/engine/agent/hooks/post-trim.js +1 -0
- package/server/engine/agent/hooks/pre-board-dirty.js +29 -0
- package/server/engine/agent/hooks/pre-defaults.js +206 -2
- package/server/engine/agent/hooks/pre-injectors.js +19 -2
- package/server/engine/agent/hooks/pre-peer-guard.js +88 -0
- package/server/engine/agent/hooks/pre-unknown-params.js +82 -0
- package/server/engine/agent/hooks/pre-workspace-scope-guard.js +33 -0
- package/server/engine/agent/hooks/resident-role-lifecycle.js +100 -0
- package/server/engine/agent/hooks/slot-alias.js +30 -0
- package/server/engine/agent/hooks/user-prompt-submit.js +208 -47
- package/server/engine/agent/hooks.js +80 -14
- package/server/engine/agent/isolation.js +25 -2
- package/server/engine/agent/memory-config.js +87 -0
- package/server/engine/agent/model-context.js +102 -18
- package/server/engine/agent/model-table.js +361 -58
- package/server/engine/agent/prompts/nodesign-prelude.md +579 -70
- package/server/engine/agent/prompts/tools/direct-edit-protocol.md +1 -1
- package/server/engine/agent/prompts/tools/generate-image-cookbook.md +26 -9
- package/server/engine/agent/prompts/tools/paint-still-cookbook.md +1 -1
- package/server/engine/agent/prompts/tools/site-reference.md +15 -4
- package/server/engine/agent/role-card.js +175 -0
- package/server/engine/agent/rp-mode.js +17 -0
- package/server/engine/agent/session-loop.js +55 -32
- package/server/engine/agent/stage-status.js +73 -0
- package/server/engine/agent/subagent-flight.js +73 -0
- package/server/engine/agent/system-prompts.js +41 -0
- package/server/engine/agent/tool-input-stream.js +165 -0
- package/server/engine/agents/explorer.md +1 -1
- package/server/engine/agents/index.js +1 -1
- package/server/engine/mcp/actor.js +59 -0
- package/server/engine/mcp/capability-gate.js +21 -2
- package/server/engine/mcp/index.js +118 -33
- package/server/engine/mcp/mode-profile.js +139 -0
- package/server/engine/mcp/server-name.js +19 -0
- package/server/engine/mcp/tools/artifact-session.js +2 -1
- package/server/engine/mcp/tools/browse-computer.js +2 -1
- package/server/engine/mcp/tools/browse-find-batch.js +95 -16
- package/server/engine/mcp/tools/browse.js +25 -3
- package/server/engine/mcp/tools/cast-role.js +167 -0
- package/server/engine/mcp/tools/crystallize-skill.js +1 -1
- package/server/engine/mcp/tools/draw-trend.js +104 -0
- package/server/engine/mcp/tools/edit-board-schema.js +56 -0
- package/server/engine/mcp/tools/edit-board.js +569 -0
- package/server/engine/mcp/tools/generate-image.js +50 -70
- package/server/engine/mcp/tools/helpers/codex-imagegen.js +139 -0
- package/server/engine/mcp/tools/helpers/rembg.js +7 -2
- package/server/engine/mcp/tools/helpers/shot-pipeline.js +109 -0
- package/server/engine/mcp/tools/list-pages.js +11 -5
- package/server/engine/mcp/tools/look-at-board.js +4 -3
- package/server/engine/mcp/tools/open-sheet.js +242 -0
- package/server/engine/mcp/tools/organize-board.js +45 -6
- package/server/engine/mcp/tools/paint-still.js +1 -1
- package/server/engine/mcp/tools/pin-to-board.js +103 -19
- package/server/engine/mcp/tools/read-board.js +123 -16
- package/server/engine/mcp/tools/read-tavern-json.js +87 -19
- package/server/engine/mcp/tools/report-issue.js +15 -7
- package/server/engine/mcp/tools/role-memory.js +83 -0
- package/server/engine/mcp/tools/roll-dice.js +82 -0
- package/server/engine/mcp/tools/roll-film.js +1 -1
- package/server/engine/mcp/tools/screenshot.js +19 -9
- package/server/engine/mcp/tools/set-vars.js +150 -0
- package/server/engine/mcp/tools/sheet-replan.js +46 -0
- package/server/engine/mcp/tools/web-search.js +12 -16
- package/server/engine/mcp/tools/write-on-board-flow.js +108 -0
- package/server/engine/mcp/tools/write-on-board-place.js +211 -0
- package/server/engine/mcp/tools/write-on-board-role-anchor.js +48 -0
- package/server/engine/mcp/tools/write-on-board-schema.js +125 -0
- package/server/engine/mcp/tools/write-on-board.js +568 -99
- package/server/engine/plugins/nodesign/skills/blackboard-rp/SKILL.md +215 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +14 -7
- package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +44 -5
- package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +16 -0
- package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +24 -8
- package/server/engine/plugins/nodesign/skills/story-craft/SKILL.md +94 -0
- package/server/engine/plugins/nodesign/skills/story-import/SKILL.md +112 -0
- package/server/engine/plugins/nodesign/skills/story-intimacy/SKILL.md +72 -0
- package/server/engine/plugins/nodesign/skills/story-voice/SKILL.md +66 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//345/206/267/347/241/254.md +33 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//346/203/205/346/204/237/347/233/264/347/273/231.md +42 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//346/204/217/350/257/206/346/265/201.md +42 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//346/255/246/344/276/240/347/231/275/350/257/235.md +34 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//347/224/237/346/264/273/346/265/201.md +35 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//347/231/275/346/217/217.md +37 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//350/210/236/345/217/260/345/211/247.md +32 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//351/230/266/346/256/265/345/210/207/346/215/242.md +42 -0
- package/server/engine/runs/board-seater.js +272 -0
- package/server/engine/runs/board-tasklist.js +90 -48
- package/server/engine/runs/turn-relay.js +24 -2
- package/server/engine/runs/user-chalk-echo.js +44 -0
- package/server/index.js +2 -2
- package/server/lib/artifact-target.js +31 -14
- package/server/lib/board-anchor.js +49 -0
- package/server/lib/board-dirty.js +93 -0
- package/server/lib/board-excerpt.js +80 -0
- package/server/lib/board-follow.js +111 -0
- package/server/lib/board-groups.js +1 -5
- package/server/lib/board-hero.js +3 -1
- package/server/lib/board-kind-sizes.js +38 -3
- package/server/lib/board-lanes.js +58 -0
- package/server/lib/board-obstacles.js +80 -0
- package/server/lib/board-relations.js +5 -1
- package/server/lib/board-sheets.js +467 -0
- package/server/lib/board-shelf.js +86 -0
- package/server/lib/board-transform.js +62 -0
- package/server/lib/canvas-id.js +46 -4
- package/server/lib/chalk-flow.js +88 -0
- package/server/lib/chalk-rewrite.js +63 -0
- package/server/lib/chalk-size-pref.js +48 -0
- package/server/lib/chalk.js +42 -3
- package/server/lib/export-collect.js +3 -1
- package/server/lib/ingress/forward-openai-chat.js +16 -4
- package/server/lib/ingress/openai-chat.js +23 -12
- package/server/lib/ingress/session-routes.js +3 -3
- package/server/lib/ingress/stream-watchdog.js +53 -0
- package/server/lib/ingress/upstream-billing.js +15 -0
- package/server/lib/ingress/upstream-error-hints.js +36 -0
- package/server/lib/issues-store.js +3 -1
- package/server/lib/json-preview.js +58 -0
- package/server/lib/kinds/docx.js +11 -4
- package/server/lib/kinds/index.js +6 -16
- package/server/lib/kinds/site.js +40 -7
- package/server/lib/model-ingress.js +26 -1
- package/server/lib/moderation.js +10 -5
- package/server/lib/quota.js +26 -6
- package/server/lib/rect.js +50 -0
- package/server/lib/rewrite-refs.js +110 -0
- package/server/lib/screen.js +36 -0
- package/server/lib/sheet-state.js +19 -0
- package/server/lib/sketch-array.js +118 -0
- package/server/lib/sketch-hatch.js +99 -0
- package/server/lib/sketch-layout.js +269 -69
- package/server/lib/sketch-shapes.js +152 -0
- package/server/lib/sketch-stencils.js +170 -0
- package/server/lib/ssrf-guard.js +9 -0
- package/server/lib/state-table.js +237 -0
- package/server/lib/state-trend.js +94 -0
- package/server/lib/state-triggers.js +199 -0
- package/server/lib/task-scan.js +18 -0
- package/server/lib/tavern-json.js +48 -0
- package/server/lib/user-uploads.js +88 -0
- package/server/projects/board-limits.js +5 -3
- package/server/projects/board-sanitize.js +220 -10
- package/server/projects/board-store.js +137 -7
- package/server/projects/memory-migration.js +85 -0
- package/server/projects/move-entry.js +11 -1
- package/server/projects/session-jsonl.js +110 -0
- package/server/projects/store.js +56 -3
- package/server/projects/ui-config.js +2 -1
- package/server/projects/viewpoint-store.js +35 -3
- package/server/projects/workspace-slots.js +30 -0
- package/server/projects/workspace-templates.js +42 -10
- package/server/projects/workspace.js +25 -26
- package/server/runtime/capabilities.js +6 -2
- package/server/services/rembg-launcher.js +5 -2
- package/server/shared/locales.js +70 -0
- package/server/shared/messages-en.js +69 -0
- package/server/shared/messages.js +65 -0
- package/server/ws/broker.js +3 -0
- package/server/ws/index.js +43 -3
- package/web/README.md +1 -1
- package/web/dist/assets/{BrowserWindow-P_vIdh-G.js → BrowserWindow-CcaGRWz5.js} +1 -1
- package/web/dist/assets/DeckWindow-BIr2KGFT.js +6 -0
- package/web/dist/assets/{DocxWindow-DSAU1-tD.js → DocxWindow-B9KZmU5z.js} +2 -2
- package/web/dist/assets/{MermaidBlock-DR1GikCv.js → MermaidBlock-DrfRnz8E.js} +43 -43
- package/web/dist/assets/{SiteWindow-92uBuRRR.js → SiteWindow-CJOjCF7d.js} +6 -6
- package/web/dist/assets/{abnfDiagram-VCTEODGH-uA8Esix4.js → abnfDiagram-VCTEODGH-CUZbQGIg.js} +1 -1
- package/web/dist/assets/{arc-D_khH_xN.js → arc-C1-HSUny.js} +1 -1
- package/web/dist/assets/{architectureDiagram-5GKGNRK7-DydJHqOt.js → architectureDiagram-5GKGNRK7-B5nwvu60.js} +2 -2
- package/web/dist/assets/{blockDiagram-NRAW4CY4-B7wBbKiW.js → blockDiagram-NRAW4CY4-uPPuFLwR.js} +1 -1
- package/web/dist/assets/{c4Diagram-UCG6FXSJ-6mrH0x7Y.js → c4Diagram-UCG6FXSJ-C0mKBFlz.js} +1 -1
- package/web/dist/assets/channel-BxpWNOGU.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-Cuikho5Q.js → chunk-2Q5K7J3B-Cs0rlZ4q.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-3-KZ5AUz.js → chunk-5VM5RSS4-DtR63oYb.js} +1 -1
- package/web/dist/assets/{chunk-F27PBJKO-Bkwq2gGU.js → chunk-F27PBJKO-B3MRdezJ.js} +1 -1
- package/web/dist/assets/{chunk-G27WJ6UU-BLyQzdGE.js → chunk-G27WJ6UU-BtwntwpS.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-B7HxxQds.js → chunk-JWPE2WC7-BHsTe5PD.js} +1 -1
- package/web/dist/assets/{chunk-LCL6LL3I-CtXrnUwF.js → chunk-LCL6LL3I-knY15Qdm.js} +1 -1
- package/web/dist/assets/{chunk-POPQ4Y6H-dxddZlXt.js → chunk-POPQ4Y6H-C51tgK_I.js} +1 -1
- package/web/dist/assets/{chunk-SVP7TREG-DpkmrgHn.js → chunk-SVP7TREG-CnwRZmF_.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-BwZ7RbpQ.js → chunk-XXDRQBXY-BtXzVF-_.js} +1 -1
- package/web/dist/assets/classDiagram-DTDB5LWJ-DUEKmDfo.js +1 -0
- package/web/dist/assets/classDiagram-v2-JRS7N3AN-DUEKmDfo.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-DGFOsMUt.js → cose-bilkent-JH36ORCC-CzWlGP6i.js} +1 -1
- package/web/dist/assets/{cynefin-OW5HDTMX-DUwn3ehS.js → cynefin-OW5HDTMX-vK64EGSv.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-5FMLGOSQ-DlLAOZ9x.js → cynefinDiagram-5FMLGOSQ-Bs3g1Gvh.js} +1 -1
- package/web/dist/assets/{dagre-3AP2YEHR-CyourLfz.js → dagre-3AP2YEHR-BhgkYe8V.js} +1 -1
- package/web/dist/assets/{diagram-S7CK7UJ4-BTMbx2np.js → diagram-S7CK7UJ4-C_6mvD2V.js} +1 -1
- package/web/dist/assets/{diagram-UQ7AKVKN-UK94_1-P.js → diagram-UQ7AKVKN-qW7tssUW.js} +1 -1
- package/web/dist/assets/{diagram-VSXAHHWV-C7zS-v9v.js → diagram-VSXAHHWV-txJPHpvG.js} +1 -1
- package/web/dist/assets/{diagram-VX7I27RA-DKJRa6CK.js → diagram-VX7I27RA-CQTMsCnL.js} +1 -1
- package/web/dist/assets/{diagram-Z3DM3KII-kUbAfGAp.js → diagram-Z3DM3KII--cb1hoE6.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-PWID7BFC-CVW16_vb.js → ebnfDiagram-PWID7BFC-Da6lDU77.js} +1 -1
- package/web/dist/assets/{erDiagram-SSCWMZ5O-BRQMFQ5y.js → erDiagram-SSCWMZ5O-DGCMrsQn.js} +1 -1
- package/web/dist/assets/{flowDiagram-A5DVABFB-S34LpLtK.js → flowDiagram-A5DVABFB-GdOL_qno.js} +1 -1
- package/web/dist/assets/{ganttDiagram-EL5Y4UJY-BFiqk1-G.js → ganttDiagram-EL5Y4UJY-Cx1F_jBK.js} +4 -4
- package/web/dist/assets/{gitGraphDiagram-WWUBYQGX-LZsrdyyv.js → gitGraphDiagram-WWUBYQGX-B5_6JhR3.js} +1 -1
- package/web/dist/assets/hand-CuvZZQGW.webp +0 -0
- package/web/dist/assets/index-BYKfMnmv.js +2759 -0
- package/web/dist/assets/index-CW7VsrVg.css +1 -0
- package/web/dist/assets/{infoDiagram-RXCK75RN-0DvN6KIp.js → infoDiagram-RXCK75RN-CjvKpSPA.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-5VMMS53U-CaY3A-Bx.js → ishikawaDiagram-5VMMS53U-CxLoAPhx.js} +1 -1
- package/web/dist/assets/{journeyDiagram-EYS64GPL-BBeiaCrj.js → journeyDiagram-EYS64GPL-BfvS1upJ.js} +1 -1
- package/web/dist/assets/{kanban-definition-3QL26DDD-ux42DRXQ.js → kanban-definition-3QL26DDD-DKPPu0w4.js} +1 -1
- package/web/dist/assets/{layout-CnXiF_NB.js → layout-Dhgck-Zo.js} +1 -1
- package/web/dist/assets/{linear-BMW0u6G6.js → linear-DMSQ-aUw.js} +1 -1
- package/web/dist/assets/lxgw-nd-app-bold-D1pKA1bR.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-app-regular-BDe3d26v.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-bold-Bufvlg03.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-regular-DQwoI4OP.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-100-DLPe7tEt.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-101-d3VsZKWT.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-102-ClzJuLq6.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-103-DuDnQ6K-.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-104-kPGW3Eem.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-105-BzD54h-0.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-106-BQ7fIzlx.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-107-D8tDfFHI.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-108-tGhkpwep.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-109-7SMVSrly.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-110-_uDKrFed.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-111-Bio825RE.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-112-D2ee0i-C.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-113-JPFVDrga.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-114-BcfxYA4w.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-115-B1HeeOsd.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-116-hY12tXpq.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-117-BvuZwmre.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-118-tzvs_H7j.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-119-J2DWuYDN.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-21-pDubCJV5.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-22-ChAPMpqp.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-23-BBx3oNpT.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-24-CYt-Oagk.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-25-Cx3j-ZUv.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-26-B9AfZ3LL.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-27-Bh2s9MNE.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-28-C02JPoKP.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-29-T_tbUGhk.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-30-DMfyNzhz.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-31-DFfdzbkg.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-32-Bs1fz2iW.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-33-cpB96OkZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-34-BhinPYWX.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-35-D_njRQqA.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-36-DqXlLE3t.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-37-j5LbvMph.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-38-oO4jWdKm.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-39-CabsX4rw.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-4-PatS3YHh.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-40-yrLg3C3e.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-41-DBXmCQhc.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-42-Bqij_UiQ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-43-D3kp0Ntc.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-44-B_BNGvK4.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-45-CZc0cQWs.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-46-CHQX5Kd9.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-47-Ci8gLrHA.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-48-run3ugD3.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-49-DgfiUb6u.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-5-B0CtGbX_.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-50-CdCG-POr.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-51-L2okeQZ2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-52-BZAqyGJW.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-53-B0xWqLeU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-54-BZVQX-EA.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-55-093mkIUJ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-56-CbhWG8G-.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-57-pLQuvo6c.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-58-BHhoY50x.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-59-D1_6DuIQ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-6-BnAelmYU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-60-HDBIuLTZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-61-C-c13SeU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-62-kHRVOhZ2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-63-C0MCsvFv.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-64-DZekguCj.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-65-CtP6c3IU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-66-DLVWoMSf.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-67-BVuOnayH.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-68-DpxQ6Mnx.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-69-qesBxLmC.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-70-BBt1xN7h.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-71-BWqZacwB.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-72-B1wP_Yst.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-73-C99T7jNZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-74-DrUTsWBe.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-75-C4M5XXQe.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-76-Bq8spVWF.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-77-D9vNupE2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-78-D6th7Gnu.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-79-bezM85cS.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-80-DD8pnhG2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-81-N6jtSyBs.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-82-D6c180bZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-83-em17AadM.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-84-CJ8gdrMK.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-85-9PPGO1eB.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-86-CjWPf-u5.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-87-8D_X_yOC.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-88-9vtKFPBx.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-89-BhE5jAKX.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-90-D2Npfm5E.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-91-C6Ft1-Eu.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-97-CaExyQP9.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-98-CPoVbsUd.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-99-ZUQZGVgH.woff2 +0 -0
- package/web/dist/assets/{mindmap-definition-FBJOCRG2-hUxSpdKn.js → mindmap-definition-FBJOCRG2-CalfL_Kn.js} +1 -1
- package/web/dist/assets/{pegDiagram-XKGWAZYB-aCaQPH0w.js → pegDiagram-XKGWAZYB-D9mVWz7l.js} +1 -1
- package/web/dist/assets/pending-edit-apply-kwMFWsUe.js +31 -0
- package/web/dist/assets/{pieDiagram-E7YTZNPT-CmOEZ8zg.js → pieDiagram-E7YTZNPT-DELzbZNS.js} +1 -1
- package/web/dist/assets/{quadrantDiagram-AXDQQJYC-VW6fAhsV.js → quadrantDiagram-AXDQQJYC-TZqp4mfA.js} +1 -1
- package/web/dist/assets/{railroadDiagram-O6MQD6OU-CKJ7Ba5K.js → railroadDiagram-O6MQD6OU-DxX59f0i.js} +1 -1
- package/web/dist/assets/{requirementDiagram-EFPCY7ZU-pNGkq9Vw.js → requirementDiagram-EFPCY7ZU-DFdsJLIe.js} +1 -1
- package/web/dist/assets/{rotate-cw-KAfChbGV.js → rotate-cw-62liz7LX.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-P5KCCOFB-BIMmDTfZ.js → sankeyDiagram-P5KCCOFB-ByYcd3fG.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-WJ2MYXX4-BBW7wzKY.js → sequenceDiagram-WJ2MYXX4-Bj-ml1jS.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-DdkXmLYR.js → sizeCapture-X5ZJPWSS-KhsXThyD.js} +1 -1
- package/web/dist/assets/{square-dashed-mouse-pointer-CDUlhUP7.js → square-dashed-mouse-pointer-D_VrqVpy.js} +1 -1
- package/web/dist/assets/{stateDiagram-HBIQ2CUA-W313Uz16.js → stateDiagram-HBIQ2CUA-BJlpHfWU.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-4QOOHH4V-CR2kYAH2.js +1 -0
- package/web/dist/assets/{swimlanes-XN3QIQJK-C29TL26K.js → swimlanes-XN3QIQJK-DUB9OJVs.js} +1 -1
- package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BitOxnG2.js +8 -0
- package/web/dist/assets/{timeline-definition-24CTP7MA-CF0JMObX.js → timeline-definition-24CTP7MA-C03NX82A.js} +1 -1
- package/web/dist/assets/{vennDiagram-4TSXK5OY-DrDFsaH3.js → vennDiagram-4TSXK5OY-B9pjzngp.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-VM6X3IG4-CVrnRIS_.js → wardleyDiagram-VM6X3IG4-UqWq_mcT.js} +1 -1
- package/web/dist/assets/{xychartDiagram-S5SC5T6Z-BvkN1zsT.js → xychartDiagram-S5SC5T6Z-D2pDKtOv.js} +1 -1
- package/web/dist/index.html +2 -2
- package/server/api/memory.js +0 -185
- package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +0 -583
- package/server/engine/mcp/tools/arrange-on-board.js +0 -124
- package/server/engine/mcp/tools/create-on-board.js +0 -152
- package/server/engine/mcp/tools/edit-sketch.js +0 -167
- package/server/engine/mcp/tools/record-decision.js +0 -140
- package/server/engine/mcp/tools/relate-on-board.js +0 -112
- package/server/engine/mcp/tools/sketch-on-board.js +0 -337
- package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +0 -235
- package/server/engine/plugins/nodesign/skills/rp-craft/patterns//346/274/224/345/207/272/351/241/265-/346/234/200/345/260/217/345/256/236/347/216/260.html +0 -78
- package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +0 -268
- package/web/dist/assets/DeckWindow-BpLv5082.js +0 -16
- package/web/dist/assets/channel-DGkccMO1.js +0 -1
- package/web/dist/assets/classDiagram-DTDB5LWJ-BGscw3OD.js +0 -1
- package/web/dist/assets/classDiagram-v2-JRS7N3AN-BGscw3OD.js +0 -1
- package/web/dist/assets/index-C0KUf6Wd.css +0 -1
- package/web/dist/assets/index-GAamh1p4.js +0 -2152
- package/web/dist/assets/lxgw-nd-bold-DbRfiTgo.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-regular-CpMw9Ehg.woff2 +0 -0
- package/web/dist/assets/pending-edit-apply-AcdOQ9Ij.js +0 -31
- package/web/dist/assets/stateDiagram-v2-4QOOHH4V-DDhJBZzf.js +0 -1
- package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BfN-Xdmu.js +0 -8
|
@@ -41,7 +41,6 @@
|
|
|
41
41
|
|
|
42
42
|
import path from 'node:path';
|
|
43
43
|
import fs from 'node:fs/promises';
|
|
44
|
-
import { spawn } from 'node:child_process';
|
|
45
44
|
import { tool } from '@anthropic-ai/claude-agent-sdk';
|
|
46
45
|
import { Events } from '../../agent/events.js';
|
|
47
46
|
import { z } from 'zod';
|
|
@@ -49,6 +48,9 @@ import sharp from 'sharp';
|
|
|
49
48
|
import {
|
|
50
49
|
THUMBNAIL_MAX_DIM, THUMBNAIL_QUALITY, writeWebpSibling, // 预热由 writeWebpSibling 顺带做
|
|
51
50
|
} from '../../../lib/image-variant.js';
|
|
51
|
+
import {
|
|
52
|
+
buildCodexBridgePrompt, runCodexImageGen, buildVariationPrompt, PRESERVE_KEYS,
|
|
53
|
+
} from './helpers/codex-imagegen.js';
|
|
52
54
|
|
|
53
55
|
// Thumbnail 配置(env 可调)。**原图不动**——保留 Gemini 输出的全分辨率(通常
|
|
54
56
|
// 1080×1920+ PNG,6-8MB)让用户最终交付不损失质量。仅生成低清 thumbnail 给
|
|
@@ -136,71 +138,9 @@ const DEFAULT_DMXAPI_BASE = 'https://www.dmxapi.cn';
|
|
|
136
138
|
const DEFAULT_CHANNEL = 'DMX';
|
|
137
139
|
|
|
138
140
|
// ── codex 生图桥(2026-07-27:NoDesk 网关退役,codex 成为默认 provider)──
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
// 风格参照有效)。桥接 prompt 必须写死"逐字传递零改写"——codex agent 默认会
|
|
142
|
-
// 按自己的 Augmentation rules 润色 prompt。
|
|
141
|
+
// 桥的 prompt 组装 + 子进程执行 + 变体模式骨架都在 helpers/codex-imagegen.js
|
|
142
|
+
// (2026-08-24 迁出)。这里只留 provider 分流开关。
|
|
143
143
|
const IMAGE_PROVIDER = () => (process.env.NODESIGN_IMAGE_PROVIDER || 'codex').toLowerCase();
|
|
144
|
-
const CODEX_BIN = process.env.NODESIGN_CODEX_BIN || 'codex';
|
|
145
|
-
const CODEX_IMAGE_TIMEOUT_MS = Number(process.env.NODESIGN_CODEX_IMAGE_TIMEOUT_MS) || 240_000;
|
|
146
|
-
|
|
147
|
-
function buildCodexBridgePrompt({ prompt, aspectRatio, absOut, refCount }) {
|
|
148
|
-
return [
|
|
149
|
-
'你是图像生成管道的执行端,只做下面几件事,不做任何多余动作:',
|
|
150
|
-
'1. 调用你的图像生成工具生成一张图。<image-prompt> 标签内的内容必须逐字作为生成 prompt,禁止改写、增删、翻译或润色。',
|
|
151
|
-
`2. 输出比例:${aspectRatio}。优先用工具的比例/尺寸参数;工具没有对应参数时,作为补充说明传给工具,但不修改 <image-prompt> 原文。`,
|
|
152
|
-
refCount > 0
|
|
153
|
-
? `3. 本消息附带 ${refCount} 张参考图,把它们作为图像生成的参考输入(风格 / 主体一致性参照)。`
|
|
154
|
-
: '3. 本次无参考图。',
|
|
155
|
-
`4. 生成后把图片文件复制到精确路径 ${absOut}(目录已存在)。`,
|
|
156
|
-
'5. 最后只回复该绝对路径。',
|
|
157
|
-
'<image-prompt>',
|
|
158
|
-
prompt,
|
|
159
|
-
'</image-prompt>',
|
|
160
|
-
].join('\n');
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* 跑一次 codex exec 生图,以目标文件落盘为成功标准(codex 的文本回复不可信),
|
|
165
|
-
* 失败自动重试一次。abort signal / 超时都 SIGKILL 子进程。
|
|
166
|
-
*/
|
|
167
|
-
async function runCodexImageGen({ bridgePrompt, refPaths, cwd, signal, expectFile, timeoutMs = CODEX_IMAGE_TIMEOUT_MS }) {
|
|
168
|
-
const args = ['exec', '--skip-git-repo-check', '-s', 'workspace-write', '-C', cwd, bridgePrompt];
|
|
169
|
-
for (const p of refPaths) args.push('-i', p);
|
|
170
|
-
|
|
171
|
-
const runOnce = () => new Promise((resolve, reject) => {
|
|
172
|
-
const child = spawn(CODEX_BIN, args, { stdio: ['ignore', 'pipe', 'pipe'], env: process.env });
|
|
173
|
-
let stderrTail = '';
|
|
174
|
-
child.stdout.on('data', () => { /* 排空防背压 */ });
|
|
175
|
-
child.stderr.on('data', (d) => { stderrTail = (stderrTail + d.toString()).slice(-2000); });
|
|
176
|
-
const killTimer = setTimeout(() => {
|
|
177
|
-
try { child.kill('SIGKILL'); } catch { /* */ }
|
|
178
|
-
reject(new Error(`codex exec timeout after ${Math.round(timeoutMs / 1000)}s`));
|
|
179
|
-
}, timeoutMs);
|
|
180
|
-
const onAbort = () => { try { child.kill('SIGKILL'); } catch { /* */ } };
|
|
181
|
-
signal?.addEventListener?.('abort', onAbort, { once: true });
|
|
182
|
-
child.on('error', (err) => { clearTimeout(killTimer); reject(err); });
|
|
183
|
-
child.on('close', (code) => {
|
|
184
|
-
clearTimeout(killTimer);
|
|
185
|
-
signal?.removeEventListener?.('abort', onAbort);
|
|
186
|
-
if (signal?.aborted) return reject(new Error('aborted'));
|
|
187
|
-
if (code !== 0) return reject(new Error(`codex exec exited ${code}: ${stderrTail.slice(-300) || 'no stderr'}`));
|
|
188
|
-
resolve();
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
193
|
-
try {
|
|
194
|
-
await runOnce();
|
|
195
|
-
const st = await fs.stat(expectFile).catch(() => null);
|
|
196
|
-
if (st && st.size > 0) return;
|
|
197
|
-
throw new Error(`codex finished but target file missing/empty: ${expectFile}`);
|
|
198
|
-
} catch (err) {
|
|
199
|
-
if (attempt === 2 || signal?.aborted) throw err;
|
|
200
|
-
console.warn(`[generate-image] codex attempt ${attempt} failed (${err.message}), retrying once`);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
144
|
|
|
205
145
|
const PASSTHROUGH_PATH = '/default/passthrough';
|
|
206
146
|
// model id 在 callGateway 时动态拼,因为支持 flash / pro 路由
|
|
@@ -413,6 +353,15 @@ REFERENCES:
|
|
|
413
353
|
conversation. To iterate on a previous image, pass it back in referenceImages
|
|
414
354
|
and restate the invariants every round.
|
|
415
355
|
|
|
356
|
+
VARIATION MODE (series consistency — outfit swaps, one-detail edits):
|
|
357
|
+
Instead of hand-writing "change only X, keep Y" prompts, pass
|
|
358
|
+
variationOf (path of the base image) + change (the ONE thing to alter)
|
|
359
|
+
+ optional preserve (which aspects must stay identical; default all).
|
|
360
|
+
The tool expands these into a battle-tested skeleton incl. standard
|
|
361
|
+
prohibitions (no invented straps/ornaments, absences preserved). Build
|
|
362
|
+
series as a tree: one anchor, then vary one axis per call. In this mode
|
|
363
|
+
prompt is optional (used as extra notes).
|
|
364
|
+
|
|
416
365
|
WHEN TO USE:
|
|
417
366
|
- You're building a deck / landing / report and want real imagery
|
|
418
367
|
- You need a backdrop that pure CSS gradient can't achieve
|
|
@@ -424,15 +373,29 @@ WHEN NOT TO USE:
|
|
|
424
373
|
- Data charts — use Recharts/ECharts/Mermaid via React mount
|
|
425
374
|
- Simple inline icons (≤5 per page) — use lucide-react inline SVG
|
|
426
375
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
rationale (both required), scope, alternatives — there is no "topic" param.`,
|
|
376
|
+
After a generation that settles style direction, record the settled anchor as a
|
|
377
|
+
memory (记忆/, type: project) so later sessions inherit it.`,
|
|
430
378
|
{
|
|
431
379
|
prompt: z
|
|
432
380
|
.string()
|
|
433
381
|
.min(4)
|
|
434
382
|
.max(3500)
|
|
435
|
-
.
|
|
383
|
+
.optional()
|
|
384
|
+
.describe('Natural-language scene description. Describe, don\'t list keywords. Required unless variationOf is set (then it is appended as extra notes).'),
|
|
385
|
+
variationOf: z
|
|
386
|
+
.string()
|
|
387
|
+
.optional()
|
|
388
|
+
.describe('VARIATION MODE: workspace-relative path of the base image to reproduce with ONE change. Becomes the first reference image (edit target).'),
|
|
389
|
+
change: z
|
|
390
|
+
.string()
|
|
391
|
+
.min(4)
|
|
392
|
+
.max(500)
|
|
393
|
+
.optional()
|
|
394
|
+
.describe('VARIATION MODE: the single thing to alter, e.g. "裙子换成薄荷绿短裙,长度到膝盖以上". Required with variationOf.'),
|
|
395
|
+
preserve: z
|
|
396
|
+
.array(z.enum(PRESERVE_KEYS))
|
|
397
|
+
.optional()
|
|
398
|
+
.describe('VARIATION MODE: aspects that must stay identical. Omit = all of them (safest).'),
|
|
436
399
|
aspectRatio: z
|
|
437
400
|
.enum(ASPECT_RATIOS)
|
|
438
401
|
.optional()
|
|
@@ -476,6 +439,9 @@ rationale (both required), scope, alternatives — there is no "topic" param.`,
|
|
|
476
439
|
},
|
|
477
440
|
async ({
|
|
478
441
|
prompt,
|
|
442
|
+
variationOf,
|
|
443
|
+
change,
|
|
444
|
+
preserve,
|
|
479
445
|
aspectRatio = '16:9',
|
|
480
446
|
imageSize = '1K',
|
|
481
447
|
referenceImages,
|
|
@@ -496,6 +462,19 @@ rationale (both required), scope, alternatives — there is no "topic" param.`,
|
|
|
496
462
|
isError: true,
|
|
497
463
|
};
|
|
498
464
|
}
|
|
465
|
+
// 变体模式归一化:variationOf 只是 referenceImages[0] 的语义化别名,
|
|
466
|
+
// prompt 换成固定骨架 —— 下游(ref 解析 / -i 附件 / gateway inline)零改动。
|
|
467
|
+
const isVariation = Boolean(variationOf);
|
|
468
|
+
if (isVariation && !change) {
|
|
469
|
+
return { content: [{ type: 'text', text: 'generate_image failed: variationOf requires `change` (the ONE thing to alter).' }], isError: true };
|
|
470
|
+
}
|
|
471
|
+
if (!isVariation && !prompt) {
|
|
472
|
+
return { content: [{ type: 'text', text: 'generate_image failed: `prompt` is required (or use variationOf + change).' }], isError: true };
|
|
473
|
+
}
|
|
474
|
+
if (isVariation) {
|
|
475
|
+
prompt = buildVariationPrompt({ change, preserve, extra: prompt });
|
|
476
|
+
referenceImages = [variationOf, ...(referenceImages || [])];
|
|
477
|
+
}
|
|
499
478
|
// 1. provider 分流(2026-07-27:NoDesk 网关退役,默认 codex 订阅生图;
|
|
500
479
|
// gateway 分支保留给显式 NODESIGN_IMAGE_PROVIDER=gateway 的场景)
|
|
501
480
|
const provider = IMAGE_PROVIDER();
|
|
@@ -550,7 +529,7 @@ rationale (both required), scope, alternatives — there is no "topic" param.`,
|
|
|
550
529
|
fileName = `${finalName}.png`;
|
|
551
530
|
absOut = path.join(outDir, fileName);
|
|
552
531
|
const bridgePrompt = buildCodexBridgePrompt({
|
|
553
|
-
prompt, aspectRatio, absOut, refCount: resolvedRefs.length,
|
|
532
|
+
prompt, aspectRatio, absOut, refCount: resolvedRefs.length, variation: isVariation,
|
|
554
533
|
});
|
|
555
534
|
try {
|
|
556
535
|
await runCodexImageGen({
|
|
@@ -706,6 +685,7 @@ rationale (both required), scope, alternatives — there is no "topic" param.`,
|
|
|
706
685
|
provider,
|
|
707
686
|
model: provider === 'codex' ? 'codex' : model,
|
|
708
687
|
referenceImageCount: resolvedRefs.length,
|
|
688
|
+
variationOf: variationOf || null, // 变体血缘:产物墙 / 引用层能顺出系列树
|
|
709
689
|
sessionId: ctx?.sessionId || null,
|
|
710
690
|
runId: ctx?.runId || null,
|
|
711
691
|
ts: new Date().toISOString(),
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* helpers/codex-imagegen.js — codex 生图桥的 prompt 组装 + 子进程执行
|
|
3
|
+
* (2026-08-24 从 generate-image.js 迁出:给棘轮腾行 + prompt 组装层归位一处)
|
|
4
|
+
*
|
|
5
|
+
* 两层各管一事:
|
|
6
|
+
* - buildVariationPrompt:变体模式(variationOf)的 prompt 骨架。把"要保持的
|
|
7
|
+
* 东西逐项列出"从 agent 手写咒语变成参数 —— 32 张换装立绘实战里,漏写一项
|
|
8
|
+
* (袜子)整行就坏,这套骨架把踩过的坑固化成默认禁止项。
|
|
9
|
+
* - buildCodexBridgePrompt / runCodexImageGen:codex CLI 桥(原样迁出,
|
|
10
|
+
* 仅第 3 条在变体模式下改口径:参考图第 1 张是编辑基底,不是风格参照)。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'node:fs/promises';
|
|
14
|
+
import { spawn } from 'node:child_process';
|
|
15
|
+
|
|
16
|
+
export const CODEX_BIN = process.env.NODESIGN_CODEX_BIN || 'codex';
|
|
17
|
+
export const CODEX_IMAGE_TIMEOUT_MS = Number(process.env.NODESIGN_CODEX_IMAGE_TIMEOUT_MS) || 240_000;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 变体模式的 preserve 词表。键是 agent 传的枚举值,值是展开进 prompt 的英文短语。
|
|
21
|
+
* 短语必须把"缺席也是要保持的"写死(no socks 的参考图,结果也必须 no socks)——
|
|
22
|
+
* 这是实战里最容易漂的一类。
|
|
23
|
+
*/
|
|
24
|
+
export const PRESERVE_PHRASES = {
|
|
25
|
+
pose: 'the exact same pose and body position',
|
|
26
|
+
framing: 'the same framing, camera distance and position of the subject in the frame',
|
|
27
|
+
proportions: 'the same height and body proportions',
|
|
28
|
+
face: 'the same face and the same expression',
|
|
29
|
+
hair: 'the same hairstyle and hair color',
|
|
30
|
+
hands: 'the same hand positions, holding the same things (empty hands stay empty)',
|
|
31
|
+
top: 'the same top garment — identical cut, color, sleeves, neckline and every detail',
|
|
32
|
+
bottom: 'the same bottom garment (skirt / trousers) — identical in every detail',
|
|
33
|
+
shoes: 'the same footwear, identical in every detail',
|
|
34
|
+
socks: 'the same socks / legwear (if the reference has none, the result has none)',
|
|
35
|
+
accessories: 'the same accessories — nothing added, nothing removed',
|
|
36
|
+
background: 'the same background',
|
|
37
|
+
lighting: 'the same lighting and color grading',
|
|
38
|
+
style: 'the same art style and rendering technique',
|
|
39
|
+
};
|
|
40
|
+
export const PRESERVE_KEYS = Object.keys(PRESERVE_PHRASES);
|
|
41
|
+
|
|
42
|
+
// 不论 preserve 传什么都追加的通用军规 —— 全部是真实翻过车的:凭空长出背带、
|
|
43
|
+
// 白袜整行消失、取景悄悄拉近。
|
|
44
|
+
const VARIATION_HARD_RULES = [
|
|
45
|
+
'Do NOT invent anything that is not in the reference: no added straps, suspenders,'
|
|
46
|
+
+ ' belts, hair ornaments, jewelry or extra garments.',
|
|
47
|
+
'Absences are part of the reference: whatever the reference does not have, the result must not have.',
|
|
48
|
+
'Do NOT change the subject\'s height, body proportions, or position in the frame.',
|
|
49
|
+
].join('\n');
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* variationOf 模式:把 change + preserve 展开成"只改一处、其余照搬"的固定骨架。
|
|
53
|
+
* @param {object} o
|
|
54
|
+
* @param {string} o.change 唯一要改的那一处(自然语言)
|
|
55
|
+
* @param {string[]} [o.preserve] PRESERVE_KEYS 的子集;缺省 = 全部
|
|
56
|
+
* @param {string} [o.extra] 附注(agent 想额外说明的,放在骨架之后)
|
|
57
|
+
*/
|
|
58
|
+
export function buildVariationPrompt({ change, preserve, extra }) {
|
|
59
|
+
const keys = (preserve && preserve.length > 0) ? preserve : PRESERVE_KEYS;
|
|
60
|
+
const phrases = keys.map((k) => PRESERVE_PHRASES[k]).filter(Boolean);
|
|
61
|
+
return [
|
|
62
|
+
'Reproduce the reference illustration (the first reference image) with ONE single change.',
|
|
63
|
+
`THE ONLY CHANGE: ${change}`,
|
|
64
|
+
`Everything else must be identical to the reference: ${phrases.join('; ')}.`,
|
|
65
|
+
VARIATION_HARD_RULES,
|
|
66
|
+
...(extra ? [extra] : []),
|
|
67
|
+
].join('\n');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* codex 桥接 prompt。必须写死"逐字传递零改写"——codex agent 默认会按自己的
|
|
72
|
+
* Augmentation rules 润色 prompt。变体模式下第 3 条改口径:第 1 张参考图是
|
|
73
|
+
* **编辑基底**,必须除指定改动外逐细节复现("风格参照"的软口径会被当成
|
|
74
|
+
* style reference,收敛不住)。
|
|
75
|
+
*/
|
|
76
|
+
export function buildCodexBridgePrompt({ prompt, aspectRatio, absOut, refCount, variation = false }) {
|
|
77
|
+
let refLine;
|
|
78
|
+
if (refCount > 0 && variation) {
|
|
79
|
+
refLine = `3. 本消息附带 ${refCount} 张参考图。第 1 张是**编辑基底**:除 <image-prompt> 指定的那一处改动外,`
|
|
80
|
+
+ '必须逐细节复现它的全部内容;其余参考图(如有)才是风格 / 一致性参照。';
|
|
81
|
+
} else if (refCount > 0) {
|
|
82
|
+
refLine = `3. 本消息附带 ${refCount} 张参考图,把它们作为图像生成的参考输入(风格 / 主体一致性参照)。`;
|
|
83
|
+
} else {
|
|
84
|
+
refLine = '3. 本次无参考图。';
|
|
85
|
+
}
|
|
86
|
+
return [
|
|
87
|
+
'你是图像生成管道的执行端,只做下面几件事,不做任何多余动作:',
|
|
88
|
+
'1. 调用你的图像生成工具生成一张图。<image-prompt> 标签内的内容必须逐字作为生成 prompt,禁止改写、增删、翻译或润色。',
|
|
89
|
+
`2. 输出比例:${aspectRatio}。优先用工具的比例/尺寸参数;工具没有对应参数时,作为补充说明传给工具,但不修改 <image-prompt> 原文。`,
|
|
90
|
+
refLine,
|
|
91
|
+
`4. 生成后把图片文件复制到精确路径 ${absOut}(目录已存在)。`,
|
|
92
|
+
'5. 最后只回复该绝对路径。',
|
|
93
|
+
'<image-prompt>',
|
|
94
|
+
prompt,
|
|
95
|
+
'</image-prompt>',
|
|
96
|
+
].join('\n');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 跑一次 codex exec 生图,以目标文件落盘为成功标准(codex 的文本回复不可信),
|
|
101
|
+
* 失败自动重试一次。abort signal / 超时都 SIGKILL 子进程。
|
|
102
|
+
*/
|
|
103
|
+
export async function runCodexImageGen({ bridgePrompt, refPaths, cwd, signal, expectFile, timeoutMs = CODEX_IMAGE_TIMEOUT_MS }) {
|
|
104
|
+
const args = ['exec', '--skip-git-repo-check', '-s', 'workspace-write', '-C', cwd, bridgePrompt];
|
|
105
|
+
for (const p of refPaths) args.push('-i', p);
|
|
106
|
+
|
|
107
|
+
const runOnce = () => new Promise((resolve, reject) => {
|
|
108
|
+
const child = spawn(CODEX_BIN, args, { stdio: ['ignore', 'pipe', 'pipe'], env: process.env });
|
|
109
|
+
let stderrTail = '';
|
|
110
|
+
child.stdout.on('data', () => { /* 排空防背压 */ });
|
|
111
|
+
child.stderr.on('data', (d) => { stderrTail = (stderrTail + d.toString()).slice(-2000); });
|
|
112
|
+
const killTimer = setTimeout(() => {
|
|
113
|
+
try { child.kill('SIGKILL'); } catch { /* */ }
|
|
114
|
+
reject(new Error(`codex exec timeout after ${Math.round(timeoutMs / 1000)}s`));
|
|
115
|
+
}, timeoutMs);
|
|
116
|
+
const onAbort = () => { try { child.kill('SIGKILL'); } catch { /* */ } };
|
|
117
|
+
signal?.addEventListener?.('abort', onAbort, { once: true });
|
|
118
|
+
child.on('error', (err) => { clearTimeout(killTimer); reject(err); });
|
|
119
|
+
child.on('close', (code) => {
|
|
120
|
+
clearTimeout(killTimer);
|
|
121
|
+
signal?.removeEventListener?.('abort', onAbort);
|
|
122
|
+
if (signal?.aborted) return reject(new Error('aborted'));
|
|
123
|
+
if (code !== 0) return reject(new Error(`codex exec exited ${code}: ${stderrTail.slice(-300) || 'no stderr'}`));
|
|
124
|
+
resolve();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
129
|
+
try {
|
|
130
|
+
await runOnce();
|
|
131
|
+
const st = await fs.stat(expectFile).catch(() => null);
|
|
132
|
+
if (st && st.size > 0) return;
|
|
133
|
+
throw new Error(`codex finished but target file missing/empty: ${expectFile}`);
|
|
134
|
+
} catch (err) {
|
|
135
|
+
if (attempt === 2 || signal?.aborted) throw err;
|
|
136
|
+
console.warn(`[generate-image] codex attempt ${attempt} failed (${err.message}), retrying once`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -39,13 +39,18 @@ export const REMBG_SETUP_HINT = IS_WIN
|
|
|
39
39
|
: 'cd server && python3 -m venv .venv-rembg && .venv-rembg/bin/python3 -m pip install rembg onnxruntime';
|
|
40
40
|
const DEFAULT_PYTHON = REMBG_VENV_PYTHON;
|
|
41
41
|
const DEFAULT_HELPER = path.join(__dirname, 'rembg-bridge.py');
|
|
42
|
-
const DEFAULT_SOCKET = '/tmp/nodesign-rembg.sock';
|
|
43
42
|
// fallback spawn 的 timeout——首次冷启 + 模型 load 留余量
|
|
44
43
|
const DEFAULT_FALLBACK_TIMEOUT_MS = 60_000;
|
|
45
44
|
|
|
46
45
|
function resolvePython() { return process.env.NODESIGN_REMBG_PYTHON || DEFAULT_PYTHON; }
|
|
47
46
|
function resolveHelper() { return process.env.NODESIGN_REMBG_HELPER || DEFAULT_HELPER; }
|
|
48
|
-
|
|
47
|
+
// socket 默认按**端口**派生(08-24 案:prod/exp 共用一个默认路径,exp 启动把生产的
|
|
48
|
+
// 抠图服务当 stale 杀掉再自己占坑)。端口一实例一个,是现成的实例身份。
|
|
49
|
+
// 这里是全仓唯一一份真相源,rembg-launcher.js import 它。
|
|
50
|
+
export function resolveRembgSocket() {
|
|
51
|
+
return process.env.NODESIGN_REMBG_SOCKET || `/tmp/nodesign-rembg-${process.env.PORT || '4001'}.sock`;
|
|
52
|
+
}
|
|
53
|
+
function resolveSocket() { return resolveRembgSocket(); }
|
|
49
54
|
|
|
50
55
|
// service health check 缓存(避免每次 request 都探一遍 /health)
|
|
51
56
|
let serviceHealthCache = { ok: false, checkedAt: 0 };
|
|
@@ -278,3 +278,112 @@ export async function runBeforeShot(page, beforeShot) {
|
|
|
278
278
|
return `beforeShot error: ${err?.message || err}`;
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 带兜底的整页/裁剪截图(iss_mt6tru7p:WebGL 持续动画页 + device:'mobile' 下
|
|
284
|
+
* page.screenshot 等稳定帧/字体 30s 必超时——rAF 每帧都在重绘,"稳定"永远
|
|
285
|
+
* 等不到)。先给 playwright 一次机会(15s,够正常页),超时改走 CDP
|
|
286
|
+
* Page.captureScreenshot:**抓当前帧不等任何东西**。持续动画页抓哪一帧都是
|
|
287
|
+
* 对的——它本来就没有"稳定帧"这回事。
|
|
288
|
+
*
|
|
289
|
+
* @returns {Promise<{buf: Buffer, degraded: boolean}>} degraded=true 走了 CDP 兜底
|
|
290
|
+
*/
|
|
291
|
+
export async function shotWithFallback(page, opts = {}) {
|
|
292
|
+
try {
|
|
293
|
+
return { buf: await page.screenshot({ ...opts, timeout: 15_000 }), degraded: false };
|
|
294
|
+
} catch (err) {
|
|
295
|
+
if (!/Timeout/i.test(String(err?.message))) throw err;
|
|
296
|
+
const cdp = await page.context().newCDPSession(page);
|
|
297
|
+
try {
|
|
298
|
+
const params = { format: 'png', captureBeyondViewport: !!opts.fullPage };
|
|
299
|
+
// CDP 的 clip:captureBeyondViewport 下是文档坐标,与我们 fullPage+clip 的语义一致
|
|
300
|
+
if (opts.clip) params.clip = { x: opts.clip.x, y: opts.clip.y, width: opts.clip.width, height: opts.clip.height, scale: 1 };
|
|
301
|
+
const { data } = await cdp.send('Page.captureScreenshot', params);
|
|
302
|
+
return { buf: Buffer.from(data, 'base64'), degraded: true };
|
|
303
|
+
} finally {
|
|
304
|
+
await cdp.detach().catch(() => {});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* 超长页 fullPage → 滚动位置联络表(iss_mt0365b8):48000px 的整页压进 API 长边
|
|
311
|
+
* 上限后只剩 47px 宽,什么都看不见——工具自己承认不可读还是把废图塞给了模型。
|
|
312
|
+
* 超过阈值改成:按滚动位置取 5 帧视口,拼一张带位置标注的联络表(跟胶片条同一
|
|
313
|
+
* 套版式,横轴从时间换成滚动位置)。一次调用看清整页节奏;要某段的原尺寸用
|
|
314
|
+
* scrollTo 参数去截那一段。
|
|
315
|
+
*/
|
|
316
|
+
export const LONG_PAGE_LIMIT = 12_000;
|
|
317
|
+
|
|
318
|
+
export async function longPageSheet(page, { positions = [0, 0.22, 0.45, 0.68, 0.9] } = {}) {
|
|
319
|
+
const dims = await page.evaluate(() => ({
|
|
320
|
+
docH: Math.max(document.documentElement.scrollHeight, document.body?.scrollHeight || 0),
|
|
321
|
+
vpH: window.innerHeight, vpW: window.innerWidth, y0: window.scrollY,
|
|
322
|
+
}));
|
|
323
|
+
if (!(dims.docH > LONG_PAGE_LIMIT)) return null;
|
|
324
|
+
const shots = [];
|
|
325
|
+
for (const p of positions) {
|
|
326
|
+
const y = Math.round(p * (dims.docH - dims.vpH));
|
|
327
|
+
await page.evaluate((yy) => window.scrollTo(0, yy), y).catch(() => {});
|
|
328
|
+
await page.waitForTimeout(300); // 给懒加载/reveal 一点点时间,不求全到
|
|
329
|
+
const { buf } = await shotWithFallback(page, { type: 'png' });
|
|
330
|
+
shots.push({ buf, label: `${Math.round(p * 100)}% y=${y}` });
|
|
331
|
+
}
|
|
332
|
+
await page.evaluate((yy) => window.scrollTo(0, yy), dims.y0).catch(() => {});
|
|
333
|
+
|
|
334
|
+
const { default: sharp } = await import('sharp');
|
|
335
|
+
const { sheetLayout } = await import('./motion-lab.js');
|
|
336
|
+
const meta = await sharp(shots[0].buf).metadata();
|
|
337
|
+
const L = sheetLayout(shots.length, meta.width, meta.height);
|
|
338
|
+
const composites = []; const labels = [];
|
|
339
|
+
for (let i = 0; i < shots.length; i += 1) {
|
|
340
|
+
const col = i % L.cols; const row = Math.floor(i / L.cols);
|
|
341
|
+
const left = L.gap + col * (L.cellW + L.gap);
|
|
342
|
+
const top = L.gap + row * (L.cellH + L.gap);
|
|
343
|
+
composites.push({ input: await sharp(shots[i].buf).resize(L.cellW, L.cellH, { fit: 'fill' }).png().toBuffer(), left, top });
|
|
344
|
+
labels.push({ left, top, label: shots[i].label });
|
|
345
|
+
}
|
|
346
|
+
const fontH = Math.max(13, Math.round(L.cellH * 0.055));
|
|
347
|
+
const escXml = (t) => String(t).replace(/[<>&]/g, (c) => ({ '<': '<', '>': '>', '&': '&' }[c]));
|
|
348
|
+
const svg = [`<svg xmlns="http://www.w3.org/2000/svg" width="${L.sheetW}" height="${L.sheetH}">`,
|
|
349
|
+
...labels.map((l) => `<rect x="${l.left}" y="${l.top}" width="${(l.label.length * fontH * 0.62 + 12).toFixed(0)}" height="${fontH + 10}" fill="#000" fill-opacity="0.72"/>`
|
|
350
|
+
+ `<text x="${l.left + 6}" y="${l.top + fontH + 2}" font-size="${fontH}" fill="#fff" font-family="monospace">${escXml(l.label)}</text>`),
|
|
351
|
+
'</svg>'].join('');
|
|
352
|
+
const buf = await sharp({ create: { width: L.sheetW, height: L.sheetH, channels: 3, background: '#15150f' } })
|
|
353
|
+
.composite([...composites, { input: Buffer.from(svg), left: 0, top: 0 }]).png().toBuffer();
|
|
354
|
+
return {
|
|
355
|
+
buf,
|
|
356
|
+
mode: `long-page contact sheet (doc ${dims.docH}px, ${shots.length} viewports)`,
|
|
357
|
+
note: `整页高 ${dims.docH}px,fullPage 会压成不可读的细条 —— 已改为 ${shots.length} 帧滚动位置联络表(标注=滚动百分比与 y)。要某一段的原尺寸,用 scrollTo 参数(像素/百分比/选择器)单独截那一屏。`,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* fullPage+clip 的"Clipped area is either empty or outside the resulting image"
|
|
363
|
+
* 兜底(iss_mszxp0zy):Chromium 整页光栅有高度上限(~16384px 纹理),元素落在
|
|
364
|
+
* 超高页更深处时文档坐标 clip 会落到成图之外。兜底=滚到元素处,改用**视口坐标**
|
|
365
|
+
* clip 截当前屏(clip 语义两套:fullPage 下文档坐标 / 非 fullPage 视口坐标,
|
|
366
|
+
* 本机探针 08-19 定过案)。
|
|
367
|
+
*/
|
|
368
|
+
export async function clipShotWithFallback(page, { clip, selector }) {
|
|
369
|
+
try {
|
|
370
|
+
return await shotWithFallback(page, { fullPage: true, clip, type: 'png' });
|
|
371
|
+
} catch (err) {
|
|
372
|
+
if (!/Clipped area/i.test(String(err?.message)) || !selector) throw err;
|
|
373
|
+
await page.locator(selector).first().scrollIntoViewIfNeeded({ timeout: 5000 }).catch(() => {});
|
|
374
|
+
const r = await page.evaluate((sel) => {
|
|
375
|
+
const el = document.querySelector(sel);
|
|
376
|
+
if (!el) return null;
|
|
377
|
+
const b = el.getBoundingClientRect();
|
|
378
|
+
return { x: b.x, y: b.y, width: b.width, height: b.height, vw: window.innerWidth, vh: window.innerHeight };
|
|
379
|
+
}, selector);
|
|
380
|
+
if (!r) throw err;
|
|
381
|
+
const vClip = {
|
|
382
|
+
x: Math.max(0, r.x), y: Math.max(0, r.y),
|
|
383
|
+
width: Math.max(1, Math.min(r.width, r.vw - Math.max(0, r.x))),
|
|
384
|
+
height: Math.max(1, Math.min(r.height, r.vh - Math.max(0, r.y))),
|
|
385
|
+
};
|
|
386
|
+
const out = await shotWithFallback(page, { clip: vClip, type: 'png' });
|
|
387
|
+
return { ...out, degraded: true };
|
|
388
|
+
}
|
|
389
|
+
}
|
|
@@ -152,8 +152,8 @@ async function listSiteStructure(target) {
|
|
|
152
152
|
return {
|
|
153
153
|
content: [{
|
|
154
154
|
type: 'text',
|
|
155
|
-
text:
|
|
156
|
-
+ (art?.root ? '
|
|
155
|
+
text: `这个站点(${art?.root ? `产物根 ${art.root}/` : '任务根'})还没有 html 页面。`
|
|
156
|
+
+ (art?.root ? '(源写完了的话,先跑构建让产物落进去;构建后确认产物真的落在这个目录。)' : ''),
|
|
157
157
|
}],
|
|
158
158
|
};
|
|
159
159
|
}
|
|
@@ -186,6 +186,9 @@ async function listSiteStructure(target) {
|
|
|
186
186
|
});
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
// 输出统一成**工作区相对**路径:screenshot_canvas / artifact_open 收的就是这种。
|
|
190
|
+
// 以前 file 是产物根相对(index.html),agent 原样回喂必然 not found(08-24 案)
|
|
191
|
+
const prefix = art?.root ? `${art.root}/` : '';
|
|
189
192
|
const known = new Set(pages);
|
|
190
193
|
const broken = [];
|
|
191
194
|
for (const p of out) {
|
|
@@ -199,7 +202,7 @@ async function listSiteStructure(target) {
|
|
|
199
202
|
if (seg === '..') stack.pop();
|
|
200
203
|
else stack.push(seg);
|
|
201
204
|
}
|
|
202
|
-
if (!known.has(stack.join('/'))) broken.push(`${p.file} → ${href}`);
|
|
205
|
+
if (!known.has(stack.join('/'))) broken.push(`${prefix}${p.file} → ${href}`);
|
|
203
206
|
}
|
|
204
207
|
}
|
|
205
208
|
const brokenNote = broken.length
|
|
@@ -212,11 +215,14 @@ async function listSiteStructure(target) {
|
|
|
212
215
|
? `\n\n同任务的其他产物(平等,各自寻址):${siblings.map(a => a.entryRel).join(' / ')}`
|
|
213
216
|
: '';
|
|
214
217
|
|
|
218
|
+
const outWs = prefix ? out.map(p => ({ ...p, file: prefix + p.file })) : out;
|
|
219
|
+
|
|
215
220
|
return {
|
|
216
221
|
content: [{
|
|
217
222
|
type: 'text',
|
|
218
|
-
text:
|
|
219
|
-
+
|
|
223
|
+
text: `站点${art?.title ? ` ${art.title}` : ''} · ${out.length} 个页面(入口 ${prefix}${pages[0]})${rootNote}`
|
|
224
|
+
+ ',file 是工作区相对路径,可直接传给 screenshot_canvas / artifact_open:\n\n'
|
|
225
|
+
+ `${JSON.stringify(outWs, null, 2)}${brokenNote}${siblingNote}`,
|
|
220
226
|
}],
|
|
221
227
|
};
|
|
222
228
|
}
|
|
@@ -26,7 +26,7 @@ import { platform } from '../../../runtime/platform.js';
|
|
|
26
26
|
import { launchPerceptionBrowser, PERCEPTION_ORIGIN } from './helpers/perception-page.js';
|
|
27
27
|
import { readBoard } from '../../../projects/board-store.js';
|
|
28
28
|
import { estimateSizeOn } from '../../../lib/board-kind-sizes.js';
|
|
29
|
-
import { normalizeCanvasId } from '../../../lib/canvas-id.js';
|
|
29
|
+
import { normalizeCanvasId, bareTag } from '../../../lib/canvas-id.js';
|
|
30
30
|
|
|
31
31
|
const VIEW = { width: 1400, height: 900 };
|
|
32
32
|
const READY_TIMEOUT_MS = 25_000;
|
|
@@ -46,7 +46,7 @@ export function makeLookAtBoardTool({ projectId }) {
|
|
|
46
46
|
return tool(
|
|
47
47
|
'look_at_board',
|
|
48
48
|
`LOOK at the canvas (a real screenshot of the board as the user sees it). Use it after
|
|
49
|
-
|
|
49
|
+
write_on_board / edit_board to check what you drew, or when the
|
|
50
50
|
user talks about how things look on the board. Pick a region — the whole board shrunk
|
|
51
51
|
into one image is unreadable:
|
|
52
52
|
- tag: frame the group carrying this #tag (a sketch you made)
|
|
@@ -60,7 +60,8 @@ Costs a few seconds and ~1.7k tokens; don't call it in a loop.`,
|
|
|
60
60
|
margin: z.number().min(0).max(2000).optional().describe('World px of margin around `around` (default 160)'),
|
|
61
61
|
view: z.object({ x: z.number(), y: z.number(), w: z.number().min(50), h: z.number().min(50) }).optional(),
|
|
62
62
|
},
|
|
63
|
-
async ({ tag, around, margin, view }) => {
|
|
63
|
+
async ({ tag: rawTag, around, margin, view }) => {
|
|
64
|
+
const tag = rawTag ? bareTag(rawTag) : rawTag; // 同上:查询侧统一剥 #
|
|
64
65
|
const err = (t) => ({ content: [{ type: 'text', text: t }], isError: true });
|
|
65
66
|
if (!projectId) return err('No project bound.');
|
|
66
67
|
const origin = webOrigin();
|