@walkhi/code-relax 0.1.0-beta.2 → 0.1.0-beta.21

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.
Files changed (120) hide show
  1. package/README.md +55 -71
  2. package/dist/bin/codex-remote.mjs +150 -126
  3. package/dist/bin/manage-relay-admin.mjs +20 -0
  4. package/dist/bin/migrate-relay-db.mjs +107 -0
  5. package/dist/bin/self-relay-admin.mjs +8 -0
  6. package/dist/bin/self-relay-demo.mjs +2 -2
  7. package/dist/bin/self-relay-server.mjs +18 -10
  8. package/dist/shared/app-server-events.cjs +42 -15
  9. package/dist/shared/file-mentions.cjs +35 -0
  10. package/dist/shared/server-requests.cjs +86 -58
  11. package/dist/skills/code-relax/SKILL.md +7 -6
  12. package/dist/src/desktop-launcher.mjs +16 -21
  13. package/dist/src/file-upload-session.mjs +124 -0
  14. package/dist/src/lan-socket-server.mjs +52 -9
  15. package/dist/src/managed-app-server.mjs +44 -13
  16. package/dist/src/message-images.mjs +0 -18
  17. package/dist/src/onboarding.mjs +9 -11
  18. package/dist/src/platform/README.md +1 -1
  19. package/dist/src/platform/windows/background-process.mjs +1 -1
  20. package/dist/src/platform/windows/codex-runtime.mjs +251 -0
  21. package/dist/src/platform/windows/desktop-host.mjs +17 -3
  22. package/dist/src/platform/windows/desktop-info.ps1 +2 -4
  23. package/dist/src/platform/windows/desktop-runtime.ps1 +10 -0
  24. package/dist/src/platform/windows/desktop-shortcuts.ps1 +7 -14
  25. package/dist/src/platform/windows/launch-worker.mjs +42 -0
  26. package/dist/src/platform/windows/resident-start.js +20 -0
  27. package/dist/src/platform/windows/resident-startup.ps1 +76 -20
  28. package/dist/src/platform/windows/service-host.mjs +4 -16
  29. package/dist/src/platform/windows/start-hidden-console.ps1 +10 -5
  30. package/dist/src/platform/windows/stop-desktop.ps1 +7 -4
  31. package/dist/src/platform/windows/user-environment.mjs +1 -1
  32. package/dist/src/runtime-discovery.mjs +43 -0
  33. package/dist/src/self-relay/admin-cloud.mjs +101 -0
  34. package/dist/src/self-relay/admin-page.mjs +79 -0
  35. package/dist/src/self-relay/admin-server.mjs +66 -0
  36. package/dist/src/self-relay/admin-state.mjs +82 -19
  37. package/dist/src/self-relay/admin-style.mjs +9 -0
  38. package/dist/src/self-relay/admin-usage-page.mjs +28 -0
  39. package/dist/src/self-relay/connector.mjs +243 -30
  40. package/dist/src/self-relay/demo.mjs +3 -4
  41. package/dist/src/self-relay/email-accounts.mjs +141 -0
  42. package/dist/src/self-relay/lifecycle.mjs +99 -142
  43. package/dist/src/self-relay/mysql-state.mjs +104 -0
  44. package/dist/src/self-relay/p2p-probe.mjs +1 -1
  45. package/dist/src/self-relay/server.mjs +335 -59
  46. package/dist/src/self-relay/usage-diagnostics.mjs +46 -0
  47. package/dist/src/self-relay/usage-meter.mjs +125 -0
  48. package/dist/src/server.mjs +731 -1439
  49. package/dist/src/service-control.mjs +4 -4
  50. package/dist/src/service-doctor.mjs +27 -29
  51. package/dist/src/service-health.mjs +2 -2
  52. package/dist/src/service-install.mjs +1 -4
  53. package/dist/src/service-lifecycle.mjs +19 -19
  54. package/dist/src/shared-app-server.mjs +277 -270
  55. package/dist/src/shared-catalog.mjs +1 -11
  56. package/dist/src/shared-recovery.mjs +11 -9
  57. package/dist/src/shared-thread-stream.mjs +1 -1
  58. package/dist/src/skill-install.mjs +0 -13
  59. package/package.json +17 -18
  60. package/tools/postinstall.mjs +69 -15
  61. package/dist/src/app-server-client.mjs +0 -468
  62. package/dist/src/app-server-tasks.mjs +0 -358
  63. package/dist/src/debug-ui.mjs +0 -131
  64. package/dist/src/platform/windows/desktop-monitor.mjs +0 -34
  65. package/dist/src/platform/windows/desktop-shortcut-run.ps1 +0 -5
  66. package/dist/src/platform/windows/desktop-tools.mjs +0 -48
  67. package/dist/src/platform/windows/launch-worker.ps1 +0 -20
  68. package/dist/src/thread-catalog.mjs +0 -47
  69. package/dist/tools/find-desktop-pipe.mjs +0 -10
  70. package/dist/web/activity-view.js +0 -283
  71. package/dist/web/approval-ui.js +0 -188
  72. package/dist/web/capabilities.js +0 -57
  73. package/dist/web/chat-transport.js +0 -202
  74. package/dist/web/chat.css +0 -782
  75. package/dist/web/chat.js +0 -3830
  76. package/dist/web/client-platform.js +0 -11
  77. package/dist/web/community-view.js +0 -20
  78. package/dist/web/community.css +0 -77
  79. package/dist/web/community.html +0 -37
  80. package/dist/web/composer-controller.js +0 -101
  81. package/dist/web/conversation-controller.js +0 -99
  82. package/dist/web/disclosure-state-controller.js +0 -95
  83. package/dist/web/draft-controller.js +0 -103
  84. package/dist/web/harmony-platform.js +0 -52
  85. package/dist/web/highlight-powershell.min.js +0 -39
  86. package/dist/web/highlight.min.js +0 -1262
  87. package/dist/web/history-cache.js +0 -240
  88. package/dist/web/history-controller.js +0 -167
  89. package/dist/web/index.html +0 -325
  90. package/dist/web/lan-session.js +0 -122
  91. package/dist/web/link-action-controller.js +0 -212
  92. package/dist/web/message-send-controller.js +0 -177
  93. package/dist/web/message-view.js +0 -98
  94. package/dist/web/p2p-data-channel.js +0 -50
  95. package/dist/web/p2p-diagnostics.js +0 -28
  96. package/dist/web/p2p-probe.js +0 -123
  97. package/dist/web/page-resume.js +0 -28
  98. package/dist/web/pending-message-store.js +0 -108
  99. package/dist/web/queue-controller.js +0 -82
  100. package/dist/web/resources.json +0 -1
  101. package/dist/web/self-relay-session.js +0 -129
  102. package/dist/web/server-requests.js +0 -67
  103. package/dist/web/station-connection-controller.js +0 -75
  104. package/dist/web/task-list-view.js +0 -296
  105. package/dist/web/thread-attention-controller.js +0 -124
  106. package/dist/web/thread-context-controller.js +0 -30
  107. package/dist/web/thread-list-controller.js +0 -61
  108. package/dist/web/thread-list-sync.js +0 -86
  109. package/dist/web/thread-title-controller.js +0 -57
  110. package/dist/web/timeline-formatters.js +0 -249
  111. package/dist/web/timeline-reducer.js +0 -81
  112. package/dist/web/timeline-renderer.js +0 -161
  113. package/dist/web/timeline-scroll-controller.js +0 -50
  114. package/dist/web/timeline-sync.js +0 -67
  115. package/dist/web/usage-controller.js +0 -258
  116. package/dist/web/vendor/README.md +0 -14
  117. package/dist/web/vendor/highlight-powershell.min.js +0 -39
  118. package/dist/web/vendor/highlight.LICENSE.txt +0 -29
  119. package/dist/web/vendor/highlight.min.js +0 -1262
  120. package/dist/web/vendor/lucide.LICENSE.txt +0 -17
@@ -29,7 +29,7 @@ export async function openControl({ instanceId, secret, shutdown, status = () =>
29
29
  }
30
30
  if (request.method === 'POST') response.once('finish', () => {
31
31
  Promise.resolve(shutdown()).catch(error => {
32
- console.error('Bridge 关闭失败:', error.message);
32
+ console.error('桌面服务关闭失败:', error.message);
33
33
  process.exitCode = 1;
34
34
  });
35
35
  });
@@ -56,16 +56,16 @@ export function controlRequest(session, method = 'GET', action = '') {
56
56
  response.on('end', () => {
57
57
  try {
58
58
  if (response.statusCode === 409) throw new Error(JSON.parse(body).error);
59
- if (response.statusCode !== 200) throw new Error('Bridge 管理通道身份验证失败。');
59
+ if (response.statusCode !== 200) throw new Error('桌面服务管理通道身份验证失败。');
60
60
  const identity = JSON.parse(body);
61
61
  if (identity.instanceId !== session.instanceId || identity.pid !== session.pid) {
62
- throw new Error('Bridge 实例身份不匹配。');
62
+ throw new Error('桌面服务实例身份不匹配。');
63
63
  }
64
64
  resolve(identity);
65
65
  } catch (error) { reject(error); }
66
66
  });
67
67
  });
68
- request.on('timeout', () => request.destroy(new Error('Bridge 管理通道响应超时。')));
68
+ request.on('timeout', () => request.destroy(new Error('桌面服务管理通道响应超时。')));
69
69
  request.on('error', reject);
70
70
  request.end();
71
71
  });
@@ -2,11 +2,11 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { readRecord } from './service-lifecycle.mjs';
4
4
  import { inspectSkill } from './skill-install.mjs';
5
- import { readManagedEnvironment } from './platform/windows/user-environment.mjs';
6
- import { managedAppServerUrl } from './managed-app-server.mjs';
5
+ import { recoveryStatus } from './shared-recovery.mjs';
7
6
 
8
- export async function doctor({ paths, host, service, skillTarget }) {
7
+ export async function doctor({ paths, host, service, skillTarget, ensureAutostart }) {
9
8
  const checks = [];
9
+ let installedVersion;
10
10
  const add = (id, status, message) => checks.push({ id, status, message });
11
11
  const check = async (id, task) => {
12
12
  try { await task(); } catch (error) { add(id, 'error', error.message); }
@@ -15,49 +15,47 @@ export async function doctor({ paths, host, service, skillTarget }) {
15
15
  const record = readRecord(path.join(paths.serviceRoot, 'installation.json'));
16
16
  if (!record || !fs.existsSync(path.join(paths.serviceRoot, 'dist/bin/codex-remote.mjs'))) throw Error('尚未完整部署服务,请重新安装 Code Relax。');
17
17
  if (!record.nodeExecutable || !fs.existsSync(record.nodeExecutable)) throw Error('记录的 Node 已失效,请用可用的 Node 重新安装 Code Relax。');
18
- add('installation', 'ok', `服务 ${record.version};Node:${record.nodeExecutable}`);
18
+ installedVersion = record.version;
19
+ add('installation', 'ok', `已安装版本:${record.version};Node:${record.nodeExecutable}`);
19
20
  });
20
- let sharedUrl;
21
- await check('shared-service', async () => {
22
- sharedUrl = await host.sharedServiceUrl();
23
- add('shared-service', 'ok', sharedUrl ? '完整控制连接可用。' : '当前使用基础控制。');
24
- });
25
- await check('desktop', async () => {
26
- await host.desktopEnvironment(readRecord(path.join(paths.stateRoot, 'bridge-startup-context.json')), sharedUrl);
27
- add('desktop', 'ok', 'Desktop 来源上下文和工具管道已定位;实际任务操作尚未验证。');
21
+ await check('bridge-runtime', async () => {
22
+ await host.bridgeEnvironment();
23
+ add('bridge-runtime', 'ok', '桌面服务启动环境已就绪;实际任务操作尚未验证。');
28
24
  });
29
25
  await check('network', async () => {
30
26
  const address = await host.lanAddress();
31
27
  if (!address) throw Error('未找到局域网 IPv4;请连接受信任的 Wi-Fi 或有线网络。');
32
28
  add('network', 'ok', `局域网地址:${address}`);
33
29
  });
30
+ await check('autostart', async () => {
31
+ const state = await ensureAutostart();
32
+ add('autostart', 'ok', `当前用户登录自启:${state.repaired ? '已自动恢复' : '已启用'}。`);
33
+ });
34
34
  await check('service', async () => {
35
35
  const state = await service.status();
36
36
  add('service', state.running ? 'ok' : 'warning', state.running
37
- ? `本机服务正常:${state.mode},端口 ${state.port}` : '服务未就绪;安装后执行 relax start。');
37
+ ? `本机服务正常:${state.mode},端口 ${state.port},运行版本:${state.version || '未知'}` : '服务未就绪;安装后执行 relax start。');
38
+ if (state.running && installedVersion && state.version !== installedVersion) add('service-version', 'warning', state.version
39
+ ? `已安装版本 ${installedVersion},但当前运行版本为 ${state.version};请重新执行安装或重启工作站服务。`
40
+ : `已安装版本 ${installedVersion},但当前运行的桌面服务未报告版本,可能仍是旧进程;请重新执行安装或重启桌面服务。`);
38
41
  if (state.running) add('managed-app-server', state.managedAppServer?.state === 'ready' ? 'ok' : 'error',
39
- state.managedAppServer?.state === 'ready' ? '受管 app-server 已就绪。'
40
- : state.managedAppServer?.error || '受管 app-server 尚未就绪。');
42
+ state.managedAppServer?.state === 'ready' ? 'Codex 内核已就绪。'
43
+ : state.managedAppServer?.error || 'Codex 内核尚未就绪。');
41
44
  if (state.managedAppServer?.updatePending) add('managed-app-server-update', 'warning',
42
- 'Codex Desktop 已提供新版 CLI;当前 app-server 会继续完成任务,并在下次进程自然重启或重新登录 Windows 后采用新版。');
45
+ '已选择另一版本 CLI;当前 app-server 继续完成任务,下次内核启动时采用选定版本。');
46
+ if (state.managedAppServer?.launcherRestartRequired) add('managed-app-server-launcher', 'warning',
47
+ '安装文件已经更新,但当前 app-server 仍由旧启动链创建;隐藏控制台等运行时修复尚未生效,请执行 relax setup 或重启 Windows。');
43
48
  });
44
- await check('desktop-environment', async () => {
45
- const environment = await readManagedEnvironment();
46
- if (environment.forceCliUser === '1' || environment.forceCliMachine === '1') {
47
- add('desktop-environment', 'error', 'CODEX_APP_SERVER_FORCE_CLI=1 会阻止 Desktop 使用受管 app-server。');
48
- } else if (environment.userValue === managedAppServerUrl) {
49
- add('desktop-environment', 'ok', `Desktop 后续启动地址:${managedAppServerUrl}`);
50
- } else {
51
- add('desktop-environment', 'warning', environment.userValue
52
- ? `用户配置了其他 app-server 地址:${environment.userValue}`
53
- : '尚未配置 Desktop 后续启动地址;请重新执行 setup。');
54
- }
49
+ await check('desktop-recovery', async () => {
50
+ const state = await recoveryStatus(paths.stateRoot);
51
+ add('desktop-recovery', state.status === 'failed' ? 'warning' : 'ok',
52
+ `最近 Desktop 接入:${state.message || state.status}${state.error ? `;${state.error}` : ''}`);
55
53
  });
56
54
  await check('skill', async () => {
57
55
  const state = inspectSkill(skillTarget);
58
56
  add('skill', state.managed ? 'ok' : 'warning', state.managed ? '本包管理的 Skill 已安装。'
59
- : state.installed ? '存在同名自定义 Skill,本包不会覆盖。' : '可执行 relax skill-install 安装 Skill。');
57
+ : state.installed ? '存在同名自定义 Skill,本包不会覆盖。' : 'Skill 未安装;重新执行 relax setup。');
60
58
  });
61
- add('firewall', 'warning', '手机连通性未验证。首次启动请允许记录的 Node 在受信任的专用网络通信;若曾拒绝,可在 Windows 安全中心的防火墙“允许应用通过防火墙”中检查该 Node。不要关闭整个防火墙。');
59
+ add('firewall', 'warning', '移动端连通性未验证。首次启动请允许记录的 Node 在受信任的专用网络通信;若曾拒绝,可在 Windows 安全中心的防火墙“允许应用通过防火墙”中检查该 Node。不要关闭整个防火墙。');
62
60
  return { ready: !checks.some(item => item.status === 'error'), lanAccess: 'unverified', checks };
63
61
  }
@@ -14,7 +14,7 @@ export function serviceHealth(session) {
14
14
  socket.terminate();
15
15
  if (error) reject(error); else resolve(value);
16
16
  };
17
- const timer = setTimeout(() => finish(new Error('Bridge 健康检查超时。')), 6000);
17
+ const timer = setTimeout(() => finish(new Error('桌面服务健康检查超时。')), 6000);
18
18
  socket.on('open', () => socket.send(JSON.stringify({ type: 'auth', token })));
19
19
  socket.on('message', data => {
20
20
  try {
@@ -28,6 +28,6 @@ export function serviceHealth(session) {
28
28
  });
29
29
  socket.on('error', error => finish(error.code === 'ECONNREFUSED' ? null : error));
30
30
  socket.on('close', code => finish(new Error(code === 1008
31
- ? 'Bridge 健康检查鉴权失败。' : 'Bridge 健康检查连接已断开。')));
31
+ ? '桌面服务健康检查鉴权失败。' : '桌面服务健康检查连接已断开。')));
32
32
  });
33
33
  }
@@ -22,8 +22,6 @@ export async function installService(source, target, executable, stateRoot, hook
22
22
  if (!fs.existsSync(path.join(source, 'dist/bin/codex-remote.mjs'))) throw new Error('缺少发布构建;源码目录请先执行 npm run build。');
23
23
  if (!fs.existsSync(path.join(source, 'node_modules'))) throw new Error('缺少包依赖,请重新安装完整 npm 包。');
24
24
  const parent = path.dirname(target);
25
- const legacyUploadsRoots = readRecord(path.join(target, 'installation.json'))?.legacyUploadsRoots || [];
26
- if (fs.existsSync(path.join(source, 'src/server.mjs'))) legacyUploadsRoots.push(path.join(source, 'uploads'));
27
25
  fs.mkdirSync(parent, { recursive: true });
28
26
  const stage = path.join(parent, `service-stage-${randomUUID()}`);
29
27
  const backup = path.join(parent, `service-backup-${randomUUID()}`);
@@ -34,8 +32,7 @@ export async function installService(source, target, executable, stateRoot, hook
34
32
  }
35
33
  await exec(executable, [path.join(stage, 'dist/bin/codex-remote.mjs'), '--help'], { windowsHide: true });
36
34
  await exec(executable, ['-e', 'const r=require("node:module").createRequire(process.argv[1]); r("sharp"); r("qrcode"); r("ws"); r("node-datachannel");', path.join(stage, 'package.json')], { windowsHide: true });
37
- writeRecord(path.join(stage, 'installation.json'), { installedAt: new Date().toISOString(), source, nodeExecutable: executable, stateRoot,
38
- legacyUploadsRoots: [...new Set(legacyUploadsRoots)],
35
+ writeRecord(path.join(stage, 'installation.json'), { installedAt: new Date().toISOString(), source, nodeExecutable: executable, stateRoot,
39
36
  version: readRecord(path.join(source, 'package.json')).version });
40
37
  const previous = fs.existsSync(target);
41
38
  let previousMoved = false, activated = false;
@@ -23,12 +23,11 @@ export function lifecycle({ stateRoot, host }) {
23
23
  fs.mkdirSync(stateRoot, { recursive: true });
24
24
  const sessionPath = path.join(stateRoot, 'bridge-session.json');
25
25
  const configPath = path.join(stateRoot, 'bridge-config.json');
26
- const contextPath = path.join(stateRoot, 'bridge-startup-context.json');
27
26
  const scriptPath = fileURLToPath(new URL('./server.mjs', import.meta.url));
28
27
 
29
28
  async function alive(session) {
30
29
  if (!session?.controlPort || !session.instanceId || !session.controlSecret) {
31
- if (await health(session)) throw new Error('当前为旧版 Bridge,请先使用旧版停止入口结束服务,再启动新版。');
30
+ if (await health(session)) throw new Error('当前为旧版桌面服务,请先使用旧版停止入口结束服务,再启动新版。');
32
31
  return false;
33
32
  }
34
33
  try { await controlRequest(session); return true; }
@@ -36,7 +35,7 @@ export function lifecycle({ stateRoot, host }) {
36
35
  }
37
36
  async function stop() {
38
37
  const session = readRecord(sessionPath);
39
- if (!await alive(session)) return { stopped: true, message: 'Bridge 已停止。' };
38
+ if (!await alive(session)) return { stopped: true, message: '桌面服务已停止。' };
40
39
  await controlRequest(session, 'POST');
41
40
  const deadline = Date.now() + 10000;
42
41
  while (true) {
@@ -46,22 +45,22 @@ export function lifecycle({ stateRoot, host }) {
46
45
  // Verify refusal on the next connection; never resend the shutdown request.
47
46
  if (error.code !== 'ECONNRESET') throw error;
48
47
  }
49
- if (Date.now() > deadline) throw new Error('Bridge 未在 10 秒内退出;未强杀进程。');
48
+ if (Date.now() > deadline) throw new Error('桌面服务未在 10 秒内退出;未强杀进程。');
50
49
  await delay(100);
51
50
  }
52
- return { stopped: true, message: 'Bridge 已正常关闭。' };
51
+ return { stopped: true, message: '桌面服务已正常关闭。' };
53
52
  }
54
53
  async function status() {
55
54
  const session = readRecord(sessionPath);
56
55
  const running = await alive(session);
57
56
  const result = running ? await health(session) : null;
58
57
  return { running: running && result?.status === 'ok', processRunning: running, pid: session?.pid,
59
- port: session?.port, mode: result?.transportMode, execution: result?.execution,
58
+ port: session?.port, mode: result?.transportMode, version: result?.version, execution: result?.execution,
60
59
  managedAppServer: result?.managedAppServer, desktop: result?.desktop,
61
60
  desktopRestartRecommended: result?.desktopRestartRecommended === true,
62
61
  resident: result?.resident === true };
63
62
  }
64
- async function pair() {
63
+ async function pair() {
65
64
  const session = readRecord(sessionPath);
66
65
  const connection = await alive(session) ? await health(session) : null;
67
66
  if (connection?.status !== 'ok' || !session.lan) {
@@ -76,15 +75,17 @@ export function lifecycle({ stateRoot, host }) {
76
75
  execution: connection.execution, managedAppServer: connection.managedAppServer, desktop: connection.desktop,
77
76
  desktopRestartRecommended: connection.desktopRestartRecommended === true, accessUrl: session.accessUrl,
78
77
  qrPath: path.join(stateRoot, 'connection-qr.png') };
79
- }
78
+ }
79
+ async function restartManagedAppServer() {
80
+ const session = readRecord(sessionPath);
81
+ if (!await alive(session)) throw new Error('桌面服务尚未运行,无法迁移 Codex 内核。');
82
+ return controlRequest(session, 'POST', 'restart-managed-app-server');
83
+ }
80
84
  async function start() {
81
85
  let session = readRecord(sessionPath);
82
86
  if (await alive(session)) {
83
87
  const result = await health(session);
84
88
  if (session.scriptPath === scriptPath && session.lan && result?.status === 'ok' && result.resident) {
85
- const context = readRecord(contextPath) || {};
86
- const sourceThreadId = process.env.CODEX_THREAD_ID || process.env.CODEX_SESSION_ID || context.sourceThreadId;
87
- writeRecord(contextPath, { sourceThreadId });
88
89
  await controlRequest(session, 'POST', 'refresh-execution');
89
90
  return pair();
90
91
  }
@@ -97,9 +98,8 @@ export function lifecycle({ stateRoot, host }) {
97
98
  throw new Error('bridge-config.json 中端口或 Token 无效;不会自动重置凭据。');
98
99
  }
99
100
  const address = await host.lanAddress() || '127.0.0.1';
100
- const { executable, environment } = await host.desktopEnvironment(readRecord(contextPath), '');
101
- writeRecord(configPath, config);
102
- writeRecord(contextPath, { sourceThreadId: environment.CODEX_THREAD_ID || environment.CODEX_SESSION_ID });
101
+ const { executable, environment } = await host.bridgeEnvironment();
102
+ writeRecord(configPath, config);
103
103
  const instanceId = randomUUID();
104
104
  const controlSecret = randomBytes(32).toString('base64url');
105
105
  const readyPath = path.join(stateRoot, `bridge-ready-${instanceId}.json`);
@@ -110,11 +110,11 @@ export function lifecycle({ stateRoot, host }) {
110
110
  const deadline = Date.now() + 10000;
111
111
  let ready;
112
112
  while (!(ready = readRecord(readyPath))) {
113
- if (!processAlive(pid)) throw new Error('Bridge 启动失败,请查看 bridge.stderr.log。');
114
- if (Date.now() > deadline) throw new Error('Bridge 未在 10 秒内就绪。');
113
+ if (!processAlive(pid)) throw new Error('桌面服务启动失败,请查看 bridge.stderr.log。');
114
+ if (Date.now() > deadline) throw new Error('桌面服务未在 10 秒内就绪。');
115
115
  await delay(100);
116
116
  }
117
- if (ready.instanceId !== instanceId || ready.pid !== pid) throw new Error('Bridge 启动实例不匹配。');
117
+ if (ready.instanceId !== instanceId || ready.pid !== pid) throw new Error('桌面服务启动实例不匹配。');
118
118
  session = { ...ready, controlSecret, executablePath: executable, scriptPath, port: config.port,
119
119
  lan: true, appServer: false, accessUrl: `http://${address}:${config.port}/?token=${config.token}`,
120
120
  startedAt: new Date().toISOString() };
@@ -126,5 +126,5 @@ export function lifecycle({ stateRoot, host }) {
126
126
  throw error;
127
127
  } finally { fs.rmSync(readyPath, { force: true }); }
128
128
  }
129
- return { start, pair, status, stop };
130
- }
129
+ return { start, pair, status, stop, restartManagedAppServer };
130
+ }