yymaxapi 1.0.95 → 1.0.97
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 +161 -6
- package/package.json +1 -1
package/bin/yymaxapi.js
CHANGED
|
@@ -639,6 +639,51 @@ function getGatewayListenOwner(port = 18789) {
|
|
|
639
639
|
return ownerResult.output.trim().split(/\s+/)[0] || null;
|
|
640
640
|
}
|
|
641
641
|
|
|
642
|
+
function getGatewayProcessSignature(port = 18789, environment = detectGatewayEnv()) {
|
|
643
|
+
if (!port) return '';
|
|
644
|
+
|
|
645
|
+
let result = null;
|
|
646
|
+
|
|
647
|
+
if (environment === 'docker') {
|
|
648
|
+
if (!_selectedDockerContainer?.id) return '';
|
|
649
|
+
result = safeExec(
|
|
650
|
+
dockerCmd(`exec ${_selectedDockerContainer.id} sh -c "lsof -ti :${port} 2>/dev/null"`),
|
|
651
|
+
{ timeout: 3000 }
|
|
652
|
+
);
|
|
653
|
+
} else if (environment === 'wsl') {
|
|
654
|
+
result = safeExec(`wsl -- bash -c "lsof -ti :${port} 2>/dev/null"`, { timeout: 3000 });
|
|
655
|
+
} else if (process.platform === 'win32') {
|
|
656
|
+
result = safeExec(`netstat -ano | findstr ":${port}"`, { timeout: 3000 });
|
|
657
|
+
} else {
|
|
658
|
+
result = safeExec(`lsof -ti :${port} 2>/dev/null`, { timeout: 3000 });
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const text = [result?.output, result?.stdout, result?.stderr]
|
|
662
|
+
.filter(Boolean)
|
|
663
|
+
.join('\n');
|
|
664
|
+
if (!text) return '';
|
|
665
|
+
|
|
666
|
+
const pids = process.platform === 'win32'
|
|
667
|
+
? text
|
|
668
|
+
.split('\n')
|
|
669
|
+
.map(line => line.trim())
|
|
670
|
+
.filter(line => line.includes('LISTENING') && line.includes(`:${port}`))
|
|
671
|
+
.map(line => line.split(/\s+/).pop() || '')
|
|
672
|
+
.filter(pid => /^\d+$/.test(pid))
|
|
673
|
+
: text
|
|
674
|
+
.split('\n')
|
|
675
|
+
.map(line => line.trim())
|
|
676
|
+
.filter(pid => /^\d+$/.test(pid));
|
|
677
|
+
|
|
678
|
+
return [...new Set(pids)].sort().join(',');
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function didGatewayProcessRestart({ portWasOpenBefore, beforeSignature, afterSignature }) {
|
|
682
|
+
if (!portWasOpenBefore) return true;
|
|
683
|
+
if (!beforeSignature || !afterSignature) return true;
|
|
684
|
+
return beforeSignature !== afterSignature;
|
|
685
|
+
}
|
|
686
|
+
|
|
642
687
|
function getConfigPath() {
|
|
643
688
|
const homeDir = os.homedir();
|
|
644
689
|
const openclawStateDir = process.env.OPENCLAW_STATE_DIR || path.join(homeDir, '.openclaw');
|
|
@@ -2789,6 +2834,88 @@ function syncMirroredAuthStores(paths) {
|
|
|
2789
2834
|
}
|
|
2790
2835
|
}
|
|
2791
2836
|
|
|
2837
|
+
function getManagedAgentSessionDirs(paths, agentId) {
|
|
2838
|
+
if (!paths?.configDir || !agentId) return [];
|
|
2839
|
+
const dirs = [path.join(paths.configDir, 'agents', agentId, 'sessions')];
|
|
2840
|
+
|
|
2841
|
+
for (const target of paths.syncTargets || []) {
|
|
2842
|
+
const baseDir = path.dirname(target);
|
|
2843
|
+
dirs.push(path.join(baseDir, 'agents', agentId, 'sessions'));
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2846
|
+
return [...new Set(dirs.filter(Boolean))];
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2849
|
+
function archiveManagedSessionFile(sessionDir, sessionId) {
|
|
2850
|
+
if (!sessionDir || !sessionId) return false;
|
|
2851
|
+
const sessionFile = path.join(sessionDir, `${sessionId}.jsonl`);
|
|
2852
|
+
if (!fs.existsSync(sessionFile)) return false;
|
|
2853
|
+
|
|
2854
|
+
const archiveDir = path.join(sessionDir, 'archive');
|
|
2855
|
+
if (!fs.existsSync(archiveDir)) {
|
|
2856
|
+
fs.mkdirSync(archiveDir, { recursive: true });
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
const archiveName = `${sessionId}-${Date.now()}.jsonl`;
|
|
2860
|
+
fs.renameSync(sessionFile, path.join(archiveDir, archiveName));
|
|
2861
|
+
return true;
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
function resetManagedAgentSessionStore(sessionDir, agentId) {
|
|
2865
|
+
if (!sessionDir || !agentId) return { changed: false, archived: 0 };
|
|
2866
|
+
const sessionsPath = path.join(sessionDir, 'sessions.json');
|
|
2867
|
+
if (!fs.existsSync(sessionsPath)) return { changed: false, archived: 0 };
|
|
2868
|
+
|
|
2869
|
+
let store;
|
|
2870
|
+
try {
|
|
2871
|
+
store = JSON.parse(fs.readFileSync(sessionsPath, 'utf8'));
|
|
2872
|
+
} catch {
|
|
2873
|
+
return { changed: false, archived: 0 };
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
if (!store || typeof store !== 'object' || Array.isArray(store)) {
|
|
2877
|
+
return { changed: false, archived: 0 };
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
const keyPrefix = `agent:${agentId}:`;
|
|
2881
|
+
const keysToRemove = Object.keys(store).filter(key => key.startsWith(keyPrefix));
|
|
2882
|
+
if (keysToRemove.length === 0) return { changed: false, archived: 0 };
|
|
2883
|
+
|
|
2884
|
+
const remaining = { ...store };
|
|
2885
|
+
const removedSessionIds = new Set();
|
|
2886
|
+
for (const key of keysToRemove) {
|
|
2887
|
+
const sessionId = String(store[key]?.sessionId || '').trim();
|
|
2888
|
+
if (sessionId) removedSessionIds.add(sessionId);
|
|
2889
|
+
delete remaining[key];
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
let archived = 0;
|
|
2893
|
+
for (const sessionId of removedSessionIds) {
|
|
2894
|
+
const stillReferenced = Object.values(remaining).some(entry => String(entry?.sessionId || '').trim() === sessionId);
|
|
2895
|
+
if (stillReferenced) continue;
|
|
2896
|
+
if (archiveManagedSessionFile(sessionDir, sessionId)) archived += 1;
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
fs.writeFileSync(sessionsPath, JSON.stringify(remaining, null, 2), 'utf8');
|
|
2900
|
+
return { changed: true, archived };
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
function resetManagedAgentSessionsWithSync(paths, agentId) {
|
|
2904
|
+
const dirs = getManagedAgentSessionDirs(paths, agentId);
|
|
2905
|
+
let changed = false;
|
|
2906
|
+
let archived = 0;
|
|
2907
|
+
|
|
2908
|
+
for (const sessionDir of dirs) {
|
|
2909
|
+
try {
|
|
2910
|
+
const result = resetManagedAgentSessionStore(sessionDir, agentId);
|
|
2911
|
+
if (result.changed) changed = true;
|
|
2912
|
+
archived += result.archived || 0;
|
|
2913
|
+
} catch { /* best-effort */ }
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
return { changed, archived };
|
|
2917
|
+
}
|
|
2918
|
+
|
|
2792
2919
|
function renameProviderInAuthStore(paths, oldProvider, newProvider) {
|
|
2793
2920
|
if (!paths?.authProfiles || !oldProvider || !newProvider || oldProvider === newProvider) return false;
|
|
2794
2921
|
const store = readAuthStore(paths.authProfiles);
|
|
@@ -4571,6 +4698,12 @@ async function autoActivate(paths, args = {}) {
|
|
|
4571
4698
|
if (yunyiLayoutResult.applied) {
|
|
4572
4699
|
syncManagedYunyiAuthProfiles(paths, config);
|
|
4573
4700
|
}
|
|
4701
|
+
if (!isClaudePrimary) {
|
|
4702
|
+
const resetResult = resetManagedAgentSessionsWithSync(paths, YYMAXAPI_OPENCLAW_GPT_AGENT_ID);
|
|
4703
|
+
if (resetResult.changed) {
|
|
4704
|
+
console.log(chalk.gray(` 已重置 ${YYMAXAPI_OPENCLAW_GPT_AGENT_ID} 的活动会话映射`));
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4574
4707
|
const opencodeDefaultModelKey = isClaudePrimary ? `yunyi-claude/${claudeModelId}` : `yunyi-codex/${codexModelId}`;
|
|
4575
4708
|
try { syncExternalTools('claude', claudeBaseUrl, apiKey, { codexBaseUrl, claudeModelId, opencodeDefaultModelKey }); } catch { /* ignore */ }
|
|
4576
4709
|
try { syncExternalTools('codex', codexBaseUrl, apiKey, { modelId: codexModelId }); } catch { /* ignore */ }
|
|
@@ -5544,6 +5677,12 @@ async function switchModel(paths) {
|
|
|
5544
5677
|
primary = finalSelected;
|
|
5545
5678
|
ensureGatewaySettings(config);
|
|
5546
5679
|
writeConfigWithSync(paths, config);
|
|
5680
|
+
if (selectedAgentId === YYMAXAPI_OPENCLAW_GPT_AGENT_ID) {
|
|
5681
|
+
const resetResult = resetManagedAgentSessionsWithSync(paths, selectedAgentId);
|
|
5682
|
+
if (resetResult.changed) {
|
|
5683
|
+
console.log(chalk.gray(` 已重置 ${selectedAgentId} 的活动会话映射`));
|
|
5684
|
+
}
|
|
5685
|
+
}
|
|
5547
5686
|
|
|
5548
5687
|
const selectedProviderKey = finalSelected.split('/')[0];
|
|
5549
5688
|
const selectedProviderConfig = providers[selectedProviderKey];
|
|
@@ -5940,14 +6079,19 @@ async function restartGatewayDocker(gatewayPort, silent = false) {
|
|
|
5940
6079
|
|
|
5941
6080
|
const cid = container.id;
|
|
5942
6081
|
const shellVariants = ['sh -c', 'bash -lc', 'bash -c'];
|
|
6082
|
+
const portWasOpenBefore = await isPortOpen(gatewayPort, '127.0.0.1', 500);
|
|
6083
|
+
const beforeSignature = portWasOpenBefore ? getGatewayProcessSignature(gatewayPort, 'docker') : '';
|
|
5943
6084
|
|
|
5944
6085
|
// 策略 A:exec restart 命令(短超时),然后端口探测
|
|
5945
6086
|
for (const cmd of buildDockerInnerCmds(container, 'gateway restart')) {
|
|
5946
6087
|
for (const shell of shellVariants) {
|
|
5947
6088
|
safeExec(dockerCmd(`exec ${cid} ${shell} "${cmd}"`), { timeout: 8000 });
|
|
5948
6089
|
if (await waitForGateway(gatewayPort, '127.0.0.1', 5000)) {
|
|
5949
|
-
|
|
5950
|
-
|
|
6090
|
+
const afterSignature = getGatewayProcessSignature(gatewayPort, 'docker');
|
|
6091
|
+
if (didGatewayProcessRestart({ portWasOpenBefore, beforeSignature, afterSignature })) {
|
|
6092
|
+
if (!silent) console.log(chalk.green(`✅ Gateway 已重启 (Docker: ${container.name})`));
|
|
6093
|
+
return true;
|
|
6094
|
+
}
|
|
5951
6095
|
}
|
|
5952
6096
|
}
|
|
5953
6097
|
}
|
|
@@ -5980,6 +6124,8 @@ async function restartGatewayWsl(gatewayPort, silent = false) {
|
|
|
5980
6124
|
if (!silent) console.log(chalk.gray(' [检测] Gateway 运行在 WSL 中'));
|
|
5981
6125
|
const wslCli = getWslCliBinary();
|
|
5982
6126
|
const names = ['openclaw', 'clawdbot', 'moltbot'];
|
|
6127
|
+
const portWasOpenBefore = await isPortOpen(gatewayPort, '127.0.0.1', 500);
|
|
6128
|
+
const beforeSignature = portWasOpenBefore ? getGatewayProcessSignature(gatewayPort, 'wsl') : '';
|
|
5983
6129
|
|
|
5984
6130
|
// 构建 WSL 重启命令
|
|
5985
6131
|
const wslRestartCmds = [];
|
|
@@ -5990,8 +6136,11 @@ async function restartGatewayWsl(gatewayPort, silent = false) {
|
|
|
5990
6136
|
for (const cmd of wslRestartCmds) {
|
|
5991
6137
|
safeExec(cmd, { timeout: 8000 });
|
|
5992
6138
|
if (await waitForGateway(gatewayPort, '127.0.0.1', 5000)) {
|
|
5993
|
-
|
|
5994
|
-
|
|
6139
|
+
const afterSignature = getGatewayProcessSignature(gatewayPort, 'wsl');
|
|
6140
|
+
if (didGatewayProcessRestart({ portWasOpenBefore, beforeSignature, afterSignature })) {
|
|
6141
|
+
if (!silent) console.log(chalk.green('✅ Gateway 已重启 (WSL)'));
|
|
6142
|
+
return true;
|
|
6143
|
+
}
|
|
5995
6144
|
}
|
|
5996
6145
|
}
|
|
5997
6146
|
|
|
@@ -6030,6 +6179,8 @@ async function restartGatewayNative(silent = false) {
|
|
|
6030
6179
|
const configPaths = getConfigPath();
|
|
6031
6180
|
const gwConfig = readConfig(configPaths.openclawConfig);
|
|
6032
6181
|
const gatewayPort = gwConfig?.gateway?.port || 18789;
|
|
6182
|
+
const portWasOpenBefore = await isPortOpen(gatewayPort, '127.0.0.1', 500);
|
|
6183
|
+
const beforeSignature = portWasOpenBefore ? getGatewayProcessSignature(gatewayPort, 'native') : '';
|
|
6033
6184
|
|
|
6034
6185
|
// 策略 A:尝试 exec "gateway restart"(短超时),然后端口探测
|
|
6035
6186
|
const restartCmds = buildGatewayCommands(resolved, nodeInfo, useNode, 'restart');
|
|
@@ -6037,8 +6188,12 @@ async function restartGatewayNative(silent = false) {
|
|
|
6037
6188
|
// 只尝试第一条(最精确的路径),避免逐条超时累积
|
|
6038
6189
|
safeExec(restartCmds[0], { timeout: 8000, env });
|
|
6039
6190
|
if (await waitForGateway(gatewayPort, '127.0.0.1', 5000)) {
|
|
6040
|
-
|
|
6041
|
-
|
|
6191
|
+
const afterSignature = getGatewayProcessSignature(gatewayPort, 'native');
|
|
6192
|
+
if (didGatewayProcessRestart({ portWasOpenBefore, beforeSignature, afterSignature })) {
|
|
6193
|
+
if (!silent) console.log(chalk.green(`✅ Gateway 已重启`));
|
|
6194
|
+
return true;
|
|
6195
|
+
}
|
|
6196
|
+
if (!silent) console.log(chalk.gray(' 检测到 Gateway 监听进程未变化,继续执行强制重启...'));
|
|
6042
6197
|
}
|
|
6043
6198
|
}
|
|
6044
6199
|
|