@okclaw-build/cli 1.0.0-beta.6 → 1.0.0-beta.60
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/dist/bridge/daemon/config.d.ts +24 -0
- package/dist/bridge/daemon/config.js +96 -0
- package/dist/bridge/daemon/config.js.map +1 -0
- package/dist/bridge/daemon/daemon.d.ts +7 -0
- package/dist/bridge/daemon/daemon.js +23 -0
- package/dist/bridge/daemon/daemon.js.map +1 -0
- package/dist/bridge/daemon/event-id.d.ts +2 -0
- package/dist/bridge/daemon/event-id.js +18 -0
- package/dist/bridge/daemon/event-id.js.map +1 -0
- package/dist/bridge/daemon/frames.d.ts +81 -0
- package/dist/bridge/daemon/frames.js +64 -0
- package/dist/bridge/daemon/frames.js.map +1 -0
- package/dist/bridge/daemon/openclaw-chat-runtime.d.ts +80 -0
- package/dist/bridge/daemon/openclaw-chat-runtime.js +1222 -0
- package/dist/bridge/daemon/openclaw-chat-runtime.js.map +1 -0
- package/dist/bridge/daemon/openclaw-gateway-client.d.ts +58 -0
- package/dist/bridge/daemon/openclaw-gateway-client.js +251 -0
- package/dist/bridge/daemon/openclaw-gateway-client.js.map +1 -0
- package/dist/bridge/daemon/relay-client.d.ts +75 -0
- package/dist/bridge/daemon/relay-client.js +369 -0
- package/dist/bridge/daemon/relay-client.js.map +1 -0
- package/dist/bridge/daemon/stub-chat-runtime.d.ts +4 -0
- package/dist/bridge/daemon/stub-chat-runtime.js +91 -0
- package/dist/bridge/daemon/stub-chat-runtime.js.map +1 -0
- package/dist/bridge/service.d.ts +12 -0
- package/dist/bridge/service.js +86 -0
- package/dist/bridge/service.js.map +1 -0
- package/dist/commands/backup.d.ts +16 -0
- package/dist/commands/backup.js +99 -0
- package/dist/commands/backup.js.map +1 -0
- package/dist/commands/bridge.d.ts +7 -0
- package/dist/commands/bridge.js +100 -0
- package/dist/commands/bridge.js.map +1 -0
- package/dist/commands/check.js +22 -6
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/edit.d.ts +2 -7
- package/dist/commands/edit.js +28 -16
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/feed.js +48 -16
- package/dist/commands/feed.js.map +1 -1
- package/dist/commands/install.js +99 -18
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/migration.d.ts +1 -0
- package/dist/commands/migration.js +205 -0
- package/dist/commands/migration.js.map +1 -0
- package/dist/commands/openclaw-channel-login.d.ts +54 -0
- package/dist/commands/openclaw-channel-login.js +334 -0
- package/dist/commands/openclaw-channel-login.js.map +1 -0
- package/dist/commands/restore.d.ts +16 -0
- package/dist/commands/restore.js +94 -0
- package/dist/commands/restore.js.map +1 -0
- package/dist/commands/service.js +56 -13
- package/dist/commands/service.js.map +1 -1
- package/dist/commands/show.d.ts +13 -0
- package/dist/commands/show.js +70 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/skill.d.ts +1 -0
- package/dist/commands/skill.js +137 -0
- package/dist/commands/skill.js.map +1 -0
- package/dist/commands/uninstall.d.ts +1 -0
- package/dist/commands/uninstall.js +46 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/index.js +121 -12
- package/dist/index.js.map +1 -1
- package/dist/installers/base.d.ts +3 -1
- package/dist/installers/channel.d.ts +10 -2
- package/dist/installers/channel.js +143 -24
- package/dist/installers/channel.js.map +1 -1
- package/dist/installers/openclaw.d.ts +19 -2
- package/dist/installers/openclaw.js +186 -55
- package/dist/installers/openclaw.js.map +1 -1
- package/dist/installers/openviking-purge.d.ts +52 -0
- package/dist/installers/openviking-purge.js +380 -0
- package/dist/installers/openviking-purge.js.map +1 -0
- package/dist/installers/openviking.js +1 -2
- package/dist/installers/openviking.js.map +1 -1
- package/dist/installers/skill.d.ts +5 -2
- package/dist/installers/skill.js +67 -18
- package/dist/installers/skill.js.map +1 -1
- package/dist/migration/archive.d.ts +14 -0
- package/dist/migration/archive.js +84 -0
- package/dist/migration/archive.js.map +1 -0
- package/dist/migration/crypto.d.ts +16 -0
- package/dist/migration/crypto.js +56 -0
- package/dist/migration/crypto.js.map +1 -0
- package/dist/migration/manifest.d.ts +39 -0
- package/dist/migration/manifest.js +140 -0
- package/dist/migration/manifest.js.map +1 -0
- package/dist/openclaw-user-data.d.ts +86 -0
- package/dist/openclaw-user-data.js +422 -0
- package/dist/openclaw-user-data.js.map +1 -0
- package/dist/output/mode.d.ts +11 -0
- package/dist/output/mode.js +27 -0
- package/dist/output/mode.js.map +1 -0
- package/dist/output/ndjson.d.ts +57 -0
- package/dist/output/ndjson.js +136 -0
- package/dist/output/ndjson.js.map +1 -0
- package/dist/utils/constants.d.ts +14 -13
- package/dist/utils/constants.js +40 -21
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/deps.d.ts +34 -3
- package/dist/utils/deps.js +62 -100
- package/dist/utils/deps.js.map +1 -1
- package/dist/utils/install-safety.d.ts +25 -0
- package/dist/utils/install-safety.js +97 -0
- package/dist/utils/install-safety.js.map +1 -0
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.js +31 -4
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/mirror.js +14 -0
- package/dist/utils/mirror.js.map +1 -1
- package/dist/utils/openclaw-config-cli.d.ts +19 -0
- package/dist/utils/openclaw-config-cli.js +81 -0
- package/dist/utils/openclaw-config-cli.js.map +1 -0
- package/dist/utils/openclaw-daemon.d.ts +58 -0
- package/dist/utils/openclaw-daemon.js +154 -0
- package/dist/utils/openclaw-daemon.js.map +1 -0
- package/dist/utils/shell.d.ts +23 -2
- package/dist/utils/shell.js +138 -5
- package/dist/utils/shell.js.map +1 -1
- package/package.json +6 -4
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { isAbsolute, resolve, sep } from 'node:path';
|
|
2
|
+
import { BUNDLED_PLUGINS, CHANNEL_ALIASES, CHANNEL_NPM_PACKAGES, OKCLAW_TMP_DIR, OPENCLAW_HOME, OPENCLAW_WORKSPACE, } from './constants.js';
|
|
3
|
+
const CHANNEL_NAMES = new Set([
|
|
4
|
+
...Object.keys(CHANNEL_NPM_PACKAGES),
|
|
5
|
+
...Object.keys(CHANNEL_ALIASES),
|
|
6
|
+
...BUNDLED_PLUGINS,
|
|
7
|
+
's3',
|
|
8
|
+
]);
|
|
9
|
+
const NAME_TOKEN_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
10
|
+
const VERSION_PATTERN = /^[A-Za-z0-9._+-]+$/;
|
|
11
|
+
const SHELL_METACHAR_PATTERN = /[`$;|<>]/;
|
|
12
|
+
const CONTROL_PATTERN = /[\x00-\x1F\x7F]/;
|
|
13
|
+
export function parseInstallComponent(component) {
|
|
14
|
+
if (component === 'openclaw')
|
|
15
|
+
return { kind: 'openclaw', raw: component };
|
|
16
|
+
if (component.startsWith('skill:')) {
|
|
17
|
+
const skillName = component.slice('skill:'.length);
|
|
18
|
+
return { kind: 'skill', raw: component, skillName: assertSafeInstallName('skill', skillName) };
|
|
19
|
+
}
|
|
20
|
+
if (CHANNEL_NAMES.has(component)) {
|
|
21
|
+
const channelName = CHANNEL_ALIASES[component] ?? component;
|
|
22
|
+
return { kind: 'channel', raw: component, channelName: assertSafeInstallName('channel', channelName) };
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`Unsupported install component: ${component}`);
|
|
25
|
+
}
|
|
26
|
+
export function assertSafeInstallName(kind, name) {
|
|
27
|
+
const message = `Invalid ${kind} name: ${name}`;
|
|
28
|
+
const maxLength = kind === 'skill' ? 128 : 64;
|
|
29
|
+
if (!name || name.length > maxLength || name === '.' || name === '..') {
|
|
30
|
+
throw new Error(message);
|
|
31
|
+
}
|
|
32
|
+
if (!NAME_TOKEN_PATTERN.test(name) || name.includes('..')) {
|
|
33
|
+
throw new Error(message);
|
|
34
|
+
}
|
|
35
|
+
return name;
|
|
36
|
+
}
|
|
37
|
+
export function assertSafePackageVersion(version) {
|
|
38
|
+
if (!version
|
|
39
|
+
|| version.length > 128
|
|
40
|
+
|| !VERSION_PATTERN.test(version)
|
|
41
|
+
|| version.includes('..')
|
|
42
|
+
|| version.includes('/')
|
|
43
|
+
|| version.includes('\\')
|
|
44
|
+
|| /\s/.test(version)
|
|
45
|
+
|| CONTROL_PATTERN.test(version)) {
|
|
46
|
+
throw new Error(`Invalid package version: ${version}`);
|
|
47
|
+
}
|
|
48
|
+
return version;
|
|
49
|
+
}
|
|
50
|
+
export function parsePackageSource(raw) {
|
|
51
|
+
if (hasUnsafePackageSourceChars(raw)) {
|
|
52
|
+
throw new Error(`Invalid packageUrl: ${raw}`);
|
|
53
|
+
}
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = new URL(raw);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
parsed = undefined;
|
|
60
|
+
}
|
|
61
|
+
if (parsed?.protocol === 'http:' || parsed?.protocol === 'https:') {
|
|
62
|
+
return { kind: 'remote', url: raw };
|
|
63
|
+
}
|
|
64
|
+
if (parsed && parsed.protocol !== 'file:') {
|
|
65
|
+
throw new Error(`Invalid packageUrl: ${raw}`);
|
|
66
|
+
}
|
|
67
|
+
if (!isAbsolute(raw)) {
|
|
68
|
+
throw new Error(`Invalid packageUrl: local package must be an absolute path: ${raw}`);
|
|
69
|
+
}
|
|
70
|
+
if (raw.split(/[\\/]+/).includes('..')) {
|
|
71
|
+
throw new Error(`Invalid packageUrl: ${raw}`);
|
|
72
|
+
}
|
|
73
|
+
return { kind: 'local', path: raw };
|
|
74
|
+
}
|
|
75
|
+
export function resolveInside(baseDir, ...segments) {
|
|
76
|
+
const base = resolve(baseDir);
|
|
77
|
+
const target = resolve(base, ...segments);
|
|
78
|
+
if (target !== base && !target.startsWith(`${base}${sep}`)) {
|
|
79
|
+
throw new Error(`Resolved path outside base directory: ${target}`);
|
|
80
|
+
}
|
|
81
|
+
return target;
|
|
82
|
+
}
|
|
83
|
+
export function channelExtensionDir(channelName) {
|
|
84
|
+
return resolveInside(OPENCLAW_HOME, 'extensions', assertSafeInstallName('channel', channelName));
|
|
85
|
+
}
|
|
86
|
+
export function skillInstallDir(skillName) {
|
|
87
|
+
return resolveInside(OPENCLAW_WORKSPACE, 'skills', assertSafeInstallName('skill', skillName));
|
|
88
|
+
}
|
|
89
|
+
export function tmpInstallPath(fileName) {
|
|
90
|
+
return resolveInside(OKCLAW_TMP_DIR, assertSafeInstallName('skill', fileName));
|
|
91
|
+
}
|
|
92
|
+
function hasUnsafePackageSourceChars(raw) {
|
|
93
|
+
return SHELL_METACHAR_PATTERN.test(raw)
|
|
94
|
+
|| CONTROL_PATTERN.test(raw)
|
|
95
|
+
|| /\s/.test(raw);
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=install-safety.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-safety.js","sourceRoot":"","sources":["../../src/utils/install-safety.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 人类可读 log。
|
|
3
|
+
*
|
|
4
|
+
* ndjson 模式下:所有业务级 log(info / warn / success / error)静默。
|
|
5
|
+
* - info/warn/success 本就无关紧要,静默避免污染 stdout 结构化流
|
|
6
|
+
* - error 也静默(bug#5 修):业务错误必须通过 emitter.end(false, code, { error: msg }) 承载,
|
|
7
|
+
* 与 design 契约对齐(stderr 仅给真正的崩溃栈,由 registerExitHandlers 的 uncaughtException 打印)
|
|
8
|
+
*
|
|
9
|
+
* 调用方注意:ndjson 模式下调 error(msg) 不会让用户看见消息,务必把 msg 塞进 emitter.end 的 fields。
|
|
10
|
+
*/
|
|
1
11
|
export declare function info(msg: string): void;
|
|
2
12
|
export declare function warn(msg: string): void;
|
|
3
13
|
export declare function error(msg: string): void;
|
package/dist/utils/logger.js
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { isNdjson } from '../output/mode.js';
|
|
2
|
+
/**
|
|
3
|
+
* 人类可读 log。
|
|
4
|
+
*
|
|
5
|
+
* ndjson 模式下:所有业务级 log(info / warn / success / error)静默。
|
|
6
|
+
* - info/warn/success 本就无关紧要,静默避免污染 stdout 结构化流
|
|
7
|
+
* - error 也静默(bug#5 修):业务错误必须通过 emitter.end(false, code, { error: msg }) 承载,
|
|
8
|
+
* 与 design 契约对齐(stderr 仅给真正的崩溃栈,由 registerExitHandlers 的 uncaughtException 打印)
|
|
9
|
+
*
|
|
10
|
+
* 调用方注意:ndjson 模式下调 error(msg) 不会让用户看见消息,务必把 msg 塞进 emitter.end 的 fields。
|
|
11
|
+
*/
|
|
12
|
+
export function info(msg) {
|
|
13
|
+
if (isNdjson())
|
|
14
|
+
return;
|
|
15
|
+
console.log(`[okclaw] ${msg}`);
|
|
16
|
+
}
|
|
17
|
+
export function warn(msg) {
|
|
18
|
+
if (isNdjson())
|
|
19
|
+
return;
|
|
20
|
+
console.log(`[okclaw] WARN: ${msg}`);
|
|
21
|
+
}
|
|
22
|
+
export function error(msg) {
|
|
23
|
+
if (isNdjson())
|
|
24
|
+
return;
|
|
25
|
+
console.error(`[okclaw] ERROR: ${msg}`);
|
|
26
|
+
}
|
|
27
|
+
export function success(msg) {
|
|
28
|
+
if (isNdjson())
|
|
29
|
+
return;
|
|
30
|
+
console.log(`[okclaw] ✓ ${msg}`);
|
|
31
|
+
}
|
|
5
32
|
//# sourceMappingURL=logger.js.map
|
package/dist/utils/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,IAAI,CAAC,GAAW,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC,CAAC
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C;;;;;;;;;GASG;AAEH,MAAM,UAAU,IAAI,CAAC,GAAW;IAC9B,IAAI,QAAQ,EAAE;QAAE,OAAO;IACvB,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,GAAW;IAC9B,IAAI,QAAQ,EAAE;QAAE,OAAO;IACvB,OAAO,CAAC,GAAG,CAAC,kBAAkB,GAAG,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAW;IAC/B,IAAI,QAAQ,EAAE;QAAE,OAAO;IACvB,OAAO,CAAC,KAAK,CAAC,mBAAmB,GAAG,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,IAAI,QAAQ,EAAE;QAAE,OAAO;IACvB,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,EAAE,CAAC,CAAC;AACnC,CAAC"}
|
package/dist/utils/mirror.js
CHANGED
|
@@ -4,6 +4,20 @@ import { NPM_REGISTRY, PIP_INDEX_URL, PIP_TRUSTED_HOST } from './constants.js';
|
|
|
4
4
|
export async function configureMirrors() {
|
|
5
5
|
info('Configuring China mirrors...');
|
|
6
6
|
await shell(`npm config set registry ${NPM_REGISTRY}`).catch(() => { });
|
|
7
|
+
// 第三方插件(如 @soimy/dingtalk)的 devDependencies 偶发会因为上游版本漂移
|
|
8
|
+
// 出现 peer dep 冲突(实例:oxlint@1.63.0 要求 oxlint-tsgolint>=0.22.1,但插件
|
|
9
|
+
// 钉死 ^0.18.0),npm 10 默认严格 peer 解析会报 ERESOLVE 直接挂掉
|
|
10
|
+
// openclaw plugins install 内部的 npm install。openclaw 没暴露 install flag 给我们传,
|
|
11
|
+
// 设成全局 npm config 是唯一能影响它的方式。lint/format devDep 的版本拉新对运行时
|
|
12
|
+
// 无影响,让 npm 用旧 peer 解析逻辑通过即可。
|
|
13
|
+
await shell(`npm config set legacy-peer-deps true`).catch(() => { });
|
|
14
|
+
// 部分 npm 包(如 openclaw 依赖的 libsignal-node)用 git+ssh://git@github.com/...
|
|
15
|
+
// 引用 GitHub 仓库。云主机 root 账户默认没有 GitHub SSH key 会拉取失败。
|
|
16
|
+
// 这里把 ssh://git@github.com/ 和 git@github.com: 都重写成 https://github.com/(匿名可读)。
|
|
17
|
+
// 注意: 同一个 URL name 下要写多个 insteadOf 必须用 --add,否则第二次 set 会覆盖第一次。
|
|
18
|
+
await shell(`git config --global --unset-all url."https://github.com/".insteadOf 2>/dev/null || true`).catch(() => { });
|
|
19
|
+
await shell(`git config --global --add url."https://github.com/".insteadOf ssh://git@github.com/`).catch(() => { });
|
|
20
|
+
await shell(`git config --global --add url."https://github.com/".insteadOf git@github.com:`).catch(() => { });
|
|
7
21
|
// Use /root/.pip — not ~/
|
|
8
22
|
await shell(`mkdir -p /root/.pip && cat > /root/.pip/pip.conf << 'EOF'
|
|
9
23
|
[global]
|
package/dist/utils/mirror.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mirror.js","sourceRoot":"","sources":["../../src/utils/mirror.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAE/E,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IAErC,MAAM,KAAK,CAAC,2BAA2B,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAEvE,0BAA0B;IAC1B,MAAM,KAAK,CAAC;;cAEA,aAAa;iBACV,gBAAgB;IAC7B,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAEpB,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAC9B,CAAC"}
|
|
1
|
+
{"version":3,"file":"mirror.js","sourceRoot":"","sources":["../../src/utils/mirror.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAE/E,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IAErC,MAAM,KAAK,CAAC,2BAA2B,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAEvE,wDAAwD;IACxD,iEAAiE;IACjE,kDAAkD;IAClD,2EAA2E;IAC3E,0DAA0D;IAC1D,8BAA8B;IAC9B,MAAM,KAAK,CAAC,sCAAsC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAEpE,wEAAwE;IACxE,qDAAqD;IACrD,8EAA8E;IAC9E,+DAA+D;IAC/D,MAAM,KAAK,CACT,yFAAyF,CAC1F,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAClB,MAAM,KAAK,CACT,qFAAqF,CACtF,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAClB,MAAM,KAAK,CACT,+EAA+E,CAChF,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAElB,0BAA0B;IAC1B,MAAM,KAAK,CAAC;;cAEA,aAAa;iBACV,gBAAgB;IAC7B,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAEpB,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare class OpenclawConfigCliError extends Error {
|
|
2
|
+
readonly cmd: string;
|
|
3
|
+
readonly exitCode: number;
|
|
4
|
+
readonly stdout: string;
|
|
5
|
+
readonly stderr: string;
|
|
6
|
+
constructor(cmd: string, exitCode: number, stdout: string, stderr: string);
|
|
7
|
+
}
|
|
8
|
+
export interface CliRunner {
|
|
9
|
+
run(args: string[]): {
|
|
10
|
+
exitCode: number;
|
|
11
|
+
stdout: string;
|
|
12
|
+
stderr: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export declare const defaultCliRunner: CliRunner;
|
|
16
|
+
export declare function configGet(path: string, runner?: CliRunner): unknown | undefined;
|
|
17
|
+
export declare function configSet(path: string, valueJson: string, runner?: CliRunner): void;
|
|
18
|
+
export declare function configUnset(path: string, runner?: CliRunner): void;
|
|
19
|
+
export declare function configValidate(runner?: CliRunner): boolean;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
export class OpenclawConfigCliError extends Error {
|
|
3
|
+
cmd;
|
|
4
|
+
exitCode;
|
|
5
|
+
stdout;
|
|
6
|
+
stderr;
|
|
7
|
+
constructor(cmd, exitCode, stdout, stderr) {
|
|
8
|
+
super(`openclaw ${cmd} failed (exit ${exitCode}): ${stderr || stdout}`);
|
|
9
|
+
this.cmd = cmd;
|
|
10
|
+
this.exitCode = exitCode;
|
|
11
|
+
this.stdout = stdout;
|
|
12
|
+
this.stderr = stderr;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function stringifyOutput(value) {
|
|
16
|
+
if (typeof value === 'string')
|
|
17
|
+
return value;
|
|
18
|
+
if (Buffer.isBuffer(value))
|
|
19
|
+
return value.toString('utf8');
|
|
20
|
+
return '';
|
|
21
|
+
}
|
|
22
|
+
export const defaultCliRunner = {
|
|
23
|
+
run(args) {
|
|
24
|
+
try {
|
|
25
|
+
const stdout = execFileSync('openclaw', args, {
|
|
26
|
+
encoding: 'utf8',
|
|
27
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
28
|
+
});
|
|
29
|
+
return { exitCode: 0, stdout, stderr: '' };
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const error = err;
|
|
33
|
+
return {
|
|
34
|
+
exitCode: typeof error.status === 'number' ? error.status : 127,
|
|
35
|
+
stdout: stringifyOutput(error.stdout),
|
|
36
|
+
stderr: stringifyOutput(error.stderr || error.message),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
function runOrThrow(args, runner = defaultCliRunner) {
|
|
42
|
+
const result = runner.run(args);
|
|
43
|
+
if (result.exitCode !== 0) {
|
|
44
|
+
throw new OpenclawConfigCliError(args.join(' '), result.exitCode, result.stdout, result.stderr);
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
function isNotFoundOutput(output) {
|
|
49
|
+
return /Config\s+path\s+not\s+found/i.test(output);
|
|
50
|
+
}
|
|
51
|
+
export function configGet(path, runner = defaultCliRunner) {
|
|
52
|
+
const args = ['config', 'get', path];
|
|
53
|
+
const result = runner.run(args);
|
|
54
|
+
if (result.exitCode !== 0) {
|
|
55
|
+
if (isNotFoundOutput(`${result.stderr}\n${result.stdout}`))
|
|
56
|
+
return undefined;
|
|
57
|
+
throw new OpenclawConfigCliError(args.join(' '), result.exitCode, result.stdout, result.stderr);
|
|
58
|
+
}
|
|
59
|
+
const stdout = result.stdout.trim();
|
|
60
|
+
if (!stdout)
|
|
61
|
+
return undefined;
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(stdout);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// openclaw config get prints scalar string values as raw text without JSON
|
|
67
|
+
// quoting (e.g. `openviking` rather than `"openviking"`). number / bool / null
|
|
68
|
+
// are valid JSON literals so they parse above; only unquoted strings reach here.
|
|
69
|
+
return stdout;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export function configSet(path, valueJson, runner = defaultCliRunner) {
|
|
73
|
+
runOrThrow(['config', 'set', path, valueJson, '--strict-json'], runner);
|
|
74
|
+
}
|
|
75
|
+
export function configUnset(path, runner = defaultCliRunner) {
|
|
76
|
+
runOrThrow(['config', 'unset', path], runner);
|
|
77
|
+
}
|
|
78
|
+
export function configValidate(runner = defaultCliRunner) {
|
|
79
|
+
return runner.run(['config', 'validate']).exitCode === 0;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=openclaw-config-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclaw-config-cli.js","sourceRoot":"","sources":["../../src/utils/openclaw-config-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 确保 gateway 的 systemd 服务已安装且能常驻。
|
|
3
|
+
* - `loginctl enable-linger`:systemd user 服务默认随最后一个登录会话退出而停,
|
|
4
|
+
* 开 linger 后才能在部署 SSH 断开后 / 主机重启后继续运行。
|
|
5
|
+
* - `openclaw gateway install --force`:幂等(重装覆盖),顺带在 openclaw 升级后
|
|
6
|
+
* 刷新 unit 的 ExecStart。
|
|
7
|
+
*/
|
|
8
|
+
export declare function installGatewayService(): Promise<void>;
|
|
9
|
+
/** 启动 gateway(先确保 systemd 服务已装)。 */
|
|
10
|
+
export declare function startGateway(): Promise<void>;
|
|
11
|
+
/** 重启 gateway(先确保 systemd 服务已装)。 */
|
|
12
|
+
export declare function restartGateway(): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* 停掉 gateway:先停掉所有已知的 systemd unit(新 + 旧 unit 名),再按监听
|
|
15
|
+
* 端口兜底杀掉非 systemd 托管的残留进程。顺序见 {@link STOP_GATEWAY_UNITS}。
|
|
16
|
+
*/
|
|
17
|
+
export declare function stopGateway(): Promise<void>;
|
|
18
|
+
/** gateway 运行状态。健康的唯一标准是端口已监听。 */
|
|
19
|
+
export type GatewayState = 'listening' | 'starting' | 'down';
|
|
20
|
+
/**
|
|
21
|
+
* 探测 gateway 状态。**健康的唯一标准是 18789 端口已监听** —— 进程 active
|
|
22
|
+
* 不等于对外可服务。
|
|
23
|
+
*
|
|
24
|
+
* - `listening`:端口已监听,健康。
|
|
25
|
+
* - `starting` :systemd 服务 active,但端口在 bounded wait 内始终没监听 ——
|
|
26
|
+
* 配置异常 / 启动卡住 / 仍在初始化,**不算健康**。
|
|
27
|
+
* - `down` :systemd 服务非 active 且端口未监听。
|
|
28
|
+
*
|
|
29
|
+
* `openclaw gateway restart` 返回后服务立刻是 active,但 gateway 进程还要几秒
|
|
30
|
+
* 才 bind 端口。所以端口没起来、服务却 active 时给一段 bounded wait 覆盖这个
|
|
31
|
+
* 启动窗口;服务都不 active 则直接 `down`,不等。
|
|
32
|
+
*/
|
|
33
|
+
export declare function probeGatewayState(): Promise<GatewayState>;
|
|
34
|
+
/**
|
|
35
|
+
* 清掉 systemd user 目录下的 gateway unit 残留。`openclaw gateway uninstall`
|
|
36
|
+
* 只删主 `.service` 文件,残留的 `.service.bak` 备份和 `.service.d/` drop-in
|
|
37
|
+
* 会在下次 install 时被 systemd 自动 merge 进来(注入过时的 env 等),必须一并清。
|
|
38
|
+
*/
|
|
39
|
+
export declare function cleanupGatewayServiceFiles(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Best-effort stop of the openclaw gateway so cli operations have exclusive
|
|
42
|
+
* write access to openclaw.json + extensions/.
|
|
43
|
+
*
|
|
44
|
+
* Why this is needed: the gateway has a config file watcher that writes its
|
|
45
|
+
* own metadata back into openclaw.json on every config change. Concurrent
|
|
46
|
+
* writes during cli's cleanup / `openclaw plugins install` / `openclaw config
|
|
47
|
+
* set` race with that watcher and produce:
|
|
48
|
+
* - ENOTEMPTY: rmdir '.../node_modules/.cache/jiti' (jiti cache write race)
|
|
49
|
+
* - ConfigMutationConflictError: config changed since last load (hash check
|
|
50
|
+
* in openclaw plugins install commit / openclaw config set atomic write)
|
|
51
|
+
*
|
|
52
|
+
* Bootstrap orchestrator (commands/install.ts) is responsible for restarting
|
|
53
|
+
* the gateway after the full component install pipeline finishes.
|
|
54
|
+
*
|
|
55
|
+
* @param context A short label for logs, e.g. "dingtalk install" or
|
|
56
|
+
* "openclaw deployment defaults".
|
|
57
|
+
*/
|
|
58
|
+
export declare function stopDaemonForExclusiveAccess(context: string): Promise<void>;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { shell, shellCapture } from './shell.js';
|
|
2
|
+
import { info } from './logger.js';
|
|
3
|
+
import { OPENCLAW_GATEWAY_PORT } from './constants.js';
|
|
4
|
+
/**
|
|
5
|
+
* openclaw gateway 生命周期管理。
|
|
6
|
+
*
|
|
7
|
+
* 关键决策:gateway 交给 openclaw 自带的 systemd 服务管理,cli 不再自己用
|
|
8
|
+
* nohup + ps + pkill 管进程。
|
|
9
|
+
*
|
|
10
|
+
* 为什么:openclaw 2026.5 的 gateway 有自己的单实例 lock、restart-handoff、
|
|
11
|
+
* 进程改名(`openclaw` / `node`)机制。cli 旧实现按进程名 `openclaw-gateway`
|
|
12
|
+
* 识别 + nohup 拉起 + 手动 kill,实测都顶不住:
|
|
13
|
+
* - 进程名匹配 0 个 → restart 存活检测恒失败,报 E_RESTART_FAILED
|
|
14
|
+
* (即使 gateway 其实已经起来了)
|
|
15
|
+
* - 改用 `openclaw gateway run --force` + nohup,会和 openclaw 自身的
|
|
16
|
+
* lock / handoff 互相赛跑 → 双进程、端口悬空、健康检查 abnormal closure
|
|
17
|
+
* 而 `openclaw gateway install/start/stop/restart`(systemd user 服务)实测
|
|
18
|
+
* 连续重启始终单进程、端口干净、状态准确。所以一律走 openclaw 的 service 命令。
|
|
19
|
+
*/
|
|
20
|
+
// gateway / systemctl 命令的环境前缀。
|
|
21
|
+
//
|
|
22
|
+
// unset SUDO_*:cli 在非 root 登录的主机(如 AWS Ubuntu AMI 的 ubuntu 用户)
|
|
23
|
+
// 上由 os-api 用 `sudo -H` 提权到 root 跑。`sudo -H` 把 HOME 设成 /root,
|
|
24
|
+
// 于是 `openclaw gateway install` 把 unit 文件写进 /root/.config/systemd/user/;
|
|
25
|
+
// 但 openclaw 又据 SUDO_USER 判定"当前用户是 ubuntu",enable 时跑的是
|
|
26
|
+
// `systemctl --machine ubuntu@ --user ...` —— 去 ubuntu 的 systemd 里找 unit,
|
|
27
|
+
// 找不到 → "Unit file does not exist" → install 失败。unset 掉 SUDO_* 让
|
|
28
|
+
// openclaw 当成纯 root 调用,写 unit 与操作 systemd 都对着 root,一致。
|
|
29
|
+
// XDG_RUNTIME_DIR:systemctl --user 需要它;非交互 SSH exec 下通常已由
|
|
30
|
+
// pam_systemd 注入,这里再兜底一次,防个别主机 sshd PAM 配置不注入。
|
|
31
|
+
const SYSTEMD_ENV = 'unset SUDO_USER SUDO_UID SUDO_GID; ' +
|
|
32
|
+
'export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"; ';
|
|
33
|
+
// gateway 没被 systemd 接管时(老主机残留 / 异常)按监听端口兜底杀进程。
|
|
34
|
+
// 覆盖 `openclaw` / `node` 两种进程名;且绝不会误杀正在执行本 CLI 的进程。
|
|
35
|
+
// 用 awk 解析 `pid=NNN` 而不是 `grep -oP` —— grep 的 -P(PCRE,含 \K)是 GNU
|
|
36
|
+
// 扩展,非 GNU 环境(BSD grep 等)会整条命令报错、兜底路径静默退化成 no-op。
|
|
37
|
+
// awk -F'pid=' 把每个 `pid=` 后的字段数值化($i+0 取前导数字),POSIX 通用。
|
|
38
|
+
const KILL_GATEWAY_BY_PORT = `ss -ltnHp 'sport = :${OPENCLAW_GATEWAY_PORT}' 2>/dev/null` +
|
|
39
|
+
` | awk -F'pid=' '{for(i=2;i<=NF;i++){p=$i+0;if(p>0)print p}}'` +
|
|
40
|
+
` | sort -u | xargs -r kill 2>/dev/null || true`;
|
|
41
|
+
/**
|
|
42
|
+
* 确保 gateway 的 systemd 服务已安装且能常驻。
|
|
43
|
+
* - `loginctl enable-linger`:systemd user 服务默认随最后一个登录会话退出而停,
|
|
44
|
+
* 开 linger 后才能在部署 SSH 断开后 / 主机重启后继续运行。
|
|
45
|
+
* - `openclaw gateway install --force`:幂等(重装覆盖),顺带在 openclaw 升级后
|
|
46
|
+
* 刷新 unit 的 ExecStart。
|
|
47
|
+
*/
|
|
48
|
+
export async function installGatewayService() {
|
|
49
|
+
await shell(`loginctl enable-linger root 2>/dev/null || true`).catch(() => { });
|
|
50
|
+
await shell(`${SYSTEMD_ENV}openclaw gateway install --force --port ${OPENCLAW_GATEWAY_PORT}`);
|
|
51
|
+
}
|
|
52
|
+
/** 启动 gateway(先确保 systemd 服务已装)。 */
|
|
53
|
+
export async function startGateway() {
|
|
54
|
+
await installGatewayService();
|
|
55
|
+
await shell(`${SYSTEMD_ENV}openclaw gateway start`);
|
|
56
|
+
}
|
|
57
|
+
/** 重启 gateway(先确保 systemd 服务已装)。 */
|
|
58
|
+
export async function restartGateway() {
|
|
59
|
+
await installGatewayService();
|
|
60
|
+
await shell(`${SYSTEMD_ENV}openclaw gateway restart`);
|
|
61
|
+
}
|
|
62
|
+
// 停掉所有已知的 gateway systemd unit。新版 openclaw 的 unit 名是
|
|
63
|
+
// openclaw-gateway.service;老主机可能是 openclaw.service(早期 cli 用过的
|
|
64
|
+
// unit 名,--user / system 两种 scope 都覆盖)。
|
|
65
|
+
//
|
|
66
|
+
// **必须在按端口 kill 之前先停 unit**:先停 unit,systemd 视为"主动停止"、
|
|
67
|
+
// 不会触发 Restart=;反过来先 kill 进程,systemd 当成异常退出,会按
|
|
68
|
+
// Restart=always/on-failure 立刻把 gateway 拉回来 —— stopDaemonForExclusiveAccess
|
|
69
|
+
// 就拿不到真正的独占窗口,config watcher 写配置的竞争(ConfigMutationConflict)复现。
|
|
70
|
+
// 按端口 kill 只作为非 systemd 托管残留进程的兜底,不能替代停 supervisor。
|
|
71
|
+
const STOP_GATEWAY_UNITS = `${SYSTEMD_ENV}` +
|
|
72
|
+
`openclaw gateway stop 2>/dev/null || true; ` +
|
|
73
|
+
`systemctl --user stop openclaw-gateway.service 2>/dev/null || true; ` +
|
|
74
|
+
`systemctl --user stop openclaw.service 2>/dev/null || true; ` +
|
|
75
|
+
`systemctl stop openclaw.service 2>/dev/null || true`;
|
|
76
|
+
/**
|
|
77
|
+
* 停掉 gateway:先停掉所有已知的 systemd unit(新 + 旧 unit 名),再按监听
|
|
78
|
+
* 端口兜底杀掉非 systemd 托管的残留进程。顺序见 {@link STOP_GATEWAY_UNITS}。
|
|
79
|
+
*/
|
|
80
|
+
export async function stopGateway() {
|
|
81
|
+
await shell(STOP_GATEWAY_UNITS).catch(() => { });
|
|
82
|
+
await shell(KILL_GATEWAY_BY_PORT).catch(() => { });
|
|
83
|
+
}
|
|
84
|
+
// `active 但端口未监听` 的最长等待:覆盖 restart 后进程已 active、端口还没
|
|
85
|
+
// bind 的启动窗口。等满仍未监听就判 starting(配置异常 / 卡住),不再傻等。
|
|
86
|
+
const STATUS_PORT_WAIT_ATTEMPTS = 20;
|
|
87
|
+
async function isPortListening() {
|
|
88
|
+
const r = await shellCapture(`ss -ltnH 'sport = :${OPENCLAW_GATEWAY_PORT}' 2>/dev/null || true`);
|
|
89
|
+
return r.stdout.trim().length > 0;
|
|
90
|
+
}
|
|
91
|
+
async function isServiceActive() {
|
|
92
|
+
const r = await shellCapture(`${SYSTEMD_ENV}systemctl --user is-active openclaw-gateway.service 2>/dev/null || true`);
|
|
93
|
+
return r.stdout.trim() === 'active';
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 探测 gateway 状态。**健康的唯一标准是 18789 端口已监听** —— 进程 active
|
|
97
|
+
* 不等于对外可服务。
|
|
98
|
+
*
|
|
99
|
+
* - `listening`:端口已监听,健康。
|
|
100
|
+
* - `starting` :systemd 服务 active,但端口在 bounded wait 内始终没监听 ——
|
|
101
|
+
* 配置异常 / 启动卡住 / 仍在初始化,**不算健康**。
|
|
102
|
+
* - `down` :systemd 服务非 active 且端口未监听。
|
|
103
|
+
*
|
|
104
|
+
* `openclaw gateway restart` 返回后服务立刻是 active,但 gateway 进程还要几秒
|
|
105
|
+
* 才 bind 端口。所以端口没起来、服务却 active 时给一段 bounded wait 覆盖这个
|
|
106
|
+
* 启动窗口;服务都不 active 则直接 `down`,不等。
|
|
107
|
+
*/
|
|
108
|
+
export async function probeGatewayState() {
|
|
109
|
+
if (await isPortListening())
|
|
110
|
+
return 'listening';
|
|
111
|
+
if (!(await isServiceActive()))
|
|
112
|
+
return 'down';
|
|
113
|
+
for (let i = 0; i < STATUS_PORT_WAIT_ATTEMPTS; i++) {
|
|
114
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
115
|
+
if (await isPortListening())
|
|
116
|
+
return 'listening';
|
|
117
|
+
}
|
|
118
|
+
return 'starting';
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 清掉 systemd user 目录下的 gateway unit 残留。`openclaw gateway uninstall`
|
|
122
|
+
* 只删主 `.service` 文件,残留的 `.service.bak` 备份和 `.service.d/` drop-in
|
|
123
|
+
* 会在下次 install 时被 systemd 自动 merge 进来(注入过时的 env 等),必须一并清。
|
|
124
|
+
*/
|
|
125
|
+
export async function cleanupGatewayServiceFiles() {
|
|
126
|
+
await shell(`${SYSTEMD_ENV}rm -rf /root/.config/systemd/user/openclaw-gateway.service.d` +
|
|
127
|
+
` /root/.config/systemd/user/openclaw-gateway.service.bak;` +
|
|
128
|
+
` systemctl --user daemon-reload 2>/dev/null || true`).catch(() => { });
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Best-effort stop of the openclaw gateway so cli operations have exclusive
|
|
132
|
+
* write access to openclaw.json + extensions/.
|
|
133
|
+
*
|
|
134
|
+
* Why this is needed: the gateway has a config file watcher that writes its
|
|
135
|
+
* own metadata back into openclaw.json on every config change. Concurrent
|
|
136
|
+
* writes during cli's cleanup / `openclaw plugins install` / `openclaw config
|
|
137
|
+
* set` race with that watcher and produce:
|
|
138
|
+
* - ENOTEMPTY: rmdir '.../node_modules/.cache/jiti' (jiti cache write race)
|
|
139
|
+
* - ConfigMutationConflictError: config changed since last load (hash check
|
|
140
|
+
* in openclaw plugins install commit / openclaw config set atomic write)
|
|
141
|
+
*
|
|
142
|
+
* Bootstrap orchestrator (commands/install.ts) is responsible for restarting
|
|
143
|
+
* the gateway after the full component install pipeline finishes.
|
|
144
|
+
*
|
|
145
|
+
* @param context A short label for logs, e.g. "dingtalk install" or
|
|
146
|
+
* "openclaw deployment defaults".
|
|
147
|
+
*/
|
|
148
|
+
export async function stopDaemonForExclusiveAccess(context) {
|
|
149
|
+
info(`Stopping openclaw gateway for exclusive access during ${context}...`);
|
|
150
|
+
await stopGateway();
|
|
151
|
+
// `systemctl stop` 是显式停,Restart=always 不会把它拉回来;留 1.5s 兜底观察。
|
|
152
|
+
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=openclaw-daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclaw-daemon.js","sourceRoot":"","sources":["../../src/utils/openclaw-daemon.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/shell.d.ts
CHANGED
|
@@ -3,22 +3,43 @@ export interface ShellResult {
|
|
|
3
3
|
stdout: string;
|
|
4
4
|
stderr: string;
|
|
5
5
|
}
|
|
6
|
+
export interface CommandOptions {
|
|
7
|
+
cwd?: string;
|
|
8
|
+
env?: NodeJS.ProcessEnv;
|
|
9
|
+
}
|
|
6
10
|
/**
|
|
7
11
|
* Ensure running as root. If not, re-exec with sudo and exit.
|
|
8
12
|
*/
|
|
9
13
|
export declare function ensureRoot(): void;
|
|
10
14
|
/**
|
|
11
|
-
* Run a shell command
|
|
12
|
-
*
|
|
15
|
+
* Run a shell command and wait for exit.
|
|
16
|
+
*
|
|
17
|
+
* text 模式:stdio:inherit,子进程输出直通父进程 TTY(本地交互体验不变)。
|
|
18
|
+
*
|
|
19
|
+
* ndjson 模式:捕获 stdout/stderr 并按行合成 phase="log" 事件(stdout→level=info,
|
|
20
|
+
* stderr→level=warn),通过 {@link getActiveEmitter} 拿到当前 op 的 emitter 发出。
|
|
21
|
+
* 这样像 `npm install` 这种几分钟才回一行 ndjson 的长操作,其实时进度也能通过
|
|
22
|
+
* 事件流推给 os-api / 前端,用户不会看到 UI 卡死 10 分钟。
|
|
23
|
+
*
|
|
24
|
+
* 透传是"尽力而为":emitter 缺失(理论上只有本地脚本直接 import shell() 且没走
|
|
25
|
+
* 正常 command 入口才会发生)时静默吞,不影响命令本身执行。
|
|
26
|
+
*
|
|
13
27
|
* Throws on non-zero exit.
|
|
14
28
|
*/
|
|
15
29
|
export declare function shell(cmd: string): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Run a command with argv instead of interpolating external values into a shell
|
|
32
|
+
* string. The fixed bash wrapper only initializes nvm, then `exec "$@"`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function runCommand(cmd: string, args?: string[], options?: CommandOptions): Promise<void>;
|
|
16
35
|
/**
|
|
17
36
|
* Run a shell command with retries.
|
|
18
37
|
*/
|
|
19
38
|
export declare function shellRetry(cmd: string, retries?: number, delayMs?: number): Promise<void>;
|
|
39
|
+
export declare function runCommandRetry(cmd: string, args?: string[], retries?: number, delayMs?: number, options?: CommandOptions): Promise<void>;
|
|
20
40
|
/**
|
|
21
41
|
* Run a shell command and capture stdout/stderr.
|
|
22
42
|
*/
|
|
23
43
|
export declare function shellCapture(cmd: string): Promise<ShellResult>;
|
|
44
|
+
export declare function runCommandCapture(cmd: string, args?: string[], options?: CommandOptions): Promise<ShellResult>;
|
|
24
45
|
export declare function commandExists(cmd: string): Promise<boolean>;
|