@sfmc-bds/cli 0.1.0 → 0.2.0-beta.0
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 +7 -1
- package/dist/cli-result.d.ts +12 -0
- package/dist/cli-result.d.ts.map +1 -0
- package/dist/cli-result.js +15 -0
- package/dist/cli-result.js.map +7 -0
- package/dist/commands-behavior-pack.d.ts +11 -42
- package/dist/commands-behavior-pack.d.ts.map +1 -1
- package/dist/commands-behavior-pack.js +40 -159
- package/dist/commands-behavior-pack.js.map +7 -1
- package/dist/commands-reload.d.ts +9 -0
- package/dist/commands-reload.d.ts.map +1 -0
- package/dist/commands-reload.js +30 -0
- package/dist/commands-reload.js.map +7 -0
- package/dist/commands.d.ts +7 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +182 -172
- package/dist/commands.js.map +7 -1
- package/dist/i18n/index.d.ts +26 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +107 -0
- package/dist/i18n/index.js.map +7 -0
- package/dist/i18n/locales/en.d.ts +381 -0
- package/dist/i18n/locales/en.d.ts.map +1 -0
- package/dist/i18n/locales/en.js +393 -0
- package/dist/i18n/locales/en.js.map +7 -0
- package/dist/i18n/locales/zh-CN.d.ts +4 -0
- package/dist/i18n/locales/zh-CN.d.ts.map +1 -0
- package/dist/i18n/locales/zh-CN.js +379 -0
- package/dist/i18n/locales/zh-CN.js.map +7 -0
- package/dist/i18n/types.d.ts +7 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +17 -0
- package/dist/i18n/types.js.map +7 -0
- package/dist/index.d.ts +5 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -43
- package/dist/index.js.map +7 -1
- package/dist/interactive-prompts.d.ts +8 -0
- package/dist/interactive-prompts.d.ts.map +1 -0
- package/dist/interactive-prompts.js +57 -0
- package/dist/interactive-prompts.js.map +7 -0
- package/dist/locale-command.d.ts +3 -0
- package/dist/locale-command.d.ts.map +1 -0
- package/dist/locale-command.js +19 -0
- package/dist/locale-command.js.map +7 -0
- package/dist/logs.d.ts +22 -4
- package/dist/logs.d.ts.map +1 -1
- package/dist/logs.js +208 -193
- package/dist/logs.js.map +7 -1
- package/dist/main.js +135 -173
- package/dist/main.js.map +7 -1
- package/dist/module-commands.d.ts +43 -4
- package/dist/module-commands.d.ts.map +1 -1
- package/dist/module-commands.js +440 -327
- package/dist/module-commands.js.map +7 -1
- package/dist/module-fingerprint.d.ts +7 -0
- package/dist/module-fingerprint.d.ts.map +1 -0
- package/dist/module-fingerprint.js +31 -0
- package/dist/module-fingerprint.js.map +7 -0
- package/dist/module-wizard.d.ts +14 -0
- package/dist/module-wizard.d.ts.map +1 -0
- package/dist/module-wizard.js +313 -0
- package/dist/module-wizard.js.map +7 -0
- package/dist/pack-lifecycle.d.ts +95 -0
- package/dist/pack-lifecycle.d.ts.map +1 -0
- package/dist/pack-lifecycle.js +634 -0
- package/dist/pack-lifecycle.js.map +7 -0
- package/dist/pack-update/bindings.d.ts +12 -0
- package/dist/pack-update/bindings.d.ts.map +1 -0
- package/dist/pack-update/bindings.js +64 -0
- package/dist/pack-update/bindings.js.map +7 -0
- package/dist/pack-update/config.d.ts +22 -0
- package/dist/pack-update/config.d.ts.map +1 -0
- package/dist/pack-update/config.js +141 -0
- package/dist/pack-update/config.js.map +7 -0
- package/dist/pack-update/providers/curseforge.d.ts +23 -0
- package/dist/pack-update/providers/curseforge.d.ts.map +1 -0
- package/dist/pack-update/providers/curseforge.js +209 -0
- package/dist/pack-update/providers/curseforge.js.map +7 -0
- package/dist/pack-update/providers/index.d.ts +16 -0
- package/dist/pack-update/providers/index.d.ts.map +1 -0
- package/dist/pack-update/providers/index.js +37 -0
- package/dist/pack-update/providers/index.js.map +7 -0
- package/dist/pack-update/service.d.ts +21 -0
- package/dist/pack-update/service.d.ts.map +1 -0
- package/dist/pack-update/service.js +567 -0
- package/dist/pack-update/service.js.map +7 -0
- package/dist/pack-update/types.d.ts +126 -0
- package/dist/pack-update/types.d.ts.map +1 -0
- package/dist/pack-update/types.js +8 -0
- package/dist/pack-update/types.js.map +7 -0
- package/dist/pack-update/version-policy.d.ts +80 -0
- package/dist/pack-update/version-policy.d.ts.map +1 -0
- package/dist/pack-update/version-policy.js +216 -0
- package/dist/pack-update/version-policy.js.map +7 -0
- package/dist/registry.d.ts +12 -4
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +73 -106
- package/dist/registry.js.map +7 -1
- package/dist/remote-agent.d.ts.map +1 -1
- package/dist/remote-agent.js +160 -176
- package/dist/remote-agent.js.map +7 -1
- package/dist/repl.d.ts +4 -0
- package/dist/repl.d.ts.map +1 -1
- package/dist/repl.js +683 -605
- package/dist/repl.js.map +7 -1
- package/dist/runtime.d.ts +40 -14
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +185 -62
- package/dist/runtime.js.map +7 -1
- package/dist/services.d.ts +2 -0
- package/dist/services.d.ts.map +1 -1
- package/dist/services.js +251 -238
- package/dist/services.js.map +7 -1
- package/dist/sfmc-modules-root.d.ts +24 -0
- package/dist/sfmc-modules-root.d.ts.map +1 -0
- package/dist/sfmc-modules-root.js +70 -0
- package/dist/sfmc-modules-root.js.map +7 -0
- package/dist/theme.d.ts +4 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +134 -48
- package/dist/theme.js.map +7 -1
- package/dist/wizard.d.ts.map +1 -1
- package/dist/wizard.js +363 -438
- package/dist/wizard.js.map +7 -1
- package/dist/world-packs.d.ts +22 -0
- package/dist/world-packs.d.ts.map +1 -0
- package/dist/world-packs.js +872 -0
- package/dist/world-packs.js.map +7 -0
- package/package.json +10 -10
- package/dist/dispatcher.d.ts +0 -13
- package/dist/dispatcher.d.ts.map +0 -1
- package/dist/dispatcher.js +0 -23
- package/dist/dispatcher.js.map +0 -1
- package/dist/terminal.d.ts +0 -3
- package/dist/terminal.d.ts.map +0 -1
- package/dist/terminal.js +0 -178
- package/dist/terminal.js.map +0 -1
|
@@ -0,0 +1,872 @@
|
|
|
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
|
+
import {
|
|
6
|
+
bumpPackPatchVersion,
|
|
7
|
+
discoverPackRoots,
|
|
8
|
+
disableInstalledPack,
|
|
9
|
+
enableInstalledPack,
|
|
10
|
+
extractArchiveToTemp,
|
|
11
|
+
findInstalledPackById,
|
|
12
|
+
installPackDirectory,
|
|
13
|
+
isPackArchive,
|
|
14
|
+
listInstalledWorldPacks,
|
|
15
|
+
listWorldEnableListResult,
|
|
16
|
+
readPackDependencyUuids,
|
|
17
|
+
readPackManifestInfo,
|
|
18
|
+
uninstallInstalledPack,
|
|
19
|
+
worldPackParentDir
|
|
20
|
+
} from "@sfmc-bds/bds-tools/world-packs";
|
|
21
|
+
import { t } from "./i18n/index.js";
|
|
22
|
+
import { BP_NAME, RP_NAME, resolveBdsContext } from "./pack-lifecycle.js";
|
|
23
|
+
import {
|
|
24
|
+
bindPackSource,
|
|
25
|
+
bindingLabelForUuid,
|
|
26
|
+
checkPackUpdates,
|
|
27
|
+
formatSourcesList,
|
|
28
|
+
probeSourceAfterInstall,
|
|
29
|
+
searchRemote
|
|
30
|
+
} from "./pack-update/service.js";
|
|
31
|
+
import { getBinding, removeBinding, packSourcesPath } from "./pack-update/bindings.js";
|
|
32
|
+
import {
|
|
33
|
+
resolvePackTrashDir,
|
|
34
|
+
resolveUninstallTrashDir
|
|
35
|
+
} from "./pack-update/config.js";
|
|
36
|
+
import { ROOT } from "./runtime.js";
|
|
37
|
+
import { c } from "./theme.js";
|
|
38
|
+
const PACKS_CMD_NAMES = ["packs", "addon"];
|
|
39
|
+
const PACKS_SUBCOMMANDS = [
|
|
40
|
+
"list",
|
|
41
|
+
"search",
|
|
42
|
+
"enable",
|
|
43
|
+
"disable",
|
|
44
|
+
"uninstall",
|
|
45
|
+
"bump",
|
|
46
|
+
"install",
|
|
47
|
+
"scan",
|
|
48
|
+
"doctor",
|
|
49
|
+
"path",
|
|
50
|
+
"bind",
|
|
51
|
+
"unbind",
|
|
52
|
+
"sources",
|
|
53
|
+
"check",
|
|
54
|
+
"update"
|
|
55
|
+
];
|
|
56
|
+
function restartHint() {
|
|
57
|
+
return t("packs.restartHint");
|
|
58
|
+
}
|
|
59
|
+
function rejoinHint() {
|
|
60
|
+
return t("packs.rejoinHint");
|
|
61
|
+
}
|
|
62
|
+
function isPacksCommand(cmd) {
|
|
63
|
+
return !!cmd && PACKS_CMD_NAMES.includes(cmd);
|
|
64
|
+
}
|
|
65
|
+
function packsInboxDir() {
|
|
66
|
+
return path.join(ROOT, "packs");
|
|
67
|
+
}
|
|
68
|
+
function doneDir() {
|
|
69
|
+
return path.join(packsInboxDir(), "_done");
|
|
70
|
+
}
|
|
71
|
+
function failedDir() {
|
|
72
|
+
return path.join(packsInboxDir(), "_failed");
|
|
73
|
+
}
|
|
74
|
+
function statePath() {
|
|
75
|
+
return path.join(packsInboxDir(), "inbox-state.json");
|
|
76
|
+
}
|
|
77
|
+
function ensureInboxLayout() {
|
|
78
|
+
for (const d of [packsInboxDir(), doneDir(), failedDir(), resolvePackTrashDir()]) {
|
|
79
|
+
fs.mkdirSync(d, { recursive: true });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function readState() {
|
|
83
|
+
const file = statePath();
|
|
84
|
+
if (!fs.existsSync(file)) return { installed: {} };
|
|
85
|
+
try {
|
|
86
|
+
const raw = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
87
|
+
return { installed: raw.installed ?? {} };
|
|
88
|
+
} catch {
|
|
89
|
+
return { installed: {} };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function writeState(state) {
|
|
93
|
+
ensureInboxLayout();
|
|
94
|
+
fs.writeFileSync(statePath(), `${JSON.stringify(state, null, 2)}
|
|
95
|
+
`, "utf8");
|
|
96
|
+
}
|
|
97
|
+
function sourceFingerprint(absPath) {
|
|
98
|
+
const st = fs.statSync(absPath);
|
|
99
|
+
const payload = `${absPath}|${st.size}|${st.mtimeMs}`;
|
|
100
|
+
return crypto.createHash("sha1").update(payload).digest("hex");
|
|
101
|
+
}
|
|
102
|
+
function stampName(name) {
|
|
103
|
+
const safe = name.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").slice(0, 80);
|
|
104
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
105
|
+
return `${ts}-${safe || "pack"}`;
|
|
106
|
+
}
|
|
107
|
+
function moveTo(src, destParent, label) {
|
|
108
|
+
fs.mkdirSync(destParent, { recursive: true });
|
|
109
|
+
const dest = path.join(destParent, stampName(label));
|
|
110
|
+
fs.renameSync(src, dest);
|
|
111
|
+
return dest;
|
|
112
|
+
}
|
|
113
|
+
function fmtVer(v) {
|
|
114
|
+
return v.join(".");
|
|
115
|
+
}
|
|
116
|
+
function pruneInboxStateByUuid(uuid) {
|
|
117
|
+
const state = readState();
|
|
118
|
+
let changed = false;
|
|
119
|
+
for (const [fp, meta] of Object.entries(state.installed)) {
|
|
120
|
+
if (meta.uuid.toLowerCase() === uuid.toLowerCase()) {
|
|
121
|
+
delete state.installed[fp];
|
|
122
|
+
changed = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (changed) writeState(state);
|
|
126
|
+
}
|
|
127
|
+
function isProtectedSfmcPack(pack) {
|
|
128
|
+
const folder = pack.folderName.toLowerCase();
|
|
129
|
+
return folder === BP_NAME.toLowerCase() || folder === RP_NAME.toLowerCase();
|
|
130
|
+
}
|
|
131
|
+
function formatUninstallOutcome(r) {
|
|
132
|
+
if (r.status === "protected") {
|
|
133
|
+
return c.red(t("packs.uninstall.protected", { folder: r.folder }));
|
|
134
|
+
}
|
|
135
|
+
if (r.status === "trashed") {
|
|
136
|
+
return c.green(t("packs.uninstall.trashed", { folder: r.folder, dest: r.dest }));
|
|
137
|
+
}
|
|
138
|
+
if (r.status === "missing") {
|
|
139
|
+
return c.yellow(t("packs.uninstall.dirMissing", { folder: r.folder }));
|
|
140
|
+
}
|
|
141
|
+
return c.green(t("packs.uninstall.deleted", { folder: r.folder }));
|
|
142
|
+
}
|
|
143
|
+
async function uninstallOneInstalledPack(pack, opts) {
|
|
144
|
+
if (isProtectedSfmcPack(pack)) {
|
|
145
|
+
return { status: "protected", folder: pack.folderName };
|
|
146
|
+
}
|
|
147
|
+
const trash = resolveUninstallTrashDir({ purge: opts.purge });
|
|
148
|
+
const r = await uninstallInstalledPack({
|
|
149
|
+
bdsRoot: opts.bdsRoot,
|
|
150
|
+
levelName: opts.levelName,
|
|
151
|
+
pack,
|
|
152
|
+
trashDir: trash
|
|
153
|
+
});
|
|
154
|
+
pruneInboxStateByUuid(pack.uuid);
|
|
155
|
+
if (r.action === "trashed") {
|
|
156
|
+
return { status: "trashed", folder: pack.folderName, dest: r.dest };
|
|
157
|
+
}
|
|
158
|
+
if (r.action === "missing") {
|
|
159
|
+
return { status: "missing", folder: pack.folderName };
|
|
160
|
+
}
|
|
161
|
+
return { status: "deleted", folder: pack.folderName };
|
|
162
|
+
}
|
|
163
|
+
function resolvePairedResource(pack, packs, noPaired) {
|
|
164
|
+
if (pack.kind !== "behavior" || noPaired) return null;
|
|
165
|
+
const binding = getBinding(pack.uuid);
|
|
166
|
+
const pairedUuid = binding?.pairedResourceUuid ?? (fs.existsSync(pack.dir) ? readPackDependencyUuids(pack.dir)[0] : void 0) ?? null;
|
|
167
|
+
if (!pairedUuid) return null;
|
|
168
|
+
return packs.find((p) => p.kind === "resource" && p.uuid.toLowerCase() === pairedUuid.toLowerCase()) ?? null;
|
|
169
|
+
}
|
|
170
|
+
function formatUninstallPickLabel(pack) {
|
|
171
|
+
const kind = pack.kind === "behavior" ? "BP" : "RP";
|
|
172
|
+
const en = pack.enabled ? t("packs.list.on") : t("packs.list.off");
|
|
173
|
+
return `[${kind}] ${pack.name} ${pack.folderName} ${en.trim()} ${pack.uuid.slice(0, 8)}`;
|
|
174
|
+
}
|
|
175
|
+
async function pickPacksForUninstall(packs) {
|
|
176
|
+
const candidates = packs.filter((p) => !isProtectedSfmcPack(p));
|
|
177
|
+
if (candidates.length === 0) return [];
|
|
178
|
+
const bp = candidates.filter((p) => p.kind === "behavior");
|
|
179
|
+
const rp = candidates.filter((p) => p.kind === "resource");
|
|
180
|
+
const options = [
|
|
181
|
+
...bp.map((p) => ({ value: p.uuid, label: formatUninstallPickLabel(p) })),
|
|
182
|
+
...rp.map((p) => ({ value: p.uuid, label: formatUninstallPickLabel(p) }))
|
|
183
|
+
];
|
|
184
|
+
const selected = await multiselect({
|
|
185
|
+
message: t("packs.uninstall.pick"),
|
|
186
|
+
options,
|
|
187
|
+
required: true
|
|
188
|
+
});
|
|
189
|
+
if (isCancel(selected)) return null;
|
|
190
|
+
const set = new Set(selected);
|
|
191
|
+
return candidates.filter((p) => set.has(p.uuid));
|
|
192
|
+
}
|
|
193
|
+
async function uninstallPacksBatch(selected, allPacks, opts) {
|
|
194
|
+
if (selected.length === 0) return c.yellow(t("packs.uninstall.none"));
|
|
195
|
+
const queue = [];
|
|
196
|
+
const seen = /* @__PURE__ */ new Set();
|
|
197
|
+
for (const pack of selected) {
|
|
198
|
+
const key = pack.uuid.toLowerCase();
|
|
199
|
+
if (seen.has(key)) continue;
|
|
200
|
+
seen.add(key);
|
|
201
|
+
queue.push({ pack });
|
|
202
|
+
if (isProtectedSfmcPack(pack)) continue;
|
|
203
|
+
const paired = resolvePairedResource(pack, allPacks, opts.noPaired);
|
|
204
|
+
if (paired) {
|
|
205
|
+
const pk = paired.uuid.toLowerCase();
|
|
206
|
+
if (!seen.has(pk)) {
|
|
207
|
+
seen.add(pk);
|
|
208
|
+
queue.push({ pack: paired, pairedOf: pack.folderName });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (opts.askConfirm && process.stdin.isTTY) {
|
|
213
|
+
const preview = queue.map((q) => {
|
|
214
|
+
const kind = q.pack.kind === "behavior" ? "BP" : "RP";
|
|
215
|
+
const extra = q.pairedOf ? ` ${c.dim(t("packs.uninstall.pairedOf", { folder: q.pairedOf }))}` : "";
|
|
216
|
+
return ` [${kind}] ${q.pack.folderName}${extra}`;
|
|
217
|
+
}).join("\n");
|
|
218
|
+
console.log(t("packs.uninstall.summary", { count: queue.length }));
|
|
219
|
+
console.log(preview);
|
|
220
|
+
const ans = await confirm({
|
|
221
|
+
message: t("packs.uninstall.confirm", { count: String(queue.length) }),
|
|
222
|
+
initialValue: false
|
|
223
|
+
});
|
|
224
|
+
if (isCancel(ans) || !ans) return c.dim(t("packs.uninstall.cancelled"));
|
|
225
|
+
}
|
|
226
|
+
const lines = [];
|
|
227
|
+
for (const item of queue) {
|
|
228
|
+
const outcome = await uninstallOneInstalledPack(item.pack, opts);
|
|
229
|
+
lines.push(formatUninstallOutcome(outcome));
|
|
230
|
+
if (item.pack.kind === "behavior" && outcome.status !== "protected") {
|
|
231
|
+
const removed = removeBinding(item.pack.uuid);
|
|
232
|
+
if (removed) {
|
|
233
|
+
lines.push(c.dim(t("packs.uninstall.bindingRemoved", { uuid: item.pack.uuid })));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
lines.push(c.yellow(restartHint()));
|
|
238
|
+
return lines.join("\n");
|
|
239
|
+
}
|
|
240
|
+
function printConflict(existing, incoming) {
|
|
241
|
+
console.log(c.yellow(t("packs.conflict")));
|
|
242
|
+
console.log(
|
|
243
|
+
t("packs.conflict.existing", {
|
|
244
|
+
name: existing.name,
|
|
245
|
+
version: fmtVer(existing.version),
|
|
246
|
+
uuid: existing.uuid,
|
|
247
|
+
dir: existing.dir
|
|
248
|
+
})
|
|
249
|
+
);
|
|
250
|
+
console.log(
|
|
251
|
+
t("packs.conflict.incoming", {
|
|
252
|
+
name: incoming.name,
|
|
253
|
+
version: fmtVer(incoming.version),
|
|
254
|
+
uuid: incoming.uuid
|
|
255
|
+
})
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
async function askOverwrite() {
|
|
259
|
+
if (!process.stdin.isTTY) return false;
|
|
260
|
+
const ans = await confirm({
|
|
261
|
+
message: t("packs.overwriteAsk"),
|
|
262
|
+
initialValue: false
|
|
263
|
+
});
|
|
264
|
+
if (isCancel(ans)) return false;
|
|
265
|
+
return !!ans;
|
|
266
|
+
}
|
|
267
|
+
async function installOnePackRoot(opts) {
|
|
268
|
+
const info = readPackManifestInfo(opts.srcDir);
|
|
269
|
+
if (!info) {
|
|
270
|
+
return { ok: false, reason: t("packs.badManifest") };
|
|
271
|
+
}
|
|
272
|
+
const destParent = worldPackParentDir(opts.bdsRoot, opts.levelName, info.kind);
|
|
273
|
+
let force = !!opts.force;
|
|
274
|
+
let result = await installPackDirectory({
|
|
275
|
+
srcDir: opts.srcDir,
|
|
276
|
+
destParent,
|
|
277
|
+
force: false,
|
|
278
|
+
...opts.folderHint ? { folderName: opts.folderHint } : {}
|
|
279
|
+
});
|
|
280
|
+
if (result.conflict) {
|
|
281
|
+
printConflict(result.conflict.existing, result.conflict.incoming);
|
|
282
|
+
if (force) {
|
|
283
|
+
} else if (opts.interactive && process.stdin.isTTY) {
|
|
284
|
+
force = await askOverwrite();
|
|
285
|
+
if (!force) {
|
|
286
|
+
console.log(c.dim(t("packs.skippedNoOverwrite")));
|
|
287
|
+
return { ok: false, skipped: true, reason: "conflict:skipped", info };
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
console.log(c.yellow(t("packs.conflictSkip", { name: info.name })));
|
|
291
|
+
return { ok: false, skipped: true, reason: "conflict:noninteractive", info };
|
|
292
|
+
}
|
|
293
|
+
result = await installPackDirectory({
|
|
294
|
+
srcDir: opts.srcDir,
|
|
295
|
+
destParent,
|
|
296
|
+
force: true,
|
|
297
|
+
...opts.folderHint ? { folderName: opts.folderHint } : {}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
if (!result.ok || !result.info || !result.destDir) {
|
|
301
|
+
return { ok: false, reason: result.reason ?? "install failed", info };
|
|
302
|
+
}
|
|
303
|
+
await enableInstalledPack({
|
|
304
|
+
bdsRoot: opts.bdsRoot,
|
|
305
|
+
levelName: opts.levelName,
|
|
306
|
+
info: result.info
|
|
307
|
+
});
|
|
308
|
+
try {
|
|
309
|
+
await probeSourceAfterInstall({
|
|
310
|
+
info: result.info,
|
|
311
|
+
packDir: result.destDir,
|
|
312
|
+
...result.folderName ? { folderName: result.folderName } : {},
|
|
313
|
+
...opts.interactive !== void 0 ? { interactive: opts.interactive } : {}
|
|
314
|
+
});
|
|
315
|
+
} catch (e) {
|
|
316
|
+
console.log(c.yellow(`[packs] probe: ${e.message}`));
|
|
317
|
+
}
|
|
318
|
+
return { ok: true, info: result.info };
|
|
319
|
+
}
|
|
320
|
+
function listInboxCandidates() {
|
|
321
|
+
const inbox = packsInboxDir();
|
|
322
|
+
if (!fs.existsSync(inbox)) return [];
|
|
323
|
+
const out = [];
|
|
324
|
+
for (const e of fs.readdirSync(inbox, { withFileTypes: true })) {
|
|
325
|
+
if (e.name.startsWith("_") || e.name === "inbox-state.json") continue;
|
|
326
|
+
const full = path.join(inbox, e.name);
|
|
327
|
+
if (e.isDirectory()) out.push(full);
|
|
328
|
+
else if (e.isFile() && isPackArchive(full)) out.push(full);
|
|
329
|
+
}
|
|
330
|
+
return out;
|
|
331
|
+
}
|
|
332
|
+
async function scanAndInstallInbox(opts) {
|
|
333
|
+
ensureInboxLayout();
|
|
334
|
+
const candidates = listInboxCandidates();
|
|
335
|
+
if (candidates.length === 0) {
|
|
336
|
+
return { installed: 0, skipped: 0, failed: 0 };
|
|
337
|
+
}
|
|
338
|
+
let bdsRoot;
|
|
339
|
+
let levelName;
|
|
340
|
+
try {
|
|
341
|
+
({ bdsRoot, levelName } = resolveBdsContext());
|
|
342
|
+
} catch (e) {
|
|
343
|
+
console.log(c.yellow(t("packs.inboxSkip", { message: e.message })));
|
|
344
|
+
return { installed: 0, skipped: 0, failed: candidates.length };
|
|
345
|
+
}
|
|
346
|
+
const interactive = opts?.interactive ?? !!process.stdin.isTTY;
|
|
347
|
+
const force = !!opts?.force;
|
|
348
|
+
const dryRun = !!opts?.dryRun;
|
|
349
|
+
const state = readState();
|
|
350
|
+
let installed = 0;
|
|
351
|
+
let skipped = 0;
|
|
352
|
+
let failed = 0;
|
|
353
|
+
console.log(c.dim(t("packs.inboxFound", { count: candidates.length, level: levelName })));
|
|
354
|
+
for (const src of candidates) {
|
|
355
|
+
const base = path.basename(src);
|
|
356
|
+
const fp = sourceFingerprint(src);
|
|
357
|
+
if (state.installed[fp] && !force) {
|
|
358
|
+
console.log(c.dim(t("packs.alreadyDone", { name: base })));
|
|
359
|
+
skipped++;
|
|
360
|
+
if (!dryRun) {
|
|
361
|
+
try {
|
|
362
|
+
moveTo(src, doneDir(), base);
|
|
363
|
+
} catch {
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
let tempDir = null;
|
|
369
|
+
try {
|
|
370
|
+
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 })));
|
|
376
|
+
skipped++;
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
tempDir = await extractArchiveToTemp(src);
|
|
380
|
+
roots = discoverPackRoots(tempDir, { maxDepth: 2 });
|
|
381
|
+
} else {
|
|
382
|
+
failed++;
|
|
383
|
+
if (!dryRun) moveTo(src, failedDir(), base);
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
if (roots.length === 0) {
|
|
387
|
+
console.log(c.red(t("packs.noPackRoot", { name: base })));
|
|
388
|
+
failed++;
|
|
389
|
+
if (!dryRun && !tempDir) moveTo(src, failedDir(), `${base}-no-root`);
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (dryRun) {
|
|
393
|
+
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
|
+
);
|
|
400
|
+
}
|
|
401
|
+
skipped++;
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
let okCount = 0;
|
|
405
|
+
let skipCount = 0;
|
|
406
|
+
let failCount = 0;
|
|
407
|
+
let lastUuid = "";
|
|
408
|
+
for (const root of roots) {
|
|
409
|
+
const folderHint = roots.length === 1 && fs.statSync(src).isFile() ? path.basename(src, path.extname(src)) : path.basename(root);
|
|
410
|
+
const r = await installOnePackRoot({
|
|
411
|
+
srcDir: root,
|
|
412
|
+
bdsRoot,
|
|
413
|
+
levelName,
|
|
414
|
+
folderHint,
|
|
415
|
+
force,
|
|
416
|
+
interactive
|
|
417
|
+
});
|
|
418
|
+
if (r.ok && r.info) {
|
|
419
|
+
okCount++;
|
|
420
|
+
lastUuid = r.info.uuid;
|
|
421
|
+
console.log(
|
|
422
|
+
c.green(
|
|
423
|
+
t("packs.installedEnabled", {
|
|
424
|
+
kind: r.info.kind === "resource" ? "RP" : "BP",
|
|
425
|
+
name: r.info.name,
|
|
426
|
+
version: fmtVer(r.info.version)
|
|
427
|
+
})
|
|
428
|
+
)
|
|
429
|
+
);
|
|
430
|
+
} else if (r.skipped) {
|
|
431
|
+
skipCount++;
|
|
432
|
+
} else {
|
|
433
|
+
failCount++;
|
|
434
|
+
console.log(
|
|
435
|
+
c.red(t("packs.installFailed", { name: path.basename(root), reason: r.reason ?? "?" }))
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
installed += okCount > 0 ? 1 : 0;
|
|
440
|
+
skipped += skipCount;
|
|
441
|
+
failed += failCount;
|
|
442
|
+
if (okCount > 0) {
|
|
443
|
+
state.installed[fp] = { uuid: lastUuid, at: (/* @__PURE__ */ new Date()).toISOString(), folderName: base };
|
|
444
|
+
writeState(state);
|
|
445
|
+
moveTo(src, doneDir(), base);
|
|
446
|
+
console.log(c.yellow(`[packs] ${restartHint()}`));
|
|
447
|
+
} else if (failCount > 0 && skipCount === 0) {
|
|
448
|
+
moveTo(src, failedDir(), base);
|
|
449
|
+
}
|
|
450
|
+
} catch (e) {
|
|
451
|
+
console.log(c.red(`[packs] ${base}: ${e.message}`));
|
|
452
|
+
failed++;
|
|
453
|
+
if (!dryRun && fs.existsSync(src)) {
|
|
454
|
+
try {
|
|
455
|
+
moveTo(src, failedDir(), base);
|
|
456
|
+
} catch {
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
} finally {
|
|
460
|
+
if (tempDir) {
|
|
461
|
+
try {
|
|
462
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
463
|
+
} catch {
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return { installed, skipped, failed };
|
|
469
|
+
}
|
|
470
|
+
function filterPacks(packs, kind, search) {
|
|
471
|
+
let list = packs;
|
|
472
|
+
if (kind === "bp") list = list.filter((p) => p.kind === "behavior");
|
|
473
|
+
if (kind === "rp") list = list.filter((p) => p.kind === "resource");
|
|
474
|
+
if (search) {
|
|
475
|
+
const q = search.toLowerCase();
|
|
476
|
+
list = list.filter(
|
|
477
|
+
(p) => p.name.toLowerCase().includes(q) || p.folderName.toLowerCase().includes(q) || p.uuid.toLowerCase().includes(q)
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
return list;
|
|
481
|
+
}
|
|
482
|
+
function formatPackList(packs) {
|
|
483
|
+
if (packs.length === 0) return c.dim(t("packs.listEmpty"));
|
|
484
|
+
const lines = [];
|
|
485
|
+
const bp = packs.filter((p) => p.kind === "behavior");
|
|
486
|
+
const rp = packs.filter((p) => p.kind === "resource");
|
|
487
|
+
if (bp.length) {
|
|
488
|
+
lines.push(c.bold(t("packs.list.bpHeader")));
|
|
489
|
+
for (const p of bp) {
|
|
490
|
+
const en = p.enabled ? c.green(t("packs.list.on")) : c.dim(t("packs.list.off"));
|
|
491
|
+
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
|
+
);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
if (rp.length) {
|
|
498
|
+
lines.push(c.bold(t("packs.list.rpHeader")));
|
|
499
|
+
for (const p of rp) {
|
|
500
|
+
const en = p.enabled ? c.green(t("packs.list.on")) : c.dim(t("packs.list.off"));
|
|
501
|
+
lines.push(` [${en}] ${p.folderName} ${p.name} v${fmtVer(p.version)} ${c.dim(p.uuid)}`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return lines.join("\n");
|
|
505
|
+
}
|
|
506
|
+
function parseListFlags(args) {
|
|
507
|
+
let kind = "all";
|
|
508
|
+
let search;
|
|
509
|
+
for (let i = 0; i < args.length; i++) {
|
|
510
|
+
const a = args[i];
|
|
511
|
+
if (a === "--kind" && args[i + 1]) {
|
|
512
|
+
const k = args[++i].toLowerCase();
|
|
513
|
+
if (k === "bp" || k === "behavior") kind = "bp";
|
|
514
|
+
else if (k === "rp" || k === "resource") kind = "rp";
|
|
515
|
+
else kind = "all";
|
|
516
|
+
} else if (a === "--search" && args[i + 1]) {
|
|
517
|
+
search = args[++i];
|
|
518
|
+
} else if (a && !a.startsWith("-")) {
|
|
519
|
+
search = a;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return { kind, ...search ? { search } : {} };
|
|
523
|
+
}
|
|
524
|
+
function hasFlag(args, name) {
|
|
525
|
+
return args.includes(name);
|
|
526
|
+
}
|
|
527
|
+
async function cmdDoctor() {
|
|
528
|
+
const { bdsRoot, levelName } = resolveBdsContext();
|
|
529
|
+
const packs = listInstalledWorldPacks(bdsRoot, levelName);
|
|
530
|
+
const issues = [];
|
|
531
|
+
for (const kind of ["behavior", "resource"]) {
|
|
532
|
+
const snap = listWorldEnableListResult(bdsRoot, levelName, kind);
|
|
533
|
+
if (snap.parseFailedFile) {
|
|
534
|
+
issues.push(t("packs.doctor.parseFail", { file: path.basename(snap.parseFailedFile) }));
|
|
535
|
+
}
|
|
536
|
+
const byUuid = new Map(packs.filter((p) => p.kind === kind).map((p) => [p.uuid, p]));
|
|
537
|
+
for (const e of snap.entries) {
|
|
538
|
+
const p = byUuid.get(e.pack_id);
|
|
539
|
+
if (!p) {
|
|
540
|
+
issues.push(t("packs.doctor.missingDir", { kind, uuid: e.pack_id }));
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
const ev = e.version;
|
|
544
|
+
if (Array.isArray(ev) && ev.length >= 3 && (ev[0] !== p.version[0] || ev[1] !== p.version[1] || ev[2] !== p.version[2])) {
|
|
545
|
+
issues.push(
|
|
546
|
+
t("packs.doctor.versionMismatch", {
|
|
547
|
+
kind,
|
|
548
|
+
folder: p.folderName,
|
|
549
|
+
listVer: ev.join("."),
|
|
550
|
+
diskVer: fmtVer(p.version)
|
|
551
|
+
})
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
for (const p of packs) {
|
|
557
|
+
if (!p.enabled) {
|
|
558
|
+
issues.push(
|
|
559
|
+
t("packs.doctor.notEnabled", {
|
|
560
|
+
kind: p.kind === "resource" ? "RP" : "BP",
|
|
561
|
+
folder: p.folderName,
|
|
562
|
+
uuid: p.uuid
|
|
563
|
+
})
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
if (issues.length === 0) return c.green(t("packs.doctor.ok"));
|
|
568
|
+
return t("packs.doctor.found", {
|
|
569
|
+
count: issues.length,
|
|
570
|
+
list: issues.map((x) => ` - ${x}`).join("\n")
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
async function dispatchPacksCommand(sub, args) {
|
|
574
|
+
const verb = (sub ?? "").toLowerCase();
|
|
575
|
+
if (!verb || verb === "help" || verb === "-h" || verb === "--help") {
|
|
576
|
+
return packsUsage();
|
|
577
|
+
}
|
|
578
|
+
try {
|
|
579
|
+
switch (verb) {
|
|
580
|
+
case "list": {
|
|
581
|
+
const { bdsRoot, levelName } = resolveBdsContext();
|
|
582
|
+
const flags = parseListFlags(args);
|
|
583
|
+
const packs = filterPacks(listInstalledWorldPacks(bdsRoot, levelName), flags.kind, flags.search);
|
|
584
|
+
return formatPackList(packs);
|
|
585
|
+
}
|
|
586
|
+
case "search": {
|
|
587
|
+
const q = args[0];
|
|
588
|
+
if (!q) return c.yellow(t("packs.search.usage"));
|
|
589
|
+
return await searchRemote(q);
|
|
590
|
+
}
|
|
591
|
+
case "bind": {
|
|
592
|
+
const packId = args[0];
|
|
593
|
+
const ref = args[1];
|
|
594
|
+
if (!packId || !ref) return c.yellow(t("packs.bind.usage"));
|
|
595
|
+
return await bindPackSource(packId, ref);
|
|
596
|
+
}
|
|
597
|
+
case "unbind": {
|
|
598
|
+
const packId = args[0];
|
|
599
|
+
if (!packId) return c.yellow(t("packs.unbind.usage"));
|
|
600
|
+
const { bdsRoot, levelName } = resolveBdsContext();
|
|
601
|
+
const packs = listInstalledWorldPacks(bdsRoot, levelName);
|
|
602
|
+
const pack = findInstalledPackById(packs, packId);
|
|
603
|
+
const uuid = pack?.uuid ?? packId;
|
|
604
|
+
const ok = removeBinding(uuid);
|
|
605
|
+
return ok ? c.green(t("packs.unbindOk", { uuid, path: packSourcesPath() })) : c.yellow(t("packs.unbindMiss", { id: packId }));
|
|
606
|
+
}
|
|
607
|
+
case "sources":
|
|
608
|
+
return formatSourcesList();
|
|
609
|
+
case "check": {
|
|
610
|
+
const id = args.find((a) => !a.startsWith("-"));
|
|
611
|
+
return await checkPackUpdates({ ...id ? { packId: id } : {}, apply: false });
|
|
612
|
+
}
|
|
613
|
+
case "update": {
|
|
614
|
+
const all = hasFlag(args, "--all");
|
|
615
|
+
const id = args.find((a) => !a.startsWith("-") && a !== "--all");
|
|
616
|
+
if (!all && !id) return c.yellow(t("packs.update.usage"));
|
|
617
|
+
return await checkPackUpdates({
|
|
618
|
+
...id ? { packId: id } : {},
|
|
619
|
+
apply: true
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
case "enable":
|
|
623
|
+
case "disable": {
|
|
624
|
+
const id = args[0];
|
|
625
|
+
if (!id) return c.yellow(t("packs.toggle.usage", { verb }));
|
|
626
|
+
const { bdsRoot, levelName } = resolveBdsContext();
|
|
627
|
+
const packs = listInstalledWorldPacks(bdsRoot, levelName);
|
|
628
|
+
const pack = findInstalledPackById(packs, id);
|
|
629
|
+
if (!pack) return c.red(t("packs.notFound", { id }));
|
|
630
|
+
if (verb === "enable") {
|
|
631
|
+
await enableInstalledPack({
|
|
632
|
+
bdsRoot,
|
|
633
|
+
levelName,
|
|
634
|
+
info: {
|
|
635
|
+
name: pack.name,
|
|
636
|
+
uuid: pack.uuid,
|
|
637
|
+
version: pack.version,
|
|
638
|
+
kind: pack.kind
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
} else {
|
|
642
|
+
await disableInstalledPack({
|
|
643
|
+
bdsRoot,
|
|
644
|
+
levelName,
|
|
645
|
+
kind: pack.kind,
|
|
646
|
+
packUuid: pack.uuid,
|
|
647
|
+
version: pack.version
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
return c.green(t("packs.toggleDone", { verb, folder: pack.folderName, hint: restartHint() }));
|
|
651
|
+
}
|
|
652
|
+
case "uninstall": {
|
|
653
|
+
const ids = args.filter((a) => !a.startsWith("-"));
|
|
654
|
+
const purge = hasFlag(args, "--purge");
|
|
655
|
+
const noPaired = hasFlag(args, "--no-paired");
|
|
656
|
+
const { bdsRoot, levelName } = resolveBdsContext();
|
|
657
|
+
const packs = listInstalledWorldPacks(bdsRoot, levelName);
|
|
658
|
+
let selected = [];
|
|
659
|
+
if (ids.length === 0) {
|
|
660
|
+
if (!process.stdin.isTTY) return c.yellow(t("packs.uninstall.usage"));
|
|
661
|
+
const picked = await pickPacksForUninstall(packs);
|
|
662
|
+
if (picked === null) return c.dim(t("packs.uninstall.cancelled"));
|
|
663
|
+
if (picked.length === 0) return c.yellow(t("packs.uninstall.none"));
|
|
664
|
+
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
|
+
} 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);
|
|
682
|
+
}
|
|
683
|
+
if (missing.length) {
|
|
684
|
+
return c.red(t("packs.notFound", { id: missing.join(", ") }));
|
|
685
|
+
}
|
|
686
|
+
selected = [...byUuid.values()];
|
|
687
|
+
}
|
|
688
|
+
return await uninstallPacksBatch(selected, packs, {
|
|
689
|
+
bdsRoot,
|
|
690
|
+
levelName,
|
|
691
|
+
purge,
|
|
692
|
+
noPaired,
|
|
693
|
+
/* 多选或一次卸多个时确认;单 id 保持原「直接卸」手感 */
|
|
694
|
+
askConfirm: ids.length !== 1
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
case "bump": {
|
|
698
|
+
const id = args[0];
|
|
699
|
+
if (!id) return c.yellow(t("packs.bump.usage"));
|
|
700
|
+
const { bdsRoot, levelName } = resolveBdsContext();
|
|
701
|
+
const packs = listInstalledWorldPacks(bdsRoot, levelName);
|
|
702
|
+
const pack = findInstalledPackById(packs, id);
|
|
703
|
+
if (!pack) return c.red(t("packs.notFound", { id }));
|
|
704
|
+
if (pack.kind !== "resource") {
|
|
705
|
+
return c.red(t("packs.bump.rpOnly"));
|
|
706
|
+
}
|
|
707
|
+
const next = bumpPackPatchVersion(pack.dir);
|
|
708
|
+
if (pack.enabled) {
|
|
709
|
+
await enableInstalledPack({
|
|
710
|
+
bdsRoot,
|
|
711
|
+
levelName,
|
|
712
|
+
info: {
|
|
713
|
+
name: pack.name,
|
|
714
|
+
uuid: pack.uuid,
|
|
715
|
+
version: next,
|
|
716
|
+
kind: "resource"
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
return c.green(
|
|
721
|
+
t("packs.bumped", {
|
|
722
|
+
folder: pack.folderName,
|
|
723
|
+
version: fmtVer(next),
|
|
724
|
+
restart: restartHint(),
|
|
725
|
+
rejoin: rejoinHint()
|
|
726
|
+
})
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
case "install": {
|
|
730
|
+
const force = hasFlag(args, "--force");
|
|
731
|
+
const inbox = hasFlag(args, "--inbox") || args.length === 0 || args.every((a) => a.startsWith("-"));
|
|
732
|
+
if (inbox) {
|
|
733
|
+
const r = await scanAndInstallInbox({ force, interactive: true });
|
|
734
|
+
return c.dim(
|
|
735
|
+
t("packs.installInbox", {
|
|
736
|
+
installed: r.installed,
|
|
737
|
+
skipped: r.skipped,
|
|
738
|
+
failed: r.failed
|
|
739
|
+
})
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
const target = args.find((a) => !a.startsWith("-"));
|
|
743
|
+
if (!target) return c.yellow(t("packs.install.usage"));
|
|
744
|
+
const abs = path.resolve(target);
|
|
745
|
+
if (!fs.existsSync(abs)) return c.red(t("packs.pathMissing", { path: abs }));
|
|
746
|
+
const { bdsRoot, levelName } = resolveBdsContext();
|
|
747
|
+
let tempDir = null;
|
|
748
|
+
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 {
|
|
756
|
+
return c.red(t("packs.notArchive"));
|
|
757
|
+
}
|
|
758
|
+
if (roots.length === 0) return c.red(t("packs.noManifestRoot"));
|
|
759
|
+
const lines = [];
|
|
760
|
+
for (const root of roots) {
|
|
761
|
+
const r = await installOnePackRoot({
|
|
762
|
+
srcDir: root,
|
|
763
|
+
bdsRoot,
|
|
764
|
+
levelName,
|
|
765
|
+
folderHint: path.basename(root),
|
|
766
|
+
force,
|
|
767
|
+
interactive: true
|
|
768
|
+
});
|
|
769
|
+
if (r.ok && r.info) {
|
|
770
|
+
lines.push(
|
|
771
|
+
c.green(
|
|
772
|
+
t("packs.installedLine", {
|
|
773
|
+
name: r.info.name,
|
|
774
|
+
version: fmtVer(r.info.version),
|
|
775
|
+
kind: r.info.kind
|
|
776
|
+
})
|
|
777
|
+
)
|
|
778
|
+
);
|
|
779
|
+
} else {
|
|
780
|
+
lines.push(
|
|
781
|
+
c.yellow(
|
|
782
|
+
t("packs.skipFailLine", {
|
|
783
|
+
name: path.basename(root),
|
|
784
|
+
reason: r.reason ?? "?"
|
|
785
|
+
})
|
|
786
|
+
)
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
lines.push(c.yellow(restartHint()));
|
|
791
|
+
return lines.join("\n");
|
|
792
|
+
} finally {
|
|
793
|
+
if (tempDir) fs.rmSync(tempDir, { recursive: true, force: true });
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
case "scan": {
|
|
797
|
+
const force = hasFlag(args, "--force");
|
|
798
|
+
const dryRun = hasFlag(args, "--dry-run");
|
|
799
|
+
const r = await scanAndInstallInbox({
|
|
800
|
+
force,
|
|
801
|
+
dryRun,
|
|
802
|
+
interactive: !!process.stdin.isTTY
|
|
803
|
+
});
|
|
804
|
+
if (r.installed === 0 && r.skipped === 0 && r.failed === 0) {
|
|
805
|
+
return c.dim(t("packs.inboxEmpty"));
|
|
806
|
+
}
|
|
807
|
+
return c.dim(
|
|
808
|
+
t("packs.scanResult", {
|
|
809
|
+
installed: r.installed,
|
|
810
|
+
skipped: r.skipped,
|
|
811
|
+
failed: r.failed
|
|
812
|
+
})
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
case "doctor":
|
|
816
|
+
return await cmdDoctor();
|
|
817
|
+
case "path": {
|
|
818
|
+
ensureInboxLayout();
|
|
819
|
+
const { bdsRoot, levelName } = resolveBdsContext();
|
|
820
|
+
return [
|
|
821
|
+
`bdsRoot: ${bdsRoot}`,
|
|
822
|
+
`level: ${levelName}`,
|
|
823
|
+
`behavior: ${worldPackParentDir(bdsRoot, levelName, "behavior")}`,
|
|
824
|
+
`resource: ${worldPackParentDir(bdsRoot, levelName, "resource")}`,
|
|
825
|
+
`inbox: ${packsInboxDir()}`,
|
|
826
|
+
`trash: ${resolvePackTrashDir()}`,
|
|
827
|
+
`build: ${path.join(packsInboxDir(), "_build")}`,
|
|
828
|
+
`sources: ${packSourcesPath()}`
|
|
829
|
+
].join("\n");
|
|
830
|
+
}
|
|
831
|
+
default:
|
|
832
|
+
return c.red(t("packs.unknownSub", { verb })) + packsUsage();
|
|
833
|
+
}
|
|
834
|
+
} catch (e) {
|
|
835
|
+
return c.red(e.message);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
function packsUsage() {
|
|
839
|
+
return `${t("packs.usage.title", { cmd: c.bold("sfmc packs"), alias: c.green("addon") })}
|
|
840
|
+
|
|
841
|
+
${c.green("packs list")} [--kind bp|rp|all] [--search q]
|
|
842
|
+
${c.green("packs search")} <q> ${t("packs.usage.cfSearch")}
|
|
843
|
+
${c.green("packs bind")} <id> <project|slug|url>
|
|
844
|
+
${c.green("packs unbind")} <id>
|
|
845
|
+
${c.green("packs sources")}
|
|
846
|
+
${c.green("packs check")} [id]
|
|
847
|
+
${c.green("packs update")} <id|--all>
|
|
848
|
+
${c.green("packs enable|disable")} <uuid|folder>
|
|
849
|
+
${c.green("packs uninstall")} [id...] [--purge] [--no-paired]
|
|
850
|
+
${c.green("packs bump")} <id> ${t("packs.usage.bump")}
|
|
851
|
+
${c.green("packs install")} [path|--inbox] [--force]
|
|
852
|
+
${c.green("packs scan")} [--force] [--dry-run]
|
|
853
|
+
${c.green("packs doctor")}
|
|
854
|
+
${c.green("packs path")}
|
|
855
|
+
|
|
856
|
+
${t("packs.usage.inbox", { path: packsInboxDir() })}
|
|
857
|
+
${t("packs.usage.trash", { path: resolvePackTrashDir() })}
|
|
858
|
+
${t("packs.usage.sources", { path: packSourcesPath() })}
|
|
859
|
+
${t("packs.usage.enableNote", { hint: restartHint() })}
|
|
860
|
+
${t("packs.usage.moduleNote", { cmd: c.green("sfmc mod") })}`;
|
|
861
|
+
}
|
|
862
|
+
export {
|
|
863
|
+
PACKS_CMD_NAMES,
|
|
864
|
+
PACKS_SUBCOMMANDS,
|
|
865
|
+
dispatchPacksCommand,
|
|
866
|
+
isPacksCommand,
|
|
867
|
+
isProtectedSfmcPack,
|
|
868
|
+
packsInboxDir,
|
|
869
|
+
packsUsage,
|
|
870
|
+
scanAndInstallInbox
|
|
871
|
+
};
|
|
872
|
+
//# sourceMappingURL=world-packs.js.map
|