@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
package/dist/module-commands.js
CHANGED
|
@@ -1,367 +1,480 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*/
|
|
1
|
+
const MODULE_CMD_NAMES = ["module", "mod"];
|
|
2
|
+
function isModuleCommand(cmd) {
|
|
3
|
+
return !!cmd && MODULE_CMD_NAMES.includes(cmd);
|
|
4
|
+
}
|
|
5
|
+
function paintModuleCmdAlias(paint) {
|
|
6
|
+
return MODULE_CMD_NAMES.map((name) => paint(name)).join("/");
|
|
7
|
+
}
|
|
8
|
+
const MODULE_SUBCOMMANDS = [
|
|
9
|
+
"list",
|
|
10
|
+
"search",
|
|
11
|
+
"install",
|
|
12
|
+
"uninstall",
|
|
13
|
+
"verify",
|
|
14
|
+
"info",
|
|
15
|
+
"enable",
|
|
16
|
+
"disable",
|
|
17
|
+
"build",
|
|
18
|
+
"reload",
|
|
19
|
+
"create",
|
|
20
|
+
"link",
|
|
21
|
+
"dev"
|
|
22
|
+
];
|
|
24
23
|
import fs from "node:fs/promises";
|
|
25
|
-
import { existsSync } from "node:fs";
|
|
24
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
26
25
|
import path from "node:path";
|
|
27
|
-
import { createHash } from "node:crypto";
|
|
28
26
|
import { spawn } from "node:child_process";
|
|
29
|
-
import { configPath, readJson } from "@sfmc-bds/sdk/node/config";
|
|
27
|
+
import { configPath, modulePath, readJson } from "@sfmc-bds/sdk/node/config";
|
|
28
|
+
import { failResult, okResult } from "./cli-result.js";
|
|
29
|
+
import { t } from "./i18n/index.js";
|
|
30
30
|
import { c } from "./theme.js";
|
|
31
|
-
import { ROOT } from "./runtime.js";
|
|
32
|
-
import {
|
|
33
|
-
|
|
31
|
+
import { ROOT, resolveFetchModule } from "./runtime.js";
|
|
32
|
+
import { dirFingerprint } from "./module-fingerprint.js";
|
|
33
|
+
import {
|
|
34
|
+
DEFAULT_REGISTRY_REPO,
|
|
35
|
+
DEFAULT_REGISTRY_TAG,
|
|
36
|
+
findUnknownModules,
|
|
37
|
+
resolveRegistryIndex
|
|
38
|
+
} from "./registry.js";
|
|
39
|
+
function moduleUsage() {
|
|
40
|
+
return t("mod.usage", {
|
|
41
|
+
cmds: MODULE_CMD_NAMES.join("|"),
|
|
42
|
+
subs: MODULE_SUBCOMMANDS.join("|")
|
|
43
|
+
});
|
|
44
|
+
}
|
|
34
45
|
function modulesDir() {
|
|
35
|
-
|
|
46
|
+
return path.join(ROOT, "modules", "packages");
|
|
36
47
|
}
|
|
37
48
|
function fmtBytes(n) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return `${(n / 1024).toFixed(1)} KB`;
|
|
42
|
-
return `${(n / 1024 / 1024).toFixed(2)} MB`;
|
|
49
|
+
if (n < 1024) return `${n} B`;
|
|
50
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
51
|
+
return `${(n / 1024 / 1024).toFixed(2)} MB`;
|
|
43
52
|
}
|
|
44
53
|
function shortHash(h) {
|
|
45
|
-
|
|
54
|
+
return h.slice(0, 8) + "\u2026" + h.slice(-8);
|
|
46
55
|
}
|
|
47
|
-
/** Enumerate every installed module by scanning `modules/packages/<id>/`. */
|
|
48
56
|
async function scanInstalled() {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
/* corrupt manifest is not fatal — just skip its fields */
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
const { totalBytes, fileCount } = await dirSize(modPath);
|
|
70
|
-
const fingerprint = await dirFingerprint(modPath);
|
|
71
|
-
out.push({ id, path: modPath, manifest, totalBytes, fileCount, fingerprint });
|
|
57
|
+
const dir = modulesDir();
|
|
58
|
+
if (!existsSync(dir)) return [];
|
|
59
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
60
|
+
const out = [];
|
|
61
|
+
for (const e of entries) {
|
|
62
|
+
if (!e.isDirectory()) continue;
|
|
63
|
+
const id = e.name;
|
|
64
|
+
const modPath = path.join(dir, id);
|
|
65
|
+
const manifestPath = path.join(modPath, "sapi", "manifest.json");
|
|
66
|
+
let manifest = null;
|
|
67
|
+
if (existsSync(manifestPath)) {
|
|
68
|
+
try {
|
|
69
|
+
manifest = JSON.parse(await fs.readFile(manifestPath, "utf8"));
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
72
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const entries = [];
|
|
80
|
-
async function walk(rel) {
|
|
81
|
-
const full = path.join(rootDir, rel);
|
|
82
|
-
const items = await fs.readdir(full, { withFileTypes: true });
|
|
83
|
-
const sorted = items.sort((a, b) => a.name.localeCompare(b.name));
|
|
84
|
-
for (const it of sorted) {
|
|
85
|
-
const child = rel ? `${rel}/${it.name}` : it.name;
|
|
86
|
-
if (it.isDirectory())
|
|
87
|
-
await walk(child);
|
|
88
|
-
else if (it.isFile())
|
|
89
|
-
entries.push(child);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
await walk("");
|
|
93
|
-
entries.sort();
|
|
94
|
-
for (const rel of entries) {
|
|
95
|
-
const data = await fs.readFile(path.join(rootDir, rel));
|
|
96
|
-
hash.update(rel.replaceAll("\\", "/"));
|
|
97
|
-
hash.update("\n");
|
|
98
|
-
hash.update(data);
|
|
99
|
-
hash.update("\n");
|
|
100
|
-
}
|
|
101
|
-
return hash.digest("hex");
|
|
73
|
+
const { totalBytes, fileCount } = await dirSize(modPath);
|
|
74
|
+
const fingerprint = await dirFingerprint(modPath);
|
|
75
|
+
out.push({ id, path: modPath, manifest, totalBytes, fileCount, fingerprint });
|
|
76
|
+
}
|
|
77
|
+
out.sort((a, b) => a.id.localeCompare(b.id));
|
|
78
|
+
return out;
|
|
102
79
|
}
|
|
103
80
|
async function dirSize(dir) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
}
|
|
81
|
+
let totalBytes = 0;
|
|
82
|
+
let fileCount = 0;
|
|
83
|
+
async function walk(p) {
|
|
84
|
+
for (const e of await fs.readdir(p, { withFileTypes: true })) {
|
|
85
|
+
const child = path.join(p, e.name);
|
|
86
|
+
if (e.isDirectory()) await walk(child);
|
|
87
|
+
else if (e.isFile()) {
|
|
88
|
+
const s = await fs.stat(child);
|
|
89
|
+
totalBytes += s.size;
|
|
90
|
+
fileCount++;
|
|
91
|
+
}
|
|
117
92
|
}
|
|
118
|
-
|
|
119
|
-
|
|
93
|
+
}
|
|
94
|
+
await walk(dir);
|
|
95
|
+
return { totalBytes, fileCount };
|
|
120
96
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
97
|
+
async function cmdModuleList(_args) {
|
|
98
|
+
const installed = await scanInstalled();
|
|
99
|
+
if (installed.length === 0) {
|
|
100
|
+
return c.dim(t("mod.noneInstalled", { dir: modulesDir() }));
|
|
101
|
+
}
|
|
102
|
+
const lock = readJson(modulePath(path.join(ROOT, "modules"), "module-lock.json")) ?? {
|
|
103
|
+
version: 1,
|
|
104
|
+
modules: {}
|
|
105
|
+
};
|
|
106
|
+
const ids = installed.map((m) => m.id);
|
|
107
|
+
const unknown = new Set(await findUnknownModules(ids));
|
|
108
|
+
const lines = [c.bold(`
|
|
109
|
+
${t("mod.list.title")}`), c.dim(` ${modulesDir()}`)];
|
|
110
|
+
const header = ` ${"id".padEnd(24)}${"state".padEnd(8)}${"files".padEnd(8)}${"size".padEnd(10)}${"fingerprint"}`;
|
|
111
|
+
lines.push(c.dim(header));
|
|
112
|
+
for (const m of installed) {
|
|
113
|
+
const logicalId = typeof m.manifest?.id === "string" && m.manifest.id ? m.manifest.id : m.id;
|
|
114
|
+
const enabled = lock.modules?.[logicalId]?.enabled === true;
|
|
115
|
+
const stateCol = (enabled ? c.green("on") : c.dim("off")).padEnd(8);
|
|
116
|
+
let mark;
|
|
117
|
+
if (!m.manifest) {
|
|
118
|
+
mark = c.yellow("\u25CB");
|
|
119
|
+
} else if (unknown.has(m.id)) {
|
|
120
|
+
mark = c.yellow("?");
|
|
121
|
+
} else {
|
|
122
|
+
mark = enabled ? c.green("\u25CF") : c.dim("\u25CB");
|
|
147
123
|
}
|
|
148
|
-
|
|
124
|
+
const idLabel = logicalId !== m.id ? `${m.id}(${logicalId})` : m.id;
|
|
125
|
+
lines.push(
|
|
126
|
+
` ${mark} ${idLabel.padEnd(22)}${stateCol}${String(m.fileCount).padEnd(8)}${fmtBytes(m.totalBytes).padEnd(10)}${shortHash(m.fingerprint)}`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
lines.push(c.dim(`
|
|
130
|
+
tip: ${MODULE_CMD_NAMES[1] ?? "mod"} enable|disable <logicalId>`));
|
|
131
|
+
return lines.join("\n") + "\n";
|
|
149
132
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (installed.length === 0)
|
|
160
|
-
return "";
|
|
161
|
-
const unknown = await findUnknownModules(installed.map((m) => m.id));
|
|
162
|
-
if (unknown.length === 0)
|
|
163
|
-
return "";
|
|
164
|
-
const list = unknown.map((id) => ` ${c.yellow("?")} ${id}`).join("\n");
|
|
165
|
-
return `${c.yellow("[sfmc] modules installed from unknown publisher — verify before use:")}\n${list}\n`;
|
|
133
|
+
async function scanAndWarnUnknown() {
|
|
134
|
+
const installed = await scanInstalled();
|
|
135
|
+
if (installed.length === 0) return "";
|
|
136
|
+
const unknown = await findUnknownModules(installed.map((m) => m.id));
|
|
137
|
+
if (unknown.length === 0) return "";
|
|
138
|
+
const list = unknown.map((id) => ` ${c.yellow("?")} ${id}`).join("\n");
|
|
139
|
+
return `${c.yellow("[sfmc] modules installed from unknown publisher \u2014 verify before use:")}
|
|
140
|
+
${list}
|
|
141
|
+
`;
|
|
166
142
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
lines.push(c.dim(` v${mg.version} ${mg.name}`));
|
|
194
|
-
}
|
|
195
|
-
if (m.manifest.notes)
|
|
196
|
-
lines.push(` notes : ${c.dim(m.manifest.notes)}`);
|
|
143
|
+
async function cmdModuleSearch(args) {
|
|
144
|
+
const query = args[0];
|
|
145
|
+
const { index, stale } = await resolveRegistryIndex({ force: true });
|
|
146
|
+
const ids = Object.keys(index).sort((a, b) => a.localeCompare(b));
|
|
147
|
+
if (ids.length === 0) {
|
|
148
|
+
return c.red(
|
|
149
|
+
`
|
|
150
|
+
Registry empty or unreachable (${DEFAULT_REGISTRY_REPO}@${DEFAULT_REGISTRY_TAG}).
|
|
151
|
+
Check network, or install with an explicit source: sfmc mod install <id> --from github:owner/repo@tag
|
|
152
|
+
`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
const staleNote = stale ? c.yellow(" (offline \u2014 showing cached index)\n") : "";
|
|
156
|
+
if (!query) {
|
|
157
|
+
const installed = new Set(listInstalledModuleIdsSync());
|
|
158
|
+
const lines2 = [
|
|
159
|
+
c.bold(`
|
|
160
|
+
First-party registry (${DEFAULT_REGISTRY_REPO}@${DEFAULT_REGISTRY_TAG}) \u2014 ${ids.length} modules`)
|
|
161
|
+
];
|
|
162
|
+
if (staleNote) lines2.push(staleNote.trimEnd());
|
|
163
|
+
lines2.push(c.dim(` ${"id".padEnd(28)}${"source".padEnd(40)}local`));
|
|
164
|
+
for (const id of ids) {
|
|
165
|
+
const e = index[id];
|
|
166
|
+
const src = `${e.repo}@${e.tag}`;
|
|
167
|
+
const local = installed.has(id) ? c.green("\u25CF") : c.dim("\u25CB");
|
|
168
|
+
lines2.push(` ${local} ${id.padEnd(26)}${src.padEnd(40)}${installed.has(id) ? "installed" : ""}`);
|
|
197
169
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return
|
|
170
|
+
lines2.push(c.dim(`
|
|
171
|
+
tip: sfmc mod search <id> \u2192 registry info`));
|
|
172
|
+
lines2.push(c.dim(` sfmc mod install <id> \u2192 download + catalog sync`));
|
|
173
|
+
return lines2.join("\n") + "\n";
|
|
174
|
+
}
|
|
175
|
+
const entry = index[query];
|
|
176
|
+
if (!entry) {
|
|
177
|
+
const hints = ids.filter((id) => id.includes(query) || query.includes(id)).slice(0, 8);
|
|
178
|
+
const hintBlock = hints.length > 0 ? c.dim(`
|
|
179
|
+
Did you mean:
|
|
180
|
+
`) + hints.map((h) => ` ${h}`).join("\n") : c.dim(`
|
|
181
|
+
Known ids: ${ids.slice(0, 12).join(", ")}${ids.length > 12 ? ", \u2026" : ""}`);
|
|
182
|
+
return c.red(t("mod.notInRegistry", { query })) + hintBlock + "\n";
|
|
183
|
+
}
|
|
184
|
+
const installedPath = path.join(modulesDir(), query);
|
|
185
|
+
const isInstalled = existsSync(installedPath);
|
|
186
|
+
const lines = [c.bold(`
|
|
187
|
+
${query}`)];
|
|
188
|
+
if (staleNote) lines.push(staleNote.trimEnd());
|
|
189
|
+
lines.push(` registry : ${DEFAULT_REGISTRY_REPO}@${DEFAULT_REGISTRY_TAG}`);
|
|
190
|
+
lines.push(` repo : ${entry.repo}`);
|
|
191
|
+
lines.push(` tag : ${entry.tag}`);
|
|
192
|
+
lines.push(` source : github:${entry.repo}@${entry.tag}`);
|
|
193
|
+
lines.push(` github : https://github.com/${entry.repo}/tree/${entry.tag}`);
|
|
194
|
+
lines.push(
|
|
195
|
+
` local : ${isInstalled ? c.green(`installed @ ${installedPath}`) : c.dim("not installed")}`
|
|
196
|
+
);
|
|
197
|
+
if (!isInstalled) {
|
|
198
|
+
lines.push(c.dim(`
|
|
199
|
+
install: sfmc mod install ${query}`));
|
|
200
|
+
} else {
|
|
201
|
+
lines.push(c.dim(`
|
|
202
|
+
details: sfmc mod info ${query}`));
|
|
203
|
+
}
|
|
204
|
+
return lines.join("\n") + "\n";
|
|
202
205
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
206
|
+
async function cmdModuleInfo(args) {
|
|
207
|
+
const id = args[0];
|
|
208
|
+
if (!id) return c.yellow(t("mod.info.usage"));
|
|
209
|
+
const all = await scanInstalled();
|
|
210
|
+
const m = all.find((x) => x.id === id);
|
|
211
|
+
if (!m) return c.red(t("mod.notInstalledAt", { id, path: path.join(modulesDir(), id) }));
|
|
212
|
+
const lines = [c.bold(`
|
|
213
|
+
${id}`)];
|
|
214
|
+
lines.push(` path : ${m.path}`);
|
|
215
|
+
lines.push(` files : ${m.fileCount}`);
|
|
216
|
+
lines.push(` size : ${fmtBytes(m.totalBytes)}`);
|
|
217
|
+
lines.push(` fingerprint : ${m.fingerprint}`);
|
|
218
|
+
if (m.manifest) {
|
|
219
|
+
lines.push(` schemaVer : ${m.manifest.schemaVersion ?? "(none)"}`);
|
|
220
|
+
lines.push(` routes : ${m.manifest.routes?.length ?? 0}`);
|
|
221
|
+
if (m.manifest.routes?.length) {
|
|
222
|
+
for (const r of m.manifest.routes) lines.push(c.dim(` ${r.method.padEnd(7)} ${r.path.padEnd(36)} ${r.handler}`));
|
|
214
223
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
lines.push(` ${m.id.padEnd(28)} ${shortHash(m.fingerprint)}`);
|
|
224
|
+
lines.push(` migrations : ${m.manifest.migrations?.length ?? 0}`);
|
|
225
|
+
if (m.manifest.migrations?.length) {
|
|
226
|
+
for (const mg of m.manifest.migrations) lines.push(c.dim(` v${mg.version} ${mg.name}`));
|
|
219
227
|
}
|
|
220
|
-
if (
|
|
221
|
-
|
|
222
|
-
|
|
228
|
+
if (m.manifest.notes) lines.push(` notes : ${c.dim(m.manifest.notes)}`);
|
|
229
|
+
} else {
|
|
230
|
+
lines.push(c.yellow(` manifest : (missing or unreadable)`));
|
|
231
|
+
}
|
|
232
|
+
return lines.join("\n") + "\n";
|
|
233
|
+
}
|
|
234
|
+
async function cmdModuleVerify(args) {
|
|
235
|
+
const id = args[0];
|
|
236
|
+
if (id) {
|
|
237
|
+
const all2 = await scanInstalled();
|
|
238
|
+
const m = all2.find((x) => x.id === id);
|
|
239
|
+
if (!m) return c.red(t("mod.notInstalled", { id }));
|
|
240
|
+
return c.green(t("mod.verifyOk", { id, fp: m.fingerprint }));
|
|
241
|
+
}
|
|
242
|
+
const all = await scanInstalled();
|
|
243
|
+
const lines = [c.bold("\nVerifying installed modules")];
|
|
244
|
+
for (const m of all) {
|
|
245
|
+
lines.push(` ${m.id.padEnd(28)} ${shortHash(m.fingerprint)}`);
|
|
246
|
+
}
|
|
247
|
+
if (all.length === 0) lines.push(c.dim(" (none)"));
|
|
248
|
+
return lines.join("\n") + "\n";
|
|
223
249
|
}
|
|
224
|
-
/* ─────────────────────────────────────────────────────────────────
|
|
225
|
-
* install — shells out to tools/fetch-module.mjs
|
|
226
|
-
* ──────────────────────────────────────────────────────────────── */
|
|
227
|
-
/* ───────────────────────────────────────────────────────────────
|
|
228
|
-
* enable / disable — talk to db-server over loopback HTTP
|
|
229
|
-
* ─────────────────────────────────────────────────────────────── */
|
|
230
250
|
function readDbConfig() {
|
|
231
|
-
|
|
251
|
+
return readJson(configPath(ROOT, "db_config.json")) ?? {};
|
|
232
252
|
}
|
|
233
253
|
async function postModuleToggle(id, action) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
254
|
+
const cfg = readDbConfig();
|
|
255
|
+
const port = cfg.db_port ?? 3001;
|
|
256
|
+
const token = cfg.http_auth || "";
|
|
257
|
+
const url = `http://127.0.0.1:${port}/api/sfmc/modules/${encodeURIComponent(id)}/${action}`;
|
|
258
|
+
let res;
|
|
259
|
+
try {
|
|
260
|
+
res = await fetch(url, {
|
|
261
|
+
method: "POST",
|
|
262
|
+
headers: {
|
|
263
|
+
"Content-Type": "application/json",
|
|
264
|
+
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
} catch (err) {
|
|
268
|
+
return failResult(
|
|
269
|
+
c.red(t("mod.dbUnreachable", { url, message: err.message }))
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
const text = await res.text();
|
|
273
|
+
let body;
|
|
274
|
+
try {
|
|
275
|
+
body = JSON.parse(text);
|
|
276
|
+
} catch {
|
|
277
|
+
body = text;
|
|
278
|
+
}
|
|
279
|
+
if (!res.ok) {
|
|
280
|
+
const err = body?.error ?? `HTTP ${res.status}`;
|
|
281
|
+
return failResult(c.red(t("mod.toggleFailed", { action, id, err })));
|
|
282
|
+
}
|
|
283
|
+
const ok = body?.success !== false;
|
|
284
|
+
if (ok) {
|
|
285
|
+
const label = action === "enable" ? t("mod.action.enabled") : t("mod.action.disabled");
|
|
286
|
+
return okResult(c.green(t("mod.toggleOk", { label, id })));
|
|
287
|
+
}
|
|
288
|
+
return failResult(c.red(t("mod.toggleBadBody", { action, id, text })));
|
|
265
289
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return postModuleToggle(id, "enable");
|
|
290
|
+
async function cmdModuleEnable(args) {
|
|
291
|
+
const id = args[0];
|
|
292
|
+
if (!id) return failResult(c.yellow(t("mod.enable.usage")));
|
|
293
|
+
return postModuleToggle(id, "enable");
|
|
271
294
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
return postModuleToggle(id, "disable");
|
|
295
|
+
async function cmdModuleDisable(args) {
|
|
296
|
+
const id = args[0];
|
|
297
|
+
if (!id) return failResult(c.yellow(t("mod.disable.usage")));
|
|
298
|
+
return postModuleToggle(id, "disable");
|
|
277
299
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
for (let i = 0; i < args.length; i++) {
|
|
286
|
-
if (args[i] === "--from" || args[i] === "--sha256") {
|
|
287
|
-
i++;
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
if (args[i]?.startsWith("--from=") || args[i]?.startsWith("--sha256="))
|
|
291
|
-
continue;
|
|
292
|
-
if (args[i]?.startsWith("--"))
|
|
293
|
-
continue;
|
|
294
|
-
positional.push(args[i]);
|
|
300
|
+
async function cmdModuleInstall(args) {
|
|
301
|
+
const flags = parseFlags(args);
|
|
302
|
+
const positional = [];
|
|
303
|
+
for (let i = 0; i < args.length; i++) {
|
|
304
|
+
if (args[i] === "--from" || args[i] === "--sha256") {
|
|
305
|
+
i++;
|
|
306
|
+
continue;
|
|
295
307
|
}
|
|
296
|
-
if (
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
308
|
+
if (args[i]?.startsWith("--from=") || args[i]?.startsWith("--sha256=")) continue;
|
|
309
|
+
if (args[i]?.startsWith("--")) continue;
|
|
310
|
+
positional.push(args[i]);
|
|
311
|
+
}
|
|
312
|
+
if (positional.length === 0) {
|
|
313
|
+
return c.yellow(t("mod.install.usage"));
|
|
314
|
+
}
|
|
315
|
+
if (flags.link && !flags.from) {
|
|
316
|
+
const { resolveSfmcModulesRoot, packageDirForId } = await import("./sfmc-modules-root.js");
|
|
317
|
+
const modulesRoot = resolveSfmcModulesRoot();
|
|
318
|
+
if (!modulesRoot) {
|
|
319
|
+
return c.red(t("mod.linkNeedsFrom"));
|
|
301
320
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
321
|
+
flags.from = positional.length === 1 ? `dir:${packageDirForId(modulesRoot, positional[0])}` : `dir:${path.join(modulesRoot, "packages")}`;
|
|
322
|
+
}
|
|
323
|
+
const fetchScript = resolveFetchModule();
|
|
324
|
+
if (!fetchScript) {
|
|
325
|
+
return c.red(t("mod.fetchMissing"));
|
|
326
|
+
}
|
|
327
|
+
const sub = ["install", ...positional];
|
|
328
|
+
if (flags.from) sub.push("--from", flags.from);
|
|
329
|
+
if (flags.sha256) sub.push("--sha256", flags.sha256);
|
|
330
|
+
if (flags.link) sub.push("--link");
|
|
331
|
+
return new Promise((resolve) => {
|
|
332
|
+
const proc = spawn(process.execPath, [fetchScript, ...sub], {
|
|
333
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
334
|
+
env: { ...process.env, SFMC_ROOT: ROOT }
|
|
335
|
+
});
|
|
336
|
+
let out = "";
|
|
337
|
+
proc.stdout?.on("data", (d) => {
|
|
338
|
+
out += d.toString();
|
|
339
|
+
});
|
|
340
|
+
proc.stderr?.on("data", (d) => {
|
|
341
|
+
out += d.toString();
|
|
342
|
+
});
|
|
343
|
+
proc.on("exit", (code) => {
|
|
344
|
+
resolve(out + (code === 0 ? "" : `
|
|
345
|
+
[exit ${code}]`));
|
|
320
346
|
});
|
|
347
|
+
proc.on("error", (e) => resolve(c.red(`spawn failed: ${e.message}`)));
|
|
348
|
+
});
|
|
321
349
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const fetchScript = path.join(ROOT, "tools", "fetch-module.mjs");
|
|
330
|
-
if (!existsSync(fetchScript)) {
|
|
331
|
-
// 回退:仅删目录(旧行为)
|
|
332
|
-
const target = path.join(modulesDir(), id);
|
|
333
|
-
if (!existsSync(target))
|
|
334
|
-
return c.yellow(`Module ${id} not installed (no folder at ${target})`);
|
|
335
|
-
await fs.rm(target, { recursive: true, force: true });
|
|
336
|
-
return c.green(`Removed ${id} from ${target}\n`);
|
|
350
|
+
async function cmdModuleLink(args) {
|
|
351
|
+
const flags = parseFlags(args);
|
|
352
|
+
const positional = [];
|
|
353
|
+
for (let i = 0; i < args.length; i++) {
|
|
354
|
+
if (args[i] === "--from" || args[i] === "--sha256") {
|
|
355
|
+
i++;
|
|
356
|
+
continue;
|
|
337
357
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
358
|
+
if (args[i]?.startsWith("--from=") || args[i]?.startsWith("--sha256=")) continue;
|
|
359
|
+
if (args[i]?.startsWith("--")) continue;
|
|
360
|
+
positional.push(args[i]);
|
|
361
|
+
}
|
|
362
|
+
if (positional.length === 0) {
|
|
363
|
+
const { runModuleLinkWizard } = await import("./module-wizard.js");
|
|
364
|
+
return runModuleLinkWizard();
|
|
365
|
+
}
|
|
366
|
+
const id = positional[0];
|
|
367
|
+
const installArgs = [id, "--link"];
|
|
368
|
+
if (flags.from) installArgs.push("--from", flags.from);
|
|
369
|
+
return cmdModuleInstall(installArgs);
|
|
370
|
+
}
|
|
371
|
+
async function cmdModuleUninstall(args) {
|
|
372
|
+
const id = args[0];
|
|
373
|
+
if (!id) return c.yellow(t("mod.uninstall.usage"));
|
|
374
|
+
const fetchScript = resolveFetchModule();
|
|
375
|
+
if (!fetchScript) {
|
|
376
|
+
const target = path.join(modulesDir(), id);
|
|
377
|
+
if (!existsSync(target)) return c.yellow(t("mod.notInstalledFolder", { id, path: target }));
|
|
378
|
+
await fs.rm(target, { recursive: true, force: true });
|
|
379
|
+
return c.green(t("mod.removed", { id, path: target }));
|
|
380
|
+
}
|
|
381
|
+
return new Promise((resolve) => {
|
|
382
|
+
const proc = spawn(process.execPath, [fetchScript, "uninstall", id], {
|
|
383
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
384
|
+
env: { ...process.env, SFMC_ROOT: ROOT }
|
|
385
|
+
});
|
|
386
|
+
let out = "";
|
|
387
|
+
proc.stdout?.on("data", (d) => {
|
|
388
|
+
out += d.toString();
|
|
389
|
+
});
|
|
390
|
+
proc.stderr?.on("data", (d) => {
|
|
391
|
+
out += d.toString();
|
|
353
392
|
});
|
|
393
|
+
proc.on("exit", (code) => {
|
|
394
|
+
resolve(out + (code === 0 ? "" : `
|
|
395
|
+
[exit ${code}]`));
|
|
396
|
+
});
|
|
397
|
+
proc.on("error", (e) => resolve(c.red(`spawn failed: ${e.message}`)));
|
|
398
|
+
});
|
|
354
399
|
}
|
|
355
400
|
function parseFlags(args) {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
401
|
+
const flags = { from: null, sha256: null, link: false };
|
|
402
|
+
for (let i = 0; i < args.length; i++) {
|
|
403
|
+
if (args[i] === "--from") flags.from = args[++i] ?? null;
|
|
404
|
+
else if (args[i]?.startsWith("--from=")) flags.from = args[i].slice("--from=".length);
|
|
405
|
+
else if (args[i] === "--sha256") flags.sha256 = args[++i] ?? null;
|
|
406
|
+
else if (args[i]?.startsWith("--sha256=")) flags.sha256 = args[i].slice("--sha256=".length);
|
|
407
|
+
else if (args[i] === "--link") flags.link = true;
|
|
408
|
+
}
|
|
409
|
+
return flags;
|
|
410
|
+
}
|
|
411
|
+
async function dispatchModuleCommand(sub, args) {
|
|
412
|
+
switch (sub) {
|
|
413
|
+
case "list":
|
|
414
|
+
return cmdModuleList(args);
|
|
415
|
+
case "search":
|
|
416
|
+
return cmdModuleSearch(args);
|
|
417
|
+
case "install":
|
|
418
|
+
return cmdModuleInstall(args);
|
|
419
|
+
case "uninstall":
|
|
420
|
+
case "remove":
|
|
421
|
+
return cmdModuleUninstall(args);
|
|
422
|
+
case "verify":
|
|
423
|
+
return cmdModuleVerify(args);
|
|
424
|
+
case "info":
|
|
425
|
+
return cmdModuleInfo(args);
|
|
426
|
+
case "enable":
|
|
427
|
+
return (await cmdModuleEnable(args)).message;
|
|
428
|
+
case "disable":
|
|
429
|
+
return (await cmdModuleDisable(args)).message;
|
|
430
|
+
case "build": {
|
|
431
|
+
const { cmdPackBuild } = await import("./pack-lifecycle.js");
|
|
432
|
+
return (await cmdPackBuild(args)).message;
|
|
433
|
+
}
|
|
434
|
+
case "reload": {
|
|
435
|
+
const { cmdReload } = await import("./commands-reload.js");
|
|
436
|
+
return cmdReload(args);
|
|
364
437
|
}
|
|
365
|
-
|
|
438
|
+
case "create": {
|
|
439
|
+
const { runModuleCreateWizard } = await import("./module-wizard.js");
|
|
440
|
+
return runModuleCreateWizard();
|
|
441
|
+
}
|
|
442
|
+
case "link":
|
|
443
|
+
return cmdModuleLink(args);
|
|
444
|
+
case "dev": {
|
|
445
|
+
const { runModuleDevWizard } = await import("./module-wizard.js");
|
|
446
|
+
return runModuleDevWizard();
|
|
447
|
+
}
|
|
448
|
+
default:
|
|
449
|
+
return c.yellow(moduleUsage());
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
function listInstalledModuleIdsSync() {
|
|
453
|
+
const dir = modulesDir();
|
|
454
|
+
if (!existsSync(dir)) return [];
|
|
455
|
+
try {
|
|
456
|
+
return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
457
|
+
} catch {
|
|
458
|
+
return [];
|
|
459
|
+
}
|
|
366
460
|
}
|
|
367
|
-
|
|
461
|
+
export {
|
|
462
|
+
MODULE_CMD_NAMES,
|
|
463
|
+
MODULE_SUBCOMMANDS,
|
|
464
|
+
cmdModuleDisable,
|
|
465
|
+
cmdModuleEnable,
|
|
466
|
+
cmdModuleInfo,
|
|
467
|
+
cmdModuleInstall,
|
|
468
|
+
cmdModuleLink,
|
|
469
|
+
cmdModuleList,
|
|
470
|
+
cmdModuleSearch,
|
|
471
|
+
cmdModuleUninstall,
|
|
472
|
+
cmdModuleVerify,
|
|
473
|
+
dispatchModuleCommand,
|
|
474
|
+
isModuleCommand,
|
|
475
|
+
listInstalledModuleIdsSync,
|
|
476
|
+
moduleUsage,
|
|
477
|
+
paintModuleCmdAlias,
|
|
478
|
+
scanAndWarnUnknown
|
|
479
|
+
};
|
|
480
|
+
//# sourceMappingURL=module-commands.js.map
|