dsh-unity-mcp-bridge 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/DETAILS.md CHANGED
@@ -113,7 +113,7 @@ unity-mcp-cli install-plugin <unity-project> --with-server --server-version 9.3.
113
113
  --server-source "<plugin>\vendor\gamedev-mcp-server-win-x64-9.3.0.zip"
114
114
  ```
115
115
 
116
- `install-plugin` **没有** `--plugin-source`;离线路径是 Unity 官方支持的 `file:` 本地 tarball 依赖。CLI 不会覆盖 `file:` / `git+` / `http:` 依赖(`dist/utils/manifest.js` 的 `nonSemverPrefixes`),所以之后重跑 `install-plugin` 也安全。
116
+ `install-plugin` **没有** `--plugin-source`;离线路径是 Unity 官方支持的 `file:` 本地 tarball 依赖。`offline-package.mjs install` 默认(`--vendor project`)把 tarball 复制到工程根 `UnityMcpOffline/`,写相对 `Packages` 的依赖(`file:../UnityMcpOffline/…`,profile 重建/换机器都不断);`--vendor bridge` 保留旧的插件目录绝对路径。CLI 不会覆盖 `file:` / `git+` / `http:` 依赖(`dist/utils/manifest.js` 的 `nonSemverPrefixes`),所以之后重跑 `install-plugin` 也安全。
117
117
 
118
118
  ---
119
119
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-unity-mcp-bridge",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Opt-in Unity-MCP bridge for DeepSeek Harness: dormant by default, /unity-mcp-tools activates it, ships the 1.6.0 command catalog plus /unity-mcp-install, /unity-mcp-delete, /unity-mcp-check, /unity-mcp-update and /unity-mcp-update-tools.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -36,11 +36,21 @@ $projects
36
36
  $manifest = Join-Path $proj 'Packages\manifest.json'
37
37
  Select-String -Path $manifest -Pattern 'com\.ivanmurzak\.unity\.mcp'
38
38
  Get-ChildItem (Join-Path $proj 'Library\PackageCache') -Directory -Filter 'com.ivanmurzak.unity.mcp@*' -ErrorAction SilentlyContinue
39
+
40
+ # 若依赖是 file:,解析目标是否存在(Unity 相对 Packages 目录;绝对路径直接用)
41
+ $dep = (Get-Content $manifest -Raw | ConvertFrom-Json).dependencies.'com.ivanmurzak.unity.mcp'
42
+ if ($dep -like 'file:*') {
43
+ $rel = $dep.Substring(5)
44
+ $target = if ($rel -match '^[A-Za-z]:' -or $rel.StartsWith('/')) { $rel } else { Join-Path (Split-Path $manifest) $rel }
45
+ "dep target: $target exists=$(Test-Path $target) points-into-node_modules=$($target -like '*node_modules*')"
46
+ }
39
47
  ```
40
48
 
41
49
  - manifest 依赖 **和** `Library\PackageCache` 里都有 → 已装(依赖可能是 `file:` 本地包,属正常)。
42
50
  - 只有 manifest、没有 `PackageCache` → Unity 还没解析包:让 Unity 打开/重新加载项目一次再看。
43
51
  - 两处都没有 → **未装**:报告后交给 `/unity-mcp-install`,不要在这里装。
52
+ - `file:` 目标 **不存在** → Unity 解析必失败:重跑 `/unity-mcp-install`(现在默认 `--vendor project`,会复制 tarball 到工程 `UnityMcpOffline/` 并写相对路径)。
53
+ - `file:` 目标指向 **`node_modules`**(例如旧版写进 profile 的绝对路径)→ 现在能用,但 profile 重建/重装即失效:建议重跑 `/unity-mcp-install` 迁移到工程内相对路径。
44
54
 
45
55
  ## 2. 端口是不是 20000、模式是不是 Custom(不对就改)
46
56
 
@@ -229,6 +239,8 @@ unity-mcp-cli wait-for-ready $proj # 最长 120s
229
239
  | 改了 host / 模式但服务器没起 | 插件静态构造只在域重载时读 `connectionMode`,只改 JSON 不生效 | 同上,重启编辑器后再看 `Start listening on port 20000` |
230
240
  | 全新工程的可见工具是 8 个(默认 7 + 开关) | `tools: []` 时 `--disable-all-tools` 是空操作(CLI 1.6.0 只改已有条目) | 按第 4 步用 `--disable-tools <显式名单>` 物化条目,再 `--enable-tools tool-set-enabled-state` |
231
241
  | manifest 里有包但连不上 | Unity 还没解析包 | 用 Unity 打开项目一次 |
242
+ | `file:` 依赖找不到目标 | tarball 被删/工程从别的机器拷来但没带 `UnityMcpOffline/`,或旧版指向了已重建的 profile 路径 | 重跑 `/unity-mcp-install`(默认 `--vendor project`,复制 tarball 到工程并写相对路径) |
243
+ | `file:` 路径里含 `node_modules` | 旧版 `install` 写的是插件目录绝对路径,生命周期绑在 profile 上 | 能连就先不动;建议重跑 `/unity-mcp-install` 迁移到 `UnityMcpOffline/` 相对路径 |
232
244
  | 404 / 405 | 端点路径不对(`/mcp` ↔ `/p/<token>`) | 以 `http://localhost:20000/mcp` 为准;需要 token 路径时再补 |
233
245
  | 401 / 403 | token 缺失或过期 | 用项目配置里的 token;或 `bootstrap-local --url ... --token ...` 重新固定 |
234
246
  | 工具全被隐藏(含 `tool-set-enabled-state`) | 被 `--disable-all-tools` 或历史上写死的配置隐藏 | 单独跑一条 `unity-mcp-cli configure $proj --enable-tools tool-set-enabled-state` 救回 |
@@ -102,6 +102,10 @@ if (Test-Path $cfgPath) { Remove-Item $cfgPath -Force }
102
102
  $serverDir = Join-Path $proj 'Library\mcp-server'
103
103
  if (Test-Path $serverDir) { Remove-Item $serverDir -Recurse -Force }
104
104
 
105
+ # 新版 install 默认复制进工程的离线 tarball(UnityMcpOffline/,相对依赖用)
106
+ $offlineDir = Join-Path $proj 'UnityMcpOffline'
107
+ if (Test-Path $offlineDir) { Remove-Item $offlineDir -Recurse -Force }
108
+
105
109
  # 已解析的包缓存(Unity 下次打开也会自动清;编辑器已关闭时手动删更干净)
106
110
  Get-ChildItem (Join-Path $proj 'Library\PackageCache') -Directory -Filter 'com.ivanmurzak.unity.mcp*' -ErrorAction SilentlyContinue |
107
111
  ForEach-Object { Remove-Item $_.FullName -Recurse -Force }
@@ -130,7 +134,7 @@ Test-Path (Join-Path $proj 'Library\mcp-server')
130
134
  |---|---|
131
135
  | 定位到的工程 | `$proj` |
132
136
  | 已删除的包 | `com.ivanmurzak.unity.mcp` + 扩展列表(或「未安装,无需卸载」) |
133
- | 清理的配置/残留 | `UserSettings\AI-Game-Developer-Config.json`、`Library\mcp-server`、PackageCache 条目 |
137
+ | 清理的配置/残留 | `UserSettings\AI-Game-Developer-Config.json`、`Library\mcp-server`、`UnityMcpOffline\`、PackageCache 条目 |
134
138
  | 备份 | `Packages\manifest.json.bak-<stamp>`、`AI-Game-Developer-Config.json.bak-<stamp>` |
135
139
  | 未动的 | DSH 桥接插件、CLI 托管 Server、其他工程、`.kilocode`/agent 配置 |
136
140
  | 下一步 | 用 Unity 打开工程让其重新解析;之后 `/unity-mcp-check` 应报告「未装」 |
@@ -165,28 +165,33 @@ ERROR: fetch failed
165
165
 
166
166
  这是**兜底而非首选**:绕过了官方 zip 的哈希校验,必须向用户说明来源与版本。
167
167
 
168
- ### 5.2 离线本地包(插件自带 `vendor/`;给「新的 Unity 工程」用时优先走这条)
168
+ ### 5.2 离线本地包(插件自带 `vendor/`;现在默认写工程内相对路径)
169
169
 
170
170
  插件目录下的 `vendor/` 是一份离线包缓存:`vendor/README.md`(版本/大小/SHA256/来源)+ `vendor/manifest.json`(机器可读)。当前内容:插件 `com.ivanmurzak.unity.mcp@1.6.0`(960,306 B,SHA256 `13fd2579…c5178b`)与 Server `9.3.0` win-x64(44,088,479 B,SHA256 `53a79b42…e01dc2f`,已对官方 `SHA256SUMS` 校验通过)。
171
171
 
172
172
  ```powershell
173
- # 1) 把工程依赖指向本地 tarball(先 --dry-run 看 diff;正式跑会自动备份 manifest)
173
+ # 1) 先看会复制什么、写什么依赖(不落盘)
174
+ node "<bridge>\tools\offline-package.mjs" install --project $proj --dry-run
175
+
176
+ # 2) 复制 tarball 到工程根并写相对依赖(自动备份 manifest)
174
177
  node "<bridge>\tools\offline-package.mjs" install --project $proj
175
178
 
176
- # 2) Server 也从 vendor 的 zip 装进 CLI 托管目录(不联网)
179
+ # 3) Server 从 vendor 的 zip 装进 CLI 托管目录(不联网)
177
180
  unity-mcp-cli install-plugin $proj --with-server --server-version 9.3.0 `
178
181
  --server-source "<bridge>\vendor\gamedev-mcp-server-win-x64-9.3.0.zip"
179
182
  ```
180
183
 
181
184
  要点(都实测过):
182
185
 
183
- - `install-plugin` **没有** `--plugin-source`。离线靠的是 Unity 官方支持的本地 tarball 依赖:`"com.ivanmurzak.unity.mcp": "file:D:/…/vendor/com.ivanmurzak.unity.mcp-1.6.0.tgz"`(Unity 手册 *Local folder or tarball paths*:`file:` 允许绝对路径,Windows 用正斜杠)。
184
- - 之后再跑 `unity-mcp-cli install-plugin` **不会**把它改回版本号:CLI 的 `shouldUpdateVersion` 对 `file:` / `git+` / `http:` 开头的依赖直接跳过(`dist/utils/manifest.js` 的 `nonSemverPrefixes`)。实测那行保持 `file:` 不变。
186
+ - **默认 `--vendor project`**:把 tarball 复制到 `<工程>\UnityMcpOffline\`,manifest 写成 `"com.ivanmurzak.unity.mcp": "file:../UnityMcpOffline/com.ivanmurzak.unity.mcp-1.6.0.tgz"`。Unity 的 `file:` 相对路径**相对 `Packages` 文件夹**解析([Unity 手册](https://docs.unity3d.com/2022.3/Documentation/Manual/upm-localpath.html)),所以 `../UnityMcpOffline/…` 就是工程根下的目录。这样 **profile 重建/删除、工程拷到别的机器都不会断**;tarball 约 1 MB,可以一起提交到版本库。
187
+ - 旧工程(依赖指向 `node_modules\...\vendor\` 绝对路径)**重跑一次 `install` 就会迁移**到工程内相对路径;manifest 先备份。想保留旧行为用 `--vendor bridge`(绝对路径,profile 重建后需要重跑 install)。
188
+ - `install` 检测工程内 tarball 的 SHA256;已存在且一致就跳过复制,不一致(换了插件版本)会覆盖。
189
+ - `install-plugin` **没有** `--plugin-source`。离线靠的就是上面的本地 tarball 依赖;之后重跑 `unity-mcp-cli install-plugin` **不会**把它改回版本号(`dist/utils/manifest.js` 的 `nonSemverPrefixes` 跳过 `file:` / `git+` / `http:`)。
185
190
  - 目标版本从哪来:`vendor/manifest.json` 的 `plugin.pinnedServerVersion` —— 脚本从 tarball 里的 `Editor/Scripts/McpServerManager.cs` 读 `ServerVersion` 常量(1.6.0 钉住 **9.3.0**)。别拿「Server 上游最新」当目标。
186
191
  - 完全离线还剩一个小尾巴:插件的传递依赖 `extensions.unity.playerprefsex` 要 Unity 的 Package Manager 从 OpenUPM 解析(实测 OpenUPM 直连可达)。彻底断网的机器要么预先让 Unity 解析过一次,要么接受首次解析需要联网。
187
- - `file:` 依赖**不会自动升级**:升级 = 重新 `fetch` 刷新本地离线包 + 重新 `install` 进工程(见 `/unity-mcp-update` 的 4.2 / 4.3),换版本后照例重生成命令目录。
192
+ - `file:` 依赖**不会自动升级**:升级 = 重新 `fetch` 刷新插件目录的离线包 + 重新 `install` 进工程(见 `/unity-mcp-update` 的 4.2 / 4.3),换版本后照例重生成命令目录。
188
193
  - 换平台:`node "<bridge>\tools\offline-package.mjs" fetch --rid osx-arm64` 先补对应 zip,再 `--server-source` 指过去。
189
- - **profile 里的安装副本常常缺 zip**:40 MB 级二进制不进 git/npm,`dsh plugin add file:…` 装出来的 `node_modules\dsh-unity-mcp-bridge\vendor\` 通常只有 tarball + manifest。`install` 会明确告诉你缺不缺;缺就先 `fetch`(代理优先)补齐再跑 `--server-source`,不要拿旧版本的 zip 顶。
194
+ - **profile 里的安装副本常常缺 zip**:40 MB 级二进制不进 git/npm,`dsh plugin add …` 装出来的 `node_modules\dsh-unity-mcp-bridge\vendor\` 通常只有 tarball + manifest。`install` 会明确告诉你缺不缺;缺就先 `fetch`(代理优先)补齐再跑 `--server-source`,不要拿旧版本的 zip 顶。
190
195
 
191
196
  ## 6. 验证与下一步
192
197
 
@@ -139,7 +139,7 @@ node "<bridge>\tools\offline-package.mjs" fetch --plugin-version <latest>
139
139
  # 先看会改什么,不落盘
140
140
  node "<bridge>\tools\offline-package.mjs" install --project $proj --dry-run
141
141
 
142
- # 落盘(自动备份 Packages\manifest.json,写官方支持的 file: 本地依赖)
142
+ # 落盘(自动备份 Packages\manifest.json;默认把 tarball 复制到工程 UnityMcpOffline/ 并写相对依赖)
143
143
  node "<bridge>\tools\offline-package.mjs" install --project $proj
144
144
 
145
145
  # Server 从 vendor 的 zip 装进 CLI 托管目录(不联网)
@@ -5,7 +5,7 @@
5
5
  * 三个子命令:
6
6
  * check 比对「上游最新 / 本地 vendor 离线包 / 项目已装」三层版本,给出该不该更新
7
7
  * fetch 下载插件 tarball + GameDev-MCP-Server zip 到 vendor/,校验 SHA256,写 vendor/manifest.json
8
- * install 把一个 Unity 工程的 Packages/manifest.json 指向 vendor/ 里的本地 tarball(file: 依赖)
8
+ * install 把 tarball 复制到 <工程>/UnityMcpOffline/ 并写相对 Packages 的 file: 依赖(--vendor bridge 则写插件目录绝对路径)
9
9
  *
10
10
  * 网络策略(实测):
11
11
  * - OpenUPM / npm registry 直连可达 → 插件 tarball 与版本查询优先直连,失败再走代理。
@@ -38,7 +38,7 @@ function parseArgs(argv) {
38
38
  for (let i = 0; i < argv.length; i++) {
39
39
  const a = argv[i];
40
40
  if (a === '--no-proxy') { out.noProxy = true; continue; }
41
- const flag = ['proxy', 'project', 'plugin-version', 'server-version', 'rid', 'cli-version', 'out'].find(f => a === `--${f}`);
41
+ const flag = ['proxy', 'project', 'plugin-version', 'server-version', 'rid', 'cli-version', 'out', 'vendor'].find(f => a === `--${f}`);
42
42
  if (flag) { out[flag] = argv[++i]; continue; }
43
43
  if (a === '--json') { out.json = true; continue; }
44
44
  if (a === '--force') { out.force = true; continue; }
@@ -53,7 +53,10 @@ function usage() {
53
53
  console.log(`用法:
54
54
  node tools/offline-package.mjs check [--project <Unity工程>] [--cli-version <v>] [--json]
55
55
  node tools/offline-package.mjs fetch [--plugin-version <v>] [--server-version <v>] [--rid <rid>] [--proxy <url>] [--force]
56
- node tools/offline-package.mjs install [--project <Unity工程>] [--dry-run]
56
+ node tools/offline-package.mjs install [--project <Unity工程>] [--vendor project|bridge] [--dry-run]
57
+
58
+ --vendor project(默认)把 tarball 复制到 <工程>/UnityMcpOffline/ 并写相对路径(可移植);
59
+ --vendor bridge 沿用旧行为,写 <插件目录>/vendor/ 的绝对 file: 路径。
57
60
 
58
61
  vendor/ 目录: ${VENDOR_DIR}`);
59
62
  process.exit(0);
@@ -394,20 +397,40 @@ function cmdInstall(args) {
394
397
  const actual = sha256(tgz);
395
398
  if (vendor.plugin.sha256 && actual !== vendor.plugin.sha256) fail(`离线包 sha256 与 manifest 不符: ${actual}`);
396
399
 
400
+ const mode = args.vendor ?? 'project';
401
+ if (mode !== 'project' && mode !== 'bridge') fail(`--vendor 只能是 project 或 bridge(收到 ${mode})`);
402
+
397
403
  const proj = args.project || fail('需要 --project <Unity工程>');
398
404
  const manifestPath = path.join(proj, 'Packages', 'manifest.json');
399
405
  if (!fs.existsSync(path.join(proj, 'ProjectSettings', 'ProjectVersion.txt'))) fail(`不是 Unity 工程: ${proj}`);
400
406
  if (!fs.existsSync(manifestPath)) fail(`缺 ${manifestPath}`);
401
407
 
402
- // Unity 的 file: 依赖用正斜杠;绝对路径直接跟在 file: 后面(Unity 手册:Local folder or tarball paths)
403
- const spec = `file:${tgz.replace(/\\/g, '/')}`;
408
+ // 默认把 tarball 复制进工程,依赖写相对 Packages 的路径(Unity 手册:本地 tarball 路径相对 Packages 文件夹)。
409
+ // 这样 profile 重建、工程换机器都不会断;--vendor bridge 保留旧的绝对路径行为。
410
+ const offlineDir = path.join(proj, 'UnityMcpOffline');
411
+ const offlineTgz = path.join(offlineDir, path.basename(tgz));
412
+ const spec = mode === 'project'
413
+ ? `file:../UnityMcpOffline/${path.basename(tgz)}`
414
+ : `file:${tgz.replace(/\\/g, '/')}`;
415
+
404
416
  const mj = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
405
417
  mj.dependencies ??= {};
406
418
  const before = mj.dependencies[PKG_ID] ?? null;
407
419
  if (args.dryRun) {
408
- console.log(`dry-run: ${PKG_ID}: ${before ?? '(无)'} → ${spec}`);
420
+ console.log(`dry-run (--vendor ${mode}): ${PKG_ID}: ${before ?? '(无)'} → ${spec}`);
421
+ if (mode === 'project') console.log(` 复制: ${tgz} → ${offlineTgz}`);
409
422
  return;
410
423
  }
424
+
425
+ if (mode === 'project') {
426
+ fs.mkdirSync(offlineDir, { recursive: true });
427
+ const same = fs.existsSync(offlineTgz) && sha256(offlineTgz) === vendor.plugin.sha256;
428
+ if (!same) fs.copyFileSync(tgz, offlineTgz);
429
+ const copied = sha256(offlineTgz);
430
+ if (vendor.plugin.sha256 && copied !== vendor.plugin.sha256) fail(`工程内离线包 sha256 与 manifest 不符: ${copied}`);
431
+ console.log(`已复制离线包到工程: ${offlineTgz}${same ? '(已存在且哈希一致,跳过复制)' : ''}`);
432
+ }
433
+
411
434
  // 与 unity-mcp-cli 一致:补 OpenUPM 作用域注册表(插件的传递依赖 extensions.unity.* 要它)
412
435
  mj.scopedRegistries ??= [];
413
436
  let reg = mj.scopedRegistries.find(r => r.name === 'package.openupm.com');
@@ -423,11 +446,17 @@ function cmdInstall(args) {
423
446
  console.log(` ${PKG_ID}: ${before ?? '(无)'} → ${spec}`);
424
447
  console.log(` 备份: ${bak}`);
425
448
  console.log(` 插件版本 ${vendor.plugin.version}(钉住 Server ${vendor.plugin.pinnedServerVersion})`);
449
+ if (mode === 'project') {
450
+ console.log(' 提示: 依赖是相对 Packages 的路径;profile 重建/删除不会断,工程拷到别的机器也能离线解析。');
451
+ console.log(' tarball 在工程根 UnityMcpOffline/ 下(约 1 MB),可一起提交到版本库。');
452
+ } else {
453
+ console.log(' 提示: 依赖是插件目录的绝对路径;重建 profile / 移动工程后会失效,需要重跑 install。');
454
+ }
426
455
  const serverZip = vendor.server?.file ? path.join(VENDOR_DIR, vendor.server.file) : null;
427
456
  if (serverZip && fs.existsSync(serverZip)) {
428
457
  console.log(`下一步:unity-mcp-cli install-plugin ${proj} --with-server --server-version ${vendor.server.version} --server-source "${serverZip}"`);
429
458
  } else {
430
- console.log(`⚠ 本地缺 Server zip(${vendor.server?.file ?? '未记录'};43 MB 级二进制不进 git/npm)。先补齐再装:`);
459
+ console.log(`⚠ 本地缺 Server zip(${vendor.server?.file ?? '未记录'};40 MB 级二进制不进 git/npm)。先补齐再装:`);
431
460
  console.log(` node tools/offline-package.mjs fetch --rid ${vendor.server?.rid ?? defaultRid()} # 代理优先`);
432
461
  }
433
462
  }
package/vendor/README.md CHANGED
@@ -33,11 +33,11 @@ node "<插件目录>\tools\offline-package.mjs" fetch
33
33
  ## 离线装进 Unity 工程
34
34
 
35
35
  ```powershell
36
- node "<插件目录>\tools\offline-package.mjs" install --project <Unity工程> # 写 file: 依赖 + 备份 manifest
36
+ node "<插件目录>\tools\offline-package.mjs" install --project <Unity工程> # 复制 tarball 到工程 UnityMcpOffline/ + 写相对依赖 + 备份 manifest
37
37
  unity-mcp-cli install-plugin <Unity工程> --with-server --server-version 9.3.0 --server-source "<插件目录>\vendor\gamedev-mcp-server-win-x64-9.3.0.zip"
38
38
  ```
39
39
 
40
- `install` 写的是 Unity 官方支持的本地 tarball 依赖(`"com.ivanmurzak.unity.mcp": "file:D:/…/vendor/com.ivanmurzak.unity.mcp-1.6.0.tgz"`,见 Unity 手册 *Local folder or tarball paths*)。`unity-mcp-cli` **没有** `--plugin-source`,但它对 `file:` 这类非 semver 依赖**不会覆盖**(`dist/utils/manifest.js` 的 `nonSemverPrefixes`),所以两条命令可以并存。
40
+ `install` 默认(`--vendor project`)把 tarball 复制到工程根的 `UnityMcpOffline/`,并写成相对 `Packages` 的依赖 `"com.ivanmurzak.unity.mcp": "file:../UnityMcpOffline/com.ivanmurzak.unity.mcp-1.6.0.tgz"`(见 Unity 手册 *Local folder or tarball paths*)。这样 profile 重建、工程换机器都不会断;旧行为用 `--vendor bridge` 写插件目录的绝对路径。`unity-mcp-cli` **没有** `--plugin-source`,但它对 `file:` 这类非 semver 依赖**不会覆盖**(`dist/utils/manifest.js` 的 `nonSemverPrefixes`),所以两条命令可以并存。
41
41
 
42
42
  ## 进 git 的边界
43
43