@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
@@ -1,46 +1,36 @@
1
- import { UsageError } from '../config.js';
2
- import { clientDir, loadClientConfig, saveClientConfig, writeKeys } from './config.js';
1
+ import { randomUUID } from 'node:crypto';
2
+ import { clientDir } from './config.js';
3
3
  import { osInfo } from './osinfo.js';
4
- export async function registerWorkstation(server, clientName, token, ws) {
5
- const dir = clientDir();
6
- const existing = await loadClientConfig(dir).catch(() => undefined);
7
- const body = {
8
- name: clientName,
9
- ...(existing?.server === server ? { workstationId: existing.workstationId } : {}),
10
- os: osInfo(),
11
- defaultWorkspace: { name: ws.name, cwd: ws.cwd, shell: ws.shell },
12
- };
4
+ import { workstationEndpoint } from './control/serverIdentity.js';
5
+ import { LocalControlError } from './control/protocol.js';
6
+ import { z } from 'zod';
7
+ export const registerResponseSchema = z.object({ workstationId: z.string().min(1), ownerUserId: z.string().min(1).optional(), clientKey: z.string(), serverPub: z.string() });
8
+ /** No persistence; callers own the operation lock and identity transaction. */
9
+ export async function requestWorkstationRegistration(server, clientName, token, ws, workstationId, shell = ws?.shell ?? '/bin/sh') {
13
10
  let res;
14
11
  try {
15
- res = await fetch(new URL('/workstation/register', server), {
16
- method: 'POST',
12
+ res = await fetch(workstationEndpoint(server, '/workstation/register'), { method: 'POST', redirect: 'error', signal: AbortSignal.timeout(30_000),
17
13
  headers: { 'content-type': 'application/json', authorization: `Bearer ${token}` },
18
- body: JSON.stringify(body),
14
+ body: JSON.stringify({ name: clientName, ...(workstationId ? { workstationId } : {}), os: osInfo(), shell, ...(ws ? { defaultWorkspace: ws } : {}) }),
19
15
  });
20
16
  }
21
- catch (err) {
22
- throw new Error(`Failed to reach ${server}/workstation/register: ${err instanceof Error ? err.message : err}`);
17
+ catch {
18
+ throw new LocalControlError('OUTCOME_UNKNOWN', 'Registration was sent but its outcome could not be confirmed. Check your workstation list before trying again.');
19
+ }
20
+ if (res.status === 409)
21
+ throw new LocalControlError('BINDING_CONFLICT', 'A workstation with this name is already registered. Check Settings → Workstations.');
22
+ if (res.status === 401 || res.status === 403)
23
+ throw new LocalControlError('AUTHORIZATION_REQUIRED', 'Workstation permission is required. Run `axom auth` and select workstation, then try again.');
24
+ if (!res.ok)
25
+ throw new LocalControlError('OUTCOME_UNKNOWN', 'Registration could not be confirmed. Check your workstation list.');
26
+ try {
27
+ return registerResponseSchema.parse(await res.json());
23
28
  }
24
- if (!res.ok) {
25
- // A workstation with this name is already registered for this account — most commonly because
26
- // this same machine registered before an axom-cli upgrade and no longer has a local config at
27
- // the new config-directory path (see the client's move from ~/.axom-workstation to
28
- // ~/.axom/workstation). Rebinding the existing row silently would hand this machine's identity
29
- // to whichever caller re-registers next, so the server refuses instead of upserting.
30
- if (res.status === 409) {
31
- throw new UsageError(`A workstation named '${clientName}' is already registered for this account. This is expected `
32
- + `when the same machine re-registers after an axom-cli upgrade. Delete it under Settings → `
33
- + 'Workstations, then run `axom workstation start` again.');
34
- }
35
- if (res.status === 403) {
36
- throw new UsageError('This credential is not authorized to register a workstation. '
37
- + 'Run `axom auth` and select workstation, then try again.');
38
- }
39
- throw new Error(`Registration failed: ${res.status} ${await res.text()}`);
29
+ catch {
30
+ throw new LocalControlError('OUTCOME_UNKNOWN', 'Registration response was incomplete. Check your workstation list.');
40
31
  }
41
- const data = (await res.json());
42
- if (!data.workstationId)
43
- throw new Error('Registration response missing workstationId');
44
- await saveClientConfig(dir, { server, workstationId: data.workstationId, workspaces: [{ name: ws.name, cwd: ws.cwd }] });
45
- await writeKeys(dir, data.clientKey, data.serverPub);
32
+ }
33
+ export async function registerWorkstation(server, clientName, token, ws) {
34
+ const { enrollLegacyWorkstation } = await import('./control/enrollment.js');
35
+ await enrollLegacyWorkstation({ server, name: clientName, accessToken: token, workspace: ws, operationId: randomUUID() }, clientDir());
46
36
  }
@@ -58,6 +58,22 @@ async function assertWritable(directory) {
58
58
  }
59
59
  const defaultDeps = {
60
60
  run: runProcess,
61
+ readLaunchAgent: async (file) => {
62
+ const handle = await fs.open(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
63
+ try {
64
+ const stat = await handle.stat();
65
+ if (!stat.isFile() || stat.size > 65536)
66
+ throw new Error('Invalid LaunchAgent');
67
+ }
68
+ finally {
69
+ await handle.close();
70
+ }
71
+ const result = await runProcess('/usr/bin/plutil', ['-convert', 'json', '-o', '-', file]);
72
+ const value = JSON.parse(result.stdout);
73
+ if (!value || typeof value !== 'object' || Array.isArray(value))
74
+ throw new Error('Invalid LaunchAgent');
75
+ return value;
76
+ },
61
77
  writeTemp,
62
78
  assertWritable,
63
79
  ensureDirectory: async (directory) => { await fs.mkdir(directory, { recursive: true }); },
@@ -219,24 +235,38 @@ export function createServiceManager(context, overrides = {}) {
219
235
  // durable LaunchAgent plist. This keeps explicit stop meaningful across both service managers.
220
236
  if (action === 'stop')
221
237
  return deps.run('launchctl', ['bootout', launchdDomain]);
222
- if (action === 'restart') {
223
- try {
224
- await deps.run('launchctl', ['bootout', launchdDomain]);
225
- }
226
- catch (error) {
227
- // `restart` after an explicit `stop` is a valid request: absence means there is nothing
228
- // left to unload, and launchdStart will bootstrap the durable plist.
229
- if (!launchdAbsent(error))
230
- throw error;
238
+ return launchdStart();
239
+ };
240
+ const enabled = async (plistDisabled = false) => {
241
+ if (context.platform === 'darwin') {
242
+ const result = await deps.run('launchctl', ['print-disabled', `gui/${context.userId}`]);
243
+ if (!result.stdout.includes('{') || !result.stdout.includes('}'))
244
+ throw new Error('Cannot determine launchd enablement');
245
+ const line = result.stdout.split('\n').find(line => line.includes(`"${SERVICE_LABEL}"`));
246
+ if (line) {
247
+ const override = /=>\s*(true|false|enabled|disabled)\b/.exec(line);
248
+ if (!override)
249
+ throw new Error('Cannot determine launchd enablement');
250
+ return override[1] === 'true' || override[1] === 'disabled' ? false : true;
231
251
  }
232
- return launchdStart();
252
+ return !plistDisabled;
253
+ }
254
+ try {
255
+ return (await deps.run('systemctl', ['--user', 'is-enabled', `${SERVICE_LABEL}.service`])).stdout.trim() === 'enabled';
256
+ }
257
+ catch (error) {
258
+ if (/disabled|not-found/.test(String(error)))
259
+ return false;
260
+ throw error;
233
261
  }
234
- return launchdStart();
235
262
  };
236
263
  // Extracted so `start()` can reuse it: reinstalling is the same work whether triggered
237
264
  // explicitly (`axom workstation install`) or as `start()`'s self-heal for a missing definition
238
265
  // file. Both platform branches leave the service running, so callers need no separate start.
239
266
  const performInstall = async (_options = {}) => {
267
+ const present = await deps.pathExists(target);
268
+ const plistDisabled = context.platform === 'darwin' && present && (await deps.readLaunchAgent(target)).Disabled === true;
269
+ const preserveDisabled = (context.platform === 'darwin' || present) && !await enabled(plistDisabled);
240
270
  await deps.assertWritable(context.npmPrefix);
241
271
  await deps.ensureDirectory(path.dirname(target));
242
272
  const temp = await deps.writeTemp(contents);
@@ -246,7 +276,8 @@ export function createServiceManager(context, overrides = {}) {
246
276
  await deps.run('mv', [targetTemp, target]);
247
277
  if (context.platform === 'linux') {
248
278
  await deps.run('systemctl', ['--user', 'daemon-reload']);
249
- await deps.run('systemctl', ['--user', 'enable', '--now', `${SERVICE_LABEL}.service`]);
279
+ if (!preserveDisabled)
280
+ await deps.run('systemctl', ['--user', 'enable', '--now', `${SERVICE_LABEL}.service`]);
250
281
  }
251
282
  else {
252
283
  // Reload an existing service before bootstrapping the replacement into the GUI domain.
@@ -255,7 +286,8 @@ export function createServiceManager(context, overrides = {}) {
255
286
  // bounded probe/bootstrap path so Bootstrap failed: 5 is confirmed then kickstarted, and
256
287
  // so a launchd that never releases the job is reported instead of leaving the machine
257
288
  // with the bootout applied and nothing bootstrapped in its place.
258
- await launchdStart();
289
+ if (!preserveDisabled)
290
+ await launchdStart();
259
291
  }
260
292
  }
261
293
  finally {
@@ -303,6 +335,11 @@ export function createServiceManager(context, overrides = {}) {
303
335
  */
304
336
  async start() {
305
337
  await withGuiSessionHint(async () => {
338
+ // An explicit Start is consent to re-enable; install/update alone preserves OFF.
339
+ if (context.platform === 'darwin')
340
+ await deps.run('launchctl', ['enable', launchdDomain]);
341
+ else
342
+ await deps.run('systemctl', ['--user', 'enable', `${SERVICE_LABEL}.service`]);
306
343
  if (!(await deps.pathExists(target)))
307
344
  await performInstall({});
308
345
  await lifecycle('start');
@@ -310,6 +347,60 @@ export function createServiceManager(context, overrides = {}) {
310
347
  },
311
348
  async stop() { await withGuiSessionHint(() => lifecycle('stop')); },
312
349
  async restart() { await withGuiSessionHint(() => lifecycle('restart')); },
350
+ async setEnabled(value) {
351
+ await withGuiSessionHint(async () => {
352
+ if (context.platform === 'darwin') {
353
+ await deps.run('launchctl', [value ? 'enable' : 'disable', launchdDomain]);
354
+ if (value) {
355
+ if (!await deps.pathExists(target))
356
+ await performInstall({});
357
+ await launchdStart();
358
+ }
359
+ else {
360
+ try {
361
+ await lifecycle('stop');
362
+ }
363
+ catch (error) {
364
+ if (!launchdAbsent(error))
365
+ throw error;
366
+ }
367
+ }
368
+ }
369
+ else
370
+ await deps.run('systemctl', ['--user', value ? 'enable' : 'disable', '--now', `${SERVICE_LABEL}.service`]);
371
+ });
372
+ },
373
+ async enablement() {
374
+ const installed = await deps.pathExists(target);
375
+ if (!installed)
376
+ return { installed: false, enabled: false, loaded: false, running: false };
377
+ let value;
378
+ try {
379
+ let disabled = false;
380
+ if (context.platform === 'darwin') {
381
+ const plist = await deps.readLaunchAgent(target);
382
+ const args = plist.ProgramArguments;
383
+ const environment = plist.EnvironmentVariables;
384
+ if (plist.Label !== SERVICE_LABEL || !Array.isArray(args) || args.length !== 3 || args[0] !== context.nodePath || args[1] !== context.cliPath || args[2] !== '__daemon' || environment?.AXOM_WORKSTATION_CONFIG_DIR !== context.configDir) {
385
+ return { installed, enabled: null, loaded: false, running: false, reason: 'service_identity_mismatch' };
386
+ }
387
+ disabled = plist.Disabled === true;
388
+ }
389
+ value = await enabled(disabled);
390
+ }
391
+ catch {
392
+ return { installed, enabled: null, loaded: false, running: false, reason: 'service_state_unknown' };
393
+ }
394
+ try {
395
+ const result = await lifecycle('status');
396
+ return { installed, enabled: value, loaded: true, running: context.platform === 'darwin' ? launchdIsRunning(result.stdout) : true };
397
+ }
398
+ catch (error) {
399
+ if (launchdAbsent(error) || /inactive|not loaded|not-found/.test(String(error)))
400
+ return { installed, enabled: value, loaded: false, running: false };
401
+ return { installed, enabled: value, loaded: false, running: false, reason: 'runtime_state_unknown' };
402
+ }
403
+ },
313
404
  async status() {
314
405
  try {
315
406
  const result = await lifecycle('status');
@@ -31,6 +31,7 @@ ${keyValue('AXOM_WORKSTATION_CONFIG_DIR', context.configDir)}
31
31
  ${keyValue('AXOM_WORKSTATION_NPM_PATH', context.npmPath)}
32
32
  ${keyValue('AXOM_WORKSTATION_NPM_PREFIX', context.npmPrefix)}
33
33
  ${keyValue('PATH', context.pathEnv)}
34
+ ${context.npmConfigPrefix ? keyValue('NPM_CONFIG_PREFIX', context.npmConfigPrefix) : ''}
34
35
  \t</dict>
35
36
  \t<key>StandardOutPath</key>
36
37
  \t<string>${xml(`${context.configDir}/daemon.log`)}</string>
@@ -0,0 +1,118 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { statSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { promisify } from 'node:util';
5
+ const execFileAsync = promisify(execFile);
6
+ const LOCAL_FILESYSTEMS = new Set([
7
+ 'ext2', 'ext3', 'ext4', 'xfs', 'btrfs', 'zfs', 'f2fs', 'bcachefs',
8
+ 'vfat', 'exfat', 'ntfs', 'ntfs3', 'fuseblk',
9
+ ]);
10
+ export function linuxStoragePools(mounts) {
11
+ const pools = new Map();
12
+ for (const mount of mounts) {
13
+ if (!LOCAL_FILESYSTEMS.has(mount.fstype) || mount.options?.split(',').includes('ro'))
14
+ continue;
15
+ if (/^\/(?:boot|efi)(?:\/|$)/.test(mount.target))
16
+ continue;
17
+ const source = mount.source.split('[')[0];
18
+ const key = mount.fstype === 'zfs'
19
+ ? `zfs:${source.split('/')[0]}`
20
+ : mount.uuid ? `${mount.fstype}:${mount.uuid}` : `dev:${mount['maj:min']}`;
21
+ const existing = pools.get(key);
22
+ if (existing)
23
+ existing.paths.push(mount.target);
24
+ else
25
+ pools.set(key, { key, label: mount.label || source, paths: [mount.target] });
26
+ }
27
+ if (pools.size === 0) {
28
+ const root = mounts.find((mount) => mount.target === '/' && mount.fstype === 'overlay');
29
+ if (root)
30
+ pools.set('overlay:/', { key: 'overlay:/', label: 'Container storage', paths: ['/'] });
31
+ }
32
+ return [...pools.values()];
33
+ }
34
+ export function darwinStoragePools(disks, physicalDisks, containers) {
35
+ const physical = new Set(physicalDisks.flatMap((disk) => [disk.DeviceIdentifier, ...(disk.Partitions ?? []).map((part) => part.DeviceIdentifier)]));
36
+ const volumes = disks.flatMap((disk) => [disk, ...(disk.Partitions ?? []), ...(disk.APFSVolumes ?? [])]);
37
+ const pools = [];
38
+ for (const container of containers) {
39
+ if (!container.PhysicalStores.some((store) => physical.has(store.DeviceIdentifier)))
40
+ continue;
41
+ const userVolumes = container.Volumes.filter((volume) => volume.Roles.length === 0 || volume.Roles.includes('Data'));
42
+ userVolumes.sort((first, second) => Number(second.Roles.includes('Data')) - Number(first.Roles.includes('Data')));
43
+ const mounted = userVolumes.flatMap((volume) => {
44
+ const disk = volumes.find((candidate) => candidate.DeviceIdentifier === volume.DeviceIdentifier);
45
+ return disk?.MountPoint ? [{ name: volume.Name, mount: disk.MountPoint }] : [];
46
+ });
47
+ if (mounted.length === 0)
48
+ continue;
49
+ const system = container.Volumes.find((volume) => volume.Roles.includes('System'));
50
+ pools.push({
51
+ key: `apfs:${container.APFSContainerUUID}`,
52
+ label: system?.Name || mounted[0].name,
53
+ paths: mounted.map((volume) => volume.mount),
54
+ });
55
+ }
56
+ for (const volume of volumes) {
57
+ if (!physical.has(volume.DeviceIdentifier) || !volume.MountPoint)
58
+ continue;
59
+ if (!['Apple_HFS', 'Microsoft Basic Data', 'Windows_NTFS', 'DOS_FAT_32', 'DOS_FAT_16', 'ExFAT'].includes(volume.Content ?? ''))
60
+ continue;
61
+ pools.push({
62
+ key: `volume:${volume.VolumeUUID || volume.DiskUUID || volume.DeviceIdentifier}`,
63
+ label: volume.VolumeName || volume.MountPoint,
64
+ paths: [volume.MountPoint],
65
+ });
66
+ }
67
+ return pools;
68
+ }
69
+ async function diskutilJson(args) {
70
+ const { stdout } = await execFileAsync('/usr/sbin/diskutil', args, {
71
+ timeout: 5_000, maxBuffer: 4 * 1024 * 1024,
72
+ });
73
+ const json = await new Promise((resolve, reject) => {
74
+ const child = execFile('/usr/bin/plutil', ['-convert', 'json', '-o', '-', '-'], {
75
+ timeout: 2_000, maxBuffer: 4 * 1024 * 1024,
76
+ }, (error, output) => error ? reject(error) : resolve(output));
77
+ child.stdin?.on('error', reject);
78
+ child.stdin?.end(stdout);
79
+ });
80
+ return JSON.parse(json);
81
+ }
82
+ export async function discoverStoragePools(platform, workspaces) {
83
+ if (platform === 'linux') {
84
+ const { stdout } = await execFileAsync('findmnt', [
85
+ '--json', '--list', '--output', 'TARGET,SOURCE,FSTYPE,UUID,LABEL,MAJ:MIN,OPTIONS',
86
+ ], { timeout: 5_000, maxBuffer: 4 * 1024 * 1024, env: { ...process.env, LC_ALL: 'C' } });
87
+ const mounts = JSON.parse(stdout).filesystems;
88
+ return linuxStoragePools(mounts.filter((mount) => {
89
+ try {
90
+ return statSync(mount.target).isDirectory();
91
+ }
92
+ catch {
93
+ return false;
94
+ }
95
+ }));
96
+ }
97
+ if (platform === 'darwin') {
98
+ const [all, physical, apfs] = await Promise.all([
99
+ diskutilJson(['list', '-plist']),
100
+ diskutilJson(['list', '-plist', 'physical']),
101
+ diskutilJson(['apfs', 'list', '-plist']),
102
+ ]);
103
+ return darwinStoragePools(all.AllDisksAndPartitions, physical.AllDisksAndPartitions, apfs.Containers ?? []);
104
+ }
105
+ const pools = new Map();
106
+ for (const { cwd } of workspaces) {
107
+ try {
108
+ const key = `dev:${statSync(cwd).dev}`;
109
+ const existing = pools.get(key);
110
+ if (existing)
111
+ existing.paths.push(cwd);
112
+ else
113
+ pools.set(key, { key, label: path.parse(cwd).root, paths: [cwd] });
114
+ }
115
+ catch { }
116
+ }
117
+ return [...pools.values()];
118
+ }