hyperbrane 0.1.2 → 0.2.1

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 (300) hide show
  1. package/README.md +105 -23
  2. package/dist/agent/agentClient.d.ts +5 -0
  3. package/dist/agent/agentClient.js +4 -0
  4. package/dist/agent/agentService.d.ts +9 -37
  5. package/dist/agent/agentService.js +64 -323
  6. package/dist/agent/agentStorage.d.ts +1 -20
  7. package/dist/agent/agentStorage.js +0 -30
  8. package/dist/agent/api.d.ts +105 -152
  9. package/dist/agent/api.js +1 -8
  10. package/dist/agent/client.d.ts +3 -1
  11. package/dist/agent/client.js +2 -3
  12. package/dist/agent/control.d.ts +23 -0
  13. package/dist/agent/control.js +479 -0
  14. package/dist/agent/dimensionAgentClient.d.ts +8 -0
  15. package/dist/agent/dimensionAgentClient.js +99 -0
  16. package/dist/agent/hostTranscript.d.ts +0 -6
  17. package/dist/agent/hostTranscript.js +3 -14
  18. package/dist/agent/session/backend.d.ts +18 -32
  19. package/dist/agent/session/claude/home.d.ts +0 -1
  20. package/dist/agent/session/claude/home.js +0 -1
  21. package/dist/agent/session/claude/provision.d.ts +10 -0
  22. package/dist/agent/session/claude/provision.js +17 -0
  23. package/dist/agent/session/claude/transcript.d.ts +3 -7
  24. package/dist/agent/session/claude/transcript.js +68 -14
  25. package/dist/agent/session/client.d.ts +1 -10
  26. package/dist/agent/session/client.js +0 -3
  27. package/dist/agent/session/codex/provision.d.ts +14 -0
  28. package/dist/agent/session/codex/provision.js +25 -0
  29. package/dist/agent/session/fileTranscript.d.ts +6 -49
  30. package/dist/agent/session/fileTranscript.js +26 -82
  31. package/dist/agent/session/install.d.ts +5 -0
  32. package/dist/agent/session/install.js +35 -0
  33. package/dist/agent/session/sessionStorage.d.ts +4 -35
  34. package/dist/agent/session/sessionStorage.js +10 -16
  35. package/dist/agent/session/sharedConnection.d.ts +0 -8
  36. package/dist/agent/session/sharedConnection.js +0 -7
  37. package/dist/agent/session/storedTranscripts.d.ts +1 -6
  38. package/dist/agent/session/storedTranscripts.js +0 -3
  39. package/dist/agent/session/worker/jsonRpc.d.ts +2 -13
  40. package/dist/agent/session/worker/jsonRpc.js +2 -11
  41. package/dist/agent/session/worker/protocol.d.ts +30 -21
  42. package/dist/agent/session/worker/protocol.js +29 -18
  43. package/dist/agent/session/worker/workerParams.d.ts +0 -5
  44. package/dist/agent/session/worker/workerParams.js +24 -17
  45. package/dist/agent/session/workerConnection.d.ts +2 -13
  46. package/dist/agent/session/workerConnection.js +7 -12
  47. package/dist/agent/session/workerGatewayConnection.d.ts +18 -0
  48. package/dist/agent/session/workerGatewayConnection.js +86 -0
  49. package/dist/agent/vendor-types/manifest.json +32 -0
  50. package/dist/agent/workerBundle.d.ts +4 -19
  51. package/dist/agent/workerBundle.generated.js +1 -1
  52. package/dist/agent/workerBundle.js +3 -30
  53. package/dist/agent/workerReplacement.d.ts +3 -0
  54. package/dist/agent/workerReplacement.js +49 -0
  55. package/dist/cli/agent.js +31 -16
  56. package/dist/cli/command.d.ts +10 -11
  57. package/dist/cli/command.js +0 -4
  58. package/dist/cli/dimension.js +6 -6
  59. package/dist/cli/gateway.js +3 -6
  60. package/dist/cli/main.js +14 -31
  61. package/dist/cli/streamPrinter.d.ts +2 -10
  62. package/dist/cli/streamPrinter.js +16 -46
  63. package/dist/cli/table.d.ts +0 -3
  64. package/dist/cli/table.js +0 -4
  65. package/dist/dimension/api.d.ts +42 -27
  66. package/dist/dimension/api.js +28 -47
  67. package/dist/dimension/client.d.ts +5 -0
  68. package/dist/dimension/client.js +4 -0
  69. package/dist/dimension/control.d.ts +22 -0
  70. package/dist/dimension/control.js +346 -0
  71. package/dist/dimension/image.d.ts +0 -5
  72. package/dist/dimension/image.js +0 -8
  73. package/dist/dimension/imageFiles.generated.js +1 -1
  74. package/dist/dimension/paths.d.ts +5 -4
  75. package/dist/dimension/paths.js +15 -6
  76. package/dist/dimension/service.d.ts +8 -24
  77. package/dist/dimension/service.js +85 -227
  78. package/dist/dimension/transport-worker.d.ts +1 -0
  79. package/dist/dimension/transport-worker.js +151 -0
  80. package/dist/dimension/transportBundle.d.ts +2 -0
  81. package/dist/dimension/transportBundle.generated.d.ts +1 -0
  82. package/dist/dimension/transportBundle.generated.js +2 -0
  83. package/dist/dimension/transportBundle.js +4 -0
  84. package/dist/dimension/transportClient.d.ts +10 -0
  85. package/dist/dimension/transportClient.js +115 -0
  86. package/dist/dimension/transportProtocol.d.ts +53 -0
  87. package/dist/dimension/transportProtocol.js +8 -0
  88. package/dist/index.d.ts +7 -9
  89. package/dist/index.js +7 -19
  90. package/dist/lib/control.d.ts +11 -0
  91. package/dist/lib/control.js +62 -0
  92. package/dist/lib/docker.d.ts +0 -22
  93. package/dist/lib/docker.js +0 -10
  94. package/dist/lib/image.d.ts +0 -3
  95. package/dist/lib/image.js +0 -2
  96. package/dist/lib/jsonDirectory.d.ts +0 -13
  97. package/dist/lib/jsonDirectory.js +34 -18
  98. package/dist/lib/scriptBundle.d.ts +6 -0
  99. package/dist/lib/scriptBundle.js +28 -0
  100. package/dist/lib/sh.d.ts +2 -0
  101. package/dist/lib/sh.js +8 -0
  102. package/dist/lib/sleep.d.ts +0 -1
  103. package/dist/lib/sleep.js +0 -1
  104. package/dist/lib/streamFetch.d.ts +15 -0
  105. package/dist/lib/streamFetch.js +150 -0
  106. package/dist/lib/tar.d.ts +0 -1
  107. package/dist/lib/tar.js +5 -11
  108. package/dist/lib/unixSocket.d.ts +2 -0
  109. package/dist/lib/unixSocket.js +28 -0
  110. package/dist/portal-gateway/api.d.ts +14 -44
  111. package/dist/portal-gateway/api.js +1 -3
  112. package/dist/portal-gateway/control.d.ts +42 -0
  113. package/dist/portal-gateway/control.js +657 -0
  114. package/dist/portal-gateway/gatewayClient.d.ts +5 -0
  115. package/dist/portal-gateway/gatewayClient.js +4 -0
  116. package/dist/portal-gateway/gatewayService.d.ts +8 -38
  117. package/dist/portal-gateway/gatewayService.js +82 -254
  118. package/dist/portal-gateway/image.d.ts +0 -1
  119. package/dist/portal-gateway/image.js +0 -2
  120. package/dist/portal-gateway/imageFiles.generated.js +2 -2
  121. package/dist/portal-gateway/portalClient.d.ts +5 -0
  122. package/dist/portal-gateway/portalClient.js +4 -0
  123. package/dist/portal-gateway/portalService.d.ts +5 -47
  124. package/dist/portal-gateway/portalService.js +19 -349
  125. package/dist/portal-gateway/transport-worker.d.ts +1 -0
  126. package/dist/portal-gateway/transport-worker.js +205 -0
  127. package/dist/portal-gateway/transportBundle.d.ts +2 -0
  128. package/dist/portal-gateway/transportBundle.generated.d.ts +1 -0
  129. package/dist/portal-gateway/transportBundle.generated.js +2 -0
  130. package/dist/portal-gateway/transportBundle.js +4 -0
  131. package/dist/portal-gateway/upstream.d.ts +0 -81
  132. package/dist/portal-gateway/upstream.js +0 -1
  133. package/dist/restore.d.ts +9 -0
  134. package/dist/restore.js +10 -0
  135. package/dist/sandbox/api.d.ts +126 -187
  136. package/dist/sandbox/api.js +5 -7
  137. package/dist/sandbox/client.d.ts +3 -0
  138. package/dist/sandbox/client.js +7 -0
  139. package/dist/sandbox/data-endpoints/dataEndpoints.d.ts +13 -0
  140. package/dist/sandbox/data-endpoints/dataEndpoints.js +100 -0
  141. package/dist/sandbox/data-endpoints/firewalledPorts.d.ts +4 -0
  142. package/dist/sandbox/data-endpoints/firewalledPorts.js +28 -0
  143. package/dist/sandbox/lib/httpOverStream.d.ts +3 -0
  144. package/dist/sandbox/lib/httpOverStream.js +21 -0
  145. package/dist/sandbox/lib/remoteFileOperations.d.ts +10 -0
  146. package/dist/sandbox/lib/remoteFileOperations.js +70 -0
  147. package/dist/sandbox/lib/shellCommand.d.ts +3 -0
  148. package/dist/sandbox/lib/shellCommand.js +7 -0
  149. package/dist/sandbox/lib/sshKey.d.ts +6 -0
  150. package/dist/sandbox/lib/sshKey.js +75 -0
  151. package/dist/sandbox/providers/boat/boatService.d.ts +3 -0
  152. package/dist/sandbox/providers/boat/boatService.js +373 -0
  153. package/dist/sandbox/providers/exedev/exeDevService.d.ts +2 -0
  154. package/dist/sandbox/providers/exedev/exeDevService.js +129 -0
  155. package/dist/sandbox/providers/exedev/exeDevToken.d.ts +1 -0
  156. package/dist/sandbox/providers/exedev/exeDevToken.js +50 -0
  157. package/dist/sandbox/providers/local/localService.d.ts +2 -0
  158. package/dist/sandbox/providers/local/localService.js +197 -0
  159. package/dist/sandbox/providers/ssh/sshService.d.ts +2 -0
  160. package/dist/sandbox/providers/ssh/sshService.js +83 -0
  161. package/dist/sandbox/providers/ssh/sshTransport.d.ts +12 -0
  162. package/dist/sandbox/providers/ssh/sshTransport.js +216 -0
  163. package/dist/sandbox/service.d.ts +3 -0
  164. package/dist/sandbox/service.js +26 -0
  165. package/dist/sandbox/storage/descriptors.d.ts +6 -0
  166. package/dist/sandbox/storage/descriptors.js +25 -0
  167. package/dist/sandbox/storage/sandboxStorage.d.ts +11 -0
  168. package/dist/sandbox/storage/sandboxStorage.js +42 -0
  169. package/dist/upstreams/anthropic.d.ts +0 -5
  170. package/dist/upstreams/anthropic.js +1 -8
  171. package/dist/upstreams/index.d.ts +2 -6
  172. package/dist/upstreams/index.js +0 -4
  173. package/dist/upstreams/openai.d.ts +0 -5
  174. package/dist/upstreams/openai.js +1 -13
  175. package/dist/vendor-types/claude/agent-sdk/bridge.d.ts +20 -4
  176. package/dist/vendor-types/claude/agent-sdk/browser-sdk.d.ts +24 -0
  177. package/dist/vendor-types/claude/agent-sdk/core.d.ts +2 -0
  178. package/dist/vendor-types/claude/agent-sdk/sdk-tools.d.ts +75 -6
  179. package/dist/vendor-types/claude/agent-sdk/sdk.d.ts +569 -100
  180. package/package.json +13 -4
  181. package/dist/agent/gatewaySignIns.d.ts +0 -5
  182. package/dist/agent/gatewaySignIns.js +0 -11
  183. package/dist/agent/session/codex/home.d.ts +0 -2
  184. package/dist/agent/session/codex/home.js +0 -4
  185. package/dist/agent/session/codex/messages.d.ts +0 -30
  186. package/dist/agent/session/codex/messages.js +0 -135
  187. package/dist/agent/session/codex/transcript.d.ts +0 -12
  188. package/dist/agent/session/codex/transcript.js +0 -22
  189. package/dist/agent/session/sdkMessages.d.ts +0 -15
  190. package/dist/agent/session/sdkMessages.js +0 -28
  191. package/dist/agent/session/workerSseConnection.d.ts +0 -31
  192. package/dist/agent/session/workerSseConnection.js +0 -76
  193. package/dist/agent/vendor-types/codex/app-server/AbsolutePathBuf.d.ts +0 -10
  194. package/dist/agent/vendor-types/codex/app-server/AbsolutePathBuf.js +0 -2
  195. package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputBody.d.ts +0 -2
  196. package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputBody.js +0 -2
  197. package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputContentItem.d.ts +0 -22
  198. package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputContentItem.js +0 -2
  199. package/dist/agent/vendor-types/codex/app-server/ImageDetail.d.ts +0 -1
  200. package/dist/agent/vendor-types/codex/app-server/ImageDetail.js +0 -2
  201. package/dist/agent/vendor-types/codex/app-server/ImageGenerationFailure.d.ts +0 -5
  202. package/dist/agent/vendor-types/codex/app-server/ImageGenerationFailure.js +0 -2
  203. package/dist/agent/vendor-types/codex/app-server/ImageGenerationItem.d.ts +0 -11
  204. package/dist/agent/vendor-types/codex/app-server/ImageGenerationItem.js +0 -2
  205. package/dist/agent/vendor-types/codex/app-server/LegacyAppPathString.d.ts +0 -23
  206. package/dist/agent/vendor-types/codex/app-server/LegacyAppPathString.js +0 -2
  207. package/dist/agent/vendor-types/codex/app-server/MessagePhase.d.ts +0 -7
  208. package/dist/agent/vendor-types/codex/app-server/MessagePhase.js +0 -2
  209. package/dist/agent/vendor-types/codex/app-server/ReasoningEffort.d.ts +0 -4
  210. package/dist/agent/vendor-types/codex/app-server/ReasoningEffort.js +0 -2
  211. package/dist/agent/vendor-types/codex/app-server/SleepItem.d.ts +0 -7
  212. package/dist/agent/vendor-types/codex/app-server/SleepItem.js +0 -2
  213. package/dist/agent/vendor-types/codex/app-server/WebSearchItem.d.ts +0 -14
  214. package/dist/agent/vendor-types/codex/app-server/WebSearchItem.js +0 -2
  215. package/dist/agent/vendor-types/codex/app-server/serde_json/JsonValue.d.ts +0 -3
  216. package/dist/agent/vendor-types/codex/app-server/serde_json/JsonValue.js +0 -2
  217. package/dist/agent/vendor-types/codex/app-server/v2/AgentMessageDelivery.d.ts +0 -1
  218. package/dist/agent/vendor-types/codex/app-server/v2/AgentMessageDelivery.js +0 -2
  219. package/dist/agent/vendor-types/codex/app-server/v2/AsyncUserInputQuestion.d.ts +0 -4
  220. package/dist/agent/vendor-types/codex/app-server/v2/AsyncUserInputQuestion.js +0 -2
  221. package/dist/agent/vendor-types/codex/app-server/v2/ByteRange.d.ts +0 -4
  222. package/dist/agent/vendor-types/codex/app-server/v2/ByteRange.js +0 -2
  223. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentState.d.ts +0 -5
  224. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentState.js +0 -2
  225. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentStatus.d.ts +0 -1
  226. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentStatus.js +0 -2
  227. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentTool.d.ts +0 -1
  228. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentTool.js +0 -2
  229. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentToolCallStatus.d.ts +0 -1
  230. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentToolCallStatus.js +0 -2
  231. package/dist/agent/vendor-types/codex/app-server/v2/CommandAction.d.ts +0 -19
  232. package/dist/agent/vendor-types/codex/app-server/v2/CommandAction.js +0 -2
  233. package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionSource.d.ts +0 -1
  234. package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionSource.js +0 -2
  235. package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionStatus.d.ts +0 -1
  236. package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionStatus.js +0 -2
  237. package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallOutputContentItem.d.ts +0 -10
  238. package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallOutputContentItem.js +0 -2
  239. package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallStatus.d.ts +0 -1
  240. package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallStatus.js +0 -2
  241. package/dist/agent/vendor-types/codex/app-server/v2/FileUpdateChange.d.ts +0 -6
  242. package/dist/agent/vendor-types/codex/app-server/v2/FileUpdateChange.js +0 -2
  243. package/dist/agent/vendor-types/codex/app-server/v2/HookPromptFragment.d.ts +0 -4
  244. package/dist/agent/vendor-types/codex/app-server/v2/HookPromptFragment.js +0 -2
  245. package/dist/agent/vendor-types/codex/app-server/v2/McpAppDisplayMode.d.ts +0 -1
  246. package/dist/agent/vendor-types/codex/app-server/v2/McpAppDisplayMode.js +0 -2
  247. package/dist/agent/vendor-types/codex/app-server/v2/McpAppUi.d.ts +0 -8
  248. package/dist/agent/vendor-types/codex/app-server/v2/McpAppUi.js +0 -2
  249. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallAppContext.d.ts +0 -7
  250. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallAppContext.js +0 -2
  251. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallError.d.ts +0 -3
  252. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallError.js +0 -2
  253. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallResult.d.ts +0 -6
  254. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallResult.js +0 -2
  255. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallStatus.d.ts +0 -1
  256. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallStatus.js +0 -2
  257. package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitation.d.ts +0 -5
  258. package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitation.js +0 -2
  259. package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitationEntry.d.ts +0 -6
  260. package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitationEntry.js +0 -2
  261. package/dist/agent/vendor-types/codex/app-server/v2/PatchApplyStatus.d.ts +0 -1
  262. package/dist/agent/vendor-types/codex/app-server/v2/PatchApplyStatus.js +0 -2
  263. package/dist/agent/vendor-types/codex/app-server/v2/PatchChangeKind.d.ts +0 -8
  264. package/dist/agent/vendor-types/codex/app-server/v2/PatchChangeKind.js +0 -2
  265. package/dist/agent/vendor-types/codex/app-server/v2/SubAgentActivityKind.d.ts +0 -1
  266. package/dist/agent/vendor-types/codex/app-server/v2/SubAgentActivityKind.js +0 -2
  267. package/dist/agent/vendor-types/codex/app-server/v2/TextElement.d.ts +0 -11
  268. package/dist/agent/vendor-types/codex/app-server/v2/TextElement.js +0 -2
  269. package/dist/agent/vendor-types/codex/app-server/v2/ThreadItem.d.ts +0 -215
  270. package/dist/agent/vendor-types/codex/app-server/v2/ThreadItem.js +0 -2
  271. package/dist/agent/vendor-types/codex/app-server/v2/UserInput.d.ts +0 -35
  272. package/dist/agent/vendor-types/codex/app-server/v2/UserInput.js +0 -2
  273. package/dist/agent/vendor-types/codex/app-server/v2/WebSearchAction.d.ts +0 -14
  274. package/dist/agent/vendor-types/codex/app-server/v2/WebSearchAction.js +0 -2
  275. package/dist/cli/services.d.ts +0 -19
  276. package/dist/cli/services.js +0 -34
  277. package/dist/dimension/http.d.ts +0 -7
  278. package/dist/dimension/http.js +0 -77
  279. package/dist/dimension/storage.d.ts +0 -39
  280. package/dist/dimension/storage.js +0 -79
  281. package/dist/portal-gateway/fakes.d.ts +0 -3
  282. package/dist/portal-gateway/fakes.js +0 -11
  283. package/dist/portal-gateway/gatewayStorage.d.ts +0 -66
  284. package/dist/portal-gateway/gatewayStorage.js +0 -125
  285. package/dist/portal-gateway/portalStorage.d.ts +0 -34
  286. package/dist/portal-gateway/portalStorage.js +0 -44
  287. package/dist/sandbox/boatService.d.ts +0 -52
  288. package/dist/sandbox/boatService.js +0 -343
  289. package/dist/sandbox/containerService.d.ts +0 -15
  290. package/dist/sandbox/containerService.js +0 -184
  291. package/dist/sandbox/endpointProxy.d.ts +0 -90
  292. package/dist/sandbox/endpointProxy.js +0 -716
  293. package/dist/sandbox/exeDevAuth.d.ts +0 -16
  294. package/dist/sandbox/exeDevAuth.js +0 -49
  295. package/dist/sandbox/localService.d.ts +0 -19
  296. package/dist/sandbox/localService.js +0 -200
  297. package/dist/sandbox/sseConnection.d.ts +0 -41
  298. package/dist/sandbox/sseConnection.js +0 -166
  299. package/dist/sandbox/sshService.d.ts +0 -30
  300. package/dist/sandbox/sshService.js +0 -680
@@ -0,0 +1,479 @@
1
+ import { dimensionLayout } from "../dimension/api.js";
2
+ import { sh } from "../lib/sh.js";
3
+ import { AGENT_BACKENDS, AgentConflictError, AgentNotFoundError, } from "./api.js";
4
+ import { codexPackage } from "./session/codex/provision.js";
5
+ import { WORKER_PROTOCOL } from "./session/worker/protocol.js";
6
+ import { claudePackage } from "./session/claude/provision.js";
7
+ import { workerBundle } from "./workerBundle.js";
8
+ export const WORKER_DIR = `${dimensionLayout.statePath()}/worker`;
9
+ const OLD_WORKER_DIR = "/opt/hyperbrane/worker";
10
+ const SCRIPTS_DIR = "/opt/hyperbrane";
11
+ const START_TIMEOUT_S = 30;
12
+ const STOP_TIMEOUT_S = 10;
13
+ const EXIT = { noDimension: 40, notRunning: 41, oldDimension: 42, conflict: 43, noAgent: 44, thirdUser: 45, needBundle: 46, noSession: 47 };
14
+ const AUTH_UPSTREAMS = { claude: "anthropic", codex: "openai" };
15
+ function backendPackage(backend) {
16
+ return backend === "claude" ? claudePackage() : codexPackage();
17
+ }
18
+ export async function createAgentScript(host, agent, options = {}) {
19
+ const bundle = await workerBundle();
20
+ const env = {
21
+ ...dimensionEnv(agent.dimensionName, options),
22
+ ...(options.replaceBuild ? { REPLACE_BUILD: "1" } : {}),
23
+ AGENT: agent.name,
24
+ BUILD: bundle.build,
25
+ PROTOCOL: String(WORKER_PROTOCOL),
26
+ CA_BUNDLE: dimensionLayout.caBundlePath(),
27
+ START_TIMEOUT: String(START_TIMEOUT_S),
28
+ STOP_TIMEOUT: String(STOP_TIMEOUT_S),
29
+ };
30
+ let result = await run(host, CREATE_AGENT, env);
31
+ if (result.exitCode === EXIT.needBundle)
32
+ result = await run(host, CREATE_AGENT, { ...env, HAS_BUNDLE: "1" }, bundle.source);
33
+ check(result, `starting agent ${agent.name}`, agent);
34
+ }
35
+ export async function stopAgentScript(host, agentName, options = {}) {
36
+ const result = await run(host, STOP_AGENT, { ...dataEnv(options), AGENT: agentName, STOP_TIMEOUT: String(STOP_TIMEOUT_S) });
37
+ check(result, `stopping agent ${agentName}`);
38
+ }
39
+ export async function listAgentsScript(host, options = {}) {
40
+ const result = check(await run(host, LIST_AGENTS, dataEnv(options)), "listing agents");
41
+ return records(result.stdout).map(([, agent]) => agent);
42
+ }
43
+ export async function listSessionsScript(host, options = {}) {
44
+ const result = check(await run(host, LIST_SESSIONS, dataEnv(options)), "listing sessions");
45
+ const running = new Set(result.stdout
46
+ .split("\n")
47
+ .filter((line) => line.startsWith("running "))
48
+ .map((line) => line.slice("running ".length)));
49
+ return records(result.stdout).map(([header, record]) => {
50
+ const [agentName = "", saved = ""] = header.split("\t");
51
+ const status = !running.has(agentName) ? { state: "exited", code: null, signal: null } : (savedStatus(saved) ?? { state: "idle" });
52
+ return { ...record.spec, agentName, status, nativeId: record.nativeId, model: record.model, permissionMode: record.permissionMode ?? "ask" };
53
+ });
54
+ }
55
+ export async function installScript(host, agentName, backend, options = {}) {
56
+ const { pkg, version, executables } = backendPackage(backend);
57
+ const env = { ...dataEnv(options), AGENT: agentName, PKG: pkg, VERSION: version, EXECUTABLES: executables, CA_BUNDLE: dimensionLayout.caBundlePath() };
58
+ check(await run(host, INSTALL_BACKEND, env), `installing ${backend} for agent ${agentName}`);
59
+ }
60
+ export async function loginStateScript(host, ref, options = {}) {
61
+ const upstreams = Object.entries(AUTH_UPSTREAMS)
62
+ .map(([backend, upstream]) => `${backend}\t${upstream}`)
63
+ .join("\n");
64
+ const result = check(await run(host, LOGIN_STATE, { ...dataEnv(options), AGENT: ref.agentName, SESSION: ref.sessionName, UPSTREAMS: upstreams }), `signing in ${ref.agentName}/${ref.sessionName}`);
65
+ const [backend = "", signedIn] = result.stdout.trim().split("\t");
66
+ if (!AGENT_BACKENDS.includes(backend))
67
+ throw new Error(`session ${ref.sessionName} has no known backend (${backend})`);
68
+ return { backend: backend, gatewaySignedIn: signedIn === "1" };
69
+ }
70
+ export async function agentsDownScript(host, options = {}) {
71
+ const result = check(await run(host, AGENTS_DOWN, dataEnv(options)), "finding agents to restore");
72
+ return result.stdout
73
+ .split("\n")
74
+ .filter(Boolean)
75
+ .map((line) => {
76
+ const [name = "", dimensionName = ""] = line.split("\t");
77
+ return { name, dimensionName };
78
+ });
79
+ }
80
+ export async function workerSocketScript(host, agentName, options = {}) {
81
+ const result = check(await run(host, WORKER_SOCKET, { ...dataEnv(options), AGENT: agentName }), `finding agent ${agentName}'s worker`);
82
+ const [dimensionName = "", socket = ""] = result.stdout.split("\n");
83
+ return { dimensionName, socket };
84
+ }
85
+ export async function listBackendsScript(host, agentName, options = {}) {
86
+ const lines = AGENT_BACKENDS.map((backend) => {
87
+ const { pkg, version, executables } = backendPackage(backend);
88
+ return [backend, pkg, version, executables].join("\t");
89
+ });
90
+ const result = check(await run(host, LIST_BACKENDS, { ...dataEnv(options), AGENT: agentName, BACKENDS: lines.join("\n") }), "listing backends");
91
+ return result.stdout
92
+ .split("\n")
93
+ .filter(Boolean)
94
+ .map((line) => {
95
+ const [backend, installed = "", ready] = line.split("\t");
96
+ return { backend: backend, version: installed || null, ready: ready === "1", pinned: backendPackage(backend).version };
97
+ });
98
+ }
99
+ function dataEnv({ dataDirectory }) {
100
+ return {
101
+ ...(dataDirectory === undefined ? {} : { DATA: dataDirectory }),
102
+ CONTAINER_PREFIX: dimensionLayout.containerName(""),
103
+ RUN_PATH: dimensionLayout.runPath(),
104
+ STATE_PATH: dimensionLayout.statePath(),
105
+ WORKER_DIR,
106
+ SCRIPTS_DIR,
107
+ OLD_WORKER_DIR,
108
+ ...Object.fromEntries(Object.entries(EXIT).map(([name, code]) => [`E_${name.replace(/[A-Z]/g, (c) => `_${c}`).toUpperCase()}`, String(code)])),
109
+ };
110
+ }
111
+ function dimensionEnv(dimensionName, options) {
112
+ return { ...dataEnv(options), DIMENSION: dimensionName };
113
+ }
114
+ function run(host, script, env, stdin) {
115
+ const inDimension = { KILL_AGENT, PREPARE_WORKER, PUT_SCRIPT, START_WORKER, REMOVE_OLD_SCRIPTS, INSTALL_IN_DIMENSION };
116
+ return host.runCommand(["sh", "-c", `${PRELUDE}\n${script}`], { env: { ...inDimension, ...env }, stdin });
117
+ }
118
+ function check(result, what, agent) {
119
+ const said = result.stderr.trim();
120
+ switch (result.exitCode) {
121
+ case 0:
122
+ return result;
123
+ case EXIT.noDimension:
124
+ throw new AgentNotFoundError(`dimension ${agent?.dimensionName ?? said} does not exist`);
125
+ case EXIT.notRunning:
126
+ throw new AgentConflictError(`dimension ${agent?.dimensionName ?? said} is not running`);
127
+ case EXIT.oldDimension:
128
+ throw new AgentConflictError(`dimension ${agent?.dimensionName ?? said} lacks the ${dimensionLayout.runPath()} and ${dimensionLayout.statePath()} mounts agents need (it predates them); recreate it`);
129
+ case EXIT.conflict:
130
+ throw new AgentConflictError(`agent ${agent?.name ?? ""} already exists in another dimension: ${said}`);
131
+ case EXIT.noAgent:
132
+ throw new AgentNotFoundError(`agent ${said} does not exist`);
133
+ case EXIT.noSession:
134
+ throw new AgentNotFoundError(`session ${said} does not exist`);
135
+ case EXIT.thirdUser:
136
+ throw new AgentConflictError(`${what}: ${said}`);
137
+ default:
138
+ throw new Error(`${what} failed (exit ${result.exitCode}): ${said || result.stdout.trim()}`);
139
+ }
140
+ }
141
+ function records(stdout) {
142
+ return stdout
143
+ .split(RECORD_END)
144
+ .map((block) => block.replace(/^(running [^\n]*\n)+/, ""))
145
+ .filter((block) => block.trim())
146
+ .flatMap((block) => {
147
+ const newline = block.indexOf("\n");
148
+ try {
149
+ return [[block.slice(0, newline), JSON.parse(block.slice(newline + 1))]];
150
+ }
151
+ catch {
152
+ return [];
153
+ }
154
+ });
155
+ }
156
+ function savedStatus(file) {
157
+ try {
158
+ const { status } = JSON.parse(file);
159
+ if (!status)
160
+ return null;
161
+ if (status.state === "tool")
162
+ return Array.isArray(status.tools) ? status : null;
163
+ return SESSION_STATES.has(status.state) ? status : null;
164
+ }
165
+ catch {
166
+ return null;
167
+ }
168
+ }
169
+ const SESSION_STATES = new Set(["exited", "starting", "waiting", "idle", "thinking"]);
170
+ const RECORD_END = "\n\x1e\n";
171
+ const PRELUDE = sh `
172
+ DATA="\${DATA:-$HOME/.local/share/hyperbrane}"
173
+
174
+ agent_dimension() {
175
+ record="$DATA/agents/$1.json"
176
+ if [ ! -f "$record" ]; then
177
+ echo "$1" >&2
178
+ exit "$E_NO_AGENT"
179
+ fi
180
+ DIMENSION="$(sed -n 's/^ *"dimensionName": *"\([^"]*\)".*/\1/p' "$record")"
181
+ }
182
+
183
+ # Exits unless dimension $DIMENSION exists and runs, with the mounts agents need. Sets CONTAINER,
184
+ # DIMENSION_USER (whom it runs as, uid[:gid]) and RUN (its run directory on the machine).
185
+ require_dimension() {
186
+ CONTAINER="$CONTAINER_PREFIX$DIMENSION"
187
+ RUN="$DATA/dimension-run/$DIMENSION"
188
+ format='{{index .Config.Labels "hyperbrane.dimension"}}|{{.State.Running}}|{{range .Mounts}}{{.Destination}},{{end}}|{{.Config.User}}'
189
+ if ! info="$(docker inspect -f "$format" "$CONTAINER" 2>/dev/null)"; then
190
+ echo "$DIMENSION" >&2
191
+ exit "$E_NO_DIMENSION"
192
+ fi
193
+ IFS='|' read -r label running mounts DIMENSION_USER <<INFO
194
+ $info
195
+ INFO
196
+ if [ "$label" != "$DIMENSION" ]; then
197
+ echo "$DIMENSION" >&2
198
+ exit "$E_NO_DIMENSION"
199
+ fi
200
+ if [ "$running" != true ]; then
201
+ echo "$DIMENSION" >&2
202
+ exit "$E_NOT_RUNNING"
203
+ fi
204
+ case "$mounts" in
205
+ *"$RUN_PATH,"*"$STATE_PATH,"* | *"$STATE_PATH,"*"$RUN_PATH,"*) ;;
206
+ *)
207
+ echo "$DIMENSION" >&2
208
+ exit "$E_OLD_DIMENSION"
209
+ ;;
210
+ esac
211
+ DIMENSION_USER="\${DIMENSION_USER:-0:0}"
212
+ }
213
+
214
+ # Whether agent $1's worker runs, of any build, and can be reached: its ready file names a live
215
+ # shellcheck disable=SC2034
216
+ worker_running() {
217
+ WORKER_PID=""
218
+ WORKER_BUILD=""
219
+ WORKER_PROTOCOL=""
220
+ [ -f "$RUN/$1.ready" ] && [ -S "$RUN/$1.sock" ] || return 1
221
+ read -r WORKER_PID WORKER_BUILD WORKER_PROTOCOL < "$RUN/$1.ready" || return 1
222
+ docker exec -u 0 "$CONTAINER" grep -qs "hyperbrane/worker" "/proc/$WORKER_PID/cmdline"
223
+ }
224
+
225
+ stop_worker() {
226
+ if worker_running "$1"; then
227
+ docker exec -u 0 "$CONTAINER" kill "$WORKER_PID" 2>/dev/null || true
228
+ waited=0
229
+ while [ "$waited" -lt "$STOP_TIMEOUT" ] && docker exec -u 0 "$CONTAINER" kill -0 "$WORKER_PID" 2>/dev/null; do
230
+ sleep 1
231
+ waited=$((waited + 1))
232
+ done
233
+ fi
234
+ docker exec -u "$DIMENSION_USER" -e AGENT="$1" "$CONTAINER" sh -c "$KILL_AGENT"
235
+ rm -f "$RUN/$1.ready" 2>/dev/null || true
236
+ }
237
+ `;
238
+ const KILL_AGENT = sh `
239
+ for process in /proc/[0-9]*; do
240
+ if tr '\0' '\n' < "$process/environ" 2>/dev/null | grep -qx "HYB_AGENT=$AGENT"; then
241
+ kill -9 "\${process#/proc/}" 2>/dev/null || true
242
+ fi
243
+ done
244
+ `;
245
+ const PREPARE_WORKER = sh `
246
+ set -eu
247
+ mkdir -p "$WORKER_DIR" "$SCRIPTS_DIR"
248
+ chown "$OWNER" "$WORKER_DIR" "$SCRIPTS_DIR" "$RUN_PATH" "$STATE_PATH"
249
+ if [ ! -e "$WORKER_DIR/node_modules" ] && [ -d "$OLD_WORKER_DIR/node_modules" ]; then
250
+ cp -a "$OLD_WORKER_DIR/." "$WORKER_DIR/"
251
+ rm -rf "$OLD_WORKER_DIR"
252
+ fi
253
+ if [ ! -f "$WORKER_DIR/package.json" ]; then
254
+ printf '{\n "name": "hyperbrane-worker",\n "private": true\n}\n' > "$WORKER_DIR/package.json"
255
+ chown "$OWNER" "$WORKER_DIR/package.json"
256
+ fi
257
+ printf '[install]\ncafile = "%s"\n' "$CA_BUNDLE" > "$WORKER_DIR/bunfig.toml"
258
+ chown "$OWNER" "$WORKER_DIR/bunfig.toml"
259
+ `;
260
+ const PUT_SCRIPT = sh `
261
+ set -eu
262
+ cat > "$SCRIPT.part"
263
+ mv -f "$SCRIPT.part" "$SCRIPT"
264
+ `;
265
+ const START_WORKER = sh `
266
+ HOME="$(getent passwd "$(id -u)" | cut -d: -f6)"
267
+ export HOME
268
+ cd "$HOME" 2>/dev/null || cd /
269
+ exec bun "$HYB_WORKER_SCRIPT" >> "$HYB_LOG" 2>&1 < /dev/null
270
+ `;
271
+ const REMOVE_OLD_SCRIPTS = sh `
272
+ for old in "$SCRIPTS_DIR"/worker-*.js; do
273
+ [ "$old" = "$SCRIPTS_DIR/worker-$BUILD.js" ] || rm -f "$old"
274
+ done
275
+ `;
276
+ const CREATE_AGENT = sh `
277
+ set -eu
278
+ DIMENSION_WANTED="$DIMENSION"
279
+ require_dimension
280
+
281
+ mkdir -p "$DATA/agents"
282
+ record="$DATA/agents/$AGENT.json"
283
+ if [ ! -f "$record" ]; then
284
+ printf '{\n "name": "%s",\n "dimensionName": "%s"\n}\n' "$AGENT" "$DIMENSION" > "$record.$$"
285
+ ln "$record.$$" "$record" 2>/dev/null || true
286
+ rm -f "$record.$$"
287
+ fi
288
+ agent_dimension "$AGENT"
289
+ if [ "$DIMENSION" != "$DIMENSION_WANTED" ]; then
290
+ echo "$DIMENSION" >&2
291
+ exit "$E_CONFLICT"
292
+ fi
293
+
294
+ mkdir -p "$RUN"
295
+ exec 8> "$RUN/$AGENT.start.lock"
296
+ flock -w "$((START_TIMEOUT + STOP_TIMEOUT + 30))" 8
297
+
298
+ if worker_running "$AGENT" && { [ "$WORKER_PROTOCOL" -gt "$PROTOCOL" ] 2> /dev/null || { [ "$WORKER_PROTOCOL" = "$PROTOCOL" ] && { [ -z "\${REPLACE_BUILD:-}" ] || [ "$WORKER_BUILD" = "$BUILD" ]; }; }; }; then exit 0; fi
299
+
300
+ runs_as="\${DIMENSION_USER%%:*}"
301
+ SOCKET_OWNER=""
302
+ if [ "$runs_as" = 0 ]; then
303
+ SOCKET_OWNER="$(id -u):$(id -g)"
304
+ elif [ "$runs_as" != "$(id -u)" ]; then
305
+ echo "agent $AGENT's worker would run as uid $runs_as, whose socket uid $(id -u) can't connect to" >&2
306
+ exit "$E_THIRD_USER"
307
+ fi
308
+
309
+ docker exec -u 0 -e OWNER="$DIMENSION_USER" -e WORKER_DIR -e SCRIPTS_DIR -e OLD_WORKER_DIR -e RUN_PATH -e STATE_PATH -e CA_BUNDLE \
310
+ "$CONTAINER" sh -c "$PREPARE_WORKER"
311
+
312
+ script="$SCRIPTS_DIR/worker-$BUILD.js"
313
+ if ! docker exec "$CONTAINER" test -f "$script"; then
314
+ [ "\${HAS_BUNDLE:-}" = 1 ] || exit "$E_NEED_BUNDLE"
315
+ docker exec -i -u "$DIMENSION_USER" -e SCRIPT="$script" "$CONTAINER" sh -c "$PUT_SCRIPT"
316
+ fi
317
+
318
+ stop_worker "$AGENT"
319
+ docker exec -d -u "$DIMENSION_USER" \
320
+ -e HYB_AGENT="$AGENT" -e HYB_LOG="$RUN_PATH/$AGENT.log" -e HYB_INSTALL_DIR="$WORKER_DIR" \
321
+ -e HYB_WORKER_SCRIPT="$script" -e HYB_WORKER_BUILD="$BUILD" -e HYB_SOCKET_OWNER="$SOCKET_OWNER" \
322
+ -e NODE_EXTRA_CA_CERTS="$CA_BUNDLE" -e SSL_CERT_FILE="$CA_BUNDLE" \
323
+ "$CONTAINER" sh -c "$START_WORKER"
324
+
325
+ waited=0
326
+ until worker_running "$AGENT" && [ "$WORKER_BUILD" = "$BUILD" ]; do
327
+ if [ "$waited" -ge "$((START_TIMEOUT * 10))" ]; then
328
+ echo "agent $AGENT's worker did not come up:" >&2
329
+ tail -c 2000 "$RUN/$AGENT.log" >&2 2>/dev/null || true
330
+ exit 1
331
+ fi
332
+ sleep 0.1
333
+ waited=$((waited + 1))
334
+ done
335
+ docker exec -u 0 -e SCRIPTS_DIR -e BUILD "$CONTAINER" sh -c "$REMOVE_OLD_SCRIPTS"
336
+ `;
337
+ const STOP_AGENT = sh `
338
+ set -eu
339
+ [ -f "$DATA/agents/$AGENT.json" ] || exit 0
340
+ agent_dimension "$AGENT"
341
+ require_dimension
342
+ stop_worker "$AGENT"
343
+ rm -f "$DATA/agents/$AGENT.json"
344
+ `;
345
+ const WORKER_SOCKET = sh `
346
+ set -eu
347
+ agent_dimension "$AGENT"
348
+ printf '%s\n%s\n' "$DIMENSION" "$DATA/dimension-run/$DIMENSION/$AGENT.sock"
349
+ `;
350
+ const LIST_AGENTS = sh `
351
+ for record in "$DATA"/agents/*.json; do
352
+ [ -f "$record" ] || continue
353
+ name="\${record##*/}"
354
+ printf '%s\n' "\${name%.json}"
355
+ cat "$record"
356
+ printf '\n\036\n'
357
+ done
358
+ `;
359
+ const LIST_SESSIONS = sh `
360
+ checks="$(mktemp -d)"
361
+ for record in "$DATA"/agents/*.json; do
362
+ [ -f "$record" ] || continue
363
+ agent="\${record##*/}"
364
+ agent="\${agent%.json}"
365
+ agent_dimension "$agent"
366
+ for session in "$DATA/dimension-state/$DIMENSION/agents/$agent/sessions"/*.json; do
367
+ [ -f "$session" ] || continue
368
+ status="$(tr -d '\n' < "\${session%.json}/status.json" 2>/dev/null || true)"
369
+ printf '%s\t%s\n' "$agent" "$status"
370
+ cat "$session"
371
+ printf '\n\036\n'
372
+ done
373
+ (
374
+ # shellcheck disable=SC2034
375
+ CONTAINER="$CONTAINER_PREFIX$DIMENSION"
376
+ # shellcheck disable=SC2034
377
+ RUN="$DATA/dimension-run/$DIMENSION"
378
+ if worker_running "$agent"; then echo "running $agent" > "$checks/$agent"; fi
379
+ ) &
380
+ done
381
+ wait
382
+ cat "$checks"/* 2>/dev/null || true
383
+ rm -rf "$checks"
384
+ `;
385
+ const INSTALL_IN_DIMENSION = sh `
386
+ set -eu
387
+ cd "$WORKER_DIR"
388
+ HOME="$(getent passwd "$(id -u)" | cut -d: -f6)"
389
+ export HOME
390
+ exec 9> .install.lock
391
+ flock 9
392
+ installed() {
393
+ manifest="node_modules/$PKG/package.json"
394
+ [ -f "$manifest" ] || return 1
395
+ grep -q "\"version\": *\"$VERSION\"" "$manifest" || return 1
396
+ # shellcheck disable=SC2086
397
+ for executable in $EXECUTABLES; do
398
+ for found in node_modules/$executable; do
399
+ [ -x "$found" ] && return 0
400
+ done
401
+ done
402
+ return 1
403
+ }
404
+ installed && exit 0
405
+ force=""
406
+ if [ -f "node_modules/$PKG/package.json" ] && grep -q "\"version\": *\"$VERSION\"" "node_modules/$PKG/package.json"; then force=--force; fi
407
+ # shellcheck disable=SC2086
408
+ bun add --exact $force "$PKG@$VERSION"
409
+ installed || { echo "bun add $PKG@$VERSION left no executable ($EXECUTABLES)" >&2; exit 1; }
410
+ `;
411
+ const INSTALL_BACKEND = sh `
412
+ set -eu
413
+ agent_dimension "$AGENT"
414
+ require_dimension
415
+ docker exec -u "$DIMENSION_USER" -e WORKER_DIR -e PKG -e VERSION -e EXECUTABLES \
416
+ -e NODE_EXTRA_CA_CERTS="$CA_BUNDLE" -e SSL_CERT_FILE="$CA_BUNDLE" "$CONTAINER" sh -c "$INSTALL_IN_DIMENSION"
417
+ `;
418
+ const LIST_BACKENDS = sh `
419
+ set -eu
420
+ agent_dimension "$AGENT"
421
+ modules="$DATA/dimension-state/$DIMENSION/worker/node_modules"
422
+ tab="$(printf '\t')"
423
+ printf '%s\n' "$BACKENDS" | while IFS="$tab" read -r backend pkg _pinned executables; do
424
+ installed="$(sed -n 's/^ *"version": *"\([^"]*\)".*/\1/p' "$modules/$pkg/package.json" 2>/dev/null | head -n 1)"
425
+ ready=0
426
+ if [ -n "$installed" ]; then
427
+ # shellcheck disable=SC2086
428
+ for executable in $executables; do
429
+ for found in "$modules"/$executable; do
430
+ [ -x "$found" ] && ready=1
431
+ done
432
+ done
433
+ fi
434
+ printf '%s\t%s\t%s\n' "$backend" "$installed" "$ready"
435
+ done
436
+ `;
437
+ const LOGIN_STATE = sh `
438
+ set -eu
439
+ agent_dimension "$AGENT"
440
+ record="$DATA/dimension-state/$DIMENSION/agents/$AGENT/sessions/$SESSION.json"
441
+ if [ ! -f "$record" ]; then
442
+ echo "$SESSION" >&2
443
+ exit "$E_NO_SESSION"
444
+ fi
445
+ backend="$(sed -n 's/^ *"backend": *"\([^"]*\)".*/\1/p' "$record" | head -n 1)"
446
+ tab="$(printf '\t')"
447
+ upstream="$(printf '%s\n' "$UPSTREAMS" | while IFS="$tab" read -r name value; do
448
+ if [ "$name" = "$backend" ]; then echo "$value"; fi
449
+ done)"
450
+ signed=0
451
+ for portal in "$DATA"/portals/*.json; do
452
+ [ -f "$portal" ] || continue
453
+ grep -q "\"dimensionName\": *\"$DIMENSION\"" "$portal" || continue
454
+ gateway="$(sed -n 's/^ *"gatewayName": *"\([^"]*\)".*/\1/p' "$portal")"
455
+ state="$DATA/gateway-state/$gateway"
456
+ if [ -n "$upstream" ] && [ -f "$state/upstreams/$upstream.json" ] && [ -f "$state/credentials/$upstream.oauth.json" ]; then signed=1; fi
457
+ done
458
+ printf '%s\t%s\n' "$backend" "$signed"
459
+ `;
460
+ const AGENTS_DOWN = sh `
461
+ checks="$(mktemp -d)"
462
+ for record in "$DATA"/agents/*.json; do
463
+ [ -f "$record" ] || continue
464
+ agent="\${record##*/}"
465
+ agent="\${agent%.json}"
466
+ agent_dimension "$agent"
467
+ (
468
+ CONTAINER="$CONTAINER_PREFIX$DIMENSION"
469
+ # shellcheck disable=SC2034
470
+ RUN="$DATA/dimension-run/$DIMENSION"
471
+ [ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER" 2>/dev/null)" = true ] || exit 0
472
+ worker_running "$agent" && exit 0
473
+ printf '%s\t%s\n' "$agent" "$DIMENSION" > "$checks/$agent"
474
+ ) &
475
+ done
476
+ wait
477
+ cat "$checks"/* 2>/dev/null || true
478
+ rm -rf "$checks"
479
+ `;
@@ -0,0 +1,8 @@
1
+ import { type DimensionAgentClient, type SessionRef } from "./api.ts";
2
+ export type DimensionDirectories = {
3
+ runDirectory: string;
4
+ stateDirectory: string;
5
+ };
6
+ export declare function createDimensionAgentClient(): DimensionAgentClient;
7
+ export declare function currentSession(): SessionRef | null;
8
+ export declare function dimensionAgentClient({ runDirectory, stateDirectory }: DimensionDirectories): DimensionAgentClient;
@@ -0,0 +1,99 @@
1
+ import { readdir, stat } from "node:fs/promises";
2
+ import { dimensionLayout } from "../dimension/api.js";
3
+ import { InvalidNameError, checkName } from "../lib/jsonDirectory.js";
4
+ import { SandboxConnectionRefusedError } from "../sandbox/api.js";
5
+ import { connectUnixSocket } from "../lib/unixSocket.js";
6
+ import { AgentConflictError, AgentInvalidError, AgentNotFoundError, } from "./api.js";
7
+ import { createWorkerClient } from "./session/client.js";
8
+ import { workerSocketPath } from "./session/sessionStorage.js";
9
+ import { workerStreamConnector } from "./session/workerConnection.js";
10
+ export function createDimensionAgentClient() {
11
+ return dimensionAgentClient({ runDirectory: dimensionLayout.runPath(), stateDirectory: dimensionLayout.statePath() });
12
+ }
13
+ export function currentSession() {
14
+ const { HYB_AGENT: agentName, HYB_SESSION: sessionName } = process.env;
15
+ return agentName && sessionName ? { agentName, sessionName } : null;
16
+ }
17
+ export function dimensionAgentClient({ runDirectory, stateDirectory }) {
18
+ function connector(agentName) {
19
+ const socket = workerSocketPath(runDirectory, agentName);
20
+ const connect = workerStreamConnector(() => connectUnixSocket(socket), socket);
21
+ return async (onNotification) => {
22
+ assertValidName("agent", agentName);
23
+ try {
24
+ return await connect(onNotification);
25
+ }
26
+ catch (error) {
27
+ if (!(error instanceof SandboxConnectionRefusedError))
28
+ throw error;
29
+ const known = await stat(`${stateDirectory}/agents/${agentName}`).then(() => true, () => false);
30
+ if (!known)
31
+ throw new AgentNotFoundError(`agent ${agentName} does not exist in this dimension`);
32
+ throw new AgentConflictError(`agent ${agentName}'s worker isn't running; a call through the machine's agents client starts it`);
33
+ }
34
+ };
35
+ }
36
+ function clientFor(agentName) {
37
+ return createWorkerClient(connector(agentName));
38
+ }
39
+ const sessionClient = ({ agentName }) => clientFor(agentName);
40
+ async function listAgents() {
41
+ const entries = await readdir(runDirectory).catch(() => []);
42
+ const names = entries.filter((entry) => entry.endsWith(".ready") && !entry.startsWith(".")).map((entry) => entry.slice(0, -".ready".length));
43
+ const running = await Promise.all(names.map((name) => stat(workerSocketPath(runDirectory, name)).then(() => name, () => null)));
44
+ return running.filter((name) => name !== null).sort();
45
+ }
46
+ async function* getStream(ref, request) {
47
+ yield* sessionClient(ref).stream(ref.sessionName, request.after_id);
48
+ }
49
+ async function listSessions() {
50
+ const listed = await Promise.all((await listAgents()).map((agentName) => clientFor(agentName)
51
+ .call("session/list", {})
52
+ .catch((error) => {
53
+ if (error instanceof AgentConflictError || error instanceof AgentNotFoundError)
54
+ return [];
55
+ throw error;
56
+ })));
57
+ return listed.flat();
58
+ }
59
+ return {
60
+ listAgents,
61
+ listSessions,
62
+ install: async (agentName, backend) => void (await clientFor(agentName).call("backend/install", { backend })),
63
+ listBackends: (agentName) => clientFor(agentName).call("backend/list", {}),
64
+ createSession: (agentName, session) => clientFor(agentName).call("session/create", { session }),
65
+ updateSession: (ref, update) => sessionClient(ref).call("session/update", { session: ref.sessionName, update }),
66
+ stopSession: async (ref) => void (await sessionClient(ref).call("session/stop", { session: ref.sessionName })),
67
+ getStream,
68
+ getTranscript: (ref, { toolUseId, limit, before } = {}) => toolUseId === undefined
69
+ ? sessionClient(ref).call("session/transcript", { session: ref.sessionName, limit, before })
70
+ : sessionClient(ref).call("session/subagentTranscript", { session: ref.sessionName, toolUseId, limit, before }),
71
+ getUsage: (ref) => sessionClient(ref).call("session/usage", { session: ref.sessionName }),
72
+ cancel: async (ref) => void (await sessionClient(ref).call("session/interrupt", { session: ref.sessionName })),
73
+ sendMessage: async (ref, message) => void (await sessionClient(ref).call("session/send", { session: ref.sessionName, message })),
74
+ getSessionStatus: (ref) => sessionClient(ref).call("session/status", { session: ref.sessionName }),
75
+ getLastUserMessages: (ref) => sessionClient(ref).call("session/lastUserMessages", { session: ref.sessionName }),
76
+ listPermissionRequests: (ref) => sessionClient(ref).call("permission/list", { session: ref.sessionName }),
77
+ respondPermissionRequest: async (ref, request, result) => void (await sessionClient(ref).call("permission/respond", { session: ref.sessionName, request, result })),
78
+ listQuestionRequests: (ref) => sessionClient(ref).call("question/list", { session: ref.sessionName }),
79
+ respondQuestionRequest: async (ref, request, response) => void (await sessionClient(ref).call("question/respond", { session: ref.sessionName, request, response })),
80
+ listModels: (ref) => sessionClient(ref).call("model/list", { session: ref.sessionName }),
81
+ setModel: async (ref, model) => void (await sessionClient(ref).call("model/set", { session: ref.sessionName, model })),
82
+ setPermissionMode: async (ref, mode) => void (await sessionClient(ref).call("permission/mode", { session: ref.sessionName, mode })),
83
+ listBackgroundTasks: (ref) => sessionClient(ref).call("task/list", { session: ref.sessionName }),
84
+ stopTask: async (ref, task) => void (await sessionClient(ref).call("task/stop", { session: ref.sessionName, task })),
85
+ getAuthStatus: (ref) => sessionClient(ref).call("auth/status", { session: ref.sessionName }),
86
+ login: (ref) => sessionClient(ref).call("auth/login", { session: ref.sessionName, gatewaySignedIn: false }),
87
+ submitAuthCode: (ref, code) => sessionClient(ref).call("auth/code", { session: ref.sessionName, code }),
88
+ };
89
+ }
90
+ function assertValidName(kind, name) {
91
+ try {
92
+ checkName(name);
93
+ }
94
+ catch (error) {
95
+ if (error instanceof InvalidNameError)
96
+ throw new AgentInvalidError(`invalid ${kind} name: ${error.message}`);
97
+ throw error;
98
+ }
99
+ }
@@ -1,11 +1,5 @@
1
1
  import type { Sandbox } from "../sandbox/api.ts";
2
2
  import { type AgentTranscriptPage, type AgentTranscriptRequest } from "./api.ts";
3
- /**
4
- * A page of the session's transcript, read on `host` from the dimension's state directory (its host
5
- * side) by the backend's own layout and parsing, so nothing has to run: one command, plus a read
6
- * for each further chunk the rare page that needs more takes. Null for a session whose env moves the backend's home
7
- * elsewhere (into the container, where only its worker reaches it).
8
- */
9
3
  export declare function readHostTranscript(host: Sandbox, { stateDirectory, agentName, sessionName }: {
10
4
  stateDirectory: string;
11
5
  agentName: string;
@@ -2,12 +2,6 @@ import { AgentNotFoundError } from "./api.js";
2
2
  import { CHUNK_BYTES, PROBE_BYTES, PROBE_LINES, TIME_SLACK_MS, createFileTranscript, prefetchedFiles, sandboxFiles, transcriptPage, transcriptTime, } from "./session/fileTranscript.js";
3
3
  import { backendHomePath, sessionRecordPath } from "./session/sessionStorage.js";
4
4
  import { STORED_TRANSCRIPTS } from "./session/storedTranscripts.js";
5
- /**
6
- * In one round trip: the session's record, its backend's file (found as the backend lays them out),
7
- * the stretches written again in it, where the page starts (for a time, by bisecting the rest of the
8
- * file as FileTranscript.page does) and what comes before that (PREFETCH_BYTES of it). The same
9
- * searches as fileTranscript.ts, here where the file is. Args: a JSON object, see readHostTranscript.
10
- */
11
5
  const READ_SCRIPT = String.raw `
12
6
  import base64, datetime, fnmatch, glob, json, mmap, os, sys
13
7
  args = json.loads(sys.argv[1])
@@ -146,16 +140,9 @@ if native_id and layout:
146
140
  out.update(path=path, size=size, end=end, after=after, copies=[[a, b] for a, b in copies], pieces=pieces)
147
141
  print(json.dumps(out))
148
142
  `;
149
- /** What's sent of the file with the record: a page and the slack around it, mostly, so it takes no more reads. */
150
143
  const PREFETCH_BYTES = 8 * CHUNK_BYTES;
151
- /**
152
- * A page of the session's transcript, read on `host` from the dimension's state directory (its host
153
- * side) by the backend's own layout and parsing, so nothing has to run: one command, plus a read
154
- * for each further chunk the rare page that needs more takes. Null for a session whose env moves the backend's home
155
- * elsewhere (into the container, where only its worker reaches it).
156
- */
157
144
  export async function readHostTranscript(host, { stateDirectory, agentName, sessionName }, { limit, before, cursor }) {
158
- before ??= cursor; // its old name
145
+ before ??= cursor;
159
146
  const args = {
160
147
  record: sessionRecordPath(stateDirectory, agentName, sessionName),
161
148
  homes: Object.fromEntries(Object.keys(STORED_TRANSCRIPTS).map((backend) => [backend, backendHomePath(stateDirectory, backend)])),
@@ -174,6 +161,8 @@ export async function readHostTranscript(host, { stateDirectory, agentName, sess
174
161
  if (!record)
175
162
  throw new AgentNotFoundError(`session ${sessionName} does not exist`);
176
163
  const stored = STORED_TRANSCRIPTS[record.spec.backend];
164
+ if (!stored)
165
+ return null;
177
166
  if (Object.keys(stored.homeEnvironment("")).some((name) => record.spec.env?.[name] !== undefined))
178
167
  return null;
179
168
  if (!record.nativeId)