@asiainfo-sdd/multica-cli 0.2.1 → 0.3.7

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/lib/platform.js CHANGED
@@ -1,45 +1,45 @@
1
- 'use strict';
2
-
3
- // platform/arch → 下载资产维度。产物命名与 multica 仓
4
- // server/extensions/agentfoundry/build/build.sh 的 cli-all 模式对齐:
5
- // multica-af-<os>-<arch>[.exe]
6
-
7
- const OS_MAP = {
8
- darwin: 'darwin',
9
- linux: 'linux',
10
- win32: 'windows',
11
- };
12
-
13
- const ARCH_MAP = {
14
- x64: 'amd64',
15
- arm64: 'arm64',
16
- };
17
-
18
- // 返回 null 表示当前平台不在 6 平台支持矩阵内。
19
- function detectPlatform(platform = process.platform, arch = process.arch) {
20
- const os = OS_MAP[platform];
21
- const mappedArch = ARCH_MAP[arch];
22
- if (!os || !mappedArch) return null;
23
- return { os, arch: mappedArch, ext: platform === 'win32' ? '.exe' : '' };
24
- }
25
-
26
- function assetName({ os, arch, ext }) {
27
- return `multica-af-${os}-${arch}${ext}`;
28
- }
29
-
30
- // manifest.json 里的平台键。
31
- function manifestKey({ os, arch }) {
32
- return `${os}-${arch}`;
33
- }
34
-
35
- function vendorBinaryPath(vendorDir, plat) {
36
- const path = require('path');
37
- return path.join(vendorDir, assetName(plat));
38
- }
39
-
40
- module.exports = {
41
- detectPlatform,
42
- assetName,
43
- manifestKey,
44
- vendorBinaryPath,
45
- };
1
+ 'use strict';
2
+
3
+ // platform/arch → 下载资产维度。产物命名与 multica 仓
4
+ // server/extensions/agentfoundry/build/build.sh 的 cli-all 模式对齐:
5
+ // multica-af-<os>-<arch>[.exe]
6
+
7
+ const OS_MAP = {
8
+ darwin: 'darwin',
9
+ linux: 'linux',
10
+ win32: 'windows',
11
+ };
12
+
13
+ const ARCH_MAP = {
14
+ x64: 'amd64',
15
+ arm64: 'arm64',
16
+ };
17
+
18
+ // 返回 null 表示当前平台不在 6 平台支持矩阵内。
19
+ function detectPlatform(platform = process.platform, arch = process.arch) {
20
+ const os = OS_MAP[platform];
21
+ const mappedArch = ARCH_MAP[arch];
22
+ if (!os || !mappedArch) return null;
23
+ return { os, arch: mappedArch, ext: platform === 'win32' ? '.exe' : '' };
24
+ }
25
+
26
+ function assetName({ os, arch, ext }) {
27
+ return `multica-af-${os}-${arch}${ext}`;
28
+ }
29
+
30
+ // manifest.json 里的平台键。
31
+ function manifestKey({ os, arch }) {
32
+ return `${os}-${arch}`;
33
+ }
34
+
35
+ function vendorBinaryPath(vendorDir, plat) {
36
+ const path = require('path');
37
+ return path.join(vendorDir, assetName(plat));
38
+ }
39
+
40
+ module.exports = {
41
+ detectPlatform,
42
+ assetName,
43
+ manifestKey,
44
+ vendorBinaryPath,
45
+ };
@@ -1,102 +1,102 @@
1
- 'use strict';
2
-
3
- // 安装时把部署级配置(server_url/app_url)写入 ~/.multica/config.json,
4
- // 免去用户手工 multica config set。规则:
5
- // - 取值优先级: MULTICA_SERVER_URL/MULTICA_APP_URL env > manifest 顶层
6
- // server_url/app_url(运维可不重发 npm 包直接改) > lib/dist-config.json
7
- // - server_url/app_url 两个部署级 URL【覆盖式】更新: 与本次安装源不一致时
8
- // 直接覆盖并打印 新值 ← 旧值(升级环境后旧地址上的 token 验证/daemon 连接
9
- // 必然失败,覆盖是唯一正确动作;自定义地址用户可从日志看到旧值并改回)
10
- // - 其余字段(token/workspace_id/各超时/backends 等)一律不动
11
- // - 目标文件是 multica CLI 的标准配置(daemon 只认这里的 token),
12
- // 多 profile 场景(--profile)不在此处理,留给用户手动 config set
13
- // token 属于个人凭据,始终由用户自行 multica login --token 写入。
14
-
15
- const fs = require('fs');
16
- const os = require('os');
17
- const path = require('path');
18
-
19
- const CONFIG_KEYS = ['server_url', 'app_url'];
20
-
21
- function multicaConfigPath() {
22
- return path.join(os.homedir(), '.multica', 'config.json');
23
- }
24
-
25
- function readUserConfig() {
26
- const file = multicaConfigPath();
27
- let raw;
28
- try {
29
- raw = fs.readFileSync(file, 'utf8');
30
- } catch (err) {
31
- if (err.code === 'ENOENT') return { config: {}, existed: false };
32
- throw err;
33
- }
34
- const config = JSON.parse(raw); // 解析失败向上抛,由调用方决定跳过
35
- return { config, existed: true };
36
- }
37
-
38
- // env > manifest > dist-config;返回只含非空键的 {server_url?, app_url?}。
39
- function resolveDesired(manifest) {
40
- const distConfig = require('./dist-config.json');
41
- const desired = {};
42
- const fromEnv = {
43
- server_url: process.env.MULTICA_SERVER_URL,
44
- app_url: process.env.MULTICA_APP_URL,
45
- };
46
- for (const key of CONFIG_KEYS) {
47
- const value =
48
- (fromEnv[key] && fromEnv[key].trim()) ||
49
- (manifest && typeof manifest[key] === 'string' && manifest[key].trim()) ||
50
- (typeof distConfig[key] === 'string' && distConfig[key].trim()) ||
51
- '';
52
- if (value) desired[key] = value;
53
- }
54
- return desired;
55
- }
56
-
57
- // 返回 {written: [{key, value}], updated: [{key, from, to}]} 供上层打印;
58
- // 无可写内容或文件损坏时返回 null(不产生任何变更)。
59
- // written = 原来为空、本次填入;updated = 原有不同值、本次覆盖。
60
- function applyServerConfig(manifest) {
61
- const desired = resolveDesired(manifest);
62
- if (!desired.server_url && !desired.app_url) return null;
63
-
64
- let userConfig;
65
- let existed;
66
- try {
67
- ({ config: userConfig, existed } = readUserConfig());
68
- } catch {
69
- logWarn(`已存在 ${multicaConfigPath()} 但无法解析,已跳过配置写入——请手工 multica config set server_url`);
70
- return null;
71
- }
72
-
73
- const written = [];
74
- const updated = [];
75
- let changed = false;
76
- for (const key of CONFIG_KEYS) {
77
- if (!desired[key]) continue;
78
- if (!userConfig[key]) {
79
- userConfig[key] = desired[key];
80
- written.push({ key, value: desired[key] });
81
- changed = true;
82
- } else if (userConfig[key] !== desired[key]) {
83
- updated.push({ key, from: userConfig[key], to: desired[key] });
84
- userConfig[key] = desired[key];
85
- changed = true;
86
- }
87
- }
88
-
89
- if (!changed) return { written, updated };
90
-
91
- const file = multicaConfigPath();
92
- fs.mkdirSync(path.dirname(file), { recursive: true });
93
- // 保留用户已有其余字段,仅更新部署级键;不写 token。
94
- fs.writeFileSync(file, JSON.stringify(userConfig, null, 2) + '\n');
95
- return { written, updated };
96
- }
97
-
98
- function logWarn(msg) {
99
- process.stdout.write(`⚠ ${msg}\n`);
100
- }
101
-
102
- module.exports = { applyServerConfig, multicaConfigPath, resolveDesired };
1
+ 'use strict';
2
+
3
+ // 安装时把部署级配置(server_url/app_url)写入 ~/.multica/config.json,
4
+ // 免去用户手工 multica config set。规则:
5
+ // - 取值优先级: MULTICA_SERVER_URL/MULTICA_APP_URL env > manifest 顶层
6
+ // server_url/app_url(运维可不重发 npm 包直接改) > lib/dist-config.json
7
+ // - server_url/app_url 两个部署级 URL【覆盖式】更新: 与本次安装源不一致时
8
+ // 直接覆盖并打印 新值 ← 旧值(升级环境后旧地址上的 token 验证/daemon 连接
9
+ // 必然失败,覆盖是唯一正确动作;自定义地址用户可从日志看到旧值并改回)
10
+ // - 其余字段(token/workspace_id/各超时/backends 等)一律不动
11
+ // - 目标文件是 multica CLI 的标准配置(daemon 只认这里的 token),
12
+ // 多 profile 场景(--profile)不在此处理,留给用户手动 config set
13
+ // token 属于个人凭据,始终由用户自行 multica login --token 写入。
14
+
15
+ const fs = require('fs');
16
+ const os = require('os');
17
+ const path = require('path');
18
+
19
+ const CONFIG_KEYS = ['server_url', 'app_url'];
20
+
21
+ function multicaConfigPath() {
22
+ return path.join(os.homedir(), '.multica', 'config.json');
23
+ }
24
+
25
+ function readUserConfig() {
26
+ const file = multicaConfigPath();
27
+ let raw;
28
+ try {
29
+ raw = fs.readFileSync(file, 'utf8');
30
+ } catch (err) {
31
+ if (err.code === 'ENOENT') return { config: {}, existed: false };
32
+ throw err;
33
+ }
34
+ const config = JSON.parse(raw); // 解析失败向上抛,由调用方决定跳过
35
+ return { config, existed: true };
36
+ }
37
+
38
+ // env > manifest > dist-config;返回只含非空键的 {server_url?, app_url?}。
39
+ function resolveDesired(manifest) {
40
+ const distConfig = require('./dist-config.json');
41
+ const desired = {};
42
+ const fromEnv = {
43
+ server_url: process.env.MULTICA_SERVER_URL,
44
+ app_url: process.env.MULTICA_APP_URL,
45
+ };
46
+ for (const key of CONFIG_KEYS) {
47
+ const value =
48
+ (fromEnv[key] && fromEnv[key].trim()) ||
49
+ (manifest && typeof manifest[key] === 'string' && manifest[key].trim()) ||
50
+ (typeof distConfig[key] === 'string' && distConfig[key].trim()) ||
51
+ '';
52
+ if (value) desired[key] = value;
53
+ }
54
+ return desired;
55
+ }
56
+
57
+ // 返回 {written: [{key, value}], updated: [{key, from, to}]} 供上层打印;
58
+ // 无可写内容或文件损坏时返回 null(不产生任何变更)。
59
+ // written = 原来为空、本次填入;updated = 原有不同值、本次覆盖。
60
+ function applyServerConfig(manifest) {
61
+ const desired = resolveDesired(manifest);
62
+ if (!desired.server_url && !desired.app_url) return null;
63
+
64
+ let userConfig;
65
+ let existed;
66
+ try {
67
+ ({ config: userConfig, existed } = readUserConfig());
68
+ } catch {
69
+ logWarn(`已存在 ${multicaConfigPath()} 但无法解析,已跳过配置写入——请手工 multica config set server_url`);
70
+ return null;
71
+ }
72
+
73
+ const written = [];
74
+ const updated = [];
75
+ let changed = false;
76
+ for (const key of CONFIG_KEYS) {
77
+ if (!desired[key]) continue;
78
+ if (!userConfig[key]) {
79
+ userConfig[key] = desired[key];
80
+ written.push({ key, value: desired[key] });
81
+ changed = true;
82
+ } else if (userConfig[key] !== desired[key]) {
83
+ updated.push({ key, from: userConfig[key], to: desired[key] });
84
+ userConfig[key] = desired[key];
85
+ changed = true;
86
+ }
87
+ }
88
+
89
+ if (!changed) return { written, updated };
90
+
91
+ const file = multicaConfigPath();
92
+ fs.mkdirSync(path.dirname(file), { recursive: true });
93
+ // 保留用户已有其余字段,仅更新部署级键;不写 token。
94
+ fs.writeFileSync(file, JSON.stringify(userConfig, null, 2) + '\n');
95
+ return { written, updated };
96
+ }
97
+
98
+ function logWarn(msg) {
99
+ process.stdout.write(`⚠ ${msg}\n`);
100
+ }
101
+
102
+ module.exports = { applyServerConfig, multicaConfigPath, resolveDesired };
@@ -0,0 +1,263 @@
1
+ 'use strict';
2
+
3
+ // 卸载 multica(daemon 客户端)及其全部安装产物,一条命令覆盖:
4
+ // 停 daemon → 删 ~/.multica 配置 → 删独立二进制 → 删命令入口(shim) → 卸 npm 全局包
5
+ // 入口:bin/multica-uninstall.js(命令 multica-uninstall)。
6
+ //
7
+ // 为什么需要独立命令:npm v7 起不再执行 preuninstall 等卸载生命周期脚本,
8
+ // `npm un -g` 无法触发包内清理;而本包"只管安装",卸载由用户显式执行一次即可。
9
+ //
10
+ // 现实背景:同一台机器上 multica 命令可能来自多处——npm 全局包(test/prod 两个
11
+ // profile 名)、独立安装的 ~/.local/bin/multica[.exe]、以及历史 npm prefix 残留的
12
+ // shim。逐一处理,避免"卸了还残留、卸了还能用"。
13
+
14
+ const fs = require('fs');
15
+ const os = require('os');
16
+ const path = require('path');
17
+ const { spawnSync } = require('child_process');
18
+
19
+ // 同机可能安装的两个 profile 包名(test/prod 互斥,历史可能都残留)。
20
+ const CANDIDATE_PKGS = [
21
+ '@asiainfo-sdd/multica-cli',
22
+ '@asiainfo-sdd/multica-cli-test',
23
+ ];
24
+
25
+ const isWin = process.platform === 'win32';
26
+
27
+ const log = (msg) => process.stdout.write(`${msg}\n`);
28
+ const ok = (msg) => process.stdout.write(`✓ ${msg}\n`);
29
+ const warn = (msg) => process.stdout.write(`⚠ ${msg}\n`);
30
+
31
+ // Windows 上 npm 是 .cmd,必须经 cmd.exe 执行;直接 spawn 并用 shell:true 会触发
32
+ // Node DEP0190(args 未转义)。统一走 cmd /c 规避。
33
+ function runCmd(cmd, args) {
34
+ if (isWin) {
35
+ return spawnSync(process.env.ComSpec || 'cmd.exe', ['/c', cmd, ...args], {
36
+ encoding: 'utf8',
37
+ });
38
+ }
39
+ return spawnSync(cmd, args, { encoding: 'utf8' });
40
+ }
41
+
42
+ function npmRun(args) {
43
+ return runCmd('npm', args);
44
+ }
45
+
46
+ function configDir() {
47
+ return path.join(os.homedir(), '.multica');
48
+ }
49
+
50
+ // 独立安装(非 npm)的二进制:~/.local/bin/multica[.exe]。
51
+ function standaloneBinary() {
52
+ return path.join(os.homedir(), '.local', 'bin', `multica${isWin ? '.exe' : ''}`);
53
+ }
54
+
55
+ // 当前 PATH 上所有 multica 命令入口(可能多个)。
56
+ function findOnPath() {
57
+ const r = isWin
58
+ ? spawnSync('where', ['multica'], { encoding: 'utf8' })
59
+ : spawnSync('which', ['-a', 'multica'], { encoding: 'utf8' });
60
+ if (r.status !== 0 || !r.stdout) return [];
61
+ return r.stdout
62
+ .split(/\r?\n/)
63
+ .map((s) => s.trim())
64
+ .filter(Boolean);
65
+ }
66
+
67
+ // multica 命令可能来自多个 npm 全局目录(nvm prefix、%APPDATA%\npm、历史 prefix),
68
+ // 而 `npm root -g` 只报当前一个 —— 逐一收集,避免漏删"卸了还能用"。
69
+ function candidatePrefixes() {
70
+ const set = new Set();
71
+ const rootG = npmRun(['root', '-g']);
72
+ if (rootG.status === 0 && rootG.stdout) {
73
+ set.add(path.dirname(rootG.stdout.trim())); // …/node_modules → 前缀
74
+ }
75
+ const prefix = npmRun(['config', 'get', 'prefix']);
76
+ if (prefix.status === 0 && prefix.stdout) {
77
+ const p = prefix.stdout.trim();
78
+ if (p && p !== 'undefined') set.add(p);
79
+ }
80
+ if (isWin && process.env.APPDATA) set.add(path.join(process.env.APPDATA, 'npm'));
81
+ for (const bin of findOnPath()) set.add(path.dirname(bin)); // shim 所在目录即 prefix
82
+ return [...set].filter(Boolean);
83
+ }
84
+
85
+ function installedNpmPkgs() {
86
+ const found = []; // {prefix, name}
87
+ for (const prefix of candidatePrefixes()) {
88
+ for (const name of CANDIDATE_PKGS) {
89
+ if (fs.existsSync(path.join(prefix, 'node_modules', name))) {
90
+ found.push({ prefix, name });
91
+ }
92
+ }
93
+ }
94
+ return found;
95
+ }
96
+
97
+ function uninstallNpmPkgs(found) {
98
+ const failed = [];
99
+ for (const { prefix, name } of found) {
100
+ const r = npmRun(['uninstall', '-g', '--prefix', prefix, name]);
101
+ const dir = path.join(prefix, 'node_modules', name);
102
+ if (r.status === 0 && !fs.existsSync(dir)) {
103
+ ok(`已卸载:${name}(prefix ${prefix})`);
104
+ continue;
105
+ }
106
+ // npm uninstall 失败或残留:直接删包目录兜底。
107
+ try {
108
+ fs.rmSync(dir, { recursive: true, force: true });
109
+ fs.rmSync(path.join(prefix, 'node_modules', '@asiainfo-sdd'),
110
+ { recursive: true, force: true });
111
+ ok(`已删除包目录:${dir}`);
112
+ } catch (err) {
113
+ failed.push(`${name}(prefix ${prefix})`);
114
+ warn(`删除失败:${dir} — ${err.message}`);
115
+ }
116
+ }
117
+ return failed;
118
+ }
119
+
120
+ // ---------- 各清理步骤 ----------
121
+
122
+ function stopDaemon() {
123
+ const onPath = findOnPath();
124
+ if (!onPath.length) {
125
+ log('· 未发现 multica 命令,跳过 daemon 停止');
126
+ return;
127
+ }
128
+ const r = runCmd('multica', ['daemon', 'stop']);
129
+ const text = `${r.stdout || ''}${r.stderr || ''}`.trim();
130
+ if (r.status === 0) ok('daemon 已停止(原本未运行则无操作)');
131
+ else warn(`daemon stop 未成功(可忽略,继续卸载):${text || `exit ${r.status}`}`);
132
+ }
133
+
134
+ function removeConfig(keepConfig) {
135
+ const dir = configDir();
136
+ if (!fs.existsSync(dir)) {
137
+ log(`· 配置目录不存在:${dir}`);
138
+ return;
139
+ }
140
+ if (keepConfig) {
141
+ log(`· 保留配置目录(--keep-config):${dir}`);
142
+ return;
143
+ }
144
+ fs.rmSync(dir, { recursive: true, force: true });
145
+ ok(`已删除配置目录:${dir}`);
146
+ }
147
+
148
+ function removeStandaloneBinary() {
149
+ const bin = standaloneBinary();
150
+ if (!fs.existsSync(bin)) {
151
+ log(`· 无独立安装的二进制:${bin}`);
152
+ return;
153
+ }
154
+ try {
155
+ fs.rmSync(bin, { force: true });
156
+ ok(`已删除独立二进制:${bin}`);
157
+ } catch (err) {
158
+ warn(`删除失败(可能被占用):${bin} — ${err.message}`);
159
+ }
160
+ }
161
+
162
+ // 删除 PATH 上的命令入口(npm shim:.cmd/.ps1/无扩展名)。
163
+ // 独立 exe 由 removeStandaloneBinary 处理,这里跳过 .exe。
164
+ function removeShims() {
165
+ const bin = standaloneBinary();
166
+ let removed = 0;
167
+ for (const p of findOnPath()) {
168
+ if (path.resolve(p) === path.resolve(bin)) continue;
169
+ const base = path.basename(p).toLowerCase();
170
+ if (!/^multica(\.cmd|\.ps1|\.bat)?$/.test(base)) continue; // 只处理 shim,跳过 .exe/其他
171
+ try {
172
+ fs.rmSync(p, { force: true });
173
+ ok(`已删除命令入口:${p}`);
174
+ removed++;
175
+ } catch (err) {
176
+ warn(`删除失败:${p} — ${err.message}`);
177
+ }
178
+ }
179
+ if (!removed) log('· 无残留命令入口(shim)');
180
+ }
181
+
182
+ // ---------- 入口 ----------
183
+
184
+ function parseArgs(argv) {
185
+ const opts = { yes: false, keepConfig: false, dryRun: false };
186
+ for (const a of argv) {
187
+ if (a === '-y' || a === '--yes') opts.yes = true;
188
+ else if (a === '--keep-config') opts.keepConfig = true;
189
+ else if (a === '--dry-run') opts.dryRun = true;
190
+ else if (a === '-h' || a === '--help') opts.help = true;
191
+ }
192
+ return opts;
193
+ }
194
+
195
+ function printHelp() {
196
+ log('用法:multica-uninstall [选项]');
197
+ log('');
198
+ log('彻底卸载 multica(daemon 客户端):停 daemon、删 ~/.multica 配置、');
199
+ log('删独立二进制与命令入口、卸载 npm 全局包。');
200
+ log('');
201
+ log('选项:');
202
+ log(' -y, --yes 跳过交互确认(脚本/非交互环境必须显式指定)');
203
+ log(' --keep-config 保留 ~/.multica 配置目录(默认删除)');
204
+ log(' --dry-run 只打印将要执行的操作,不实际修改');
205
+ log(' -h, --help 显示本帮助');
206
+ }
207
+
208
+ function confirm() {
209
+ if (!process.stdin.isTTY) {
210
+ warn('非交互环境:需显式加 --yes 才会执行卸载。');
211
+ return false;
212
+ }
213
+ process.stdout.write('确认卸载 multica 及其全部本地数据(含 ~/.multica)?[y/N] ');
214
+ const buf = Buffer.alloc(1);
215
+ try {
216
+ const n = fs.readSync(0, buf, 0, 1, null);
217
+ return n > 0 && /^y/i.test(buf.toString());
218
+ } catch {
219
+ return false;
220
+ }
221
+ }
222
+
223
+ function main() {
224
+ const opts = parseArgs(process.argv.slice(2));
225
+ if (opts.help) return printHelp();
226
+
227
+ log('即将卸载 multica(daemon 客户端)');
228
+ log('检测到的 multica 命令入口:');
229
+ const onPath = findOnPath();
230
+ if (onPath.length) onPath.forEach((p) => log(` · ${p}`));
231
+ else log(' · (PATH 中未发现 multica)');
232
+ log('');
233
+
234
+ if (opts.dryRun) {
235
+ log('(--dry-run)将执行:停 daemon → 删配置 → 删独立二进制 → 删命令入口 → 卸 npm 包');
236
+ log(` 配置目录:${configDir()}`);
237
+ log(` 独立二进制:${standaloneBinary()}`);
238
+ const pkgs = installedNpmPkgs();
239
+ log(` npm 全局包:${pkgs.length ? pkgs.map((p) => `${p.name}(${p.prefix})`).join('; ') : '(无)'}`);
240
+ return;
241
+ }
242
+
243
+ if (!confirm()) {
244
+ log('已取消。');
245
+ return;
246
+ }
247
+
248
+ stopDaemon();
249
+ removeConfig(opts.keepConfig);
250
+ removeStandaloneBinary();
251
+ removeShims();
252
+ const failed = uninstallNpmPkgs(installedNpmPkgs());
253
+
254
+ log('');
255
+ if (failed.length) {
256
+ warn(`以下 npm 包未能卸载(多为当前进程占用文件,重开终端后手动执行即可):`);
257
+ failed.forEach((n) => log(` npm uninstall -g ${n}`));
258
+ } else {
259
+ ok('multica 已卸载完成。重开终端以确保 PATH 刷新。');
260
+ }
261
+ }
262
+
263
+ main();
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@asiainfo-sdd/multica-cli",
3
- "version": "0.2.1",
3
+ "version": "0.3.7",
4
4
  "description": "数字工厂 daemon 客户端安装帮助包:npm install 时按平台下载二进制并校验并写入部署级配置,不参与 daemon 的使用与管理",
5
5
  "bin": {
6
- "aido": "bin/aido.js",
7
- "multica": "bin/multica.js"
6
+ "multica": "bin/multica.js",
7
+ "multica-uninstall": "bin/multica-uninstall.js"
8
8
  },
9
9
  "scripts": {
10
10
  "postinstall": "node lib/install.js"
@@ -19,7 +19,10 @@
19
19
  },
20
20
  "license": "UNLICENSED",
21
21
  "multica": {
22
- "configKeysWrittenAtInstall": ["server_url", "app_url"],
22
+ "configKeysWrittenAtInstall": [
23
+ "server_url",
24
+ "app_url"
25
+ ],
23
26
  "notes": "安装时只填空写入部署级配置(env MULTICA_SERVER_URL/MULTICA_APP_URL > manifest 顶层 > lib/dist-config.json);不写 token、不覆盖已有值"
24
27
  }
25
28
  }
package/bin/aido.js DELETED
@@ -1,52 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- // bin wrapper:把参数原样转给 vendor/ 下的平台二进制。
5
- // aido 是对外品牌命令(multica 是兼容别名,另一入口 bin/multica.js)。
6
- // 二进制的子命令(daemon/login/config/...)与参数原样透传。
7
-
8
- const { spawn } = require('child_process');
9
- const fs = require('fs');
10
- const path = require('path');
11
- const { detectPlatform, vendorBinaryPath } = require('../lib/platform');
12
-
13
- // 对外命令名:默认 aido;用户通过 multica 别名调用时保持 multica 提示。
14
- const invokedAs = path.basename(process.argv[1] || '', '.js').replace(/\.(cmd|ps1)$/, '') || 'aido';
15
- const brand = invokedAs === 'multica' ? 'multica' : 'aido';
16
-
17
- const plat = detectPlatform();
18
- const binary =
19
- plat && vendorBinaryPath(path.join(__dirname, '..', 'vendor'), plat);
20
-
21
- if (!binary || !fs.existsSync(binary)) {
22
- console.error(`✗ ${brand} 二进制尚未下载。`);
23
- console.error(' 修复方式:npm rebuild @asiainfo-sdd/multica-cli');
24
- console.error(' (或设置 MULTICA_SKIP_BINARY_INSTALL=1 显式跳过后自备二进制)');
25
- process.exit(127);
26
- }
27
-
28
- const child = spawn(binary, process.argv.slice(2), {
29
- stdio: 'inherit',
30
- env: process.env,
31
- });
32
-
33
- // Windows 上 Ctrl-C 走 SIGINT/Break;转发常见终止信号。
34
- ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGBREAK'].forEach((sig) => {
35
- process.on(sig, () => child.kill(sig));
36
- });
37
- // Windows spawn .exe 时 kill('SIGTERM') 可能不生效,兜底强杀。
38
- process.on('exit', () => {
39
- if (!child.killed) child.kill();
40
- });
41
-
42
- child.on('error', (err) => {
43
- console.error(`✗ 无法执行 ${brand} 二进制:${err.message}`);
44
- process.exit(126);
45
- });
46
-
47
- child.on('close', (code, signal) => {
48
- if (signal) {
49
- process.exit(128);
50
- }
51
- process.exit(code ?? 1);
52
- });