@platless/axom-cli 0.3.13 → 0.6.36

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 (320) hide show
  1. package/README.md +18 -5
  2. package/dist/axom-patch/cli/README.md +119 -9
  3. package/dist/axom-patch/cli/account-limits/README.md +27 -0
  4. package/dist/axom-patch/cli/account-limits/antigravity-proxy.mjs +47 -0
  5. package/dist/axom-patch/cli/account-limits/antigravity-read.mjs +90 -0
  6. package/dist/axom-patch/cli/account-limits/antigravity.mjs +46 -0
  7. package/dist/axom-patch/cli/account-limits/generate.py +123 -0
  8. package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +81 -0
  9. package/dist/axom-patch/cli/account-limits/normalize.mjs +151 -0
  10. package/dist/axom-patch/cli/claude-account-limits.json +37 -0
  11. package/dist/axom-patch/cli/codex-account-limits.json +37 -0
  12. package/dist/axom-patch/cli/codex-terminal-output.json +16 -0
  13. package/dist/axom-patch/cli/commandcode-account-limits.json +30 -0
  14. package/dist/axom-patch/cli/kimi-quota.mjs +12 -0
  15. package/dist/axom-patch/cli/kimi-subagents.mjs +173 -0
  16. package/dist/axom-patch/cli/nightly-task.md +194 -0
  17. package/dist/axom-patch/cli/pi-subagent-subwork.json +23 -0
  18. package/dist/axom-patch/cli/probes/claude-late-async-terminal.mjs +5 -1
  19. package/dist/axom-patch/cli/probes/codex-terminal-output.mjs +33 -0
  20. package/dist/axom-patch/cli/probes/kimi-subagent-subwork.mjs +82 -0
  21. package/dist/axom-patch/cli/probes/pi-subagent-subwork.mjs +211 -0
  22. package/dist/axom-patch/cli/probes/zcode-subagent-subwork.mjs +257 -0
  23. package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1591 -0
  24. package/dist/axom-patch/cli/qoder/releases/1.1.75.json +360 -0
  25. package/dist/axom-patch/cli/qoder-background-terminal.md +13 -5
  26. package/dist/axom-patch/cli/qoder-foreground-output.md +14 -7
  27. package/dist/axom-patch/cli/qoder-native-acp.json +227 -0
  28. package/dist/axom-patch/cli/qoder-steering.md +20 -25
  29. package/dist/axom-patch/cli/qoder-subagent-subwork.md +17 -68
  30. package/dist/axom-patch/cli/runtime.json +39 -15
  31. package/dist/axom-patch/cli/zcode-account-limits.json +30 -0
  32. package/dist/axom-patch/cli/zcode-models.json +37 -0
  33. package/dist/axom-patch/cli/zcode-subagent-subwork.json +72 -0
  34. package/dist/cli.js +4 -9
  35. package/dist/commands/knowledge.js +47 -0
  36. package/dist/commands/update.js +65 -3
  37. package/dist/commands/workstation.js +73 -7
  38. package/dist/install/claude/commands.js +13 -13
  39. package/dist/keepers/acp/keeper.mjs +3083 -0
  40. package/dist/keepers/acp/manifest.json +9 -0
  41. package/dist/keepers/terminal/keeper.mjs +8469 -0
  42. package/dist/keepers/terminal/manifest.json +9 -0
  43. package/dist/keepers/terminal/node_modules/node-pty/lib/conpty_console_list_agent.js +25 -0
  44. package/dist/keepers/terminal/node_modules/node-pty/lib/eventEmitter2.js +47 -0
  45. package/dist/keepers/terminal/node_modules/node-pty/lib/index.js +52 -0
  46. package/dist/keepers/terminal/node_modules/node-pty/lib/interfaces.js +7 -0
  47. package/dist/keepers/terminal/node_modules/node-pty/lib/shared/conout.js +10 -0
  48. package/dist/keepers/terminal/node_modules/node-pty/lib/terminal.js +190 -0
  49. package/dist/keepers/terminal/node_modules/node-pty/lib/types.js +7 -0
  50. package/dist/keepers/terminal/node_modules/node-pty/lib/unixTerminal.js +351 -0
  51. package/dist/keepers/terminal/node_modules/node-pty/lib/utils.js +38 -0
  52. package/dist/keepers/terminal/node_modules/node-pty/lib/windowsConoutConnection.js +131 -0
  53. package/dist/keepers/terminal/node_modules/node-pty/lib/windowsPtyAgent.js +344 -0
  54. package/dist/keepers/terminal/node_modules/node-pty/lib/windowsTerminal.js +209 -0
  55. package/dist/keepers/terminal/node_modules/node-pty/lib/worker/conoutSocketWorker.js +22 -0
  56. package/dist/keepers/terminal/node_modules/node-pty/package.json +66 -0
  57. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
  58. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
  59. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-x64/pty.node +0 -0
  60. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
  61. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/linux-arm64/pty.node +0 -0
  62. package/dist/keepers/terminal/node_modules/node-pty/prebuilds/linux-x64/pty.node +0 -0
  63. package/dist/mcp.js +1 -1
  64. package/dist/native/darwin-arm64/axom-quic +0 -0
  65. package/dist/test/keeperKit.js +67 -0
  66. package/dist/workstation/acp/acpAgents.js +2192 -636
  67. package/dist/workstation/acp/acpClient.js +84 -495
  68. package/dist/workstation/acp/adoption.js +10 -396
  69. package/dist/workstation/acp/binaryInstall.js +100 -27
  70. package/dist/workstation/acp/builds/gc.js +245 -0
  71. package/dist/workstation/acp/builds/install/binary.js +28 -0
  72. package/dist/workstation/acp/builds/install/npm.js +324 -0
  73. package/dist/workstation/acp/builds/install/uv.js +254 -0
  74. package/dist/workstation/acp/builds/migrate.js +195 -0
  75. package/dist/workstation/acp/builds/model.js +128 -0
  76. package/dist/workstation/acp/builds/nodeGate.js +100 -0
  77. package/dist/workstation/acp/builds/store.js +352 -0
  78. package/dist/workstation/acp/commandTableProbe.js +68 -0
  79. package/dist/workstation/acp/configOptions.js +103 -1
  80. package/dist/workstation/acp/envSecrets.js +27 -0
  81. package/dist/workstation/acp/initializeResponse.js +290 -0
  82. package/dist/workstation/acp/installErrors.js +12 -2
  83. package/dist/workstation/acp/installProbe.js +1 -1
  84. package/dist/workstation/acp/managedPatches.js +239 -159
  85. package/dist/workstation/acp/managedRuntime.js +314 -0
  86. package/dist/workstation/acp/outbox.js +330 -0
  87. package/dist/workstation/acp/patchAssets.js +60 -3
  88. package/dist/workstation/acp/permission.js +42 -10
  89. package/dist/workstation/acp/processor/checkpoint.js +78 -0
  90. package/dist/workstation/acp/processor/commands.js +35 -0
  91. package/dist/workstation/acp/processor/control.js +282 -0
  92. package/dist/workstation/acp/processor/emit.js +26 -0
  93. package/dist/workstation/acp/processor/jsonrpc.js +172 -0
  94. package/dist/workstation/acp/processor/memoryLog.js +49 -0
  95. package/dist/workstation/acp/processor/origins.js +48 -0
  96. package/dist/workstation/acp/processor/output.js +67 -0
  97. package/dist/workstation/acp/processor/projection.js +42 -0
  98. package/dist/workstation/acp/processor/record.js +128 -0
  99. package/dist/workstation/acp/processor/runtime.js +105 -0
  100. package/dist/workstation/acp/processor/sdkSchemas.js +61 -0
  101. package/dist/workstation/acp/processor/session.js +496 -0
  102. package/dist/workstation/acp/processor/state.js +67 -0
  103. package/dist/workstation/acp/processor/step.js +442 -0
  104. package/dist/workstation/acp/processor/test/fixtureLog.js +58 -0
  105. package/dist/workstation/acp/processor/test/harness.js +120 -0
  106. package/dist/workstation/acp/processor/test/largeIdEcho.js +50 -0
  107. package/dist/workstation/acp/processor/test/rawAgent.js +70 -0
  108. package/dist/workstation/acp/processor/transport/generations.js +76 -0
  109. package/dist/workstation/acp/processor/transport/keeper.js +130 -0
  110. package/dist/workstation/acp/processor/transport/local.js +269 -0
  111. package/dist/workstation/acp/processor/transport/types.js +78 -0
  112. package/dist/workstation/acp/processor/updates.js +390 -0
  113. package/dist/workstation/acp/processorSession.js +1348 -0
  114. package/dist/workstation/acp/registry/catalog.js +189 -41
  115. package/dist/workstation/acp/registry/launch.js +76 -119
  116. package/dist/workstation/acp/sessionUpdateMapper.js +18 -7
  117. package/dist/workstation/acp/steering.js +58 -0
  118. package/dist/workstation/acp/switching.js +111 -0
  119. package/dist/workstation/acp/terminalLogin.js +16 -42
  120. package/dist/workstation/acp/terminalLoginSpec.js +41 -0
  121. package/dist/workstation/acp/test/adapterFixtures.js +256 -0
  122. package/dist/workstation/acp/test/daemonLife.js +106 -0
  123. package/dist/workstation/acp/test/fakeAgent.js +13 -0
  124. package/dist/workstation/acp/test/keeperWorld.js +295 -0
  125. package/dist/workstation/acp/test/memoryOutbox.js +55 -0
  126. package/dist/workstation/acp/test/raw/fixtures.js +196 -0
  127. package/dist/workstation/acp/test/raw/sanitize.js +344 -0
  128. package/dist/workstation/acp/test/raw/sources.js +135 -0
  129. package/dist/workstation/acp/test/realAdapter.js +331 -0
  130. package/dist/workstation/acp/test/scriptAgent.js +214 -0
  131. package/dist/workstation/acp/test/sessionFrames.js +42 -0
  132. package/dist/workstation/acp/timedCommand.js +4 -2
  133. package/dist/workstation/acp/uvxInstall.js +72 -122
  134. package/dist/workstation/agentFlow.js +103 -0
  135. package/dist/workstation/agentSessions.js +1289 -544
  136. package/dist/workstation/channels/agent.js +7 -5
  137. package/dist/workstation/channels/computer.js +69 -21
  138. package/dist/workstation/channels/computerExecs.js +200 -0
  139. package/dist/workstation/channels/management.js +172 -8
  140. package/dist/workstation/channels/url.js +1 -1
  141. package/dist/workstation/config.js +16 -4
  142. package/dist/workstation/connection/encryptedChannel.js +4 -1
  143. package/dist/workstation/control/bindingStore.js +200 -0
  144. package/dist/workstation/control/commands.js +124 -0
  145. package/dist/workstation/control/daemonIdentity.js +53 -0
  146. package/dist/workstation/control/enrollment.js +106 -0
  147. package/dist/workstation/control/inspect.js +71 -0
  148. package/dist/workstation/control/locks.js +63 -0
  149. package/dist/workstation/control/protocol.js +40 -0
  150. package/dist/workstation/control/restartGuard.js +46 -0
  151. package/dist/workstation/control/runtimeClient.js +97 -0
  152. package/dist/workstation/control/runtimeHost.js +148 -0
  153. package/dist/workstation/control/serverIdentity.js +35 -0
  154. package/dist/workstation/daemon.js +393 -12
  155. package/dist/workstation/daemonctl.js +103 -14
  156. package/dist/workstation/desktopControl/audio.js +99 -0
  157. package/dist/workstation/desktopControl/capabilities.js +92 -0
  158. package/dist/workstation/desktopControl/captureModes.js +9 -0
  159. package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
  160. package/dist/workstation/desktopControl/deviceSockets.js +246 -0
  161. package/dist/workstation/desktopControl/helperProcess.js +353 -0
  162. package/dist/workstation/desktopControl/lobby.js +129 -0
  163. package/dist/workstation/desktopControl/macProvider.js +155 -0
  164. package/dist/workstation/desktopControl/media.js +331 -0
  165. package/dist/workstation/desktopControl/nativeProvider.js +1018 -0
  166. package/dist/workstation/desktopControl/networkEvidence.js +43 -0
  167. package/dist/workstation/desktopControl/overview.js +58 -0
  168. package/dist/workstation/desktopControl/protocol.js +302 -0
  169. package/dist/workstation/desktopControl/quicMedia.js +240 -0
  170. package/dist/workstation/desktopControl/rateController.js +226 -0
  171. package/dist/workstation/desktopControl/supervisor.js +1681 -0
  172. package/dist/workstation/desktopControl/transport.js +27 -0
  173. package/dist/workstation/desktopControl/wallpaper.js +78 -0
  174. package/dist/workstation/exec/attach.js +126 -0
  175. package/dist/workstation/exec/command.js +40 -0
  176. package/dist/workstation/exec/durable.js +517 -0
  177. package/dist/workstation/exec/fallback.js +106 -0
  178. package/dist/workstation/exec/policy.js +837 -0
  179. package/dist/workstation/exec/shaping.js +211 -0
  180. package/dist/workstation/exec.js +3 -112
  181. package/dist/workstation/files/adapters/computerFileFrames.js +51 -0
  182. package/dist/workstation/files/adapters/toolRequests.js +121 -0
  183. package/dist/workstation/files/client.js +455 -0
  184. package/dist/workstation/files/credentials.js +142 -0
  185. package/dist/workstation/files/errors.js +28 -0
  186. package/dist/workstation/files/fence.js +350 -0
  187. package/dist/workstation/files/find/fileIndex.js +153 -0
  188. package/dist/workstation/files/find/fuzzy.js +155 -0
  189. package/dist/workstation/files/host.js +122 -0
  190. package/dist/workstation/files/hostProtocol.js +2 -0
  191. package/dist/workstation/files/media/cache.js +620 -0
  192. package/dist/workstation/files/media/encoding.js +308 -0
  193. package/dist/workstation/files/media/estimator.js +100 -0
  194. package/dist/workstation/files/media/media.js +325 -0
  195. package/dist/workstation/files/media/packedAudio.js +23 -0
  196. package/dist/workstation/files/media/scheduler.js +551 -0
  197. package/dist/workstation/files/media/types.js +5 -0
  198. package/dist/workstation/files/scope.js +45 -0
  199. package/dist/workstation/files/structured.js +284 -0
  200. package/dist/workstation/{fileOps.js → files/text.js} +152 -39
  201. package/dist/workstation/files/uploads.js +351 -0
  202. package/dist/workstation/journal.js +65 -236
  203. package/dist/workstation/keepers/acp/client.js +101 -0
  204. package/dist/workstation/keepers/acp/envelope.js +62 -0
  205. package/dist/workstation/keepers/acp/host.js +419 -0
  206. package/dist/workstation/keepers/acp/main.js +18 -0
  207. package/dist/workstation/keepers/acp/memoryStream.js +121 -0
  208. package/dist/workstation/keepers/acp/params.js +137 -0
  209. package/dist/workstation/keepers/acp/protocol.js +66 -0
  210. package/dist/workstation/keepers/acp/recover.js +120 -0
  211. package/dist/workstation/keepers/acp/session.js +584 -0
  212. package/dist/workstation/keepers/acp/stderr.js +154 -0
  213. package/dist/workstation/keepers/kit/activity.js +103 -0
  214. package/dist/workstation/keepers/kit/bootstrap.js +164 -0
  215. package/dist/workstation/keepers/kit/buildVersion.js +51 -0
  216. package/dist/workstation/keepers/kit/bundle.js +124 -0
  217. package/dist/workstation/keepers/kit/client.js +432 -0
  218. package/dist/workstation/keepers/kit/identity.js +21 -0
  219. package/dist/workstation/keepers/kit/ipc.js +112 -0
  220. package/dist/workstation/keepers/kit/launchConfig.js +8 -0
  221. package/dist/workstation/keepers/kit/launcher.js +289 -0
  222. package/dist/workstation/keepers/kit/lock.js +123 -0
  223. package/dist/workstation/keepers/kit/orphanSweep.js +86 -0
  224. package/dist/workstation/keepers/kit/paths.js +76 -0
  225. package/dist/workstation/keepers/kit/protocol.js +43 -0
  226. package/dist/workstation/keepers/kit/server.js +518 -0
  227. package/dist/workstation/keepers/kit/streamLog.js +697 -0
  228. package/dist/workstation/keepers/legacy.js +296 -0
  229. package/dist/workstation/keepers/policy.js +576 -0
  230. package/dist/workstation/keepers/stop.js +34 -0
  231. package/dist/workstation/keepers/terminal/client.js +213 -0
  232. package/dist/workstation/keepers/terminal/exec.js +981 -0
  233. package/dist/workstation/keepers/terminal/generations.js +129 -0
  234. package/dist/workstation/keepers/terminal/keeper.js +62 -0
  235. package/dist/workstation/keepers/terminal/main.js +43 -0
  236. package/dist/workstation/keepers/terminal/orphanSweep.js +3 -0
  237. package/dist/workstation/keepers/terminal/params.js +27 -0
  238. package/dist/workstation/keepers/terminal/protocol.js +56 -0
  239. package/dist/workstation/keepers/terminal/pty.js +330 -0
  240. package/dist/workstation/managedInstall.js +57 -0
  241. package/dist/workstation/protocol/accountLimits.js +47 -0
  242. package/dist/workstation/protocol/agent.js +50 -19
  243. package/dist/workstation/protocol/common.js +30 -0
  244. package/dist/workstation/protocol/computer.js +15 -4
  245. package/dist/workstation/protocol/management.js +141 -4
  246. package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
  247. package/dist/workstation/rd/artifact.js +119 -0
  248. package/dist/workstation/rd/control.js +119 -0
  249. package/dist/workstation/rd/display.js +29 -0
  250. package/dist/workstation/rd/provider.js +222 -0
  251. package/dist/workstation/rd/sessions.js +391 -0
  252. package/dist/workstation/rd/state.js +45 -0
  253. package/dist/workstation/rd/update.js +171 -0
  254. package/dist/workstation/rd/upkeep.js +38 -0
  255. package/dist/workstation/register.js +27 -37
  256. package/dist/workstation/service/installer.js +104 -13
  257. package/dist/workstation/service/launchd.js +1 -0
  258. package/dist/workstation/storageMetrics.js +118 -0
  259. package/dist/workstation/systemMetrics.js +154 -93
  260. package/dist/workstation/toolchain.js +94 -0
  261. package/dist/workstation/tools/browser.js +28 -4
  262. package/dist/workstation/tools/client.js +9 -1
  263. package/dist/workstation/tools/info.js +50 -0
  264. package/dist/workstation/tools/keeperProcess.js +15 -5
  265. package/dist/workstation/tools/router.js +55 -0
  266. package/dist/workstation/tools/terminalRouter.js +222 -0
  267. package/dist/workstation/tools/validation.js +37 -1
  268. package/dist/workstation/update/artifact.js +2 -1
  269. package/dist/workstation/update/coordinator.js +3 -0
  270. package/dist/workstation/update/globalInstall.js +116 -0
  271. package/dist/workstation/update/npmInstaller.js +49 -23
  272. package/dist/workstation/update/ptyPreflight.js +21 -0
  273. package/dist/workstation/update/rdRollback.js +145 -0
  274. package/dist/workstation/update/runUpdate.js +3 -2
  275. package/dist/workstation/update/taskRegistry.js +105 -11
  276. package/dist/workstation/update/watchdog.js +16790 -334
  277. package/package.json +11 -8
  278. package/skills/axom/SKILL.md +11 -11
  279. package/skills/axom-definition/SKILL.md +3 -3
  280. package/skills/axom-platform/SKILL.md +3 -3
  281. package/dist/axom-patch/cli/probes/qoder-steering.mjs +0 -89
  282. package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +0 -49
  283. package/dist/axom-patch/cli/qoder-steering.json +0 -52
  284. package/dist/axom-patch/cli/qoder-subagent-origin.json +0 -73
  285. package/dist/commands/guide.js +0 -38
  286. package/dist/workstation/acp/acpSession.js +0 -1475
  287. package/dist/workstation/acp/contract.js +0 -499
  288. package/dist/workstation/acp/delegationMcp.js +0 -24
  289. package/dist/workstation/acp/draftInterceptor.js +0 -163
  290. package/dist/workstation/acp/qoderStartup.js +0 -45
  291. package/dist/workstation/acp/sessionScope.js +0 -19
  292. package/dist/workstation/filePaths.js +0 -174
  293. package/dist/workstation/fileWorker.js +0 -81
  294. package/dist/workstation/fileWorkerClient.js +0 -121
  295. package/dist/workstation/qoderAdapter/backgroundProcess.js +0 -106
  296. package/dist/workstation/qoderAdapter/backgroundTerminal.js +0 -262
  297. package/dist/workstation/qoderAdapter/completedContinuation.js +0 -139
  298. package/dist/workstation/qoderAdapter/contract.js +0 -78
  299. package/dist/workstation/qoderAdapter/foregroundOutput.js +0 -30
  300. package/dist/workstation/qoderAdapter/history.js +0 -87
  301. package/dist/workstation/qoderAdapter/identityIndex.js +0 -191
  302. package/dist/workstation/qoderAdapter/inputQueue.js +0 -42
  303. package/dist/workstation/qoderAdapter/main.js +0 -93
  304. package/dist/workstation/qoderAdapter/mapper.js +0 -117
  305. package/dist/workstation/qoderAdapter/observerPlugin.js +0 -245
  306. package/dist/workstation/qoderAdapter/observerReceiver.js +0 -187
  307. package/dist/workstation/qoderAdapter/permissions.js +0 -61
  308. package/dist/workstation/qoderAdapter/profile.js +0 -93
  309. package/dist/workstation/qoderAdapter/sdkDriver.js +0 -49
  310. package/dist/workstation/qoderAdapter/session.js +0 -319
  311. package/dist/workstation/qoderAdapter/subagentMeta.js +0 -28
  312. package/dist/workstation/qoderAdapter/subwork.js +0 -77
  313. package/dist/workstation/qoderAdapter/taskControl.js +0 -38
  314. package/dist/workstation/qoderAdapter/taskEvent.js +0 -26
  315. package/dist/workstation/qoderAdapter/taskLedger.js +0 -163
  316. package/dist/workstation/qoderAdapter/usage.js +0 -37
  317. package/dist/workstation/tools/core.js +0 -50
  318. package/dist/workstation/tools/files.js +0 -525
  319. package/dist/workstation/tools/terminal.js +0 -214
  320. /package/dist/workstation/{qoderAdapter/types.js → desktopControl/provider.js} +0 -0
@@ -0,0 +1,119 @@
1
+ import { promises as fs, constants } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createHash } from 'node:crypto';
4
+ import { gunzipSync } from 'node:zlib';
5
+ import { z } from 'zod';
6
+ import { deploymentDownloadURL } from '../control/serverIdentity.js';
7
+ import { downloadVerified } from '../update/artifact.js';
8
+ import { privateDirectory } from '../keepers/kit/paths.js';
9
+ const legacyRdFiles = ['bin/axom-desktop-keeper', 'bin/axom-input-guardian', 'bin/axom-quic', 'xorg.conf'];
10
+ export const rdFiles = [...legacyRdFiles, 'bin/axom-display', 'bin/axom-desktop-session'];
11
+ const sha256 = z.string().regex(/^[a-f0-9]{64}$/);
12
+ export const rdArtifactSchema = z.object({ version: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._+-]{0,127}$/),
13
+ filename: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._+-]*\.(?:tar\.gz|tgz)$/),
14
+ url: z.string(), size: z.number().int().min(1).max(134217728), sha512: z.string().regex(/^[a-f0-9]{128}$/),
15
+ unpackedSize: z.number().int().min(1).max(268435456), ipcVersion: z.union([z.literal(1), z.literal(2)]),
16
+ files: z.record(z.string(), sha256),
17
+ }).refine(artifact => {
18
+ const files = artifact.ipcVersion === 1 ? legacyRdFiles : rdFiles;
19
+ const licensed = artifact.ipcVersion === 2 && 'opus-LICENSE' in artifact.files;
20
+ return Object.keys(artifact.files).length === files.length + (licensed ? 1 : 0) && files.every(file => file in artifact.files);
21
+ }, 'RD artifact files do not match its runtime version');
22
+ export function rdPlatform(osRelease, architecture) {
23
+ const values = Object.fromEntries(osRelease.split('\n').map(line => /^([A-Z_]+)=["']?([^"']*)["']?$/.exec(line)).filter(Boolean).map(match => [match[1], match[2]]));
24
+ if (values.ID !== 'debian' || values.VERSION_ID !== '13' || architecture !== 'x64') {
25
+ throw new Error('Remote desktop currently supports Debian 13 amd64; this distribution/architecture has no qualified RD package');
26
+ }
27
+ return 'linux-x64-debian-13';
28
+ }
29
+ export function unpackRd(bytes, artifact) {
30
+ const archive = gunzipSync(bytes, { maxOutputLength: artifact.unpackedSize + 65536 });
31
+ const files = new Map();
32
+ let unpackedSize = 0;
33
+ let terminated = false;
34
+ for (let offset = 0; offset + 512 <= archive.length;) {
35
+ const header = archive.subarray(offset, offset + 512);
36
+ if (header.every(byte => byte === 0)) {
37
+ if (offset + 1024 > archive.length || archive.subarray(offset).some(byte => byte !== 0))
38
+ throw new Error('RD archive has an invalid terminator or trailing records');
39
+ terminated = true;
40
+ break;
41
+ }
42
+ const field = (start, length) => header.subarray(start, start + length).toString('utf8').replace(/\0.*$/s, '');
43
+ const name = field(0, 100), prefix = field(345, 155), sizeText = field(124, 12).trim();
44
+ const checksum = field(148, 8).trim();
45
+ const actualChecksum = header.reduce((sum, byte, index) => sum + (index >= 148 && index < 156 ? 32 : byte), 0);
46
+ if (field(257, 6) !== 'ustar' || field(263, 2) !== '00' || !/^[0-7]+$/.test(checksum) || Number.parseInt(checksum, 8) !== actualChecksum)
47
+ throw new Error('RD archive has an invalid USTAR header');
48
+ if (prefix || !/^[0-7]+$/.test(sizeText) || !Object.hasOwn(artifact.files, name) || files.has(name) || ![0, 48].includes(header[156])) {
49
+ throw new Error('RD archive contains an unexpected file, path, link or duplicate');
50
+ }
51
+ const size = Number.parseInt(sizeText, 8);
52
+ offset += 512;
53
+ unpackedSize += size;
54
+ if (!Number.isSafeInteger(size) || offset + size > archive.length || unpackedSize > artifact.unpackedSize)
55
+ throw new Error('RD archive exceeds its declared size');
56
+ const contents = archive.subarray(offset, offset + size);
57
+ if (createHash('sha256').update(contents).digest('hex') !== artifact.files[name])
58
+ throw new Error(`RD component checksum mismatch: ${name}`);
59
+ files.set(name, contents);
60
+ offset += Math.ceil(size / 512) * 512;
61
+ }
62
+ if (!terminated || files.size !== Object.keys(artifact.files).length || unpackedSize !== artifact.unpackedSize)
63
+ throw new Error('RD archive is incomplete');
64
+ return files;
65
+ }
66
+ export async function verifyRdInstallation(directory, artifact) {
67
+ for (const name of Object.keys(rdArtifactSchema.parse(artifact).files)) {
68
+ const file = await fs.open(path.join(directory, name), constants.O_RDONLY | constants.O_NOFOLLOW);
69
+ try {
70
+ const info = await file.stat();
71
+ if (!info.isFile() || info.uid !== process.getuid?.() || info.mode & 0o022 || info.size > artifact.unpackedSize)
72
+ throw new Error('Unsafe RD installation');
73
+ if (createHash('sha256').update(await file.readFile()).digest('hex') !== artifact.files[name])
74
+ throw new Error(`RD component checksum mismatch: ${name}`);
75
+ }
76
+ finally {
77
+ await file.close();
78
+ }
79
+ }
80
+ }
81
+ export async function installRd(directory, server, platform, fetchImpl = globalThis.fetch) {
82
+ const response = await fetchImpl(deploymentDownloadURL(server, '/download/client/desktop/manifest.json'), { redirect: 'error', signal: AbortSignal.timeout(15000) });
83
+ if (!response.ok)
84
+ throw new Error(`The server does not publish Linux RD packages (HTTP ${response.status})`);
85
+ const text = await response.text();
86
+ if (Buffer.byteLength(text) > 1048576)
87
+ throw new Error('RD catalog is too large');
88
+ const catalog = z.object({ schemaVersion: z.literal(1), artifacts: z.record(z.string(), rdArtifactSchema) }).parse(JSON.parse(text));
89
+ const artifact = catalog.artifacts[platform];
90
+ if (!artifact)
91
+ throw new Error(`The server has no RD package for ${platform}`);
92
+ if (artifact.url !== `/download/client/desktop/${artifact.filename}`)
93
+ throw new Error('RD artifact URL does not match its filename');
94
+ const target = path.join(directory, 'rd', 'versions', artifact.sha512.slice(0, 32));
95
+ try {
96
+ await verifyRdInstallation(target, artifact);
97
+ return { directory: target, artifact };
98
+ }
99
+ catch (error) {
100
+ if (error.code !== 'ENOENT')
101
+ throw error;
102
+ }
103
+ const downloaded = await downloadVerified(artifact, { server, fetch: fetchImpl });
104
+ await privateDirectory(path.dirname(target));
105
+ const staging = await fs.mkdtemp(path.join(path.dirname(target), '.install-'));
106
+ try {
107
+ const files = unpackRd(await fs.readFile(downloaded.path), artifact);
108
+ await privateDirectory(path.join(staging, 'bin'));
109
+ for (const [name, contents] of files)
110
+ await fs.writeFile(path.join(staging, name), contents, { flag: 'wx', mode: name.startsWith('bin/') ? 0o700 : 0o600 });
111
+ await verifyRdInstallation(staging, artifact);
112
+ await fs.rename(staging, target);
113
+ return { directory: target, artifact };
114
+ }
115
+ finally {
116
+ await downloaded.cleanup();
117
+ await fs.rm(staging, { recursive: true, force: true });
118
+ }
119
+ }
@@ -0,0 +1,119 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { execFile } from 'node:child_process';
4
+ import { promisify } from 'node:util';
5
+ import { z } from 'zod';
6
+ import { loadConfig, resolveServer } from '../../config.js';
7
+ import { loadClientConfig } from '../config.js';
8
+ import { normalizeWorkstationServer } from '../control/serverIdentity.js';
9
+ import { withPrivateLock } from '../control/locks.js';
10
+ import { installRd, rdPlatform } from './artifact.js';
11
+ import { readRdState, writeRdState } from './state.js';
12
+ import { chooseSession, discoverSessions } from './sessions.js';
13
+ const execute = promisify(execFile);
14
+ export function rdSessionSelection(previous, options) {
15
+ const session = z.enum(['auto', 'existing', 'virtual']).parse(options.rdSession ?? previous?.session ?? 'auto');
16
+ const display = options.rdDisplay ?? (options.rdSession === undefined ? previous?.display : undefined);
17
+ if (display !== undefined && !/^:\d+(?:\.\d+)?$/.test(display))
18
+ throw new Error('--rd-display must be a local X11 display such as :10');
19
+ if (session === 'virtual' && display)
20
+ throw new Error('--rd-display selects an existing session and cannot be combined with --rd-session virtual');
21
+ return { session, display };
22
+ }
23
+ export async function configureRd(directory, options) {
24
+ if (!options.enableRd && !options.disableRd && options.rdSession === undefined && options.rdDisplay === undefined)
25
+ return;
26
+ if (process.platform !== 'linux' || process.getuid?.() === 0)
27
+ throw new Error('Linux RD must be configured and run as the workstation user, not root');
28
+ if (options.enableRd && options.disableRd)
29
+ throw new Error('--enable-rd and --disable-rd cannot be combined');
30
+ if (!options.enableRd && !options.disableRd)
31
+ throw new Error('--rd-session and --rd-display require --enable-rd');
32
+ await withPrivateLock(path.join(directory, 'rd', 'install'), async () => {
33
+ const previous = await readRdState(directory);
34
+ const registered = await loadClientConfig(directory).catch(error => {
35
+ if (error.code !== 'ENOENT')
36
+ throw error;
37
+ return undefined;
38
+ });
39
+ const server = normalizeWorkstationServer(options.server ?? registered?.server ?? resolveServer(await loadConfig()));
40
+ if (options.disableRd) {
41
+ await writeRdState(directory, previous ? { ...previous, enabled: false } : { schemaVersion: 1, enabled: false, server, session: 'auto' });
42
+ return;
43
+ }
44
+ if (registered && normalizeWorkstationServer(registered.server) !== server)
45
+ throw new Error('This workstation is already registered to another server; change its registration before enabling RD');
46
+ const { session, display } = rdSessionSelection(previous, options);
47
+ const platform = rdPlatform(await fs.readFile('/etc/os-release', 'utf8'), process.arch);
48
+ const installed = await installRd(directory, server, platform);
49
+ await ensureDependencies(installed.directory, session, display, installed.artifact);
50
+ await writeRdState(directory, { schemaVersion: 1, enabled: true, server, session, ...(display ? { display } : {}),
51
+ installation: installed.directory, artifact: installed.artifact });
52
+ console.log(`Remote desktop enabled (${session}, ${installed.artifact.version}); capture starts only when an authorized viewer connects`);
53
+ });
54
+ }
55
+ async function ensureDependencies(installation, session, display, artifact) {
56
+ let runtimeMissing = false;
57
+ try {
58
+ await execute(path.join(installation, 'bin/axom-desktop-keeper'), ['--self-test'], { timeout: 5000, maxBuffer: 8192 });
59
+ }
60
+ catch {
61
+ runtimeMissing = true;
62
+ }
63
+ const selected = chooseSession(await discoverSessions(), { session, display });
64
+ const packages = runtimeMissing ? ['libavcodec61', 'libavutil59', 'libswscale8', 'libgstreamer-plugins-bad1.0-0', 'libx11-6', 'libxext6', 'libxdamage1', 'libxfixes3', 'libxtst6', 'libxrandr2', 'libxcomposite1', 'zlib1g'] : [];
65
+ if (artifact?.ipcVersion === 2)
66
+ packages.push('python3', 'x11-xserver-utils', 'x11-utils', 'xauth', 'libpulse0', 'pulseaudio', 'pulseaudio-utils', 'libasound2-plugins');
67
+ if (!selected)
68
+ packages.push('xserver-xorg-core', 'xserver-xorg-video-dummy', 'xfce4-session', 'xfwm4', 'xfce4-panel', 'xfdesktop4', 'xfce4-settings', 'thunar', 'xfce4-terminal', 'dbus-x11', 'xauth', 'x11-utils', 'fonts-dejavu-core');
69
+ if (!packages.length)
70
+ return;
71
+ const missing = [];
72
+ for (const name of packages) {
73
+ try {
74
+ const { stdout } = await execute('dpkg-query', ['-W', '-f=${db:Status-Status}', name], { timeout: 2000, maxBuffer: 4096 });
75
+ if (stdout.trim() !== 'installed')
76
+ missing.push(name);
77
+ }
78
+ catch {
79
+ missing.push(name);
80
+ }
81
+ }
82
+ if (missing.length) {
83
+ console.log(`Installing remote desktop dependencies: ${missing.join(', ')}`);
84
+ try {
85
+ await execute('sudo', ['-n', 'apt-get', 'install', '-y', '--no-install-recommends', ...missing], { timeout: 180000, maxBuffer: 1048576 });
86
+ }
87
+ catch {
88
+ throw new Error(`Install the required RD packages, then retry: sudo apt-get install --no-install-recommends ${missing.join(' ')}`);
89
+ }
90
+ }
91
+ await execute(path.join(installation, 'bin/axom-desktop-keeper'), ['--self-test'], { timeout: 5000, maxBuffer: 8192 });
92
+ if (artifact?.ipcVersion === 2) {
93
+ for (const command of ['axom-display', 'axom-desktop-session'])
94
+ await execute(path.join(installation, 'bin', command), ['--self-test'], { timeout: 5000, maxBuffer: 8192 });
95
+ }
96
+ }
97
+ export async function rdStatus(directory) {
98
+ const state = await readRdState(directory);
99
+ if (!state)
100
+ return { enabled: false, installed: false };
101
+ let observed;
102
+ let update;
103
+ try {
104
+ observed = JSON.parse(await fs.readFile(path.join(directory, 'rd', 'status.json'), 'utf8'));
105
+ }
106
+ catch (error) {
107
+ if (error.code !== 'ENOENT')
108
+ throw error;
109
+ }
110
+ try {
111
+ update = JSON.parse(await fs.readFile(path.join(directory, 'rd', 'update-status.json'), 'utf8'));
112
+ }
113
+ catch (error) {
114
+ if (error.code !== 'ENOENT')
115
+ throw error;
116
+ }
117
+ return { enabled: state.enabled, installed: state.installation !== undefined, version: state.artifact?.version, selection: state.session, server: state.server,
118
+ ...(state.display ? { requestedDisplay: state.display } : {}), ...(state.enabled ? { observed, update } : {}) };
119
+ }
@@ -0,0 +1,29 @@
1
+ import { captureSamplingModes } from '../desktopControl/captureModes.js';
2
+ export function parseXrandr(text) {
3
+ const screen = /current (\d+) x (\d+)/.exec(text);
4
+ const outputs = [...text.matchAll(/^(\S+) connected(?: primary)? (\d+)x(\d+)\+(\d+)\+(\d+).*$/gm)];
5
+ if (!screen || outputs.length !== 1 || outputs[0][4] !== '0' || outputs[0][5] !== '0' ||
6
+ screen[1] !== outputs[0][2] || screen[2] !== outputs[0][3])
7
+ throw new Error('No consistent single-output X11 desktop is available');
8
+ const output = outputs[0];
9
+ const section = text.slice(output.index + output[0].length).split(/^\S/m)[0];
10
+ const modes = [...section.matchAll(/^\s+(\d+)x(\d+)(?:\S*)\s/gm)]
11
+ .map(match => ({ width: Number(match[1]), height: Number(match[2]) }))
12
+ .filter(mode => mode.width >= 2 && mode.height >= 2 && mode.width <= 4096 && mode.height <= 4096 && mode.width % 2 === 0 && mode.height % 2 === 0);
13
+ return { output: output[1], width: Number(output[2]), height: Number(output[3]),
14
+ modes: modes.filter((mode, index) => modes.findIndex(other => other.width === mode.width && other.height === mode.height) === index) };
15
+ }
16
+ export function desktopDisplayControl(target, actual, managed) {
17
+ if (target.kind !== 'window' && target.width === actual.width && target.height === actual.height &&
18
+ target.width % 2 === 0 && target.height % 2 === 0 && !/^rdp\d+$/i.test(actual.output)) {
19
+ if (managed && /^DUMMY\d+$/i.test(actual.output))
20
+ return { ...target, overview: true, resize: 'dynamic',
21
+ resizeRange: { minWidth: 320, minHeight: 320, maxWidth: 4096, maxHeight: 4096 },
22
+ modes: [{ width: 1280, height: 720 }, { width: 1920, height: 1080 }, { width: 1024, height: 768 }, { width: 720, height: 1280 }] };
23
+ if (actual.modes.length > 1)
24
+ return { ...target, overview: true, resize: 'modes', modes: actual.modes };
25
+ }
26
+ return { ...target, overview: true, resize: 'dynamic', captureViewport: true, capturePixelScale: 1,
27
+ resizeRange: { minWidth: 2, minHeight: 2, maxWidth: target.width & ~1, maxHeight: target.height & ~1 },
28
+ modes: captureSamplingModes(target.width, target.height) };
29
+ }
@@ -0,0 +1,222 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { execFile } from 'node:child_process';
4
+ import { promisify } from 'node:util';
5
+ import { z } from 'zod';
6
+ import { NativeProvider } from '../desktopControl/nativeProvider.js';
7
+ import { HelperProcess, NativeCommandError } from '../desktopControl/helperProcess.js';
8
+ import { desktopCapabilitiesSchema } from '../desktopControl/protocol.js';
9
+ import { captureTargetLabel } from '../desktopControl/captureTargetLabel.js';
10
+ import { normalizeWorkstationServer } from '../control/serverIdentity.js';
11
+ import { privateDirectory } from '../keepers/kit/paths.js';
12
+ import { verifyRdInstallation } from './artifact.js';
13
+ import { readRdState } from './state.js';
14
+ import { chooseSession, discoverSessions, ensureManagedSession, sessionAlive } from './sessions.js';
15
+ import { desktopDisplayControl, parseXrandr } from './display.js';
16
+ const execute = promisify(execFile);
17
+ const sourceAxis = z.number().int().min(2).max(4096);
18
+ const geometrySchema = z.object({ width: sourceAxis, height: sourceAxis, audio: z.boolean().optional() });
19
+ const windowSchema = z.array(z.object({ id: z.string().regex(/^[a-f0-9]{32}$/), kind: z.literal('window'), label: z.string().max(4096), width: sourceAxis, height: sourceAxis })).max(127);
20
+ export class LinuxDesktopProvider {
21
+ directory;
22
+ server;
23
+ legacy;
24
+ session;
25
+ provider;
26
+ state;
27
+ runtime = '';
28
+ revision = 0;
29
+ geometryIdentity = '';
30
+ initialization;
31
+ usingLegacy = false;
32
+ targetRevisions = new Map();
33
+ constructor(directory, server, legacy) {
34
+ this.directory = directory;
35
+ this.server = server;
36
+ this.legacy = legacy;
37
+ }
38
+ get automaticDpi() {
39
+ if (this.usingLegacy)
40
+ return true;
41
+ const setting = this.session?.environment.AXOM_DESKTOP_AUTO_DPI;
42
+ if (setting !== undefined)
43
+ return setting === '1';
44
+ return this.legacy !== undefined && this.session?.managed === true && this.session.environment.AXOM_DESKTOP_OWNED_DUMMY === '1';
45
+ }
46
+ async validate(session = this.session) {
47
+ const current = await readRdState(this.directory);
48
+ if (!current?.enabled || !this.state || current.artifact.sha512 !== this.state.artifact.sha512 ||
49
+ current.session !== this.state.session || current.display !== this.state.display ||
50
+ normalizeWorkstationServer(current.server) !== normalizeWorkstationServer(this.server) || !session || this.session !== session || !await sessionAlive(session)) {
51
+ throw new NativeCommandError('unavailable');
52
+ }
53
+ }
54
+ async initialize() {
55
+ if (this.initialization)
56
+ return this.initialization;
57
+ this.initialization = this.initializeCurrent();
58
+ try {
59
+ await this.initialization;
60
+ }
61
+ finally {
62
+ this.initialization = undefined;
63
+ }
64
+ }
65
+ async initializeCurrent() {
66
+ const state = await readRdState(this.directory);
67
+ this.usingLegacy = state === undefined && this.legacy !== undefined;
68
+ if (this.usingLegacy) {
69
+ this.provider = this.legacy;
70
+ this.state = undefined;
71
+ return;
72
+ }
73
+ if (!state?.enabled) {
74
+ this.provider = undefined;
75
+ this.state = undefined;
76
+ return;
77
+ }
78
+ if (normalizeWorkstationServer(state.server) !== normalizeWorkstationServer(this.server))
79
+ throw new Error('RD belongs to a different workstation server');
80
+ if (this.provider && this.state?.artifact.sha512 === state.artifact.sha512 && this.state.session === state.session && this.state.display === state.display && this.session && await sessionAlive(this.session))
81
+ return;
82
+ this.provider = undefined;
83
+ this.session = undefined;
84
+ await verifyRdInstallation(state.installation, state.artifact);
85
+ const session = chooseSession(await discoverSessions(), state) ?? await ensureManagedSession(this.directory);
86
+ this.session = session;
87
+ this.state = state;
88
+ const runtimeRoot = path.join(session.environment.XDG_RUNTIME_DIR ?? `/run/user/${process.getuid?.()}`, 'axom-rd');
89
+ await privateDirectory(runtimeRoot);
90
+ this.runtime = session.managed && session.environment.AXOM_DESKTOP_RUNTIME_DIR && path.isAbsolute(session.environment.AXOM_DESKTOP_RUNTIME_DIR) ?
91
+ session.environment.AXOM_DESKTOP_RUNTIME_DIR : path.join(runtimeRoot, session.identity);
92
+ await privateDirectory(this.runtime);
93
+ const executable = path.join(state.installation, 'bin/axom-desktop-keeper');
94
+ this.provider = new NativeProvider({ helperPath: executable, runtimeDir: this.runtime,
95
+ environment: this.environment(), enumerate: () => this.capabilities(), validateSession: () => this.validate(session),
96
+ sessionHelperFactory: async (binding) => {
97
+ await this.validate(session);
98
+ const helper = await HelperProcess.launch(executable, { ...this.environment(), AXOM_KEEPER_TARGET: binding.displayId });
99
+ return helper;
100
+ } });
101
+ process.env.AXOM_QUIC_BINARY = path.join(state.installation, 'bin/axom-quic');
102
+ }
103
+ environment() {
104
+ return { ...process.env, ...this.session.environment, AXOM_DESKTOP_RUNTIME_DIR: this.runtime, AXOM_ACCESS_TOKEN: undefined,
105
+ ...(this.state.artifact.ipcVersion === 1 ? { XDG_RUNTIME_DIR: this.runtime } : {}),
106
+ AXOM_DESKTOP_DISPLAY_CONTROL: path.join(this.state.installation, 'bin/axom-display'),
107
+ AXOM_DESKTOP_PROVIDER_FILE: path.join(this.runtime, 'provider.json'),
108
+ AXOM_DESKTOP_OWNED_DUMMY: this.session.managed && this.state.artifact.ipcVersion === 2 ? '1' : undefined };
109
+ }
110
+ async capabilities() {
111
+ await this.validate();
112
+ const executable = path.join(this.state.installation, 'bin/axom-desktop-keeper');
113
+ const options = { env: this.environment(), timeout: 2000, maxBuffer: 49152 };
114
+ const { stdout } = await execute(executable, ['--probe'], options);
115
+ const probe = geometrySchema.parse(JSON.parse(stdout));
116
+ const geometry = { width: probe.width, height: probe.height };
117
+ const sharedRuntime = this.state.artifact.ipcVersion === 2;
118
+ let actual = { output: '', ...geometry, modes: [] };
119
+ if (sharedRuntime) {
120
+ try {
121
+ actual = parseXrandr((await execute('xrandr', ['--current'], options)).stdout);
122
+ }
123
+ catch (error) {
124
+ if (this.session.managed)
125
+ throw error;
126
+ }
127
+ }
128
+ let dpi;
129
+ if (sharedRuntime && this.session.managed) {
130
+ const resources = (await execute('xrdb', ['-query'], options)).stdout;
131
+ const value = Number(/^Xft\.dpi:\s*(\d+)\s*$/m.exec(resources)?.[1] ?? 96);
132
+ dpi = z.number().int().min(64).max(384).parse(value);
133
+ }
134
+ const geometryIdentity = `${this.session.identity}:${geometry.width}:${geometry.height}:${dpi ?? ''}`;
135
+ if (geometryIdentity !== this.geometryIdentity) {
136
+ this.revision++;
137
+ this.geometryIdentity = geometryIdentity;
138
+ }
139
+ const staging = path.join(this.runtime, '.geometry.json');
140
+ await fs.writeFile(staging, JSON.stringify({ ...geometry, ...(dpi === undefined ? {} : { dpi }) }), { mode: 0o600 });
141
+ await fs.rename(staging, path.join(this.runtime, 'geometry.json'));
142
+ const targets = windowSchema.parse(JSON.parse((await execute(executable, ['--capture-targets'], options)).stdout));
143
+ const root = { id: '00000000000000000000000000000001', kind: 'display', label: this.session.managed ? 'Virtual desktop' : 'Desktop', ...geometry };
144
+ const displays = [root, ...targets].map(target => {
145
+ const identity = `${this.session.identity}:${target.width}:${target.height}`;
146
+ const previous = this.targetRevisions.get(target.id);
147
+ const revision = target.id === root.id ? this.revision : previous ? previous.revision + Number(previous.geometry !== identity) : 1;
148
+ this.targetRevisions.set(target.id, { geometry: identity, revision });
149
+ const display = desktopDisplayControl({ ...target, label: captureTargetLabel(target.label), revision }, actual, sharedRuntime && this.session.managed);
150
+ return { ...display, ...(target.id === root.id && dpi !== undefined && display.captureViewport !== true ? {
151
+ dpi, maxDpi: 384, appearanceSync: true,
152
+ } : {}) };
153
+ });
154
+ const live = new Set(displays.map(display => display.id));
155
+ for (const id of this.targetRevisions.keys())
156
+ if (!live.has(id))
157
+ this.targetRevisions.delete(id);
158
+ const failed = await Promise.all(['display-failed', 'input-failed'].map(async (name) => {
159
+ try {
160
+ await fs.lstat(path.join(this.runtime, name));
161
+ return true;
162
+ }
163
+ catch (error) {
164
+ if (error.code !== 'ENOENT')
165
+ throw error;
166
+ return false;
167
+ }
168
+ }));
169
+ const capabilities = desktopCapabilitiesSchema.parse({ protocols: { min: 2, max: probe.audio ? 3 : 2 }, providerId: 'linux-x11-v1', providerVersion: 'axom-linux-2',
170
+ configured: true, permission: 'granted', health: failed.some(Boolean) ? 'unavailable' : 'ready', reason: failed.some(Boolean) ? 'recovery_unverified' : null,
171
+ cursorEmbedded: false, formats: ['h264-420-v1'], encoderBackends: [{ id: 'libx264', kind: 'software', formats: [{ format: 'h264-420-v1',
172
+ maxWidth: 4096, maxHeight: 4096, maxPixels: 4000000, maxFps: 30, cappedRate: true }] }],
173
+ input: { pointer: true, physicalKeyboard: true, unicodeText: true, clipboard: true, textPaste: true }, displays });
174
+ const manifest = path.join(this.runtime, '.provider.json');
175
+ await fs.writeFile(manifest, JSON.stringify(capabilities), { mode: 0o600 });
176
+ await fs.rename(manifest, path.join(this.runtime, 'provider.json'));
177
+ return capabilities;
178
+ }
179
+ async probe() {
180
+ try {
181
+ await this.initialize();
182
+ if (!this.provider)
183
+ return null;
184
+ if (this.usingLegacy)
185
+ return this.provider.probe();
186
+ const capabilities = await this.provider.probe();
187
+ if (!capabilities)
188
+ return null;
189
+ await fs.writeFile(path.join(this.directory, 'rd', 'status.json'), JSON.stringify({ updatedAt: new Date().toISOString(), state: capabilities.health,
190
+ session: this.session.managed ? 'virtual' : 'existing', display: this.session.display, width: capabilities.displays[0].width, height: capabilities.displays[0].height,
191
+ resolution: capabilities.displays[0].captureViewport === true ? 'sampling' : capabilities.displays[0].resize,
192
+ modes: capabilities.displays[0].captureViewport === true ? [] : capabilities.displays[0].modes }), { mode: 0o600 });
193
+ return capabilities;
194
+ }
195
+ catch (error) {
196
+ await fs.writeFile(path.join(this.directory, 'rd', 'status.json'), JSON.stringify({ updatedAt: new Date().toISOString(), state: 'unavailable', reason: String(error) }), { mode: 0o600 }).catch(() => undefined);
197
+ throw error;
198
+ }
199
+ }
200
+ async prepare(selection, binding) {
201
+ await this.initialize();
202
+ if (!this.provider)
203
+ throw new NativeCommandError('unavailable');
204
+ return this.provider.prepare(selection, binding);
205
+ }
206
+ async wallpaper(targetId) {
207
+ await this.initialize();
208
+ if (!this.usingLegacy)
209
+ await this.validate();
210
+ if (!this.provider)
211
+ throw new NativeCommandError('unavailable');
212
+ return this.provider.wallpaper(targetId);
213
+ }
214
+ async recoverDisplay(binding, reservation) {
215
+ await this.initialize();
216
+ if (!this.usingLegacy)
217
+ await this.validate();
218
+ if (!this.provider)
219
+ throw new NativeCommandError('unavailable');
220
+ return this.provider.recoverDisplay(binding, reservation);
221
+ }
222
+ }