@walkhi/code-relax 0.1.0-beta.69 → 0.1.0-beta.71
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/build-platform.json +1 -1
- package/dist/src/platform/codex-protocol.mjs +7 -2
- package/dist/src/platform/codex-runtime.mjs +1 -0
- package/dist/src/platform/macos/codex-runtime.mjs +12 -12
- package/dist/src/platform/windows/codex-runtime.mjs +31 -21
- package/dist/src/runtime-discovery.mjs +3 -3
- package/dist/src/self-relay/connector.mjs +470 -455
- package/dist/src/service-doctor.mjs +4 -2
- package/dist/src/service-lifecycle.mjs +2 -1
- package/package.json +4 -4
package/dist/build-platform.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"layout":"npm-platform-packages","version":"0.1.0-beta.
|
|
1
|
+
{"layout":"npm-platform-packages","version":"0.1.0-beta.71"}
|
|
@@ -11,7 +11,7 @@ const requiredMethods = ['thread/start', 'thread/resume', 'thread/list', 'thread
|
|
|
11
11
|
'thread/settings/update', 'configRequirements/read', 'thread/queue/list', 'thread/queue/add',
|
|
12
12
|
'thread/queue/delete', 'thread/queue/start', 'thread/read', 'thread/unsubscribe', 'config/read', 'account/rateLimits/read'];
|
|
13
13
|
|
|
14
|
-
export function compareVersions(a, b) {
|
|
14
|
+
export function compareVersions(a, b) {
|
|
15
15
|
const parse = v => v.split(/[.-]/).map(part => /^\d+$/.test(part) ? Number(part) : part);
|
|
16
16
|
const aa = parse(a), bb = parse(b);
|
|
17
17
|
for (let i = 0; i < Math.max(aa.length, bb.length); i++) {
|
|
@@ -21,7 +21,12 @@ export function compareVersions(a, b) {
|
|
|
21
21
|
return typeof aa[i] === 'number' && typeof bb[i] === 'number' ? aa[i] - bb[i] : String(aa[i]).localeCompare(String(bb[i]));
|
|
22
22
|
}
|
|
23
23
|
return 0;
|
|
24
|
-
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function newestRuntime(candidates) {
|
|
27
|
+
return [...candidates].sort((a, b) => compareVersions(b.version, a.version)
|
|
28
|
+
|| Number(b.source === 'desktop') - Number(a.source === 'desktop'))[0];
|
|
29
|
+
}
|
|
25
30
|
|
|
26
31
|
export async function verifyProtocol(executable, stateRoot) {
|
|
27
32
|
const directory = fs.mkdtempSync(path.join(stateRoot, 'cli-schema-'));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { platformModule } from './select.mjs';
|
|
2
2
|
const adapter = await import(platformModule('codex-runtime'));
|
|
3
3
|
export const { discoverCodex, resolveCodexRuntime, runtimeFilesAvailable, codeModeHostPath, runtimeFingerprint, pruneRetainedDesktopRuntimes } = adapter;
|
|
4
|
+
export const runtimeInstallationFingerprint = adapter.runtimeInstallationFingerprint || (() => null);
|
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
5
|
import { execFile, execFileSync } from 'node:child_process';
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
|
-
import {
|
|
7
|
+
import { newestRuntime, verifyProtocol } from '../codex-protocol.mjs';
|
|
8
8
|
import { readRecord, writeRecord } from '../../service-lifecycle.mjs';
|
|
9
9
|
import { retainRuntime, runtimeFingerprint, packageRoot } from './retained-runtime.mjs';
|
|
10
10
|
export { runtimeFingerprint, pruneRetainedDesktopRuntimes } from './retained-runtime.mjs';
|
|
@@ -97,25 +97,25 @@ export async function resolveCodexRuntime(stateRoot, { refresh = false, environm
|
|
|
97
97
|
const explicit = environment.CODE_RELAX_CODEX_PATH || (saved?.source === 'explicit' ? saved.sourceExecutable : '');
|
|
98
98
|
if (!refresh && saved?.executable && (!explicit || explicit === saved.sourceExecutable) && codexHome === saved.codexHome
|
|
99
99
|
&& runtimeFilesAvailable(saved.executable) && saved.fingerprint === runtimeFingerprint(saved.executable)) return saved;
|
|
100
|
-
const candidates = []
|
|
100
|
+
const candidates = [];
|
|
101
101
|
for (const candidate of discoverCodex({ ...environment, CODEX_HOME: codexHome, CODE_RELAX_CODEX_PATH: explicit })) {
|
|
102
102
|
try {
|
|
103
|
-
if (!runtimeFilesAvailable(candidate.executable)) throw new Error('缺少配套原生 codex-code-mode-host');
|
|
104
103
|
const { stdout } = await exec(candidate.executable, ['--version'], { timeout: 10000, env: { ...environment, CODEX_HOME: codexHome } });
|
|
105
104
|
const version = /^codex-cli (\d+\.\d+\.\d+(?:-[\w.]+)?)/m.exec(stdout)?.[1];
|
|
106
105
|
if (!version) throw new Error('无法识别 Codex 版本');
|
|
107
106
|
candidates.push({ ...candidate, version });
|
|
108
|
-
} catch (error) {
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
try {
|
|
107
|
+
} catch (error) { throw new Error(`无法确认 Codex 候选版本,未降级:${candidate.executable}: ${error.message}`); }
|
|
108
|
+
}
|
|
109
|
+
const candidate = newestRuntime(candidates);
|
|
110
|
+
if (candidate) {
|
|
111
|
+
try {
|
|
112
|
+
if (!runtimeFilesAvailable(candidate.executable)) throw new Error('缺少配套原生 codex-code-mode-host');
|
|
113
113
|
const retained = await retainRuntime(candidate.executable, stateRoot, candidate.version);
|
|
114
114
|
await verifyProtocol(retained.executable, stateRoot);
|
|
115
115
|
const result = { ...candidate, ...retained, codexHome, fingerprint: runtimeFingerprint(retained.executable) };
|
|
116
116
|
writeRecord(recordFile, result);
|
|
117
117
|
return result;
|
|
118
|
-
} catch (error) {
|
|
119
|
-
}
|
|
120
|
-
throw new Error(
|
|
121
|
-
}
|
|
118
|
+
} catch (error) { throw new Error(`最新 Codex 内核 ${candidate.version} 校验失败,未降级:${candidate.executable}: ${error.message}`); }
|
|
119
|
+
}
|
|
120
|
+
throw new Error('未找到 Mac Codex 内核,请安装官方 Codex 应用或完整 CLI 并登录账号。');
|
|
121
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import {
|
|
2
|
+
import { newestRuntime, verifyProtocol } from '../codex-protocol.mjs';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
@@ -24,11 +24,16 @@ async function fileHash(file) {
|
|
|
24
24
|
return hash.digest('hex');
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
async function installedDesktopRuntime() {
|
|
27
|
+
async function installedDesktopRuntime() {
|
|
28
28
|
const { stdout } = await exec(powershell, ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
|
|
29
29
|
'-File', desktopRuntimeScript], { windowsHide: true, encoding: 'utf8', timeout: 30000 });
|
|
30
30
|
return JSON.parse(stdout.trim());
|
|
31
|
-
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function runtimeInstallationFingerprint() {
|
|
34
|
+
const installed = await installedDesktopRuntime();
|
|
35
|
+
return installed ? [installed.package, fingerprint(installed.executable)] : null;
|
|
36
|
+
}
|
|
32
37
|
|
|
33
38
|
async function matchesInstalledDesktop(executable, installed) {
|
|
34
39
|
if (!installed || !runtimeFilesAvailable(executable) || !runtimeFilesAvailable(installed.executable)) return false;
|
|
@@ -183,25 +188,30 @@ export async function resolveCodexRuntime(stateRoot, { refresh = false, environm
|
|
|
183
188
|
CODE_RELAX_CODEX_PATH: environment.CODE_RELAX_CODEX_PATH || (saved?.source === 'explicit' ? saved.executable : '') };
|
|
184
189
|
if (!refresh && saved?.executable && runtimeFilesAvailable(saved.executable)
|
|
185
190
|
&& saved.fingerprint === fingerprint(saved.executable)) return saved;
|
|
186
|
-
const candidates = []
|
|
187
|
-
let installed = null;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
const candidates = [];
|
|
192
|
+
let installed = null;
|
|
193
|
+
if (!environment.CODE_RELAX_CODEX_PATH) {
|
|
194
|
+
try { installed = await installedDesktopRuntime(); }
|
|
195
|
+
catch (error) { throw new Error(`无法确认最新 Codex Desktop 内核,未降级到其他 CLI:${error.message}`); }
|
|
196
|
+
}
|
|
197
|
+
for (const candidate of discoverCodex(environment)) {
|
|
198
|
+
try {
|
|
199
|
+
if (candidate.source === 'desktop' && !await matchesInstalledDesktop(candidate.executable, installed)) {
|
|
200
|
+
continue; // Superseded Desktop extraction directories are not installed candidates.
|
|
195
201
|
}
|
|
196
202
|
const { stdout } = await exec(candidate.executable, ['--version'], { windowsHide: true, timeout: 10000 });
|
|
197
203
|
const version = /^codex-cli (\d+\.\d+\.\d+(?:-[\w.]+)?)/m.exec(stdout)?.[1];
|
|
198
204
|
if (!version) throw new Error('无法识别版本');
|
|
199
205
|
candidates.push({ ...candidate, version });
|
|
200
|
-
} catch (error) {
|
|
201
|
-
}
|
|
202
|
-
candidates.
|
|
203
|
-
|
|
204
|
-
|
|
206
|
+
} catch (error) { throw new Error(`无法确认 Codex 候选版本,未降级:${candidate.executable}: ${error.message}`); }
|
|
207
|
+
}
|
|
208
|
+
if (installed && !candidates.some(candidate => candidate.source === 'desktop')) {
|
|
209
|
+
throw new Error(`当前 Codex Desktop 内核尚未准备完整,未降级到旧 CLI:${installed.package}。请打开更新后的桌面端完成内核提取。`);
|
|
210
|
+
}
|
|
211
|
+
const candidate = newestRuntime(candidates);
|
|
212
|
+
if (candidate) {
|
|
213
|
+
try {
|
|
214
|
+
if (!runtimeFilesAvailable(candidate.executable)) throw new Error('缺少同目录的 codex-code-mode-host.exe');
|
|
205
215
|
const executable = candidate.source === 'desktop'
|
|
206
216
|
? await retainDesktopRuntime(candidate.executable, installed, stateRoot) : candidate.executable;
|
|
207
217
|
await verifyProtocol(executable, stateRoot);
|
|
@@ -209,7 +219,7 @@ export async function resolveCodexRuntime(stateRoot, { refresh = false, environm
|
|
|
209
219
|
codexHome: path.resolve(environment.CODEX_HOME || path.join(os.homedir(), '.codex')) };
|
|
210
220
|
writeRecord(recordFile, result);
|
|
211
221
|
return result;
|
|
212
|
-
} catch (error) {
|
|
213
|
-
}
|
|
214
|
-
throw new Error(
|
|
215
|
-
}
|
|
222
|
+
} catch (error) { throw new Error(`最新 Codex 内核 ${candidate.version} 校验失败,未降级:${candidate.executable}: ${error.message}`); }
|
|
223
|
+
}
|
|
224
|
+
throw new Error('未找到 Codex CLI,请安装或更新 Codex CLI 后执行 relax setup。');
|
|
225
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { discoverCodex, resolveCodexRuntime, runtimeFingerprint } from './platform/codex-runtime.mjs';
|
|
1
|
+
import { discoverCodex, resolveCodexRuntime, runtimeFingerprint, runtimeInstallationFingerprint } from './platform/codex-runtime.mjs';
|
|
2
2
|
|
|
3
3
|
// Advisory discovery is independent of execution health. Failure here must never
|
|
4
4
|
// disable an already running app-server or cause an automatic process restart.
|
|
@@ -13,10 +13,10 @@ export class RuntimeDiscovery {
|
|
|
13
13
|
const errors = [];
|
|
14
14
|
try {
|
|
15
15
|
const candidates = discoverCodex();
|
|
16
|
-
const signature = JSON.stringify(candidates.map(candidate => {
|
|
16
|
+
const signature = JSON.stringify([await runtimeInstallationFingerprint(), candidates.map(candidate => {
|
|
17
17
|
try { return [candidate.executable, runtimeFingerprint(candidate.executable)]; }
|
|
18
18
|
catch (error) { return [candidate.executable, error.message]; }
|
|
19
|
-
}));
|
|
19
|
+
})]);
|
|
20
20
|
if (signature !== this.signature) {
|
|
21
21
|
await resolveCodexRuntime(this.stateRoot, { refresh: true });
|
|
22
22
|
this.signature = signature;
|