yymaxapi 1.0.27 → 1.0.28
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/bin/yymaxapi.js +72 -27
- package/package.json +1 -1
package/bin/yymaxapi.js
CHANGED
|
@@ -721,46 +721,87 @@ let _selectedDockerContainer = null;
|
|
|
721
721
|
|
|
722
722
|
function isDockerAvailable() {
|
|
723
723
|
if (_dockerAvailCache !== null) return _dockerAvailCache;
|
|
724
|
-
|
|
725
|
-
|
|
724
|
+
// 优先用 docker ps(权限要求更低),fallback 到 docker info
|
|
725
|
+
const r = safeExec('docker ps --no-trunc -q', { timeout: 8000 });
|
|
726
|
+
if (r.ok) { _dockerAvailCache = true; return true; }
|
|
727
|
+
const r2 = safeExec('docker info', { timeout: 8000 });
|
|
728
|
+
if (r2.ok) { _dockerAvailCache = true; return true; }
|
|
729
|
+
// 可能需要 sudo
|
|
730
|
+
const r3 = safeExec('sudo -n docker ps -q', { timeout: 8000 });
|
|
731
|
+
_dockerAvailCache = r3.ok;
|
|
726
732
|
return _dockerAvailCache;
|
|
727
733
|
}
|
|
728
734
|
|
|
735
|
+
// 封装 docker 命令(自动处理 sudo)
|
|
736
|
+
let _dockerNeedsSudo = null;
|
|
737
|
+
function dockerCmd(cmd) {
|
|
738
|
+
if (_dockerNeedsSudo === null) {
|
|
739
|
+
const test = safeExec('docker ps -q', { timeout: 5000 });
|
|
740
|
+
_dockerNeedsSudo = !test.ok;
|
|
741
|
+
}
|
|
742
|
+
return _dockerNeedsSudo ? `sudo -n docker ${cmd}` : `docker ${cmd}`;
|
|
743
|
+
}
|
|
744
|
+
|
|
729
745
|
// 查找包含 openclaw/clawdbot/moltbot 的运行中容器
|
|
730
746
|
function findOpenclawDockerContainers() {
|
|
731
747
|
if (_dockerContainerCache !== null) return _dockerContainerCache;
|
|
732
748
|
if (!isDockerAvailable()) { _dockerContainerCache = []; return []; }
|
|
733
749
|
|
|
734
|
-
const ps = safeExec('
|
|
750
|
+
const ps = safeExec(dockerCmd('ps --format "{{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}"'), { timeout: 10000 });
|
|
735
751
|
if (!ps.ok || !ps.output) { _dockerContainerCache = []; return []; }
|
|
736
752
|
|
|
737
753
|
const containers = [];
|
|
738
754
|
const lines = ps.output.split('\n').filter(Boolean);
|
|
739
755
|
|
|
740
756
|
for (const line of lines) {
|
|
741
|
-
|
|
742
|
-
const
|
|
757
|
+
// 兼容 tab 分隔和空格分隔
|
|
758
|
+
const parts = line.includes('\t') ? line.split('\t') : line.split(/\s{2,}/);
|
|
759
|
+
const [id, name, image, ...statusParts] = parts;
|
|
760
|
+
const status = statusParts.join(' ');
|
|
743
761
|
if (!id) continue;
|
|
744
762
|
|
|
745
|
-
// 检查容器内是否有 openclaw/clawdbot/moltbot
|
|
763
|
+
// 检查容器内是否有 openclaw/clawdbot/moltbot(用 command -v 代替 which,兼容精简镜像)
|
|
746
764
|
for (const cli of ['openclaw', 'clawdbot', 'moltbot']) {
|
|
747
|
-
const check = safeExec(`
|
|
748
|
-
if (check.ok && check.output) {
|
|
765
|
+
const check = safeExec(dockerCmd(`exec ${id} sh -c "command -v ${cli} 2>/dev/null || which ${cli} 2>/dev/null"`), { timeout: 8000 });
|
|
766
|
+
if (check.ok && check.output && check.output.trim()) {
|
|
749
767
|
containers.push({ id, name, image, status, cli, cliPath: check.output.trim() });
|
|
750
|
-
break;
|
|
768
|
+
break;
|
|
751
769
|
}
|
|
752
770
|
}
|
|
753
771
|
|
|
754
|
-
//
|
|
772
|
+
// 也检查常见路径(不依赖 which/command -v)
|
|
755
773
|
if (!containers.find(c => c.id === id)) {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
774
|
+
const knownPaths = [
|
|
775
|
+
'/opt/moltbot/moltbot.mjs',
|
|
776
|
+
'/opt/moltbot/bin/moltbot.mjs',
|
|
777
|
+
'/opt/moltbot/bin/moltbot.js',
|
|
778
|
+
'/opt/moltbot/src/moltbot.mjs',
|
|
779
|
+
'/usr/local/bin/openclaw',
|
|
780
|
+
'/usr/local/bin/clawdbot',
|
|
781
|
+
'/usr/local/bin/moltbot',
|
|
782
|
+
];
|
|
783
|
+
for (const p of knownPaths) {
|
|
784
|
+
const check = safeExec(dockerCmd(`exec ${id} sh -c "test -f '${p}' && echo '${p}'"`), { timeout: 5000 });
|
|
785
|
+
if (check.ok && check.output && check.output.trim()) {
|
|
786
|
+
const isMjs = p.endsWith('.mjs') || p.endsWith('.js');
|
|
787
|
+
const cliName = isMjs ? 'node' : path.basename(p);
|
|
788
|
+
containers.push({ id, name, image, status, cli: cliName, cliPath: p });
|
|
760
789
|
break;
|
|
761
790
|
}
|
|
762
791
|
}
|
|
763
792
|
}
|
|
793
|
+
|
|
794
|
+
// 最后检查容器镜像名/容器名是否包含关键字(即使内部 CLI 路径未知)
|
|
795
|
+
if (!containers.find(c => c.id === id)) {
|
|
796
|
+
const combined = `${name} ${image}`.toLowerCase();
|
|
797
|
+
if (/openclaw|clawdbot|moltbot/.test(combined)) {
|
|
798
|
+
// 尝试在容器内找任何 node 进程带 gateway 关键字
|
|
799
|
+
const findProc = safeExec(dockerCmd(`exec ${id} sh -c "ps aux 2>/dev/null | grep -E 'openclaw|clawdbot|moltbot' | grep -v grep | head -1"`), { timeout: 5000 });
|
|
800
|
+
if (findProc.ok && findProc.output && findProc.output.trim()) {
|
|
801
|
+
containers.push({ id, name, image, status, cli: 'unknown', cliPath: '' });
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
764
805
|
}
|
|
765
806
|
|
|
766
807
|
_dockerContainerCache = containers;
|
|
@@ -800,13 +841,17 @@ async function selectDockerContainer() {
|
|
|
800
841
|
// 在 Docker 容器内执行命令
|
|
801
842
|
function execInDocker(containerId, cmd, options = {}) {
|
|
802
843
|
const escaped = cmd.replace(/'/g, "'\\''");
|
|
803
|
-
return safeExec(`
|
|
844
|
+
return safeExec(dockerCmd(`exec ${containerId} bash -lc '${escaped}'`), { timeout: 30000, ...options });
|
|
804
845
|
}
|
|
805
846
|
|
|
806
847
|
// 在 Docker 容器内异步执行命令(后台启动 gateway 等)
|
|
807
848
|
function spawnDetachedInDocker(containerId, cmd, env = {}) {
|
|
808
849
|
try {
|
|
809
|
-
const
|
|
850
|
+
const prefix = _dockerNeedsSudo ? 'sudo' : 'docker';
|
|
851
|
+
const args = _dockerNeedsSudo
|
|
852
|
+
? ['-n', 'docker', 'exec', '-d', containerId, 'bash', '-lc', cmd]
|
|
853
|
+
: ['exec', '-d', containerId, 'bash', '-lc', cmd];
|
|
854
|
+
const child = spawn(prefix, args, {
|
|
810
855
|
detached: true,
|
|
811
856
|
stdio: 'ignore',
|
|
812
857
|
env: { ...process.env, ...env }
|
|
@@ -835,8 +880,8 @@ function syncConfigToDocker(localConfigPath, containerId) {
|
|
|
835
880
|
// 先确定容器内哪个配置目录存在
|
|
836
881
|
for (const target of targets) {
|
|
837
882
|
const dir = path.posix.dirname(target);
|
|
838
|
-
safeExec(`
|
|
839
|
-
const cp = safeExec(`
|
|
883
|
+
safeExec(dockerCmd(`exec ${containerId} mkdir -p "${dir}"`), { timeout: 5000 });
|
|
884
|
+
const cp = safeExec(dockerCmd(`cp "${localConfigPath}" ${containerId}:${target}`), { timeout: 10000 });
|
|
840
885
|
if (cp.ok) {
|
|
841
886
|
console.log(chalk.gray(` 已同步配置到 Docker 容器: ${target}`));
|
|
842
887
|
return;
|
|
@@ -966,7 +1011,7 @@ function execAsyncInGatewayEnv(cmd, options = {}) {
|
|
|
966
1011
|
}
|
|
967
1012
|
if (detectGatewayEnv() === 'docker' && _selectedDockerContainer) {
|
|
968
1013
|
const escaped = cmd.replace(/'/g, "'\\''");
|
|
969
|
-
return { cmd: `
|
|
1014
|
+
return { cmd: dockerCmd(`exec ${_selectedDockerContainer.id} bash -lc '${escaped}'`), options };
|
|
970
1015
|
}
|
|
971
1016
|
return { cmd, options };
|
|
972
1017
|
}
|
|
@@ -1013,7 +1058,7 @@ function cleanupAgentProcesses() {
|
|
|
1013
1058
|
if (detectGatewayEnv() === 'docker' && _selectedDockerContainer) {
|
|
1014
1059
|
try {
|
|
1015
1060
|
for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
|
|
1016
|
-
safeExec(`
|
|
1061
|
+
safeExec(dockerCmd(`exec ${_selectedDockerContainer.id} bash -c "pkill -f '${name}.*agent' 2>/dev/null || true"`), { timeout: 10000 });
|
|
1017
1062
|
}
|
|
1018
1063
|
} catch { /* ignore */ }
|
|
1019
1064
|
}
|
|
@@ -3029,7 +3074,7 @@ async function restartGateway() {
|
|
|
3029
3074
|
return;
|
|
3030
3075
|
}
|
|
3031
3076
|
const cmd = dockerCmds[tried++];
|
|
3032
|
-
const fullCmd = `
|
|
3077
|
+
const fullCmd = dockerCmd(`exec ${container.id} bash -lc "${cmd}"`);
|
|
3033
3078
|
exec(fullCmd, { timeout: 30000 }, (error) => {
|
|
3034
3079
|
if (error) {
|
|
3035
3080
|
tryNext();
|
|
@@ -3095,9 +3140,9 @@ async function forceRestartGateway(resolved, nodeInfo, useNode, env, gatewayPort
|
|
|
3095
3140
|
// 1. 杀掉容器内旧 Gateway 进程
|
|
3096
3141
|
try {
|
|
3097
3142
|
for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
|
|
3098
|
-
safeExec(`
|
|
3143
|
+
safeExec(dockerCmd(`exec ${cid} bash -c "pkill -f '${name}.*gateway' 2>/dev/null || true"`), { timeout: 5000 });
|
|
3099
3144
|
}
|
|
3100
|
-
safeExec(`
|
|
3145
|
+
safeExec(dockerCmd(`exec ${cid} bash -c "lsof -ti :${gatewayPort} 2>/dev/null | xargs -r kill -9 2>/dev/null || true"`), { timeout: 5000 });
|
|
3101
3146
|
console.log(chalk.gray(' 已尝试清理容器内旧 Gateway 进程'));
|
|
3102
3147
|
} catch { /* ignore */ }
|
|
3103
3148
|
|
|
@@ -3259,12 +3304,12 @@ async function restartGatewayNative() {
|
|
|
3259
3304
|
const dockerContainers = findOpenclawDockerContainers();
|
|
3260
3305
|
for (const c of dockerContainers) {
|
|
3261
3306
|
if (c.cli === 'node') {
|
|
3262
|
-
commands.push(`
|
|
3263
|
-
} else {
|
|
3264
|
-
commands.push(`
|
|
3307
|
+
commands.push(dockerCmd(`exec ${c.id} bash -lc "node ${c.cliPath} gateway restart"`));
|
|
3308
|
+
} else if (c.cli !== 'unknown') {
|
|
3309
|
+
commands.push(dockerCmd(`exec ${c.id} bash -lc "${c.cli} gateway restart"`));
|
|
3265
3310
|
}
|
|
3266
3311
|
for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
|
|
3267
|
-
commands.push(`
|
|
3312
|
+
commands.push(dockerCmd(`exec ${c.id} bash -lc "${name} gateway restart"`));
|
|
3268
3313
|
}
|
|
3269
3314
|
}
|
|
3270
3315
|
}
|