tanmi-dock 0.10.0-beta → 1.0.4

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.
Files changed (132) hide show
  1. package/README.md +26 -5
  2. package/dist/commands/calibrate.d.ts +10 -0
  3. package/dist/commands/calibrate.d.ts.map +1 -0
  4. package/dist/commands/calibrate.js +158 -0
  5. package/dist/commands/calibrate.js.map +1 -0
  6. package/dist/commands/check.d.ts +45 -1
  7. package/dist/commands/check.d.ts.map +1 -1
  8. package/dist/commands/check.js +322 -101
  9. package/dist/commands/check.js.map +1 -1
  10. package/dist/commands/clean.d.ts +2 -6
  11. package/dist/commands/clean.d.ts.map +1 -1
  12. package/dist/commands/clean.js +224 -209
  13. package/dist/commands/clean.js.map +1 -1
  14. package/dist/commands/dashboard.d.ts.map +1 -1
  15. package/dist/commands/dashboard.js +56 -28
  16. package/dist/commands/dashboard.js.map +1 -1
  17. package/dist/commands/link.d.ts +8 -7
  18. package/dist/commands/link.d.ts.map +1 -1
  19. package/dist/commands/link.js +717 -221
  20. package/dist/commands/link.js.map +1 -1
  21. package/dist/commands/migrate.d.ts +11 -0
  22. package/dist/commands/migrate.d.ts.map +1 -1
  23. package/dist/commands/migrate.js +277 -57
  24. package/dist/commands/migrate.js.map +1 -1
  25. package/dist/commands/reset.d.ts.map +1 -1
  26. package/dist/commands/reset.js +65 -41
  27. package/dist/commands/reset.js.map +1 -1
  28. package/dist/commands/status.d.ts +4 -0
  29. package/dist/commands/status.d.ts.map +1 -1
  30. package/dist/commands/status.js +182 -40
  31. package/dist/commands/status.js.map +1 -1
  32. package/dist/commands/unavailable.d.ts.map +1 -1
  33. package/dist/commands/unavailable.js +7 -2
  34. package/dist/commands/unavailable.js.map +1 -1
  35. package/dist/commands/unlink.d.ts.map +1 -1
  36. package/dist/commands/unlink.js +43 -39
  37. package/dist/commands/unlink.js.map +1 -1
  38. package/dist/core/codepac.d.ts +44 -0
  39. package/dist/core/codepac.d.ts.map +1 -1
  40. package/dist/core/codepac.js +287 -5
  41. package/dist/core/codepac.js.map +1 -1
  42. package/dist/core/config.d.ts +3 -1
  43. package/dist/core/config.d.ts.map +1 -1
  44. package/dist/core/config.js +7 -3
  45. package/dist/core/config.js.map +1 -1
  46. package/dist/core/linker.d.ts +7 -0
  47. package/dist/core/linker.d.ts.map +1 -1
  48. package/dist/core/linker.js +47 -0
  49. package/dist/core/linker.js.map +1 -1
  50. package/dist/core/parser.d.ts +30 -5
  51. package/dist/core/parser.d.ts.map +1 -1
  52. package/dist/core/parser.js +376 -57
  53. package/dist/core/parser.js.map +1 -1
  54. package/dist/core/registry.d.ts +11 -29
  55. package/dist/core/registry.d.ts.map +1 -1
  56. package/dist/core/registry.js +46 -113
  57. package/dist/core/registry.js.map +1 -1
  58. package/dist/core/space/calibration-executor.d.ts +16 -0
  59. package/dist/core/space/calibration-executor.d.ts.map +1 -0
  60. package/dist/core/space/calibration-executor.js +142 -0
  61. package/dist/core/space/calibration-executor.js.map +1 -0
  62. package/dist/core/space/calibration-planner.d.ts +5 -0
  63. package/dist/core/space/calibration-planner.d.ts.map +1 -0
  64. package/dist/core/space/calibration-planner.js +157 -0
  65. package/dist/core/space/calibration-planner.js.map +1 -0
  66. package/dist/core/space/cleanup-executor.d.ts +15 -0
  67. package/dist/core/space/cleanup-executor.d.ts.map +1 -0
  68. package/dist/core/space/cleanup-executor.js +188 -0
  69. package/dist/core/space/cleanup-executor.js.map +1 -0
  70. package/dist/core/space/cleanup-journal.d.ts +43 -0
  71. package/dist/core/space/cleanup-journal.d.ts.map +1 -0
  72. package/dist/core/space/cleanup-journal.js +96 -0
  73. package/dist/core/space/cleanup-journal.js.map +1 -0
  74. package/dist/core/space/cleanup-planner.d.ts +14 -0
  75. package/dist/core/space/cleanup-planner.d.ts.map +1 -0
  76. package/dist/core/space/cleanup-planner.js +166 -0
  77. package/dist/core/space/cleanup-planner.js.map +1 -0
  78. package/dist/core/space/cleanup-selection.d.ts +10 -0
  79. package/dist/core/space/cleanup-selection.d.ts.map +1 -0
  80. package/dist/core/space/cleanup-selection.js +84 -0
  81. package/dist/core/space/cleanup-selection.js.map +1 -0
  82. package/dist/core/space/estimator.d.ts +11 -0
  83. package/dist/core/space/estimator.d.ts.map +1 -0
  84. package/dist/core/space/estimator.js +142 -0
  85. package/dist/core/space/estimator.js.map +1 -0
  86. package/dist/core/space/inventory.d.ts +15 -0
  87. package/dist/core/space/inventory.d.ts.map +1 -0
  88. package/dist/core/space/inventory.js +587 -0
  89. package/dist/core/space/inventory.js.map +1 -0
  90. package/dist/core/space/meter.d.ts +5 -0
  91. package/dist/core/space/meter.d.ts.map +1 -0
  92. package/dist/core/space/meter.js +87 -0
  93. package/dist/core/space/meter.js.map +1 -0
  94. package/dist/core/space/migration-journal.d.ts +31 -0
  95. package/dist/core/space/migration-journal.d.ts.map +1 -0
  96. package/dist/core/space/migration-journal.js +121 -0
  97. package/dist/core/space/migration-journal.js.map +1 -0
  98. package/dist/core/space/service.d.ts +26 -0
  99. package/dist/core/space/service.d.ts.map +1 -0
  100. package/dist/core/space/service.js +54 -0
  101. package/dist/core/space/service.js.map +1 -0
  102. package/dist/core/space/types.d.ts +241 -0
  103. package/dist/core/space/types.d.ts.map +1 -0
  104. package/dist/core/space/types.js +13 -0
  105. package/dist/core/space/types.js.map +1 -0
  106. package/dist/core/store.d.ts +2 -0
  107. package/dist/core/store.d.ts.map +1 -1
  108. package/dist/core/store.js +44 -12
  109. package/dist/core/store.js.map +1 -1
  110. package/dist/index.js +2 -0
  111. package/dist/index.js.map +1 -1
  112. package/dist/types/index.d.ts +63 -10
  113. package/dist/types/index.d.ts.map +1 -1
  114. package/dist/types/index.js.map +1 -1
  115. package/dist/utils/atomic-file.d.ts +6 -0
  116. package/dist/utils/atomic-file.d.ts.map +1 -0
  117. package/dist/utils/atomic-file.js +37 -0
  118. package/dist/utils/atomic-file.js.map +1 -0
  119. package/dist/utils/fs-utils.d.ts +18 -1
  120. package/dist/utils/fs-utils.d.ts.map +1 -1
  121. package/dist/utils/fs-utils.js +150 -2
  122. package/dist/utils/fs-utils.js.map +1 -1
  123. package/dist/utils/git.d.ts +20 -1
  124. package/dist/utils/git.d.ts.map +1 -1
  125. package/dist/utils/git.js +181 -4
  126. package/dist/utils/git.js.map +1 -1
  127. package/dist/utils/logger.d.ts +1 -1
  128. package/dist/utils/logger.js +2 -2
  129. package/dist/utils/prompt.d.ts.map +1 -1
  130. package/dist/utils/prompt.js +4 -4
  131. package/dist/utils/prompt.js.map +1 -1
  132. package/package.json +1 -1
@@ -2,17 +2,19 @@
2
2
  * migrate 命令 - 迁移 Store 位置
3
3
  */
4
4
  import fs from 'fs/promises';
5
+ import os from 'os';
5
6
  import path from 'path';
6
7
  import { Command } from 'commander';
7
8
  import { ensureInitialized } from '../core/guard.js';
8
9
  import * as config from '../core/config.js';
9
10
  import { getRegistry } from '../core/registry.js';
10
- import * as linker from '../core/linker.js';
11
- import { expandHome, shrinkHome, isPathSafe } from '../core/platform.js';
11
+ import { expandHome, shrinkHome, isPathSafe, pathsEqual } from '../core/platform.js';
12
12
  import { formatSize, getFreeSpace } from '../utils/disk.js';
13
- import { copyDirWithProgress } from '../utils/fs-utils.js';
13
+ import { copyDirWithProgress, rewriteInternalSymlinks, verifyCopiedDirectory, } from '../utils/fs-utils.js';
14
+ import { SpaceService, estimateMigrationSpace } from '../core/space/service.js';
15
+ import { MigrationJournal } from '../core/space/migration-journal.js';
14
16
  import * as store from '../core/store.js';
15
- import { info, success, warn, error, title, blank, separator, progressBar, } from '../utils/logger.js';
17
+ import { info, success, warn, error, title, blank, separator, progressBar, debug, } from '../utils/logger.js';
16
18
  import { withGlobalLock } from '../utils/global-lock.js';
17
19
  /**
18
20
  * 创建 migrate 命令
@@ -34,11 +36,252 @@ export function createMigrateCommand() {
34
36
  }
35
37
  });
36
38
  }
39
+ function isPathWithin(root, target) {
40
+ const relative = path.relative(path.resolve(root), path.resolve(target));
41
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
42
+ }
43
+ function isCatastrophicMigrationPath(targetPath) {
44
+ const resolved = path.resolve(targetPath);
45
+ return resolved === path.parse(resolved).root || pathsEqual(resolved, os.homedir());
46
+ }
47
+ function migrationCopyOptions(sourcePath, destinationPath) {
48
+ return {
49
+ preserveSymlinks: true,
50
+ rewriteInternalAbsoluteSymlinks: {
51
+ sourceRoot: sourcePath,
52
+ destinationRoot: destinationPath,
53
+ },
54
+ };
55
+ }
56
+ async function destinationState(sourcePath, destinationPath) {
57
+ let entries;
58
+ try {
59
+ entries = await fs.readdir(destinationPath);
60
+ }
61
+ catch (err) {
62
+ if (err.code === 'ENOENT')
63
+ return 'missing';
64
+ throw err;
65
+ }
66
+ if (entries.length === 0)
67
+ return 'empty';
68
+ const verification = await verifyCopiedDirectory(sourcePath, destinationPath, migrationCopyOptions(sourcePath, destinationPath));
69
+ return verification.valid ? 'valid' : 'conflict';
70
+ }
71
+ async function verifySourceIdentity(data) {
72
+ let stat;
73
+ try {
74
+ stat = await fs.stat(data.sourcePath);
75
+ }
76
+ catch (err) {
77
+ if (err.code === 'ENOENT')
78
+ return 'missing';
79
+ throw err;
80
+ }
81
+ if (stat.dev !== data.sourceDevice || stat.ino !== data.sourceInode) {
82
+ throw new Error(`旧 Store 路径内容来源已变化,拒绝继续迁移: ${data.sourcePath}`);
83
+ }
84
+ return 'valid';
85
+ }
86
+ async function verifyDestinationAgainstSource(data, category) {
87
+ const verification = await verifyCopiedDirectory(data.sourcePath, data.destinationPath, migrationCopyOptions(data.sourcePath, data.destinationPath));
88
+ debug(`[migrate] 恢复目标验证: id=${data.id}, category=${category}, valid=${verification.valid}, error=${verification.error ?? 'none'}`);
89
+ if (!verification.valid) {
90
+ throw new Error(`迁移目标验证失败,已保留新旧目录: ${verification.error}`);
91
+ }
92
+ }
93
+ async function verifyDestinationIdentity(data, category) {
94
+ if (!Number.isFinite(data.destinationDevice) || !Number.isFinite(data.destinationInode)) {
95
+ throw new Error(`迁移目标身份缺失,无法从 ${data.phase} 阶段恢复`);
96
+ }
97
+ const stat = await fs.stat(data.destinationPath).catch(() => null);
98
+ const valid = Boolean(stat?.isDirectory() && stat.dev === data.destinationDevice && stat.ino === data.destinationInode);
99
+ debug(`[migrate] 目标身份验证: id=${data.id}, category=${category}, valid=${valid}, expectedDevice=${data.destinationDevice}, expectedInode=${data.destinationInode}, actualDevice=${stat?.dev ?? 'missing'}, actualInode=${stat?.ino ?? 'missing'}`);
100
+ if (!valid) {
101
+ throw new Error(`迁移目标目录身份已变化,已保留迁移日志: ${data.destinationPath}`);
102
+ }
103
+ }
104
+ async function verifyConfiguredDestination(data, category) {
105
+ const configuredStorePath = await config.getStorePath();
106
+ const valid = Boolean(configuredStorePath && pathsEqual(configuredStorePath, data.destinationPath));
107
+ debug(`[migrate] 目标配置验证: id=${data.id}, category=${category}, valid=${valid}, configured=${configuredStorePath ?? 'missing'}, destination=${data.destinationPath}`);
108
+ if (!valid) {
109
+ throw new Error(`迁移配置未指向目标 Store,已保留迁移日志: ${configuredStorePath ?? 'missing'}`);
110
+ }
111
+ }
112
+ async function executeMigrationJournal(journal, registry, knownRequiredSize) {
113
+ let data = journal.getData();
114
+ const { sourcePath, destinationPath, stagingPath } = data;
115
+ const destinationParent = path.dirname(destinationPath);
116
+ const expectedStagingPath = path.join(destinationParent, `.${path.basename(destinationPath)}.tanmi-dock-migrate-${data.id}`);
117
+ if (isCatastrophicMigrationPath(sourcePath) || isCatastrophicMigrationPath(destinationPath)) {
118
+ throw new Error('迁移日志不能指向文件系统根目录或用户主目录');
119
+ }
120
+ const configuredStorePath = await config.getStorePath();
121
+ if (!configuredStorePath ||
122
+ (!pathsEqual(configuredStorePath, sourcePath) &&
123
+ !pathsEqual(configuredStorePath, destinationPath))) {
124
+ throw new Error(`迁移日志与当前 Store 配置不一致: configured=${configuredStorePath ?? 'missing'}, source=${sourcePath}, destination=${destinationPath}`);
125
+ }
126
+ if (path.resolve(stagingPath) !== path.resolve(expectedStagingPath) ||
127
+ path.dirname(path.resolve(stagingPath)) !== path.resolve(destinationParent)) {
128
+ throw new Error(`迁移暂存目录无效: ${stagingPath}`);
129
+ }
130
+ if (isPathWithin(sourcePath, destinationPath) || isPathWithin(destinationPath, sourcePath)) {
131
+ throw new Error('迁移日志中的新旧 Store 目录互相包含');
132
+ }
133
+ debug(`[migrate] 恢复或执行迁移: id=${data.id}, phase=${data.phase}, source=${sourcePath}, destination=${destinationPath}, staging=${stagingPath}, keepOld=${data.keepOld}`);
134
+ const sourceIdentity = await verifySourceIdentity(data);
135
+ if (sourceIdentity === 'missing') {
136
+ if (data.phase === 'linksUpdated' && !data.keepOld) {
137
+ // 删除源目录后、记录 sourceRemoved 前发生中断时,只能依据持久目标身份继续。
138
+ await verifyDestinationIdentity(data, 'source-missing-after-removal');
139
+ await verifyConfiguredDestination(data, 'source-missing-after-removal');
140
+ await journal.update('sourceRemoved');
141
+ data = journal.getData();
142
+ }
143
+ else if (data.phase !== 'sourceRemoved') {
144
+ if (data.keepOld && data.phase === 'sourcePreserved') {
145
+ throw new Error(`迁移要求保留旧 Store,但旧目录不存在: ${sourcePath}`);
146
+ }
147
+ throw new Error(`旧 Store 不存在,无法从 ${data.phase} 阶段恢复: ${sourcePath}`);
148
+ }
149
+ }
150
+ if (sourceIdentity === 'valid' &&
151
+ ['promoted', 'configSwitched', 'linksUpdating', 'linksUpdated', 'sourcePreserved'].includes(data.phase)) {
152
+ await verifyDestinationIdentity(data, `resume-${data.phase}`);
153
+ await verifyDestinationAgainstSource(data, `resume-${data.phase}`);
154
+ }
155
+ if (data.phase === 'prepared' || data.phase === 'copying') {
156
+ const estimate = knownRequiredSize == null ? await estimateMigrationSpace(sourcePath) : null;
157
+ const requiredSize = knownRequiredSize ?? estimate?.amount?.bytes;
158
+ if (requiredSize == null || estimate?.status === 'unknown') {
159
+ throw new Error(`无法恢复迁移复制: ${estimate?.warnings.join('; ') || '容量未知'}`);
160
+ }
161
+ const freeSpace = await getFreeSpace(destinationParent);
162
+ debug(`[migrate] 恢复复制空间检查: id=${data.id}, required=${requiredSize}, available=${freeSpace}`);
163
+ if (freeSpace < requiredSize) {
164
+ throw new Error(`恢复迁移时目标空间不足: 需要 ${formatSize(requiredSize)},可用 ${formatSize(freeSpace)}`);
165
+ }
166
+ await fs.rm(stagingPath, { recursive: true, force: true });
167
+ await journal.update('copying');
168
+ info('[1/3] 复制文件到迁移暂存目录...');
169
+ await copyDirWithProgress(sourcePath, stagingPath, requiredSize, (copied, total) => progressBar(copied, total), migrationCopyOptions(sourcePath, destinationPath));
170
+ if (requiredSize > 0)
171
+ progressBar(requiredSize, requiredSize);
172
+ const verification = await verifyCopiedDirectory(sourcePath, stagingPath, migrationCopyOptions(sourcePath, destinationPath));
173
+ debug(`[migrate] 暂存复制验证返回: id=${data.id}, valid=${verification.valid}, error=${verification.error ?? 'none'}`);
174
+ if (!verification.valid)
175
+ throw new Error(`迁移复制验证失败: ${verification.error}`);
176
+ await journal.update('copied');
177
+ data = journal.getData();
178
+ }
179
+ if (data.phase === 'copied' || data.phase === 'promoting') {
180
+ await journal.update('promoting');
181
+ const state = await destinationState(sourcePath, destinationPath);
182
+ debug(`[migrate] 目标目录状态: id=${data.id}, state=${state}, destination=${destinationPath}`);
183
+ if (state === 'conflict') {
184
+ throw new Error(`迁移目标包含非本次复制内容,已保留现场: ${destinationPath}`);
185
+ }
186
+ if (state !== 'valid') {
187
+ if (state === 'empty')
188
+ await fs.rmdir(destinationPath);
189
+ await fs.rename(stagingPath, destinationPath);
190
+ }
191
+ else {
192
+ await fs.rm(stagingPath, { recursive: true, force: true });
193
+ }
194
+ const destinationStat = await fs.stat(destinationPath);
195
+ if (!destinationStat.isDirectory()) {
196
+ throw new Error(`迁移目标不是目录,无法记录目标身份: ${destinationPath}`);
197
+ }
198
+ await journal.markPromoted(destinationStat.dev, destinationStat.ino);
199
+ data = journal.getData();
200
+ }
201
+ if (data.phase === 'promoted') {
202
+ await config.setStorePath(destinationPath);
203
+ await journal.update('configSwitched');
204
+ data = journal.getData();
205
+ debug(`[migrate] 配置切换完成: id=${data.id}, store=${destinationPath}`);
206
+ }
207
+ if (data.phase === 'configSwitched' || data.phase === 'linksUpdating') {
208
+ await journal.update('linksUpdating');
209
+ info('[2/3] 更新项目符号链接...');
210
+ let updatedLinkCount = 0;
211
+ for (const project of registry.listProjects()) {
212
+ let updatedCount = 0;
213
+ const visited = new Set();
214
+ for (const dep of project.dependencies) {
215
+ const localPath = path.join(project.path, dep.linkedPath);
216
+ if (visited.has(localPath))
217
+ continue;
218
+ visited.add(localPath);
219
+ const count = await rewriteInternalSymlinks(localPath, sourcePath, destinationPath);
220
+ updatedCount += count;
221
+ updatedLinkCount += count;
222
+ }
223
+ success(` [ok] ${shrinkHome(project.path)} (${updatedCount} 个链接)`);
224
+ }
225
+ await journal.update('linksUpdated');
226
+ data = journal.getData();
227
+ debug(`[migrate] 项目链接更新完成: id=${data.id}, updatedLinks=${updatedLinkCount}`);
228
+ }
229
+ if (data.phase === 'linksUpdated') {
230
+ await verifyDestinationIdentity(data, 'before-source-handling');
231
+ await verifyDestinationAgainstSource(data, 'before-source-removal');
232
+ await verifySourceIdentity(data);
233
+ await verifyConfiguredDestination(data, 'before-source-handling');
234
+ info(data.keepOld ? '[3/3] 保留旧目录' : '[3/3] 清理旧目录...');
235
+ if (data.keepOld) {
236
+ await journal.update('sourcePreserved');
237
+ }
238
+ else {
239
+ await fs.rm(sourcePath, { recursive: true, force: true });
240
+ await journal.update('sourceRemoved');
241
+ }
242
+ data = journal.getData();
243
+ debug(`[migrate] 旧目录处理完成: id=${data.id}, keepOld=${data.keepOld}, source=${sourcePath}`);
244
+ }
245
+ if (data.phase === 'sourcePreserved' || data.phase === 'sourceRemoved') {
246
+ await verifyDestinationIdentity(data, 'before-completion');
247
+ await verifyConfiguredDestination(data, 'before-completion');
248
+ const finalSourceIdentity = await verifySourceIdentity(data);
249
+ if (data.phase === 'sourcePreserved') {
250
+ if (!data.keepOld || finalSourceIdentity !== 'valid') {
251
+ throw new Error(`迁移未满足保留旧 Store 的完成条件: ${sourcePath}`);
252
+ }
253
+ await verifyDestinationAgainstSource(data, 'before-completion');
254
+ }
255
+ else if (data.keepOld || finalSourceIdentity !== 'missing') {
256
+ throw new Error(`迁移未满足删除旧 Store 的完成条件: ${sourcePath}`);
257
+ }
258
+ await journal.complete();
259
+ success('迁移完成');
260
+ debug(`[migrate] 迁移日志完成: id=${data.id}, destination=${destinationPath}`);
261
+ }
262
+ }
263
+ /** 恢复上次进程中断留下的迁移。 */
264
+ export async function recoverPendingMigrations(registry) {
265
+ const pending = await MigrationJournal.listPending();
266
+ if (pending.length > 1) {
267
+ throw new Error(`发现 ${pending.length} 个待恢复迁移,拒绝猜测执行顺序`);
268
+ }
269
+ if (pending.length === 0)
270
+ return 0;
271
+ const journal = pending[0];
272
+ const data = journal.getData();
273
+ warn(`发现待恢复迁移 (${data.id.slice(0, 8)}),从 ${data.phase} 阶段继续`);
274
+ await executeMigrationJournal(journal, registry);
275
+ return 1;
276
+ }
37
277
  /**
38
278
  * 迁移 Store
39
279
  */
40
- async function migrateStore(newPath, options) {
280
+ export async function migrateStore(newPath, options) {
41
281
  const absoluteNewPath = expandHome(newPath);
282
+ const registry = getRegistry();
283
+ await registry.load();
284
+ await recoverPendingMigrations(registry);
42
285
  // 安全检查
43
286
  const safetyResult = isPathSafe(absoluteNewPath);
44
287
  if (!safetyResult.safe) {
@@ -46,23 +289,44 @@ async function migrateStore(newPath, options) {
46
289
  process.exit(1);
47
290
  }
48
291
  const oldPath = await store.getStorePath();
49
- if (absoluteNewPath === oldPath) {
292
+ const oldResolved = path.resolve(oldPath);
293
+ const newResolved = path.resolve(absoluteNewPath);
294
+ if (newResolved === oldResolved) {
50
295
  warn('新路径与当前路径相同');
51
296
  return;
52
297
  }
298
+ if (isPathWithin(oldResolved, newResolved) || isPathWithin(newResolved, oldResolved)) {
299
+ throw new Error('新旧 Store 目录不能互相包含');
300
+ }
53
301
  title('迁移 Store');
54
302
  blank();
55
303
  // 获取当前 Store 信息
56
304
  const libraries = await store.listLibraries();
57
- const totalSize = await store.getTotalSize();
58
- info(`当前位置: ${shrinkHome(oldPath)} (${formatSize(totalSize)}, ${libraries.length} 个库)`);
305
+ const snapshot = await new SpaceService(registry, oldPath).buildSnapshot('summary');
306
+ const migrationEstimate = await estimateMigrationSpace(oldPath);
307
+ if (migrationEstimate.status === 'unknown' || !migrationEstimate.amount) {
308
+ throw new Error(`无法估算迁移空间: ${migrationEstimate.warnings.join('; ')}`);
309
+ }
310
+ const actualSize = snapshot.totals.physical.bytes;
311
+ const requiredSize = migrationEstimate.amount.bytes;
312
+ for (const warningMessage of migrationEstimate.warnings)
313
+ warn(`迁移提示: ${warningMessage}`);
314
+ for (const evidence of migrationEstimate.evidence)
315
+ debug(`[migrate] 容量依据: ${evidence}`);
316
+ info(`当前位置: ${shrinkHome(oldPath)} (实际占用 ${formatSize(actualSize)}, ${libraries.length} 个库)`);
59
317
  info(`目标位置: ${shrinkHome(absoluteNewPath)}`);
318
+ info(`预计复制: ${formatSize(requiredSize)} (保留符号链接)`);
319
+ debug(`[migrate] 空间计算完成: source=${oldPath}, destination=${absoluteNewPath}, snapshot=${snapshot.id}, actual=${actualSize}, required=${requiredSize}, basis=${migrationEstimate.amount.basis}, quality=${migrationEstimate.amount.quality}`);
60
320
  blank();
61
321
  // 检查目标路径
62
322
  info('检查:');
63
323
  // 检查可写性
64
324
  try {
65
325
  await fs.mkdir(absoluteNewPath, { recursive: true });
326
+ const existingEntries = await fs.readdir(absoluteNewPath);
327
+ if (existingEntries.length > 0) {
328
+ throw new Error(`目标目录必须为空,当前包含 ${existingEntries.length} 个项目`);
329
+ }
66
330
  success(' [ok] 目标路径可写');
67
331
  }
68
332
  catch (err) {
@@ -71,14 +335,12 @@ async function migrateStore(newPath, options) {
71
335
  }
72
336
  // 检查空间
73
337
  const freeSpace = await getFreeSpace(absoluteNewPath);
74
- if (freeSpace < totalSize) {
75
- error(` [err] 目标空间不足 (需要 ${formatSize(totalSize)}, 可用 ${formatSize(freeSpace)})`);
338
+ if (freeSpace < requiredSize) {
339
+ error(` [err] 目标空间不足 (需要 ${formatSize(requiredSize)}, 可用 ${formatSize(freeSpace)})`);
76
340
  process.exit(1);
77
341
  }
78
342
  success(` [ok] 目标空间充足 (${formatSize(freeSpace)} 可用)`);
79
343
  // 获取需要更新的项目
80
- const registry = getRegistry();
81
- await registry.load();
82
344
  const projects = registry.listProjects();
83
345
  info(` [info] ${projects.length} 个项目的符号链接需要更新`);
84
346
  blank();
@@ -86,52 +348,10 @@ async function migrateStore(newPath, options) {
86
348
  warn('使用 --force 选项确认迁移');
87
349
  return;
88
350
  }
89
- // 执行迁移
351
+ // 执行迁移。复制、配置切换、链接更新和旧目录清理均由日志驱动。
90
352
  separator();
91
- // 1. 复制文件
92
- info('[1/3] 复制文件...');
93
- await copyDirWithProgress(oldPath, absoluteNewPath, totalSize, (copied, total) => {
94
- progressBar(copied, total);
95
- });
96
- // 确保进度条完成
97
- if (totalSize > 0) {
98
- progressBar(totalSize, totalSize);
99
- }
100
- // 2. 更新符号链接
101
- info('[2/3] 更新符号链接...');
102
- for (const project of projects) {
103
- let updatedCount = 0;
104
- for (const dep of project.dependencies) {
105
- const localPath = path.join(project.path, dep.linkedPath);
106
- // 使用依赖记录的平台,或项目的第一个平台
107
- const migratePlatform = dep.platform ?? project.platforms?.[0] ?? 'macOS';
108
- const newStorePath = store.getLibraryPath(absoluteNewPath, dep.libName, dep.commit, migratePlatform);
109
- if (await linker.isSymlink(localPath)) {
110
- await linker.unlink(localPath);
111
- await linker.link(newStorePath, localPath);
112
- updatedCount++;
113
- }
114
- }
115
- success(` [ok] ${shrinkHome(project.path)} (${updatedCount} 个链接)`);
116
- }
117
- // 3. 更新配置
118
- await config.setStorePath(absoluteNewPath);
119
- // 4. 清理旧目录
120
- if (!options.keepOld) {
121
- info('[3/3] 清理旧目录...');
122
- try {
123
- await fs.rm(oldPath, { recursive: true, force: true });
124
- success(` [ok] 已删除 ${shrinkHome(oldPath)}`);
125
- }
126
- catch (err) {
127
- warn(` [warn] 删除旧目录失败: ${err.message}`);
128
- }
129
- }
130
- else {
131
- info('[3/3] 保留旧目录');
132
- }
133
- blank();
134
- success('迁移完成');
353
+ const journal = await MigrationJournal.create(oldPath, absoluteNewPath, options.keepOld);
354
+ await executeMigrationJournal(journal, registry, requiredSize);
135
355
  }
136
356
  export default createMigrateCommand;
137
357
  //# sourceMappingURL=migrate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EACL,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,KAAK,EACL,KAAK,EACL,KAAK,EACL,SAAS,EACT,WAAW,EACX,KAAK,GACN,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC;SAC1B,WAAW,CAAC,eAAe,CAAC;SAC5B,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC;SAChC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC;SACzB,MAAM,CAAC,YAAY,EAAE,aAAa,CAAC;SACnC,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,OAAO,EAAE,EAAE;QACzC,MAAM,iBAAiB,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,cAAc,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QAC7D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAOD,SAAS,YAAY,CAAC,IAAY,EAAE,MAAc;IAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACzE,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;AACvF,CAAC;AAED,SAAS,2BAA2B,CAAC,UAAkB;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1C,OAAO,QAAQ,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,IAAI,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;AACtF,CAAC;AAED,SAAS,oBAAoB,CAAC,UAAkB,EAAE,eAAuB;IACvE,OAAO;QACL,gBAAgB,EAAE,IAAI;QACtB,+BAA+B,EAAE;YAC/B,UAAU,EAAE,UAAU;YACtB,eAAe,EAAE,eAAe;SACjC;KACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,UAAkB,EAClB,eAAuB;IAEvB,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACvE,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IACzC,MAAM,YAAY,GAAG,MAAM,qBAAqB,CAC9C,UAAU,EACV,eAAe,EACf,oBAAoB,CAAC,UAAU,EAAE,eAAe,CAAC,CAClD,CAAC;IACF,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;AACnD,CAAC;AAED,KAAK,UAAU,oBAAoB,CACjC,IAA6C;IAE7C,IAAI,IAAyC,CAAC;IAC9C,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACvE,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,8BAA8B,CAC3C,IAA6C,EAC7C,QAAgB;IAEhB,MAAM,YAAY,GAAG,MAAM,qBAAqB,CAC9C,IAAI,CAAC,UAAU,EACf,IAAI,CAAC,eAAe,EACpB,oBAAoB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC,CAC5D,CAAC;IACF,KAAK,CACH,wBAAwB,IAAI,CAAC,EAAE,cAAc,QAAQ,WAAW,YAAY,CAAC,KAAK,WAAW,YAAY,CAAC,KAAK,IAAI,MAAM,EAAE,CAC5H,CAAC;IACF,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,qBAAqB,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,KAAK,UAAU,yBAAyB,CACtC,IAA6C,EAC7C,QAAgB;IAEhB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACxF,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,OAAO,CACnB,IAAI,EAAE,WAAW,EAAE,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,gBAAgB,CACjG,CAAC;IACF,KAAK,CACH,wBAAwB,IAAI,CAAC,EAAE,cAAc,QAAQ,WAAW,KAAK,oBAAoB,IAAI,CAAC,iBAAiB,mBAAmB,IAAI,CAAC,gBAAgB,kBAAkB,IAAI,EAAE,GAAG,IAAI,SAAS,iBAAiB,IAAI,EAAE,GAAG,IAAI,SAAS,EAAE,CACzO,CAAC;IACF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC;AAED,KAAK,UAAU,2BAA2B,CACxC,IAA6C,EAC7C,QAAgB;IAEhB,MAAM,mBAAmB,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;IACxD,MAAM,KAAK,GAAG,OAAO,CACnB,mBAAmB,IAAI,UAAU,CAAC,mBAAmB,EAAE,IAAI,CAAC,eAAe,CAAC,CAC7E,CAAC;IACF,KAAK,CACH,wBAAwB,IAAI,CAAC,EAAE,cAAc,QAAQ,WAAW,KAAK,gBAAgB,mBAAmB,IAAI,SAAS,iBAAiB,IAAI,CAAC,eAAe,EAAE,CAC7J,CAAC;IACF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,4BAA4B,mBAAmB,IAAI,SAAS,EAAE,CAC/D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,uBAAuB,CACpC,OAAyB,EACzB,QAAyB,EACzB,iBAA0B;IAE1B,IAAI,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAC7B,MAAM,EAAE,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IAC1D,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IACxD,MAAM,mBAAmB,GAAG,IAAI,CAAC,IAAI,CACnC,iBAAiB,EACjB,IAAI,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,uBAAuB,IAAI,CAAC,EAAE,EAAE,CACnE,CAAC;IACF,IAAI,2BAA2B,CAAC,UAAU,CAAC,IAAI,2BAA2B,CAAC,eAAe,CAAC,EAAE,CAAC;QAC5F,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,mBAAmB,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;IACxD,IACE,CAAC,mBAAmB;QACpB,CAAC,CAAC,UAAU,CAAC,mBAAmB,EAAE,UAAU,CAAC;YAC3C,CAAC,UAAU,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC,EACpD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,mCAAmC,mBAAmB,IAAI,SAAS,YAAY,UAAU,iBAAiB,eAAe,EAAE,CAC5H,CAAC;IACJ,CAAC;IACD,IACE,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC;QAC/D,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAC3E,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,aAAa,WAAW,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,YAAY,CAAC,UAAU,EAAE,eAAe,CAAC,IAAI,YAAY,CAAC,eAAe,EAAE,UAAU,CAAC,EAAE,CAAC;QAC3F,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3C,CAAC;IACD,KAAK,CACH,yBAAyB,IAAI,CAAC,EAAE,WAAW,IAAI,CAAC,KAAK,YAAY,UAAU,iBAAiB,eAAe,aAAa,WAAW,aAAa,IAAI,CAAC,OAAO,EAAE,CAC/J,CAAC;IAEF,MAAM,cAAc,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,IAAI,CAAC,KAAK,KAAK,cAAc,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YACnD,+CAA+C;YAC/C,MAAM,yBAAyB,CAAC,IAAI,EAAE,8BAA8B,CAAC,CAAC;YACtE,MAAM,2BAA2B,CAAC,IAAI,EAAE,8BAA8B,CAAC,CAAC;YACxE,MAAM,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;YACtC,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;aAAM,IAAI,IAAI,CAAC,KAAK,KAAK,eAAe,EAAE,CAAC;YAC1C,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,KAAK,iBAAiB,EAAE,CAAC;gBACrD,MAAM,IAAI,KAAK,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAC;YAC1D,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,CAAC,KAAK,UAAU,UAAU,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,IACE,cAAc,KAAK,OAAO;QAC1B,CAAC,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,iBAAiB,CAAC,CAAC,QAAQ,CACzF,IAAI,CAAC,KAAK,CACX,EACD,CAAC;QACD,MAAM,yBAAyB,CAAC,IAAI,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC9D,MAAM,8BAA8B,CAAC,IAAI,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1D,MAAM,QAAQ,GAAG,iBAAiB,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,sBAAsB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7F,MAAM,YAAY,GAAG,iBAAiB,IAAI,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC;QAClE,IAAI,YAAY,IAAI,IAAI,IAAI,QAAQ,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;YAC3D,MAAM,IAAI,KAAK,CAAC,aAAa,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;QAC1E,CAAC;QACD,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,iBAAiB,CAAC,CAAC;QACxD,KAAK,CACH,0BAA0B,IAAI,CAAC,EAAE,cAAc,YAAY,eAAe,SAAS,EAAE,CACtF,CAAC;QACF,IAAI,SAAS,GAAG,YAAY,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CACb,mBAAmB,UAAU,CAAC,YAAY,CAAC,OAAO,UAAU,CAAC,SAAS,CAAC,EAAE,CAC1E,CAAC;QACJ,CAAC;QACD,MAAM,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,MAAM,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAChC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC7B,MAAM,mBAAmB,CACvB,UAAU,EACV,WAAW,EACX,YAAY,EACZ,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,EAC7C,oBAAoB,CAAC,UAAU,EAAE,eAAe,CAAC,CAClD,CAAC;QACF,IAAI,YAAY,GAAG,CAAC;YAAE,WAAW,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAC9D,MAAM,YAAY,GAAG,MAAM,qBAAqB,CAC9C,UAAU,EACV,WAAW,EACX,oBAAoB,CAAC,UAAU,EAAE,eAAe,CAAC,CAClD,CAAC;QACF,KAAK,CACH,0BAA0B,IAAI,CAAC,EAAE,WAAW,YAAY,CAAC,KAAK,WAAW,YAAY,CAAC,KAAK,IAAI,MAAM,EAAE,CACxG,CAAC;QACF,IAAI,CAAC,YAAY,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,aAAa,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC;QAC5E,MAAM,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/B,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;QAC1D,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAClC,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QAClE,KAAK,CAAC,wBAAwB,IAAI,CAAC,EAAE,WAAW,KAAK,iBAAiB,eAAe,EAAE,CAAC,CAAC;QACzF,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,wBAAwB,eAAe,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;YACtB,IAAI,KAAK,KAAK,OAAO;gBAAE,MAAM,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;YACvD,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,MAAM,eAAe,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACvD,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,sBAAsB,eAAe,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,MAAM,OAAO,CAAC,YAAY,CAAC,eAAe,CAAC,GAAG,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC;QACrE,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QAC9B,MAAM,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;QAC3C,MAAM,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;QACvC,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QACzB,KAAK,CAAC,wBAAwB,IAAI,CAAC,EAAE,WAAW,eAAe,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,IAAI,CAAC,KAAK,KAAK,gBAAgB,IAAI,IAAI,CAAC,KAAK,KAAK,eAAe,EAAE,CAAC;QACtE,MAAM,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QACtC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC1B,IAAI,gBAAgB,GAAG,CAAC,CAAC;QACzB,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,YAAY,EAAE,EAAE,CAAC;YAC9C,IAAI,YAAY,GAAG,CAAC,CAAC;YACrB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;YAClC,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;gBACvC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;gBAC1D,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;oBAAE,SAAS;gBACrC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBACvB,MAAM,KAAK,GAAG,MAAM,uBAAuB,CAAC,SAAS,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;gBACpF,YAAY,IAAI,KAAK,CAAC;gBACtB,gBAAgB,IAAI,KAAK,CAAC;YAC5B,CAAC;YACD,OAAO,CAAC,UAAU,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,YAAY,OAAO,CAAC,CAAC;QACtE,CAAC;QACD,MAAM,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QACrC,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QACzB,KAAK,CAAC,0BAA0B,IAAI,CAAC,EAAE,kBAAkB,gBAAgB,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,IAAI,IAAI,CAAC,KAAK,KAAK,cAAc,EAAE,CAAC;QAClC,MAAM,yBAAyB,CAAC,IAAI,EAAE,wBAAwB,CAAC,CAAC;QAChE,MAAM,8BAA8B,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAAC;QACpE,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,2BAA2B,CAAC,IAAI,EAAE,wBAAwB,CAAC,CAAC;QAClE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;QACtD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D,MAAM,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QACzB,KAAK,CAAC,yBAAyB,IAAI,CAAC,EAAE,aAAa,IAAI,CAAC,OAAO,YAAY,UAAU,EAAE,CAAC,CAAC;IAC3F,CAAC;IAED,IAAI,IAAI,CAAC,KAAK,KAAK,iBAAiB,IAAI,IAAI,CAAC,KAAK,KAAK,eAAe,EAAE,CAAC;QACvE,MAAM,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;QAC3D,MAAM,2BAA2B,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;QAC7D,MAAM,mBAAmB,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC7D,IAAI,IAAI,CAAC,KAAK,KAAK,iBAAiB,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,mBAAmB,KAAK,OAAO,EAAE,CAAC;gBACrD,MAAM,IAAI,KAAK,CAAC,yBAAyB,UAAU,EAAE,CAAC,CAAC;YACzD,CAAC;YACD,MAAM,8BAA8B,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;QAClE,CAAC;aAAM,IAAI,IAAI,CAAC,OAAO,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;YAC7D,MAAM,IAAI,KAAK,CAAC,yBAAyB,UAAU,EAAE,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,CAAC;QAChB,KAAK,CAAC,wBAAwB,IAAI,CAAC,EAAE,iBAAiB,eAAe,EAAE,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED,qBAAqB;AACrB,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,QAAyB;IACtE,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,WAAW,EAAE,CAAC;IACrD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,MAAM,OAAO,CAAC,MAAM,kBAAkB,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAC/B,IAAI,CAAC,YAAY,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC;IAC9D,MAAM,uBAAuB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACjD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe,EAAE,OAAuB;IACzE,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAE5C,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACtB,MAAM,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IAEzC,OAAO;IACP,MAAM,YAAY,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;IACjD,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QACvB,KAAK,CAAC,YAAY,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,YAAY,EAAE,CAAC;IAC3C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAElD,IAAI,WAAW,KAAK,WAAW,EAAE,CAAC;QAChC,IAAI,CAAC,YAAY,CAAC,CAAC;QACnB,OAAO;IACT,CAAC;IACD,IAAI,YAAY,CAAC,WAAW,EAAE,WAAW,CAAC,IAAI,YAAY,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC;QACrF,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,CAAC;IAClB,KAAK,EAAE,CAAC;IAER,gBAAgB;IAChB,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,aAAa,EAAE,CAAC;IAC9C,MAAM,QAAQ,GAAG,MAAM,IAAI,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IACpF,MAAM,iBAAiB,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChE,IAAI,iBAAiB,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,aAAa,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;IAClD,MAAM,YAAY,GAAG,iBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC;IACpD,KAAK,MAAM,cAAc,IAAI,iBAAiB,CAAC,QAAQ;QAAE,IAAI,CAAC,SAAS,cAAc,EAAE,CAAC,CAAC;IACzF,KAAK,MAAM,QAAQ,IAAI,iBAAiB,CAAC,QAAQ;QAAE,KAAK,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;IAExF,IAAI,CACF,SAAS,UAAU,CAAC,OAAO,CAAC,UAAU,UAAU,CAAC,UAAU,CAAC,KAAK,SAAS,CAAC,MAAM,MAAM,CACxF,CAAC;IACF,IAAI,CAAC,SAAS,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IAC7C,IAAI,CAAC,SAAS,UAAU,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;IACnD,KAAK,CACH,4BAA4B,OAAO,iBAAiB,eAAe,cAAc,QAAQ,CAAC,EAAE,YAAY,UAAU,cAAc,YAAY,WAAW,iBAAiB,CAAC,MAAM,CAAC,KAAK,aAAa,iBAAiB,CAAC,MAAM,CAAC,OAAO,EAAE,CACrO,CAAC;IACF,KAAK,EAAE,CAAC;IAER,SAAS;IACT,IAAI,CAAC,KAAK,CAAC,CAAC;IAEZ,QAAQ;IACR,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,MAAM,eAAe,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAC1D,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,iBAAiB,eAAe,CAAC,MAAM,MAAM,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,qBAAsB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO;IACP,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,eAAe,CAAC,CAAC;IACtD,IAAI,SAAS,GAAG,YAAY,EAAE,CAAC;QAC7B,KAAK,CAAC,sBAAsB,UAAU,CAAC,YAAY,CAAC,QAAQ,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,kBAAkB,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAEvD,YAAY;IACZ,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,EAAE,CAAC;IAEzC,IAAI,CAAC,YAAY,QAAQ,CAAC,MAAM,eAAe,CAAC,CAAC;IACjD,KAAK,EAAE,CAAC;IAER,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,iCAAiC;IACjC,SAAS,EAAE,CAAC;IACZ,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACzF,MAAM,uBAAuB,CAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;AACjE,CAAC;AAED,eAAe,oBAAoB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"reset.d.ts","sourceRoot":"","sources":["../../src/commands/reset.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,UAAU,YAAY;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAaD,wBAAgB,kBAAkB,IAAI,OAAO,CA6B5C;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAiDf;AA4OD,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"reset.d.ts","sourceRoot":"","sources":["../../src/commands/reset.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,UAAU,YAAY;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAaD,wBAAgB,kBAAkB,IAAI,OAAO,CA6B5C;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAwEf;AAkPD,eAAe,kBAAkB,CAAC"}
@@ -7,13 +7,14 @@ import { Command } from 'commander';
7
7
  import { ensureInitialized } from '../core/guard.js';
8
8
  import { getRegistry } from '../core/registry.js';
9
9
  import { getStorePath } from '../core/config.js';
10
- import { findCodepacConfig, parseCodepacDep, extractDependencies, extractActions, parseActionCommand, extractNestedDependencies, resolveProjectRootPath } from '../core/parser.js';
11
- import { shrinkHome } from '../core/platform.js';
10
+ import { findCodepacConfig, parseCodepacDep, extractDependencies, extractActions, buildActionExecutionPlan, extractNestedDependencies, resolveProjectRootPath } from '../core/parser.js';
11
+ import { getBaseKeyForCodepac, shrinkHome } from '../core/platform.js';
12
12
  import { isSymlink } from '../core/linker.js';
13
+ import { SpaceService } from '../core/space/service.js';
13
14
  import { withGlobalLock } from '../utils/global-lock.js';
14
15
  import { findSubmoduleConfigs } from '../utils/git.js';
15
16
  import { confirmAction, PROMPT_CANCELLED } from '../utils/prompt.js';
16
- import { info, warn, success, error, blank, separator } from '../utils/logger.js';
17
+ import { info, warn, success, error, blank, separator, debug } from '../utils/logger.js';
17
18
  export function createResetCommand() {
18
19
  return new Command('reset')
19
20
  .description('重置指定库的缓存与元数据,便于重新干净拉取')
@@ -76,9 +77,25 @@ export async function resetLibrary(libName, commit, options, currentPath) {
76
77
  if (!storePath) {
77
78
  throw new Error('Store 路径未配置,请先运行 tanmi-dock init');
78
79
  }
80
+ debug(`[reset] 开始重置: source=${projectRoot ?? 'global'}, targets=${targets.length}, affectedProjects=${affectedProjects.length}, store=${storePath}`);
81
+ const spaceService = new SpaceService(registry, storePath);
82
+ const executor = spaceService.createCleanupExecutor();
83
+ const recovered = await executor.recoverPending();
84
+ debug(`[reset] 清理日志恢复完成: recovered=${recovered}`);
79
85
  await cleanupProjectLinks(affectedProjects);
80
86
  removeProjectDependencyMetadata(registry, targets, affectedProjects);
81
- await removeStoreAndRegistryData(registry, storePath, targets);
87
+ const plan = await spaceService.buildCleanupPlan({
88
+ reason: 'reset',
89
+ selections: targets.map((target) => ({ id: target, platforms: 'all' })),
90
+ requireUnreferenced: false,
91
+ allowUnknownContent: true,
92
+ });
93
+ debug(`[reset] 调用统一清理执行器: plan=${plan.id}, groups=${plan.groups.length}, expected=${plan.expectedRelease.bytes}`);
94
+ const cleanupResult = await executor.execute(plan);
95
+ debug(`[reset] 清理执行返回: plan=${plan.id}, status=${cleanupResult.status}, succeeded=${cleanupResult.succeeded.length}, failed=${cleanupResult.failed.length}, released=${cleanupResult.released.bytes}`);
96
+ if (cleanupResult.status !== 'completed') {
97
+ throw new Error(`Store 重置未完整完成: ${cleanupResult.failed.map((item) => item.message).join('; ')}`);
98
+ }
82
99
  await registry.save();
83
100
  blank();
84
101
  separator();
@@ -118,28 +135,41 @@ async function collectProjectTargets(registry, projectRoot, libName) {
118
135
  const projectHash = registry.hashPath(projectRoot);
119
136
  const project = registry.getProject(projectHash);
120
137
  if (project) {
121
- return project.dependencies
138
+ const registryTargets = project.dependencies
122
139
  .filter((dep) => dep.libName === libName)
123
140
  .map((dep) => ({ libName: dep.libName, commit: dep.commit }));
141
+ if (registryTargets.length > 0) {
142
+ return registryTargets;
143
+ }
144
+ return collectTargetsFromConfigs(projectRoot, libName, project.platforms ?? []);
124
145
  }
125
146
  return collectTargetsFromConfigs(projectRoot, libName);
126
147
  }
127
- async function collectTargetsFromConfigs(projectRoot, libName) {
148
+ async function collectTargetsFromConfigs(projectRoot, libName, platforms = []) {
128
149
  const configPath = await findCodepacConfig(projectRoot);
129
150
  if (!configPath)
130
151
  return [];
152
+ const codepacPlatforms = [...new Set(platforms.map((platform) => getBaseKeyForCodepac(platform)))];
131
153
  const targets = [];
132
154
  const visited = new Set();
133
- const queue = [configPath];
134
- const submodules = await findSubmoduleConfigs(projectRoot);
155
+ const queue = [
156
+ { configPath, targetDir: path.dirname(configPath), codepacPlatforms },
157
+ ];
158
+ const submodules = await findSubmoduleConfigs(projectRoot, '', 0, codepacPlatforms);
135
159
  for (const submodule of submodules) {
136
- queue.push(submodule.configPath);
160
+ queue.push({
161
+ configPath: submodule.configPath,
162
+ targetDir: path.dirname(submodule.configPath),
163
+ codepacPlatforms,
164
+ });
137
165
  }
138
166
  while (queue.length > 0) {
139
- const currentConfig = queue.shift();
140
- if (visited.has(currentConfig))
167
+ const current = queue.shift();
168
+ const currentConfig = current.configPath;
169
+ const visitKey = buildResetActionVisitKey(currentConfig, current.targetDir);
170
+ if (visited.has(visitKey))
141
171
  continue;
142
- visited.add(currentConfig);
172
+ visited.add(visitKey);
143
173
  let parsed;
144
174
  try {
145
175
  parsed = await parseCodepacDep(currentConfig);
@@ -147,28 +177,41 @@ async function collectTargetsFromConfigs(projectRoot, libName) {
147
177
  catch {
148
178
  continue;
149
179
  }
150
- for (const dep of extractDependencies(parsed)) {
180
+ const extractOptions = { platforms: current.codepacPlatforms, configPath: currentConfig };
181
+ for (const dep of extractDependencies(parsed, extractOptions)) {
151
182
  if (dep.libName === libName) {
152
183
  targets.push({ libName: dep.libName, commit: dep.commit });
153
184
  }
154
185
  }
155
- for (const action of extractActions(parsed)) {
156
- let parsedAction;
186
+ for (const action of extractActions(parsed, extractOptions)) {
187
+ let plan;
157
188
  try {
158
- parsedAction = parseActionCommand(action.command);
189
+ plan = buildActionExecutionPlan(action, {
190
+ parentConfigPath: currentConfig,
191
+ parentTargetDir: current.targetDir,
192
+ inheritedCodepacPlatforms: current.codepacPlatforms,
193
+ });
159
194
  }
160
195
  catch {
161
196
  continue;
162
197
  }
163
- const nestedConfigPath = path.resolve(path.dirname(currentConfig), parsedAction.configDir, 'codepac-dep.json');
164
198
  try {
165
- const nested = await extractNestedDependencies(nestedConfigPath, parsedAction.libraries);
199
+ const nested = await extractNestedDependencies(plan.nestedConfigPath, plan.libraries, {
200
+ platforms: plan.effectiveCodepacPlatforms,
201
+ configPath: plan.nestedConfigPath,
202
+ });
166
203
  for (const dep of nested.dependencies) {
167
204
  if (dep.libName === libName) {
168
205
  targets.push({ libName: dep.libName, commit: dep.commit });
169
206
  }
170
207
  }
171
- queue.push(nestedConfigPath);
208
+ if (!plan.parsed.disableAction) {
209
+ queue.push({
210
+ configPath: plan.nestedConfigPath,
211
+ targetDir: plan.nestedTargetDir,
212
+ codepacPlatforms: plan.effectiveCodepacPlatforms,
213
+ });
214
+ }
172
215
  }
173
216
  catch {
174
217
  continue;
@@ -177,6 +220,9 @@ async function collectTargetsFromConfigs(projectRoot, libName) {
177
220
  }
178
221
  return targets;
179
222
  }
223
+ function buildResetActionVisitKey(configPath, targetDir) {
224
+ return `${configPath}::${targetDir}`;
225
+ }
180
226
  function dedupeTargets(targets) {
181
227
  const seen = new Set();
182
228
  return targets.filter((target) => {
@@ -254,27 +300,5 @@ function removeProjectDependencyMetadata(registry, targets, projectMatches) {
254
300
  registry.updateProject(match.projectHash, { dependencies: remaining });
255
301
  }
256
302
  }
257
- async function removeStoreAndRegistryData(registry, storePath, targets) {
258
- for (const target of targets) {
259
- const storeKeys = registry.getLibraryStoreKeys(target.libName, target.commit);
260
- for (const storeKey of storeKeys) {
261
- registry.removeStore(storeKey);
262
- }
263
- const libKey = registry.getLibraryKey(target.libName, target.commit);
264
- registry.removeLibrary(libKey);
265
- const commitPath = path.join(storePath, target.libName, target.commit);
266
- await fs.rm(commitPath, { recursive: true, force: true }).catch(() => { });
267
- const libPath = path.join(storePath, target.libName);
268
- try {
269
- const entries = await fs.readdir(libPath);
270
- if (entries.length === 0) {
271
- await fs.rm(libPath, { recursive: true, force: true });
272
- }
273
- }
274
- catch {
275
- // ignore
276
- }
277
- }
278
- }
279
303
  export default createResetCommand;
280
304
  //# sourceMappingURL=reset.js.map