@sfmc-bds/cli 0.2.0-beta.0 → 0.2.0-beta.10

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 (183) hide show
  1. package/dist/argv-parse.d.ts +11 -0
  2. package/dist/argv-parse.d.ts.map +1 -0
  3. package/dist/argv-parse.js +28 -0
  4. package/dist/argv-parse.js.map +7 -0
  5. package/dist/cli-gate.d.ts +11 -0
  6. package/dist/cli-gate.d.ts.map +1 -0
  7. package/dist/cli-gate.js +46 -0
  8. package/dist/cli-gate.js.map +7 -0
  9. package/dist/command-palette.d.ts +61 -0
  10. package/dist/command-palette.d.ts.map +1 -0
  11. package/dist/command-palette.js +352 -0
  12. package/dist/command-palette.js.map +7 -0
  13. package/dist/command-surface.d.ts +85 -0
  14. package/dist/command-surface.d.ts.map +1 -0
  15. package/dist/command-surface.js +292 -0
  16. package/dist/command-surface.js.map +7 -0
  17. package/dist/commands-reload.js +2 -2
  18. package/dist/commands-reload.js.map +2 -2
  19. package/dist/commands.d.ts +1 -1
  20. package/dist/commands.d.ts.map +1 -1
  21. package/dist/commands.js +196 -21
  22. package/dist/commands.js.map +2 -2
  23. package/dist/debug-command.d.ts +5 -0
  24. package/dist/debug-command.d.ts.map +1 -0
  25. package/dist/debug-command.js +118 -0
  26. package/dist/debug-command.js.map +7 -0
  27. package/dist/help-text.d.ts +3 -0
  28. package/dist/help-text.d.ts.map +1 -0
  29. package/dist/help-text.js +160 -0
  30. package/dist/help-text.js.map +7 -0
  31. package/dist/i18n/locales/en.d.ts +103 -89
  32. package/dist/i18n/locales/en.d.ts.map +1 -1
  33. package/dist/i18n/locales/en.js +105 -91
  34. package/dist/i18n/locales/en.js.map +2 -2
  35. package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
  36. package/dist/i18n/locales/zh-CN.js +138 -118
  37. package/dist/i18n/locales/zh-CN.js.map +2 -2
  38. package/dist/index.d.ts +2 -2
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +9 -4
  41. package/dist/index.js.map +2 -2
  42. package/dist/llbot-launch.d.ts +20 -0
  43. package/dist/llbot-launch.d.ts.map +1 -0
  44. package/dist/llbot-launch.js +40 -0
  45. package/dist/llbot-launch.js.map +7 -0
  46. package/dist/log-filter.d.ts +69 -0
  47. package/dist/log-filter.d.ts.map +1 -0
  48. package/dist/log-filter.js +150 -0
  49. package/dist/log-filter.js.map +7 -0
  50. package/dist/logs.d.ts +29 -7
  51. package/dist/logs.d.ts.map +1 -1
  52. package/dist/logs.js +164 -72
  53. package/dist/logs.js.map +3 -3
  54. package/dist/main.js +55 -46
  55. package/dist/main.js.map +2 -2
  56. package/dist/module-commands.d.ts +17 -40
  57. package/dist/module-commands.d.ts.map +1 -1
  58. package/dist/module-commands.js +129 -85
  59. package/dist/module-commands.js.map +2 -2
  60. package/dist/module-pack-build.d.ts +10 -0
  61. package/dist/module-pack-build.d.ts.map +1 -0
  62. package/dist/module-pack-build.js +434 -0
  63. package/dist/module-pack-build.js.map +7 -0
  64. package/dist/module-toggle.d.ts +33 -0
  65. package/dist/module-toggle.d.ts.map +1 -0
  66. package/dist/module-toggle.js +28 -0
  67. package/dist/module-toggle.js.map +7 -0
  68. package/dist/node-service-probe.d.ts +18 -0
  69. package/dist/node-service-probe.d.ts.map +1 -0
  70. package/dist/node-service-probe.js +97 -0
  71. package/dist/node-service-probe.js.map +7 -0
  72. package/dist/pack-lifecycle.d.ts +1 -1
  73. package/dist/pack-lifecycle.d.ts.map +1 -1
  74. package/dist/pack-lifecycle.js +14 -4
  75. package/dist/pack-lifecycle.js.map +2 -2
  76. package/dist/pack-update/index.d.ts +18 -0
  77. package/dist/pack-update/index.d.ts.map +1 -0
  78. package/dist/pack-update/index.js +113 -0
  79. package/dist/pack-update/index.js.map +7 -0
  80. package/dist/registry.d.ts +7 -4
  81. package/dist/registry.d.ts.map +1 -1
  82. package/dist/registry.js +15 -3
  83. package/dist/registry.js.map +2 -2
  84. package/dist/repl-windows/format-display.d.ts +10 -0
  85. package/dist/repl-windows/format-display.d.ts.map +1 -0
  86. package/dist/repl-windows/format-display.js +57 -0
  87. package/dist/repl-windows/format-display.js.map +7 -0
  88. package/dist/repl-windows/host.d.ts +60 -0
  89. package/dist/repl-windows/host.d.ts.map +1 -0
  90. package/dist/repl-windows/host.js +179 -0
  91. package/dist/repl-windows/host.js.map +7 -0
  92. package/dist/repl-windows/index.d.ts +9 -0
  93. package/dist/repl-windows/index.d.ts.map +1 -0
  94. package/dist/repl-windows/index.js +13 -0
  95. package/dist/repl-windows/index.js.map +7 -0
  96. package/dist/repl-windows/logs-filter-window.d.ts +15 -0
  97. package/dist/repl-windows/logs-filter-window.d.ts.map +1 -0
  98. package/dist/repl-windows/logs-filter-window.js +77 -0
  99. package/dist/repl-windows/logs-filter-window.js.map +7 -0
  100. package/dist/repl-windows/service-window.d.ts +4 -0
  101. package/dist/repl-windows/service-window.d.ts.map +1 -0
  102. package/dist/repl-windows/service-window.js +35 -0
  103. package/dist/repl-windows/service-window.js.map +7 -0
  104. package/dist/repl-windows/types.d.ts +56 -0
  105. package/dist/repl-windows/types.d.ts.map +1 -0
  106. package/dist/repl-windows/types.js +1 -0
  107. package/dist/repl-windows/types.js.map +7 -0
  108. package/dist/repl.d.ts +6 -9
  109. package/dist/repl.d.ts.map +1 -1
  110. package/dist/repl.js +698 -427
  111. package/dist/repl.js.map +3 -3
  112. package/dist/runtime.d.ts +6 -13
  113. package/dist/runtime.d.ts.map +1 -1
  114. package/dist/runtime.js +23 -28
  115. package/dist/runtime.js.map +2 -2
  116. package/dist/send-target.d.ts +11 -0
  117. package/dist/send-target.d.ts.map +1 -0
  118. package/dist/send-target.js +42 -0
  119. package/dist/send-target.js.map +7 -0
  120. package/dist/services.d.ts +50 -4
  121. package/dist/services.d.ts.map +1 -1
  122. package/dist/services.js +244 -20
  123. package/dist/services.js.map +2 -2
  124. package/dist/theme.js +1 -1
  125. package/dist/theme.js.map +2 -2
  126. package/dist/wizard.d.ts.map +1 -1
  127. package/dist/wizard.js +76 -16
  128. package/dist/wizard.js.map +2 -2
  129. package/dist/world-packs.d.ts +15 -0
  130. package/dist/world-packs.d.ts.map +1 -1
  131. package/dist/world-packs.js +90 -87
  132. package/dist/world-packs.js.map +2 -2
  133. package/package.json +24 -21
  134. package/scripts/module-install/check-modules.mjs +204 -0
  135. package/scripts/module-install/fetch-module.mjs +1007 -0
  136. package/scripts/module-install/install-resolver.test.mjs +48 -0
  137. package/scripts/module-install/lib/catalog.mjs +96 -0
  138. package/scripts/module-install/lib/io.mjs +37 -0
  139. package/scripts/module-install/lib/link-from.mjs +80 -0
  140. package/scripts/module-install/lib/lock.mjs +78 -0
  141. package/scripts/module-install/lib/npm-resolver.mjs +55 -0
  142. package/scripts/module-install/lib/packages.mjs +142 -0
  143. package/scripts/module-install/lib/paths.mjs +48 -0
  144. package/scripts/module-install/lib/registry-index.mjs +50 -0
  145. package/scripts/module-install/link-local.test.mjs +51 -0
  146. package/scripts/module-install/local-resolver.test.mjs +51 -0
  147. package/scripts/module-install/registry-index.test.mjs +28 -0
  148. package/dist/module-wizard.d.ts +0 -14
  149. package/dist/module-wizard.d.ts.map +0 -1
  150. package/dist/module-wizard.js +0 -313
  151. package/dist/module-wizard.js.map +0 -7
  152. package/dist/pack-update/bindings.d.ts +0 -12
  153. package/dist/pack-update/bindings.d.ts.map +0 -1
  154. package/dist/pack-update/bindings.js +0 -64
  155. package/dist/pack-update/bindings.js.map +0 -7
  156. package/dist/pack-update/config.d.ts +0 -22
  157. package/dist/pack-update/config.d.ts.map +0 -1
  158. package/dist/pack-update/config.js +0 -141
  159. package/dist/pack-update/config.js.map +0 -7
  160. package/dist/pack-update/providers/curseforge.d.ts +0 -23
  161. package/dist/pack-update/providers/curseforge.d.ts.map +0 -1
  162. package/dist/pack-update/providers/curseforge.js +0 -209
  163. package/dist/pack-update/providers/curseforge.js.map +0 -7
  164. package/dist/pack-update/providers/index.d.ts +0 -16
  165. package/dist/pack-update/providers/index.d.ts.map +0 -1
  166. package/dist/pack-update/providers/index.js +0 -37
  167. package/dist/pack-update/providers/index.js.map +0 -7
  168. package/dist/pack-update/service.d.ts +0 -21
  169. package/dist/pack-update/service.d.ts.map +0 -1
  170. package/dist/pack-update/service.js +0 -567
  171. package/dist/pack-update/service.js.map +0 -7
  172. package/dist/pack-update/types.d.ts +0 -126
  173. package/dist/pack-update/types.d.ts.map +0 -1
  174. package/dist/pack-update/types.js +0 -8
  175. package/dist/pack-update/types.js.map +0 -7
  176. package/dist/pack-update/version-policy.d.ts +0 -80
  177. package/dist/pack-update/version-policy.d.ts.map +0 -1
  178. package/dist/pack-update/version-policy.js +0 -216
  179. package/dist/pack-update/version-policy.js.map +0 -7
  180. package/dist/remote-agent.d.ts +0 -18
  181. package/dist/remote-agent.d.ts.map +0 -1
  182. package/dist/remote-agent.js +0 -196
  183. package/dist/remote-agent.js.map +0 -7
@@ -1,13 +1,8 @@
1
1
  import { confirm, isCancel, multiselect } from "@clack/prompts";
2
- import crypto from "node:crypto";
3
- import fs from "node:fs";
4
- import path from "node:path";
5
2
  import {
6
3
  bumpPackPatchVersion,
7
- discoverPackRoots,
8
4
  disableInstalledPack,
9
5
  enableInstalledPack,
10
- extractArchiveToTemp,
11
6
  findInstalledPackById,
12
7
  installPackDirectory,
13
8
  isPackArchive,
@@ -15,24 +10,29 @@ import {
15
10
  listWorldEnableListResult,
16
11
  readPackDependencyUuids,
17
12
  readPackManifestInfo,
13
+ resolvePackRoots,
18
14
  uninstallInstalledPack,
19
15
  worldPackParentDir
20
16
  } from "@sfmc-bds/bds-tools/world-packs";
17
+ import crypto from "node:crypto";
18
+ import fs from "node:fs";
19
+ import path from "node:path";
21
20
  import { t } from "./i18n/index.js";
21
+ import { pushLog } from "./logs.js";
22
22
  import { BP_NAME, RP_NAME, resolveBdsContext } from "./pack-lifecycle.js";
23
23
  import {
24
24
  bindPackSource,
25
25
  bindingLabelForUuid,
26
26
  checkPackUpdates,
27
27
  formatSourcesList,
28
+ getBinding,
29
+ packSourcesPath,
28
30
  probeSourceAfterInstall,
29
- searchRemote
30
- } from "./pack-update/service.js";
31
- import { getBinding, removeBinding, packSourcesPath } from "./pack-update/bindings.js";
32
- import {
31
+ removeBinding,
33
32
  resolvePackTrashDir,
34
- resolveUninstallTrashDir
35
- } from "./pack-update/config.js";
33
+ resolveUninstallTrashDir,
34
+ searchRemote
35
+ } from "./pack-update/index.js";
36
36
  import { ROOT } from "./runtime.js";
37
37
  import { c } from "./theme.js";
38
38
  const PACKS_CMD_NAMES = ["packs", "addon"];
@@ -59,6 +59,9 @@ function restartHint() {
59
59
  function rejoinHint() {
60
60
  return t("packs.rejoinHint");
61
61
  }
62
+ function logPack(text, level = "info") {
63
+ pushLog(text, "pack", level);
64
+ }
62
65
  function isPacksCommand(cmd) {
63
66
  return !!cmd && PACKS_CMD_NAMES.includes(cmd);
64
67
  }
@@ -172,6 +175,23 @@ function formatUninstallPickLabel(pack) {
172
175
  const en = pack.enabled ? t("packs.list.on") : t("packs.list.off");
173
176
  return `[${kind}] ${pack.name} ${pack.folderName} ${en.trim()} ${pack.uuid.slice(0, 8)}`;
174
177
  }
178
+ function resolveUninstallTargets(packs, ids) {
179
+ const missing = [];
180
+ const byUuid = /* @__PURE__ */ new Map();
181
+ for (const id of ids) {
182
+ const pack = findInstalledPackById(packs, id);
183
+ if (!pack) {
184
+ missing.push(id);
185
+ continue;
186
+ }
187
+ byUuid.set(pack.uuid.toLowerCase(), pack);
188
+ }
189
+ if (missing.length) return { status: "not_found", missing };
190
+ const selected = [...byUuid.values()];
191
+ const blocked = selected.find((p) => isProtectedSfmcPack(p));
192
+ if (blocked) return { status: "protected", folder: blocked.folderName };
193
+ return { status: "ok", selected };
194
+ }
175
195
  async function pickPacksForUninstall(packs) {
176
196
  const candidates = packs.filter((p) => !isProtectedSfmcPack(p));
177
197
  if (candidates.length === 0) return [];
@@ -215,8 +235,8 @@ async function uninstallPacksBatch(selected, allPacks, opts) {
215
235
  const extra = q.pairedOf ? ` ${c.dim(t("packs.uninstall.pairedOf", { folder: q.pairedOf }))}` : "";
216
236
  return ` [${kind}] ${q.pack.folderName}${extra}`;
217
237
  }).join("\n");
218
- console.log(t("packs.uninstall.summary", { count: queue.length }));
219
- console.log(preview);
238
+ logPack(t("packs.uninstall.summary", { count: queue.length }), "info");
239
+ logPack(preview, "info");
220
240
  const ans = await confirm({
221
241
  message: t("packs.uninstall.confirm", { count: String(queue.length) }),
222
242
  initialValue: false
@@ -238,21 +258,23 @@ async function uninstallPacksBatch(selected, allPacks, opts) {
238
258
  return lines.join("\n");
239
259
  }
240
260
  function printConflict(existing, incoming) {
241
- console.log(c.yellow(t("packs.conflict")));
242
- console.log(
261
+ logPack(c.yellow(t("packs.conflict")), "warn");
262
+ logPack(
243
263
  t("packs.conflict.existing", {
244
264
  name: existing.name,
245
265
  version: fmtVer(existing.version),
246
266
  uuid: existing.uuid,
247
267
  dir: existing.dir
248
- })
268
+ }),
269
+ "info"
249
270
  );
250
- console.log(
271
+ logPack(
251
272
  t("packs.conflict.incoming", {
252
273
  name: incoming.name,
253
274
  version: fmtVer(incoming.version),
254
275
  uuid: incoming.uuid
255
- })
276
+ }),
277
+ "info"
256
278
  );
257
279
  }
258
280
  async function askOverwrite() {
@@ -265,7 +287,12 @@ async function askOverwrite() {
265
287
  return !!ans;
266
288
  }
267
289
  async function installOnePackRoot(opts) {
268
- const info = readPackManifestInfo(opts.srcDir);
290
+ let info;
291
+ try {
292
+ info = readPackManifestInfo(opts.srcDir);
293
+ } catch {
294
+ return { ok: false, reason: t("packs.badManifest") };
295
+ }
269
296
  if (!info) {
270
297
  return { ok: false, reason: t("packs.badManifest") };
271
298
  }
@@ -283,18 +310,18 @@ async function installOnePackRoot(opts) {
283
310
  } else if (opts.interactive && process.stdin.isTTY) {
284
311
  force = await askOverwrite();
285
312
  if (!force) {
286
- console.log(c.dim(t("packs.skippedNoOverwrite")));
313
+ logPack(c.dim(t("packs.skippedNoOverwrite")), "info");
287
314
  return { ok: false, skipped: true, reason: "conflict:skipped", info };
288
315
  }
289
316
  } else {
290
- console.log(c.yellow(t("packs.conflictSkip", { name: info.name })));
317
+ logPack(c.yellow(t("packs.conflictSkip", { name: info.name })), "warn");
291
318
  return { ok: false, skipped: true, reason: "conflict:noninteractive", info };
292
319
  }
293
320
  result = await installPackDirectory({
294
321
  srcDir: opts.srcDir,
295
322
  destParent,
296
- force: true,
297
- ...opts.folderHint ? { folderName: opts.folderHint } : {}
323
+ force: true
324
+ /* 路径由同 uuid 决策锁定,不依赖 folderHint,避免旁路目录 */
298
325
  });
299
326
  }
300
327
  if (!result.ok || !result.info || !result.destDir) {
@@ -313,7 +340,7 @@ async function installOnePackRoot(opts) {
313
340
  ...opts.interactive !== void 0 ? { interactive: opts.interactive } : {}
314
341
  });
315
342
  } catch (e) {
316
- console.log(c.yellow(`[packs] probe: ${e.message}`));
343
+ logPack(c.yellow(`probe: ${e.message}`), "warn");
317
344
  }
318
345
  return { ok: true, info: result.info };
319
346
  }
@@ -340,7 +367,7 @@ async function scanAndInstallInbox(opts) {
340
367
  try {
341
368
  ({ bdsRoot, levelName } = resolveBdsContext());
342
369
  } catch (e) {
343
- console.log(c.yellow(t("packs.inboxSkip", { message: e.message })));
370
+ logPack(c.yellow(t("packs.inboxSkip", { message: e.message })), "warn");
344
371
  return { installed: 0, skipped: 0, failed: candidates.length };
345
372
  }
346
373
  const interactive = opts?.interactive ?? !!process.stdin.isTTY;
@@ -350,12 +377,12 @@ async function scanAndInstallInbox(opts) {
350
377
  let installed = 0;
351
378
  let skipped = 0;
352
379
  let failed = 0;
353
- console.log(c.dim(t("packs.inboxFound", { count: candidates.length, level: levelName })));
380
+ logPack(c.dim(t("packs.inboxFound", { count: candidates.length, level: levelName })));
354
381
  for (const src of candidates) {
355
382
  const base = path.basename(src);
356
383
  const fp = sourceFingerprint(src);
357
384
  if (state.installed[fp] && !force) {
358
- console.log(c.dim(t("packs.alreadyDone", { name: base })));
385
+ logPack(c.dim(t("packs.alreadyDone", { name: base })));
359
386
  skipped++;
360
387
  if (!dryRun) {
361
388
  try {
@@ -365,38 +392,37 @@ async function scanAndInstallInbox(opts) {
365
392
  }
366
393
  continue;
367
394
  }
368
- let tempDir = null;
395
+ let disposeRoots = null;
369
396
  try {
370
397
  let roots = [];
371
- if (fs.statSync(src).isDirectory()) {
372
- roots = discoverPackRoots(src, { maxDepth: 2 });
373
- } else if (isPackArchive(src)) {
374
- if (dryRun) {
375
- console.log(c.dim(t("packs.dryRunExtract", { name: base })));
398
+ if (fs.statSync(src).isDirectory() || isPackArchive(src)) {
399
+ if (dryRun && isPackArchive(src) && fs.statSync(src).isFile()) {
400
+ logPack(c.dim(t("packs.dryRunExtract", { name: base })));
376
401
  skipped++;
377
402
  continue;
378
403
  }
379
- tempDir = await extractArchiveToTemp(src);
380
- roots = discoverPackRoots(tempDir, { maxDepth: 2 });
404
+ const resolved = await resolvePackRoots(src);
405
+ disposeRoots = resolved.dispose;
406
+ roots = resolved.roots;
381
407
  } else {
382
408
  failed++;
383
409
  if (!dryRun) moveTo(src, failedDir(), base);
384
410
  continue;
385
411
  }
386
412
  if (roots.length === 0) {
387
- console.log(c.red(t("packs.noPackRoot", { name: base })));
413
+ logPack(c.red(t("packs.noPackRoot", { name: base })), "error");
388
414
  failed++;
389
- if (!dryRun && !tempDir) moveTo(src, failedDir(), `${base}-no-root`);
415
+ if (!dryRun && fs.statSync(src).isDirectory()) moveTo(src, failedDir(), `${base}-no-root`);
390
416
  continue;
391
417
  }
392
418
  if (dryRun) {
393
419
  for (const r of roots) {
394
- const info = readPackManifestInfo(r);
395
- console.log(
396
- c.dim(
397
- `[packs] dry-run: ${base} \u2192 ${info?.kind ?? "?"} ${info?.name ?? path.basename(r)}`
398
- )
399
- );
420
+ try {
421
+ const info = readPackManifestInfo(r);
422
+ logPack(c.dim(`dry-run: ${base} \u2192 ${info?.kind ?? "?"} ${info?.name ?? path.basename(r)}`), "info");
423
+ } catch {
424
+ logPack(c.red(t("packs.badManifest")), "error");
425
+ }
400
426
  }
401
427
  skipped++;
402
428
  continue;
@@ -418,22 +444,21 @@ async function scanAndInstallInbox(opts) {
418
444
  if (r.ok && r.info) {
419
445
  okCount++;
420
446
  lastUuid = r.info.uuid;
421
- console.log(
447
+ logPack(
422
448
  c.green(
423
449
  t("packs.installedEnabled", {
424
450
  kind: r.info.kind === "resource" ? "RP" : "BP",
425
451
  name: r.info.name,
426
452
  version: fmtVer(r.info.version)
427
453
  })
428
- )
454
+ ),
455
+ "success"
429
456
  );
430
457
  } else if (r.skipped) {
431
458
  skipCount++;
432
459
  } else {
433
460
  failCount++;
434
- console.log(
435
- c.red(t("packs.installFailed", { name: path.basename(root), reason: r.reason ?? "?" }))
436
- );
461
+ logPack(c.red(t("packs.installFailed", { name: path.basename(root), reason: r.reason ?? "?" })), "error");
437
462
  }
438
463
  }
439
464
  installed += okCount > 0 ? 1 : 0;
@@ -443,12 +468,12 @@ async function scanAndInstallInbox(opts) {
443
468
  state.installed[fp] = { uuid: lastUuid, at: (/* @__PURE__ */ new Date()).toISOString(), folderName: base };
444
469
  writeState(state);
445
470
  moveTo(src, doneDir(), base);
446
- console.log(c.yellow(`[packs] ${restartHint()}`));
471
+ logPack(c.yellow(`${restartHint()}`), "warn");
447
472
  } else if (failCount > 0 && skipCount === 0) {
448
473
  moveTo(src, failedDir(), base);
449
474
  }
450
475
  } catch (e) {
451
- console.log(c.red(`[packs] ${base}: ${e.message}`));
476
+ logPack(c.red(`${base}: ${e.message}`), "error");
452
477
  failed++;
453
478
  if (!dryRun && fs.existsSync(src)) {
454
479
  try {
@@ -457,12 +482,7 @@ async function scanAndInstallInbox(opts) {
457
482
  }
458
483
  }
459
484
  } finally {
460
- if (tempDir) {
461
- try {
462
- fs.rmSync(tempDir, { recursive: true, force: true });
463
- } catch {
464
- }
465
- }
485
+ if (disposeRoots) disposeRoots();
466
486
  }
467
487
  }
468
488
  return { installed, skipped, failed };
@@ -489,9 +509,7 @@ function formatPackList(packs) {
489
509
  for (const p of bp) {
490
510
  const en = p.enabled ? c.green(t("packs.list.on")) : c.dim(t("packs.list.off"));
491
511
  const src = bindingLabelForUuid(p.uuid);
492
- lines.push(
493
- ` [${en}] ${p.folderName} ${p.name} v${fmtVer(p.version)} ${c.dim(p.uuid)} ${c.dim(src)}`
494
- );
512
+ lines.push(` [${en}] ${p.folderName} ${p.name} v${fmtVer(p.version)} ${c.dim(p.uuid)} ${c.dim(src)}`);
495
513
  }
496
514
  }
497
515
  if (rp.length) {
@@ -662,28 +680,15 @@ async function dispatchPacksCommand(sub, args) {
662
680
  if (picked === null) return c.dim(t("packs.uninstall.cancelled"));
663
681
  if (picked.length === 0) return c.yellow(t("packs.uninstall.none"));
664
682
  selected = picked;
665
- } else if (ids.length === 1) {
666
- const pack = findInstalledPackById(packs, ids[0]);
667
- if (!pack) return c.red(t("packs.notFound", { id: ids[0] }));
668
- if (isProtectedSfmcPack(pack)) {
669
- return c.red(t("packs.uninstall.protected", { folder: pack.folderName }));
670
- }
671
- selected = [pack];
672
683
  } else {
673
- const missing = [];
674
- const byUuid = /* @__PURE__ */ new Map();
675
- for (const id of ids) {
676
- const pack = findInstalledPackById(packs, id);
677
- if (!pack) {
678
- missing.push(id);
679
- continue;
680
- }
681
- byUuid.set(pack.uuid.toLowerCase(), pack);
684
+ const resolved = resolveUninstallTargets(packs, ids);
685
+ if (resolved.status === "not_found") {
686
+ return c.red(t("packs.notFound", { id: resolved.missing.join(", ") }));
682
687
  }
683
- if (missing.length) {
684
- return c.red(t("packs.notFound", { id: missing.join(", ") }));
688
+ if (resolved.status === "protected") {
689
+ return c.red(t("packs.uninstall.protected", { folder: resolved.folder }));
685
690
  }
686
- selected = [...byUuid.values()];
691
+ selected = resolved.selected;
687
692
  }
688
693
  return await uninstallPacksBatch(selected, packs, {
689
694
  bdsRoot,
@@ -744,17 +749,14 @@ async function dispatchPacksCommand(sub, args) {
744
749
  const abs = path.resolve(target);
745
750
  if (!fs.existsSync(abs)) return c.red(t("packs.pathMissing", { path: abs }));
746
751
  const { bdsRoot, levelName } = resolveBdsContext();
747
- let tempDir = null;
752
+ let disposeRoots = null;
748
753
  try {
749
- let roots;
750
- if (fs.statSync(abs).isDirectory()) {
751
- roots = discoverPackRoots(abs, { maxDepth: 2 });
752
- } else if (isPackArchive(abs)) {
753
- tempDir = await extractArchiveToTemp(abs);
754
- roots = discoverPackRoots(tempDir, { maxDepth: 2 });
755
- } else {
754
+ if (!fs.statSync(abs).isDirectory() && !isPackArchive(abs)) {
756
755
  return c.red(t("packs.notArchive"));
757
756
  }
757
+ const resolved = await resolvePackRoots(abs);
758
+ disposeRoots = resolved.dispose;
759
+ const roots = resolved.roots;
758
760
  if (roots.length === 0) return c.red(t("packs.noManifestRoot"));
759
761
  const lines = [];
760
762
  for (const root of roots) {
@@ -790,7 +792,7 @@ async function dispatchPacksCommand(sub, args) {
790
792
  lines.push(c.yellow(restartHint()));
791
793
  return lines.join("\n");
792
794
  } finally {
793
- if (tempDir) fs.rmSync(tempDir, { recursive: true, force: true });
795
+ if (disposeRoots) disposeRoots();
794
796
  }
795
797
  }
796
798
  case "scan": {
@@ -867,6 +869,7 @@ export {
867
869
  isProtectedSfmcPack,
868
870
  packsInboxDir,
869
871
  packsUsage,
872
+ resolveUninstallTargets,
870
873
  scanAndInstallInbox
871
874
  };
872
875
  //# sourceMappingURL=world-packs.js.map