@walkhi/code-relax 0.1.0-beta.80 → 0.1.0-beta.82
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
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"layout":"npm-platform-packages","version":"0.1.0-beta.
|
|
1
|
+
{"layout":"npm-platform-packages","version":"0.1.0-beta.82"}
|
|
@@ -1,71 +1,73 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { setTimeout as delay } from 'node:timers/promises';
|
|
4
|
-
import { readRecord, lifecycle } from './service-lifecycle.mjs';
|
|
5
|
-
import * as serviceHost from './platform/service-host.mjs';
|
|
6
|
-
import { managedPortAvailable as portAvailable } from './managed-app-server.mjs';
|
|
7
|
-
import { readManagedEndpoint } from './managed-endpoint.mjs';
|
|
8
|
-
export { portAvailable };
|
|
9
|
-
function desktopEnvironment(stateRoot, environment) {
|
|
10
|
-
const childEnv = { ...environment };
|
|
11
|
-
for (const key of ['CODEX_APP_SERVER_WS_URL', 'CODEX_APP_SERVER_FORCE_CLI', 'CODEX_APP_TOOLS_PIPE_PATH']) delete childEnv[key];
|
|
12
|
-
const runtime = readRecord(path.join(stateRoot, 'codex-runtime.json'));
|
|
13
|
-
if (runtime?.codexHome) childEnv.CODEX_HOME = runtime.codexHome;
|
|
14
|
-
childEnv.CODEX_APP_SERVER_WS_URL = readManagedEndpoint(stateRoot);
|
|
15
|
-
return childEnv;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Opening the optional UI does not start, restart or supervise the kernel.
|
|
19
|
-
export async function startDesktop({ stateRoot, host, environment = process.env, restart = false }) {
|
|
20
|
-
let stage = 'inspect';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (info.
|
|
25
|
-
if (info.running) {
|
|
26
|
-
stage = '
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
if (info.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
4
|
+
import { readRecord, lifecycle } from './service-lifecycle.mjs';
|
|
5
|
+
import * as serviceHost from './platform/service-host.mjs';
|
|
6
|
+
import { managedPortAvailable as portAvailable } from './managed-app-server.mjs';
|
|
7
|
+
import { readManagedEndpoint } from './managed-endpoint.mjs';
|
|
8
|
+
export { portAvailable };
|
|
9
|
+
function desktopEnvironment(stateRoot, environment) {
|
|
10
|
+
const childEnv = { ...environment };
|
|
11
|
+
for (const key of ['CODEX_APP_SERVER_WS_URL', 'CODEX_APP_SERVER_FORCE_CLI', 'CODEX_APP_TOOLS_PIPE_PATH']) delete childEnv[key];
|
|
12
|
+
const runtime = readRecord(path.join(stateRoot, 'codex-runtime.json'));
|
|
13
|
+
if (runtime?.codexHome) childEnv.CODEX_HOME = runtime.codexHome;
|
|
14
|
+
childEnv.CODEX_APP_SERVER_WS_URL = readManagedEndpoint(stateRoot);
|
|
15
|
+
return childEnv;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Opening the optional UI does not start, restart or supervise the kernel.
|
|
19
|
+
export async function startDesktop({ stateRoot, host, environment = process.env, restart = false, progress = () => {} }) {
|
|
20
|
+
let stage = 'inspect';
|
|
21
|
+
progress(stage);
|
|
22
|
+
try {
|
|
23
|
+
const info = await host.inspect();
|
|
24
|
+
if (!info.desktop) throw new Error('未安装 Codex 桌面端。');
|
|
25
|
+
if (info.running && !restart) return { status: 'running', message: 'Codex 桌面端已启动。' };
|
|
26
|
+
if (info.running) { stage = 'close'; progress(stage); await host.closeDesktop(info.desktop); }
|
|
27
|
+
stage = 'activate'; progress(stage);
|
|
28
|
+
await host.startDesktop(info.desktop, desktopEnvironment(stateRoot, environment));
|
|
29
|
+
return { status: 'accepted', message: restart ? '已提交 Codex 桌面端重启请求。' : '已提交 Codex 桌面端启动请求。' };
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(JSON.stringify({ time: new Date().toISOString(), event: 'desktop-operation-failed', restart, stage,
|
|
32
|
+
message: error.message, code: error.code, stderr: error.stderr }));
|
|
33
|
+
throw new Error(stage === 'close' && ['MAC_DESKTOP_CLOSE_TIMEOUT', 'MAC_DESKTOP_CLOSE_CHANGED'].includes(error.code) ? error.message
|
|
34
|
+
: stage === 'close' ? '桌面端关闭失败,请查看工作站诊断日志。'
|
|
35
|
+
: stage === 'activate' ? '桌面端启动失败,请查看工作站诊断日志。' : '无法检查桌面端,请查看工作站诊断日志。', { cause: error });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function launchDesktop({ stateRoot, host, environment = process.env, wait = delay, timeout = 30000, recoveryId, progress = () => {}, service = lifecycle({ stateRoot, host: serviceHost }) }) {
|
|
40
|
+
fs.mkdirSync(stateRoot, { recursive: true });
|
|
41
|
+
const recovery = readRecord(path.join(stateRoot, 'shared-recovery.lock'));
|
|
42
|
+
if (recovery && recovery.operationId !== recoveryId) throw new Error('Codex 内核正在启动,请用 relax doctor 查看恢复状态。');
|
|
43
|
+
const lock = path.join(stateRoot, 'desktop-launch.lock');
|
|
44
|
+
let descriptor;
|
|
45
|
+
try { descriptor = fs.openSync(lock, 'wx'); }
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error.code === 'EEXIST') throw new Error('另一个 Desktop 启动操作正在进行;若上次异常退出,请确认没有启动器运行后移除 desktop-launch.lock。');
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const info = await host.inspect();
|
|
52
|
+
if (!info.desktop) throw new Error('未安装 Codex Desktop,请使用 relax start 启动工作站。');
|
|
53
|
+
const connected = await host.sharedServiceUrl();
|
|
54
|
+
if (info.running) {
|
|
55
|
+
if (connected) return { reused: true };
|
|
56
|
+
throw new Error('请先保存任务并完全退出 Codex Desktop,再接入 Codex 内核;不会自动结束正在运行的任务。');
|
|
57
|
+
}
|
|
58
|
+
progress('starting-app-server');
|
|
59
|
+
const health = await service.status();
|
|
60
|
+
if (!health.running || !health.managedAppServer?.ready) throw new Error(health.managedAppServer?.error || '工作站 Codex 内核未就绪。');
|
|
61
|
+
const childEnv = desktopEnvironment(stateRoot, environment);
|
|
62
|
+
progress('starting-desktop');
|
|
63
|
+
await host.startDesktop(info.desktop, childEnv);
|
|
64
|
+
const deadline = Date.now() + timeout;
|
|
65
|
+
while (true) {
|
|
66
|
+
const ready = await host.desktopReady();
|
|
67
|
+
if (ready) break;
|
|
68
|
+
if (Date.now() >= deadline) throw new Error('Desktop 尚未接入 Codex 内核。请查看 Desktop 窗口后重试。');
|
|
69
|
+
await wait(500);
|
|
70
|
+
}
|
|
71
|
+
return { reused: false };
|
|
72
|
+
} finally { fs.closeSync(descriptor); fs.rmSync(lock, { force: true }); }
|
|
73
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
2
|
+
import { startDesktop } from './desktop-launcher.mjs';
|
|
3
|
+
|
|
4
|
+
// The request is acknowledged before native close/start can outlive the phone's
|
|
5
|
+
// request deadline. Health carries the same receipt; a lost reply is not a retry.
|
|
6
|
+
export class DesktopOperation {
|
|
7
|
+
constructor({ stateRoot, host, sample, launch = startDesktop, wait = delay, now = Date.now, timeout = 30000 }) {
|
|
8
|
+
Object.assign(this, { stateRoot, host, sample, launch, wait, now, timeout });
|
|
9
|
+
this.record = null;
|
|
10
|
+
}
|
|
11
|
+
snapshot() { return this.record ? { ...this.record } : null; }
|
|
12
|
+
request({ operationId, restart }) {
|
|
13
|
+
if (this.record?.operationId === operationId) return this.snapshot();
|
|
14
|
+
if (this.record?.status === 'running') {
|
|
15
|
+
throw Object.assign(new Error('桌面端操作仍在进行,请刷新状态核对结果。'), { statusCode: 409 });
|
|
16
|
+
}
|
|
17
|
+
this.record = { operationId, restart, status: 'running', startedAt: new Date(this.now()).toISOString() };
|
|
18
|
+
this.completion = this.run(this.record);
|
|
19
|
+
return this.snapshot();
|
|
20
|
+
}
|
|
21
|
+
async run(record) {
|
|
22
|
+
let failure;
|
|
23
|
+
const progress = stage => {
|
|
24
|
+
Object.assign(record, { stage, updatedAt: new Date(this.now()).toISOString() });
|
|
25
|
+
console.info(JSON.stringify({ event: 'desktop-operation-stage', operationId: record.operationId,
|
|
26
|
+
restart: record.restart, stage, time: record.updatedAt }));
|
|
27
|
+
};
|
|
28
|
+
try {
|
|
29
|
+
await this.launch({ stateRoot: this.stateRoot, host: this.host, restart: record.restart, progress });
|
|
30
|
+
progress('connecting');
|
|
31
|
+
const deadline = this.now() + this.timeout;
|
|
32
|
+
while (!await this.host.desktopReady()) {
|
|
33
|
+
if (this.now() >= deadline) throw new Error('桌面端启动请求已执行,但未确认接入共享内核,请查看工作站诊断日志。');
|
|
34
|
+
await this.wait(500);
|
|
35
|
+
}
|
|
36
|
+
} catch (error) { failure = error; }
|
|
37
|
+
// Publish the final receipt with a fresh desktop sample, including failures.
|
|
38
|
+
if (!failure) progress('verifying');
|
|
39
|
+
try { await this.sample(); }
|
|
40
|
+
catch (error) { failure ||= error; }
|
|
41
|
+
Object.assign(record, { status: failure ? 'failed' : 'completed', finishedAt: new Date(this.now()).toISOString() });
|
|
42
|
+
if (failure) record.error = failure.message;
|
|
43
|
+
console.info(JSON.stringify({ event: 'desktop-operation-result', operationId: record.operationId,
|
|
44
|
+
time: record.finishedAt, stage: record.stage, status: record.status, message: record.error }));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -9,7 +9,8 @@ import { readRecord } from '../../service-lifecycle.mjs';
|
|
|
9
9
|
export { residentStartup } from './resident-startup.mjs';
|
|
10
10
|
|
|
11
11
|
import { managedEndpointUrl, readManagedEndpoint } from '../../managed-endpoint.mjs';
|
|
12
|
-
export function createDesktopHost({ request = systemRequest, wait = delay, now = Date.now, timeout =
|
|
12
|
+
export function createDesktopHost({ request = systemRequest, wait = delay, now = Date.now, timeout = 30000,
|
|
13
|
+
diagnose = record => console.info(JSON.stringify({ time: new Date().toISOString(), event: 'mac-desktop-close', ...record })),
|
|
13
14
|
stateRoot = () => runtimePaths().stateRoot, fingerprint = file => {
|
|
14
15
|
const stat = fs.statSync(path.join(file, 'Contents/Info.plist'));
|
|
15
16
|
return `${stat.ino}:${stat.size}:${stat.mtimeMs}:${stat.ctimeMs}`;
|
|
@@ -48,10 +49,37 @@ export function createDesktopHost({ request = systemRequest, wait = delay, now =
|
|
|
48
49
|
const info = await this.inspect();
|
|
49
50
|
if (!info.running) return;
|
|
50
51
|
if (info.desktop !== bundlePath || !info.process) throw new Error('Desktop 进程身份已变化,未请求退出。');
|
|
51
|
-
|
|
52
|
+
const started = now();
|
|
53
|
+
const identity = value => value ? { pid: value.pid, created: value.created, executable: value.executable } : null;
|
|
54
|
+
const context = { target: identity(info.process), version: info.version, build: info.build, timeoutMs: timeout };
|
|
55
|
+
diagnose({ ...context, stage: 'request' });
|
|
56
|
+
let result;
|
|
57
|
+
try { result = await request({ operation: 'desktop-close', bundlePath, expected: info.process }); }
|
|
58
|
+
catch (error) {
|
|
59
|
+
diagnose({ ...context, stage: 'request-failed', elapsedMs: now() - started, error: error.message });
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
diagnose({ ...context, stage: 'request-returned', elapsedMs: now() - started,
|
|
63
|
+
quitRequested: result.quitRequested === true, alreadyStopped: result.stopped === true });
|
|
52
64
|
const deadline = now() + timeout;
|
|
53
|
-
while (
|
|
54
|
-
|
|
65
|
+
while (true) {
|
|
66
|
+
let current;
|
|
67
|
+
try { current = await this.inspect(); }
|
|
68
|
+
catch (error) {
|
|
69
|
+
diagnose({ ...context, stage: 'inspect-failed', elapsedMs: now() - started, error: error.message });
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
const observation = { ...context, elapsedMs: now() - started, current: identity(current.process),
|
|
73
|
+
finishedLaunching: current.finishedLaunching, connectedToManagedServer: current.connectedToManagedServer };
|
|
74
|
+
if (!current.running) { diagnose({ ...observation, stage: 'exited' }); return; }
|
|
75
|
+
if (current.process?.pid !== info.process.pid || current.process?.created !== info.process.created) {
|
|
76
|
+
diagnose({ ...observation, stage: 'process-changed' });
|
|
77
|
+
throw Object.assign(new Error('Mac 桌面端进程已变化,请刷新状态核对;未再次请求退出。'), { code: 'MAC_DESKTOP_CLOSE_CHANGED' });
|
|
78
|
+
}
|
|
79
|
+
if (now() >= deadline) {
|
|
80
|
+
diagnose({ ...observation, stage: 'timeout', quitRequested: result.quitRequested === true });
|
|
81
|
+
throw Object.assign(new Error('Mac 桌面端退出请求已发送,但未在等待时间内退出。请在 Mac 查看桌面端退出状态和诊断日志;未强制结束进程。'), { code: 'MAC_DESKTOP_CLOSE_TIMEOUT' });
|
|
82
|
+
}
|
|
55
83
|
await wait(200);
|
|
56
84
|
}
|
|
57
85
|
},
|
|
@@ -37,14 +37,17 @@ export function prepareNativePackage(packageRoot, { platform = process.platform,
|
|
|
37
37
|
const pkg = readJson(path.join(packageRoot, 'package.json'));
|
|
38
38
|
if (build.layout !== 'npm-platform-packages' || build.version !== pkg.version)
|
|
39
39
|
throw new Error('安装包构建与当前系统、架构或版本不匹配,请重新安装。');
|
|
40
|
+
const nativeVersion = pkg.optionalDependencies?.[target.packageName];
|
|
41
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(nativeVersion || ''))
|
|
42
|
+
throw new Error('安装包未锁定当前平台原生组件的精确版本。');
|
|
40
43
|
let manifest;
|
|
41
44
|
try { manifest = createRequire(path.join(packageRoot, 'package.json')).resolve(`${target.packageName}/package.json`); }
|
|
42
45
|
catch (error) {
|
|
43
46
|
if (error.code !== 'MODULE_NOT_FOUND') throw error;
|
|
44
|
-
throw new Error(`缺少 ${target.packageName}@${
|
|
47
|
+
throw new Error(`缺少 ${target.packageName}@${nativeVersion};请勿省略 optional 依赖,使用 npm install -g @walkhi/code-relax@${pkg.version} --include=optional --allow-scripts=@walkhi/code-relax --foreground-scripts 重新安装。`);
|
|
45
48
|
}
|
|
46
49
|
const native = readJson(manifest);
|
|
47
|
-
if (native.name !== target.packageName || native.version !==
|
|
50
|
+
if (native.name !== target.packageName || native.version !== nativeVersion
|
|
48
51
|
|| native.os?.length !== 1 || native.os[0] !== platform || native.cpu?.length !== 1 || native.cpu[0] !== arch)
|
|
49
52
|
throw new Error('原生组件包的系统、架构或版本不匹配,未更新工作站。');
|
|
50
53
|
const source = path.join(path.dirname(manifest), 'native', target.file);
|
package/dist/src/server.mjs
CHANGED
|
@@ -39,7 +39,7 @@ import { DotsMessageImages } from './dots-message-images.mjs';
|
|
|
39
39
|
import { desktopDotsProfiles } from './platform/windows/dots-profile.mjs';
|
|
40
40
|
import { readPlanPricing } from './plan-pricing.mjs';
|
|
41
41
|
import { requestRecovery, recoveryStatus } from './shared-recovery.mjs';
|
|
42
|
-
import {
|
|
42
|
+
import { DesktopOperation } from './desktop-operation.mjs';
|
|
43
43
|
import { requestServiceRestart } from './service-restart.mjs';
|
|
44
44
|
import { checkServiceUpdate, requestServiceUpdate, serviceUpdateStatus } from './service-update.mjs';
|
|
45
45
|
import { TurnModelHistory } from './turn-model-history.mjs';
|
|
@@ -128,6 +128,7 @@ const dotsChat = new DotsChatServer(dotsServer, undefined, { uploadsRoot });
|
|
|
128
128
|
managedAppServer.on('state', state => { if (state.ready) dotsChat.startBackgroundActivity(); });
|
|
129
129
|
const runtimeDiscovery = new RuntimeDiscovery({ stateRoot, host: desktopHost,
|
|
130
130
|
url: () => managedAppServer.snapshot().url, diagnose: runtimeDiagnostic });
|
|
131
|
+
const desktopOperation = new DesktopOperation({ stateRoot, host: desktopHost, sample: () => runtimeDiscovery.sample() });
|
|
131
132
|
const relayManager = createRelayManager(stateRoot);
|
|
132
133
|
let sharedCatalog = null, executionMode = 'ordinary';
|
|
133
134
|
function selectSharedExecution(enabled) {
|
|
@@ -159,6 +160,7 @@ function workstationHealth() {
|
|
|
159
160
|
mode: executionMode },
|
|
160
161
|
managedAppServer: managedAppServer.snapshot(),
|
|
161
162
|
...runtimeDiscovery.state,
|
|
163
|
+
desktopOperation: desktopOperation.snapshot(),
|
|
162
164
|
sharedAppServerConnected: sharedServer?.ready === true,
|
|
163
165
|
sharedRecoveryAvailable: runtimeDiscovery.state.desktop?.installed === true,
|
|
164
166
|
mode: options.lan ? 'lan' : 'loopback',
|
|
@@ -197,8 +199,10 @@ async function handleRequest(request, response) {
|
|
|
197
199
|
return;
|
|
198
200
|
}
|
|
199
201
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
+
const remoteDesktopControl = ['/api/desktop/video-probe', '/api/desktop/frame', '/api/desktop/move', '/api/desktop/click'].includes(url.pathname)
|
|
203
|
+
|| url.pathname.startsWith('/api/desktop/frames/');
|
|
204
|
+
if (process.platform !== 'win32' && remoteDesktopControl)
|
|
205
|
+
throw new RequestError(501, '此平台不提供远程桌面控制。', { code: 'desktop_control_unsupported' });
|
|
202
206
|
|
|
203
207
|
if (url.pathname === '/api/desktop/video-probe') {
|
|
204
208
|
if (request.method === 'POST') {
|
|
@@ -291,11 +295,13 @@ async function handleRequest(request, response) {
|
|
|
291
295
|
}
|
|
292
296
|
if (request.method === 'POST' && ['/api/desktop/start', '/api/desktop/restart'].includes(url.pathname)) {
|
|
293
297
|
const body = await readJsonBody(request);
|
|
294
|
-
if (body.confirmed !== true
|
|
295
|
-
|
|
296
|
-
try {
|
|
297
|
-
|
|
298
|
-
|
|
298
|
+
if (body.confirmed !== true || !/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(body.operationId || ''))
|
|
299
|
+
throw new RequestError(400, '请确认启动桌面端并提供操作编号。');
|
|
300
|
+
try {
|
|
301
|
+
sendJson(response, 202, desktopOperation.request({ operationId: body.operationId,
|
|
302
|
+
restart: url.pathname === '/api/desktop/restart' }));
|
|
303
|
+
} catch (error) { throw new RequestError(error.statusCode || 502, error.message); }
|
|
304
|
+
return;
|
|
299
305
|
}
|
|
300
306
|
if (request.method === 'POST' && url.pathname === '/api/shared-recovery') {
|
|
301
307
|
const body = await readJsonBody(request);
|
|
@@ -346,6 +352,7 @@ async function handleRequest(request, response) {
|
|
|
346
352
|
}
|
|
347
353
|
|
|
348
354
|
if (request.method === 'GET' && url.pathname === '/api/health') {
|
|
355
|
+
if (url.searchParams.get('refresh') === 'desktop') await runtimeDiscovery.sample();
|
|
349
356
|
sendJson(response, 200, workstationHealth()); return;
|
|
350
357
|
}
|
|
351
358
|
if (request.method === 'GET' && url.pathname === '/api/health/events') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walkhi/code-relax",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.82",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"optionalDependencies": {
|
|
44
44
|
"@walkhi/code-relax-native-win32-x64": "0.1.0-beta.80",
|
|
45
|
-
"@walkhi/code-relax-native-darwin-arm64": "0.1.0-beta.
|
|
46
|
-
"@walkhi/code-relax-native-darwin-x64": "0.1.0-beta.
|
|
45
|
+
"@walkhi/code-relax-native-darwin-arm64": "0.1.0-beta.82",
|
|
46
|
+
"@walkhi/code-relax-native-darwin-x64": "0.1.0-beta.82"
|
|
47
47
|
}
|
|
48
48
|
}
|