@xiaobuyu/nodesign 0.0.1 → 0.0.2
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 +156 -106
- package/bin/nodesign.js +0 -0
- package/package.json +1 -1
- package/server/api/README.md +45 -45
- package/server/api/_guard.js +75 -75
- package/server/api/admin.js +187 -187
- package/server/api/assets/docx-page.js +103 -103
- package/server/api/assets/helpers.js +22 -22
- package/server/api/assets/notes.js +144 -144
- package/server/api/assets.js +896 -896
- package/server/api/board.js +57 -57
- package/server/api/browse.js +138 -138
- package/server/api/canvas.js +444 -444
- package/server/api/chatai.js +324 -324
- package/server/api/exports/build-standalone.js +980 -980
- package/server/api/exports/cards.js +118 -118
- package/server/api/exports/docx-pdf.js +46 -46
- package/server/api/exports/export-page.js +142 -142
- package/server/api/exports/handoff.js +147 -147
- package/server/api/exports.js +690 -690
- package/server/api/instruction.js +75 -75
- package/server/api/me.js +116 -116
- package/server/api/memory.js +185 -185
- package/server/api/pending-changes.js +366 -366
- package/server/api/plugins.js +164 -164
- package/server/api/projects.js +220 -220
- package/server/api/publish.js +64 -64
- package/server/api/recent.js +73 -73
- package/server/api/sessions.js +589 -589
- package/server/api/skills.js +77 -77
- package/server/api/standalone-fit.js +168 -168
- package/server/api/turn-compose.js +180 -180
- package/server/api/turn-inflight.js +44 -44
- package/server/auth/README.md +17 -17
- package/server/auth/middleware.js +138 -138
- package/server/auth/origin-guard.js +71 -71
- package/server/auth/session.js +108 -108
- package/server/auth/tier.js +113 -113
- package/server/auth/users-store.js +348 -348
- package/server/edit/README.md +29 -29
- package/server/engine/README.md +113 -113
- package/server/engine/agent/agent-shared.js +550 -550
- package/server/engine/agent/auto-mode-default-hard-deny.txt +4 -4
- package/server/engine/agent/auto-mode-rules.js +78 -78
- package/server/engine/agent/context.js +418 -418
- package/server/engine/agent/events.js +464 -464
- package/server/engine/agent/hooks/canvas-validate.js +199 -199
- package/server/engine/agent/hooks/failure.js +120 -120
- package/server/engine/agent/hooks/file-events.js +75 -75
- package/server/engine/agent/hooks/lifecycle.js +233 -233
- package/server/engine/agent/hooks/post-canvas-focus.js +80 -80
- package/server/engine/agent/hooks/post-guidance.js +206 -206
- package/server/engine/agent/hooks/post-subagent-report.js +104 -104
- package/server/engine/agent/hooks/post-trim.js +58 -58
- package/server/engine/agent/hooks/pre-board-neighborhood.js +34 -34
- package/server/engine/agent/hooks/pre-defaults.js +84 -84
- package/server/engine/agent/hooks/pre-injectors.js +277 -277
- package/server/engine/agent/hooks/pre-performance-log-guard.js +78 -78
- package/server/engine/agent/hooks/pre-starter-files.js +61 -61
- package/server/engine/agent/hooks/pre-workspace-scope-guard.js +78 -78
- package/server/engine/agent/hooks/site-validate.js +94 -94
- package/server/engine/agent/hooks/tool-prompts.js +31 -31
- package/server/engine/agent/hooks/turn-state-memory.js +56 -56
- package/server/engine/agent/hooks/user-prompt-submit.js +215 -215
- package/server/engine/agent/hooks.js +354 -354
- package/server/engine/agent/init-contract.js +69 -69
- package/server/engine/agent/isolation.js +159 -159
- package/server/engine/agent/model-table.js +292 -292
- package/server/engine/agent/plugin-loader.js +266 -266
- package/server/engine/agent/prompts/nodesign-prelude.md +336 -336
- package/server/engine/agent/prompts/tools/ask-user-question-protocol.md +71 -71
- package/server/engine/agent/prompts/tools/direct-edit-protocol.md +98 -98
- package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +583 -583
- package/server/engine/agent/prompts/tools/generate-image-cookbook.md +483 -483
- package/server/engine/agent/prompts/tools/hybrid-reference.md +100 -100
- package/server/engine/agent/prompts/tools/paint-still-cookbook.md +270 -270
- package/server/engine/agent/prompts/tools/roll-film-cookbook.md +70 -70
- package/server/engine/agent/prompts/tools/site-reference.md +106 -106
- package/server/engine/agent/prompts/tools/tweaks-syntax.md +125 -125
- package/server/engine/agent/prompts/tools/vision-checker-dispatch.md +48 -48
- package/server/engine/agent/session-loop.js +1024 -1024
- package/server/engine/agent/session-model.js +164 -164
- package/server/engine/agent/skill.js +204 -204
- package/server/engine/agent/task-events.js +78 -78
- package/server/engine/agents/ds-extractor.md +132 -132
- package/server/engine/agents/explorer.md +190 -190
- package/server/engine/agents/index.js +230 -230
- package/server/engine/agents/schemas/design-system.json +156 -156
- package/server/engine/agents/schemas/tweak-schema.json +113 -113
- package/server/engine/agents/tweak-proposer.md +127 -127
- package/server/engine/agents/vision-checker.md +254 -254
- package/server/engine/browse/capture.js +422 -422
- package/server/engine/browse/card.js +137 -137
- package/server/engine/browse/handover.js +59 -59
- package/server/engine/browse/page-digest.js +119 -119
- package/server/engine/browse/refs.js +196 -196
- package/server/engine/browse/registry.js +307 -307
- package/server/engine/browse/screencast.js +170 -170
- package/server/engine/browse/state.js +121 -121
- package/server/engine/chatai/chat-log.js +79 -79
- package/server/engine/chatai/index.js +193 -193
- package/server/engine/chatai/openai-compat.js +152 -152
- package/server/engine/chatai/orchestrate.js +309 -309
- package/server/engine/chatai/perform.js +40 -40
- package/server/engine/chatai/summarize.js +79 -79
- package/server/engine/mcp/capability-gate.js +52 -52
- package/server/engine/mcp/index.js +339 -339
- package/server/engine/mcp/param-sanitizer.js +142 -142
- package/server/engine/mcp/tools/arrange-on-board.js +124 -124
- package/server/engine/mcp/tools/artifact-session.js +278 -278
- package/server/engine/mcp/tools/browse-computer.js +421 -421
- package/server/engine/mcp/tools/browse-find-batch.js +183 -183
- package/server/engine/mcp/tools/browse-screenshot.js +157 -157
- package/server/engine/mcp/tools/browse.js +480 -480
- package/server/engine/mcp/tools/build-docx.js +101 -101
- package/server/engine/mcp/tools/clear-pending-changes.js +99 -99
- package/server/engine/mcp/tools/create-on-board.js +155 -155
- package/server/engine/mcp/tools/crystallize-skill.js +168 -168
- package/server/engine/mcp/tools/explain-style.js +237 -237
- package/server/engine/mcp/tools/export-handoff.js +133 -133
- package/server/engine/mcp/tools/expose-tweaks.js +149 -149
- package/server/engine/mcp/tools/generate-image.js +842 -842
- package/server/engine/mcp/tools/get-computed-styles.js +164 -164
- package/server/engine/mcp/tools/get-pending-changes.js +205 -205
- package/server/engine/mcp/tools/h3box-ssh.js +84 -84
- package/server/engine/mcp/tools/helpers/acquire-page.js +82 -82
- package/server/engine/mcp/tools/helpers/motion-lab.js +554 -554
- package/server/engine/mcp/tools/helpers/motion-scroll.js +112 -112
- package/server/engine/mcp/tools/helpers/perception-page.js +215 -215
- package/server/engine/mcp/tools/helpers/reference-download.js +103 -103
- package/server/engine/mcp/tools/helpers/rembg-bridge.py +80 -80
- package/server/engine/mcp/tools/helpers/shot-pipeline.js +280 -280
- package/server/engine/mcp/tools/highlight.js +68 -68
- package/server/engine/mcp/tools/list-pages.js +222 -222
- package/server/engine/mcp/tools/lookup-tags.js +59 -59
- package/server/engine/mcp/tools/navigate-to-page.js +60 -60
- package/server/engine/mcp/tools/organize-board.js +64 -64
- package/server/engine/mcp/tools/paint-still.js +434 -434
- package/server/engine/mcp/tools/pin-to-board.js +130 -130
- package/server/engine/mcp/tools/preview-deck.js +130 -130
- package/server/engine/mcp/tools/profile-scroll.js +305 -305
- package/server/engine/mcp/tools/publish-site.js +102 -102
- package/server/engine/mcp/tools/query-elements.js +195 -195
- package/server/engine/mcp/tools/read-board.js +103 -103
- package/server/engine/mcp/tools/read-document.js +93 -93
- package/server/engine/mcp/tools/read-page.js +264 -264
- package/server/engine/mcp/tools/read-tavern-json.js +143 -143
- package/server/engine/mcp/tools/record-decision.js +140 -140
- package/server/engine/mcp/tools/relate-on-board.js +110 -110
- package/server/engine/mcp/tools/report-issue.js +115 -115
- package/server/engine/mcp/tools/roll-film.js +266 -266
- package/server/engine/mcp/tools/screenshot-docx.js +138 -138
- package/server/engine/mcp/tools/screenshot-url.js +195 -195
- package/server/engine/mcp/tools/screenshot.js +583 -583
- package/server/engine/mcp/tools/tier-gate.js +96 -96
- package/server/engine/mcp/tools/trace-motion.js +215 -215
- package/server/engine/mcp/tools/web-search.js +548 -548
- package/server/engine/motion/inventory.js +349 -349
- package/server/engine/perception/session.js +235 -235
- package/server/engine/plugins/nodesign/.claude-plugin/plugin.json +5 -5
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +192 -192
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/canvas.template.html +667 -667
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/hybrid-grid.md +22 -22
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/image-led-cover.md +25 -25
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/portrait.md +23 -23
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/quote-backdrop.md +21 -21
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/section-divider.md +23 -23
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/text-led.md +21 -21
- package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +162 -162
- package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +407 -407
- package/server/engine/plugins/nodesign/skills/docx-craft//346/226/207/346/241/243.template.json +80 -80
- package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +228 -228
- 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 +78 -78
- package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +268 -268
- package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +323 -323
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/build-lane.md +134 -134
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/cutout-collage.md +61 -61
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/mock-app.md +163 -163
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/page-transitions.md +86 -86
- package/server/engine/runs/active-runs.js +698 -698
- package/server/engine/runs/live-turn.js +252 -252
- package/server/engine/runs/store.js +364 -364
- package/server/engine/runs/turn-relay.js +217 -217
- package/server/engine/runtime/workspace.js +164 -164
- package/server/lib/artifact-file-path.js +80 -80
- package/server/lib/artifact-target.js +284 -284
- package/server/lib/asset-refs.js +239 -239
- package/server/lib/async-queue.js +104 -104
- package/server/lib/auto-relations.js +130 -130
- package/server/lib/binding-types.js +60 -60
- package/server/lib/board-kind-sizes.js +62 -62
- package/server/lib/board-relations.js +133 -133
- package/server/lib/browse-proxy.js +180 -180
- package/server/lib/canvas-id.js +36 -36
- package/server/lib/cover.js +227 -227
- package/server/lib/danbooru-tags.js +291 -291
- package/server/lib/doc-extract.js +156 -156
- package/server/lib/docx/build-from-source.js +260 -260
- package/server/lib/docx/build.js +490 -490
- package/server/lib/docx/dump-styles.js +287 -287
- package/server/lib/docx/fonts/nodesign-cjk.conf +164 -164
- package/server/lib/docx/merge-runs.js +129 -129
- package/server/lib/docx/numbering.js +218 -218
- package/server/lib/docx/order.js +152 -152
- package/server/lib/docx/rawzip.js +172 -172
- package/server/lib/docx/render.js +92 -92
- package/server/lib/docx/text-lint.js +225 -225
- package/server/lib/docx/tokens.js +335 -335
- package/server/lib/docx/units.js +29 -29
- package/server/lib/docx/xml.js +291 -291
- package/server/lib/docx-pages.js +187 -187
- package/server/lib/export-collect.js +289 -289
- package/server/lib/export-package.js +210 -210
- package/server/lib/html-srcset.js +145 -145
- package/server/lib/image-variant.js +460 -460
- package/server/lib/ingress/forward-openai-chat.js +305 -305
- package/server/lib/ingress/openai-chat.js +491 -491
- package/server/lib/ingress/session-notice.js +66 -66
- package/server/lib/ingress/session-routes.js +68 -68
- package/server/lib/ingress/slot-probe.js +130 -130
- package/server/lib/ingress/upstream-billing.js +52 -52
- package/server/lib/ingress/upstream-fail-streak.js +72 -72
- package/server/lib/ingress/upstream-truncation.js +48 -48
- package/server/lib/issues-store.js +182 -182
- package/server/lib/kinds/deck.js +72 -72
- package/server/lib/kinds/docx.js +283 -283
- package/server/lib/kinds/file-kinds.js +113 -113
- package/server/lib/kinds/index.js +202 -202
- package/server/lib/kinds/site.js +161 -161
- package/server/lib/model-ingress.js +543 -543
- package/server/lib/moderation.js +255 -255
- package/server/lib/notice-store.js +103 -103
- package/server/lib/plugin-install.js +104 -104
- package/server/lib/plugin-validator.js +721 -721
- package/server/lib/publish-store.js +115 -115
- package/server/lib/quick-summary.js +52 -52
- package/server/lib/quota.js +270 -270
- package/server/lib/rate-window.js +29 -29
- package/server/lib/reference-assets.js +92 -92
- package/server/lib/region-shot.js +135 -135
- package/server/lib/safe-path.js +73 -73
- package/server/lib/sdk-session.js +33 -33
- package/server/lib/showcase-store.js +92 -92
- package/server/lib/site-publish.js +465 -465
- package/server/lib/ssrf-guard.js +432 -432
- package/server/lib/task-scan.js +158 -158
- package/server/lib/tavern-json.js +154 -154
- package/server/lib/video-variant.js +237 -237
- package/server/lib/workspace-path.js +33 -33
- package/server/ops/fix-sdk-musl.mjs +40 -40
- package/server/ops/install-macos-fonts.sh +114 -114
- package/server/ops/macos-fonts.conf +336 -336
- package/server/ops/sandbox-shim/bwrap +57 -57
- package/server/projects/assets-summary.js +119 -119
- package/server/projects/auto-name.js +53 -53
- package/server/projects/board-store.js +640 -640
- package/server/projects/move-entry.js +103 -103
- package/server/projects/store.js +270 -270
- package/server/projects/ui-config.js +54 -54
- package/server/projects/workspace-templates.js +53 -53
- package/server/projects/workspace.js +1025 -1025
- package/server/runtime/local-config.js +177 -177
- package/server/runtime/profile.js +72 -72
- package/server/services/rembg-service.py +334 -334
- package/server/shared/README.md +25 -25
- package/server/shared/deck.js +54 -54
- package/server/shared/time.js +49 -49
- package/server/style-pipeline/README.md +53 -53
- package/server/ws/broker.js +36 -36
- package/server/ws/browse-channel.js +177 -177
- package/server/ws/index.js +386 -386
- package/web/README.md +89 -89
- package/web/dist/assets/BrowserWindow-BYCgizHw.js +11 -0
- package/web/dist/assets/{DeckWindow-4G0xX7P3.js → DeckWindow-pNydEM4L.js} +1 -1
- package/web/dist/assets/{DocxWindow-BXQsoPkh.js → DocxWindow-PPgzh3Gq.js} +1 -1
- package/web/dist/assets/{SiteWindow-D6eS-2gM.js → SiteWindow-DM0LQPCO.js} +1 -1
- package/web/dist/assets/{index-Cbl3ATvp.js → index-BHTBJlpY.js} +32 -32
- package/web/dist/assets/{pending-edit-apply-DTjo2geW.js → pending-edit-apply-C0I13CbD.js} +1 -1
- package/web/dist/assets/{rotate-cw-LMT3x1Zj.js → rotate-cw-B1UQ8Hld.js} +1 -1
- package/web/dist/assets/{square-dashed-mouse-pointer-CBoAkfhi.js → square-dashed-mouse-pointer-8lrgqmfv.js} +1 -1
- package/web/dist/index.html +17 -17
- package/web/dist/assets/BrowserWindow-Dc7J7CYc.js +0 -11
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* move-entry.js —— 「把一个东西搬到另一个文件夹」的唯一实现(2026-08-14 抽出)。
|
|
3
|
-
*
|
|
4
|
-
* 之前它整个住在 `POST /:pid/move` 的路由体里。agent 的整理工具
|
|
5
|
-
* (organize_board)要同一套语义 —— 按单一真相源纪律抽成一份,两个调用方
|
|
6
|
-
* (用户拖拽 / agent 归纳)共用,别长出第二套"怎么算搬得动"。
|
|
7
|
-
*
|
|
8
|
-
* 语义(见原路由注释,搬运时一字未改):
|
|
9
|
-
* ① fs.rename 先动磁盘,失败画布一个字节不改
|
|
10
|
-
* ② renameBoardPaths 同一步改画布身份(物件/文件夹/归属/关系线端点)+ 转发表
|
|
11
|
-
* ③ 调用方拿到新 board(前端要用它重写 layoutRef)
|
|
12
|
-
* ④ commit 交给调用方(路由在响应后 commit;agent 工具每轮本来就落 commit)
|
|
13
|
-
*
|
|
14
|
-
* 失败用 MoveError 抛(带 status),路由映射成 http 状态码,工具映射成文案。
|
|
15
|
-
*/
|
|
16
|
-
import { promises as fs } from 'fs';
|
|
17
|
-
import path from 'path';
|
|
18
|
-
import { getSharedDir } from './workspace.js';
|
|
19
|
-
import { renameBoardPaths } from './board-store.js';
|
|
20
|
-
import { taskManifest, KIND_SITE } from '../lib/artifact-target.js';
|
|
21
|
-
import { RESERVED_DIRS } from '../lib/task-scan.js';
|
|
22
|
-
|
|
23
|
-
export class MoveError extends Error {
|
|
24
|
-
constructor(status, message) { super(message); this.status = status; }
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const norm = (p) => String(p ?? '').replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
|
|
28
|
-
|
|
29
|
-
async function exists(p) {
|
|
30
|
-
try { await fs.access(p); return true; } catch { return false; }
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 哪些东西不许搬走:真·基础设施。
|
|
35
|
-
* `assets/` 2026-08-13 放开;`notes/` 2026-08-14 用户拍板放开 —— 便签也参与
|
|
36
|
-
* 归纳。搬出 notes/ 的 .md **明码换形态**(便签卡 → 阅读器文件卡,失去分面
|
|
37
|
-
* 翻页),这是桌面语义的自然结果不是事故;搬回 notes/ 仍被目标目录守卫挡着
|
|
38
|
-
*(RESERVED_DIRS),"升格回便签"要做的话单独开闸。
|
|
39
|
-
*/
|
|
40
|
-
const NO_MOVE_OUT = new Set(['exports', 'node_modules', 'agent-memory']);
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @param {string} pid
|
|
44
|
-
* @param {string} fromRaw 工作区相对路径(文件或文件夹)
|
|
45
|
-
* @param {string} toRaw 目标文件夹('' = 工作区根)
|
|
46
|
-
* @param {object} [opts] { createFolder: 目标夹不存在就 mkdir(agent 归纳常配新夹)}
|
|
47
|
-
* @returns {Promise<{ok:true, from:string, to:string, moved:boolean, board?:object}>}
|
|
48
|
-
*/
|
|
49
|
-
export async function moveEntry(pid, fromRaw, toRaw, { createFolder = false } = {}) {
|
|
50
|
-
const root = getSharedDir(pid);
|
|
51
|
-
const from = norm(fromRaw);
|
|
52
|
-
const to = norm(toRaw);
|
|
53
|
-
if (!from) throw new MoveError(400, 'from required');
|
|
54
|
-
|
|
55
|
-
const absFrom = path.resolve(root, from);
|
|
56
|
-
const absToDir = to ? path.resolve(root, to) : root;
|
|
57
|
-
const inside = (p) => p === root || p.startsWith(root + path.sep);
|
|
58
|
-
if (!inside(absFrom) || absFrom === root || !inside(absToDir)) {
|
|
59
|
-
throw new MoveError(400, 'path escapes workspace');
|
|
60
|
-
}
|
|
61
|
-
const guardSeg = (rel) => rel.split('/')[0];
|
|
62
|
-
if (NO_MOVE_OUT.has(guardSeg(from)) || guardSeg(from).startsWith('.')) {
|
|
63
|
-
throw new MoveError(400, '这个位置的东西不参与搬家');
|
|
64
|
-
}
|
|
65
|
-
if (to && (RESERVED_DIRS.has(guardSeg(to)) || guardSeg(to).startsWith('.'))) {
|
|
66
|
-
throw new MoveError(400, '不能搬进这个目录');
|
|
67
|
-
}
|
|
68
|
-
// 搬进自己肚子里(文件夹拖到它自己的子文件夹上)—— fs.rename 会报
|
|
69
|
-
// EINVAL,但那时目录树已经没法自洽了,提前拦住
|
|
70
|
-
if (to === from || to.startsWith(from + '/')) {
|
|
71
|
-
throw new MoveError(400, '不能把文件夹搬进它自己里面');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const srcStat = await fs.stat(absFrom).catch(() => null);
|
|
75
|
-
if (!srcStat) throw new MoveError(404, 'source not found');
|
|
76
|
-
let dirStat = await fs.stat(absToDir).catch(() => null);
|
|
77
|
-
if (!dirStat?.isDirectory()) {
|
|
78
|
-
if (!createFolder || !to) throw new MoveError(404, 'target folder not found');
|
|
79
|
-
await fs.mkdir(absToDir, { recursive: true });
|
|
80
|
-
dirStat = await fs.stat(absToDir);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// 目标目录**本身是一件产物**(整站)时不许搬进去:它的内部结构由
|
|
84
|
-
// 形态解析器管,塞进去的东西会从产物枚举里彻底消失(既不是页面也不是卡)。
|
|
85
|
-
// 站点收素材的正路是它的 assets/ 子目录(那一层不是产物根,照常放行)。
|
|
86
|
-
if (to) {
|
|
87
|
-
const m = await taskManifest(absToDir);
|
|
88
|
-
const opaque = (m?.artifacts || []).some(
|
|
89
|
-
a => a.kind === KIND_SITE && !a.single && !a.root);
|
|
90
|
-
if (opaque) throw new MoveError(400, '这是一件产物,不是收纳文件夹(站点收素材放它的 assets/ 子目录)');
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const base = path.basename(from);
|
|
94
|
-
const nextRel = to ? `${to}/${base}` : base;
|
|
95
|
-
if (nextRel === from) return { ok: true, from, to: from, moved: false };
|
|
96
|
-
if (await exists(path.resolve(root, nextRel))) {
|
|
97
|
-
throw new MoveError(409, `「${base}」在那儿已经有一个了`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
await fs.rename(absFrom, path.resolve(root, nextRel)); // ①
|
|
101
|
-
const { board } = await renameBoardPaths(pid, [[from, nextRel]]); // ②
|
|
102
|
-
return { ok: true, from, to: nextRel, moved: true, board }; // ③
|
|
103
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* move-entry.js —— 「把一个东西搬到另一个文件夹」的唯一实现(2026-08-14 抽出)。
|
|
3
|
+
*
|
|
4
|
+
* 之前它整个住在 `POST /:pid/move` 的路由体里。agent 的整理工具
|
|
5
|
+
* (organize_board)要同一套语义 —— 按单一真相源纪律抽成一份,两个调用方
|
|
6
|
+
* (用户拖拽 / agent 归纳)共用,别长出第二套"怎么算搬得动"。
|
|
7
|
+
*
|
|
8
|
+
* 语义(见原路由注释,搬运时一字未改):
|
|
9
|
+
* ① fs.rename 先动磁盘,失败画布一个字节不改
|
|
10
|
+
* ② renameBoardPaths 同一步改画布身份(物件/文件夹/归属/关系线端点)+ 转发表
|
|
11
|
+
* ③ 调用方拿到新 board(前端要用它重写 layoutRef)
|
|
12
|
+
* ④ commit 交给调用方(路由在响应后 commit;agent 工具每轮本来就落 commit)
|
|
13
|
+
*
|
|
14
|
+
* 失败用 MoveError 抛(带 status),路由映射成 http 状态码,工具映射成文案。
|
|
15
|
+
*/
|
|
16
|
+
import { promises as fs } from 'fs';
|
|
17
|
+
import path from 'path';
|
|
18
|
+
import { getSharedDir } from './workspace.js';
|
|
19
|
+
import { renameBoardPaths } from './board-store.js';
|
|
20
|
+
import { taskManifest, KIND_SITE } from '../lib/artifact-target.js';
|
|
21
|
+
import { RESERVED_DIRS } from '../lib/task-scan.js';
|
|
22
|
+
|
|
23
|
+
export class MoveError extends Error {
|
|
24
|
+
constructor(status, message) { super(message); this.status = status; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const norm = (p) => String(p ?? '').replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
|
|
28
|
+
|
|
29
|
+
async function exists(p) {
|
|
30
|
+
try { await fs.access(p); return true; } catch { return false; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 哪些东西不许搬走:真·基础设施。
|
|
35
|
+
* `assets/` 2026-08-13 放开;`notes/` 2026-08-14 用户拍板放开 —— 便签也参与
|
|
36
|
+
* 归纳。搬出 notes/ 的 .md **明码换形态**(便签卡 → 阅读器文件卡,失去分面
|
|
37
|
+
* 翻页),这是桌面语义的自然结果不是事故;搬回 notes/ 仍被目标目录守卫挡着
|
|
38
|
+
*(RESERVED_DIRS),"升格回便签"要做的话单独开闸。
|
|
39
|
+
*/
|
|
40
|
+
const NO_MOVE_OUT = new Set(['exports', 'node_modules', 'agent-memory']);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {string} pid
|
|
44
|
+
* @param {string} fromRaw 工作区相对路径(文件或文件夹)
|
|
45
|
+
* @param {string} toRaw 目标文件夹('' = 工作区根)
|
|
46
|
+
* @param {object} [opts] { createFolder: 目标夹不存在就 mkdir(agent 归纳常配新夹)}
|
|
47
|
+
* @returns {Promise<{ok:true, from:string, to:string, moved:boolean, board?:object}>}
|
|
48
|
+
*/
|
|
49
|
+
export async function moveEntry(pid, fromRaw, toRaw, { createFolder = false } = {}) {
|
|
50
|
+
const root = getSharedDir(pid);
|
|
51
|
+
const from = norm(fromRaw);
|
|
52
|
+
const to = norm(toRaw);
|
|
53
|
+
if (!from) throw new MoveError(400, 'from required');
|
|
54
|
+
|
|
55
|
+
const absFrom = path.resolve(root, from);
|
|
56
|
+
const absToDir = to ? path.resolve(root, to) : root;
|
|
57
|
+
const inside = (p) => p === root || p.startsWith(root + path.sep);
|
|
58
|
+
if (!inside(absFrom) || absFrom === root || !inside(absToDir)) {
|
|
59
|
+
throw new MoveError(400, 'path escapes workspace');
|
|
60
|
+
}
|
|
61
|
+
const guardSeg = (rel) => rel.split('/')[0];
|
|
62
|
+
if (NO_MOVE_OUT.has(guardSeg(from)) || guardSeg(from).startsWith('.')) {
|
|
63
|
+
throw new MoveError(400, '这个位置的东西不参与搬家');
|
|
64
|
+
}
|
|
65
|
+
if (to && (RESERVED_DIRS.has(guardSeg(to)) || guardSeg(to).startsWith('.'))) {
|
|
66
|
+
throw new MoveError(400, '不能搬进这个目录');
|
|
67
|
+
}
|
|
68
|
+
// 搬进自己肚子里(文件夹拖到它自己的子文件夹上)—— fs.rename 会报
|
|
69
|
+
// EINVAL,但那时目录树已经没法自洽了,提前拦住
|
|
70
|
+
if (to === from || to.startsWith(from + '/')) {
|
|
71
|
+
throw new MoveError(400, '不能把文件夹搬进它自己里面');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const srcStat = await fs.stat(absFrom).catch(() => null);
|
|
75
|
+
if (!srcStat) throw new MoveError(404, 'source not found');
|
|
76
|
+
let dirStat = await fs.stat(absToDir).catch(() => null);
|
|
77
|
+
if (!dirStat?.isDirectory()) {
|
|
78
|
+
if (!createFolder || !to) throw new MoveError(404, 'target folder not found');
|
|
79
|
+
await fs.mkdir(absToDir, { recursive: true });
|
|
80
|
+
dirStat = await fs.stat(absToDir);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 目标目录**本身是一件产物**(整站)时不许搬进去:它的内部结构由
|
|
84
|
+
// 形态解析器管,塞进去的东西会从产物枚举里彻底消失(既不是页面也不是卡)。
|
|
85
|
+
// 站点收素材的正路是它的 assets/ 子目录(那一层不是产物根,照常放行)。
|
|
86
|
+
if (to) {
|
|
87
|
+
const m = await taskManifest(absToDir);
|
|
88
|
+
const opaque = (m?.artifacts || []).some(
|
|
89
|
+
a => a.kind === KIND_SITE && !a.single && !a.root);
|
|
90
|
+
if (opaque) throw new MoveError(400, '这是一件产物,不是收纳文件夹(站点收素材放它的 assets/ 子目录)');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const base = path.basename(from);
|
|
94
|
+
const nextRel = to ? `${to}/${base}` : base;
|
|
95
|
+
if (nextRel === from) return { ok: true, from, to: from, moved: false };
|
|
96
|
+
if (await exists(path.resolve(root, nextRel))) {
|
|
97
|
+
throw new MoveError(409, `「${base}」在那儿已经有一个了`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await fs.rename(absFrom, path.resolve(root, nextRel)); // ①
|
|
101
|
+
const { board } = await renameBoardPaths(pid, [[from, nextRel]]); // ②
|
|
102
|
+
return { ok: true, from, to: nextRel, moved: true, board }; // ③
|
|
103
|
+
}
|