hyperbrane 0.1.2 → 0.2.0

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 (287) hide show
  1. package/README.md +67 -25
  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 +62 -322
  6. package/dist/agent/agentStorage.d.ts +1 -20
  7. package/dist/agent/agentStorage.js +0 -30
  8. package/dist/agent/api.d.ts +80 -151
  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/hostTranscript.d.ts +0 -6
  15. package/dist/agent/hostTranscript.js +3 -14
  16. package/dist/agent/session/backend.d.ts +17 -32
  17. package/dist/agent/session/claude/home.d.ts +0 -1
  18. package/dist/agent/session/claude/home.js +0 -1
  19. package/dist/agent/session/claude/provision.d.ts +10 -0
  20. package/dist/agent/session/claude/provision.js +17 -0
  21. package/dist/agent/session/claude/transcript.d.ts +2 -7
  22. package/dist/agent/session/claude/transcript.js +68 -14
  23. package/dist/agent/session/client.d.ts +1 -10
  24. package/dist/agent/session/client.js +0 -3
  25. package/dist/agent/session/codex/provision.d.ts +14 -0
  26. package/dist/agent/session/codex/provision.js +25 -0
  27. package/dist/agent/session/fileTranscript.d.ts +6 -49
  28. package/dist/agent/session/fileTranscript.js +26 -82
  29. package/dist/agent/session/install.d.ts +5 -0
  30. package/dist/agent/session/install.js +35 -0
  31. package/dist/agent/session/sessionStorage.d.ts +4 -35
  32. package/dist/agent/session/sessionStorage.js +10 -16
  33. package/dist/agent/session/sharedConnection.d.ts +0 -8
  34. package/dist/agent/session/sharedConnection.js +0 -7
  35. package/dist/agent/session/storedTranscripts.d.ts +1 -6
  36. package/dist/agent/session/storedTranscripts.js +0 -3
  37. package/dist/agent/session/worker/jsonRpc.d.ts +2 -13
  38. package/dist/agent/session/worker/jsonRpc.js +2 -11
  39. package/dist/agent/session/worker/protocol.d.ts +26 -20
  40. package/dist/agent/session/worker/protocol.js +29 -18
  41. package/dist/agent/session/worker/workerParams.d.ts +0 -5
  42. package/dist/agent/session/worker/workerParams.js +14 -14
  43. package/dist/agent/session/workerConnection.d.ts +2 -13
  44. package/dist/agent/session/workerConnection.js +7 -12
  45. package/dist/agent/session/workerGatewayConnection.d.ts +18 -0
  46. package/dist/agent/session/workerGatewayConnection.js +86 -0
  47. package/dist/agent/vendor-types/manifest.json +87 -0
  48. package/dist/agent/workerBundle.d.ts +4 -19
  49. package/dist/agent/workerBundle.generated.js +1 -1
  50. package/dist/agent/workerBundle.js +3 -30
  51. package/dist/agent/workerReplacement.d.ts +3 -0
  52. package/dist/agent/workerReplacement.js +49 -0
  53. package/dist/cli/agent.js +31 -16
  54. package/dist/cli/command.d.ts +10 -11
  55. package/dist/cli/command.js +0 -4
  56. package/dist/cli/dimension.js +6 -6
  57. package/dist/cli/gateway.js +3 -6
  58. package/dist/cli/main.js +14 -31
  59. package/dist/cli/streamPrinter.d.ts +2 -10
  60. package/dist/cli/streamPrinter.js +16 -46
  61. package/dist/cli/table.d.ts +0 -3
  62. package/dist/cli/table.js +0 -4
  63. package/dist/dimension/api.d.ts +39 -27
  64. package/dist/dimension/api.js +28 -47
  65. package/dist/dimension/client.d.ts +5 -0
  66. package/dist/dimension/client.js +4 -0
  67. package/dist/dimension/control.d.ts +21 -0
  68. package/dist/dimension/control.js +328 -0
  69. package/dist/dimension/image.d.ts +0 -5
  70. package/dist/dimension/image.js +0 -8
  71. package/dist/dimension/imageFiles.generated.js +1 -1
  72. package/dist/dimension/paths.d.ts +5 -4
  73. package/dist/dimension/paths.js +15 -6
  74. package/dist/dimension/service.d.ts +8 -24
  75. package/dist/dimension/service.js +60 -228
  76. package/dist/dimension/transport-worker.d.ts +1 -0
  77. package/dist/dimension/transport-worker.js +151 -0
  78. package/dist/dimension/transportBundle.d.ts +2 -0
  79. package/dist/dimension/transportBundle.generated.d.ts +1 -0
  80. package/dist/dimension/transportBundle.generated.js +2 -0
  81. package/dist/dimension/transportBundle.js +4 -0
  82. package/dist/dimension/transportClient.d.ts +10 -0
  83. package/dist/dimension/transportClient.js +115 -0
  84. package/dist/dimension/transportProtocol.d.ts +53 -0
  85. package/dist/dimension/transportProtocol.js +8 -0
  86. package/dist/index.d.ts +5 -9
  87. package/dist/index.js +5 -19
  88. package/dist/lib/control.d.ts +8 -0
  89. package/dist/lib/control.js +62 -0
  90. package/dist/lib/docker.d.ts +0 -22
  91. package/dist/lib/docker.js +0 -10
  92. package/dist/lib/image.d.ts +0 -3
  93. package/dist/lib/image.js +0 -2
  94. package/dist/lib/jsonDirectory.d.ts +0 -13
  95. package/dist/lib/jsonDirectory.js +10 -15
  96. package/dist/lib/scriptBundle.d.ts +6 -0
  97. package/dist/lib/scriptBundle.js +28 -0
  98. package/dist/lib/sh.d.ts +2 -0
  99. package/dist/lib/sh.js +8 -0
  100. package/dist/lib/sleep.d.ts +0 -1
  101. package/dist/lib/sleep.js +0 -1
  102. package/dist/lib/streamFetch.d.ts +15 -0
  103. package/dist/lib/streamFetch.js +150 -0
  104. package/dist/lib/tar.d.ts +0 -1
  105. package/dist/lib/tar.js +5 -11
  106. package/dist/portal-gateway/api.d.ts +13 -44
  107. package/dist/portal-gateway/api.js +1 -3
  108. package/dist/portal-gateway/control.d.ts +42 -0
  109. package/dist/portal-gateway/control.js +657 -0
  110. package/dist/portal-gateway/gatewayClient.d.ts +5 -0
  111. package/dist/portal-gateway/gatewayClient.js +4 -0
  112. package/dist/portal-gateway/gatewayService.d.ts +8 -38
  113. package/dist/portal-gateway/gatewayService.js +68 -254
  114. package/dist/portal-gateway/image.d.ts +0 -1
  115. package/dist/portal-gateway/image.js +0 -2
  116. package/dist/portal-gateway/imageFiles.generated.js +2 -2
  117. package/dist/portal-gateway/portalClient.d.ts +5 -0
  118. package/dist/portal-gateway/portalClient.js +4 -0
  119. package/dist/portal-gateway/portalService.d.ts +5 -47
  120. package/dist/portal-gateway/portalService.js +19 -349
  121. package/dist/portal-gateway/transport-worker.d.ts +1 -0
  122. package/dist/portal-gateway/transport-worker.js +205 -0
  123. package/dist/portal-gateway/transportBundle.d.ts +2 -0
  124. package/dist/portal-gateway/transportBundle.generated.d.ts +1 -0
  125. package/dist/portal-gateway/transportBundle.generated.js +2 -0
  126. package/dist/portal-gateway/transportBundle.js +4 -0
  127. package/dist/portal-gateway/upstream.d.ts +0 -81
  128. package/dist/portal-gateway/upstream.js +0 -1
  129. package/dist/sandbox/api.d.ts +121 -187
  130. package/dist/sandbox/api.js +5 -7
  131. package/dist/sandbox/client.d.ts +2 -0
  132. package/dist/sandbox/client.js +4 -0
  133. package/dist/sandbox/data-endpoints/dataEndpoints.d.ts +13 -0
  134. package/dist/sandbox/data-endpoints/dataEndpoints.js +100 -0
  135. package/dist/sandbox/data-endpoints/firewalledPorts.d.ts +4 -0
  136. package/dist/sandbox/data-endpoints/firewalledPorts.js +28 -0
  137. package/dist/sandbox/lib/httpOverStream.d.ts +3 -0
  138. package/dist/sandbox/lib/httpOverStream.js +21 -0
  139. package/dist/sandbox/lib/remoteFileOperations.d.ts +10 -0
  140. package/dist/sandbox/lib/remoteFileOperations.js +70 -0
  141. package/dist/sandbox/lib/shellCommand.d.ts +3 -0
  142. package/dist/sandbox/lib/shellCommand.js +7 -0
  143. package/dist/sandbox/providers/boat/boatService.d.ts +2 -0
  144. package/dist/sandbox/providers/boat/boatService.js +321 -0
  145. package/dist/sandbox/providers/exedev/exeDevService.d.ts +2 -0
  146. package/dist/sandbox/providers/exedev/exeDevService.js +127 -0
  147. package/dist/sandbox/providers/exedev/exeDevToken.d.ts +1 -0
  148. package/dist/sandbox/providers/exedev/exeDevToken.js +54 -0
  149. package/dist/sandbox/providers/local/localService.d.ts +2 -0
  150. package/dist/sandbox/providers/local/localService.js +219 -0
  151. package/dist/sandbox/providers/ssh/sshService.d.ts +2 -0
  152. package/dist/sandbox/providers/ssh/sshService.js +82 -0
  153. package/dist/sandbox/providers/ssh/sshTransport.d.ts +12 -0
  154. package/dist/sandbox/providers/ssh/sshTransport.js +216 -0
  155. package/dist/sandbox/service.d.ts +2 -0
  156. package/dist/sandbox/service.js +18 -0
  157. package/dist/sandbox/storage/descriptors.d.ts +6 -0
  158. package/dist/sandbox/storage/descriptors.js +25 -0
  159. package/dist/sandbox/storage/sandboxStorage.d.ts +11 -0
  160. package/dist/sandbox/storage/sandboxStorage.js +42 -0
  161. package/dist/upstreams/anthropic.d.ts +0 -5
  162. package/dist/upstreams/anthropic.js +1 -8
  163. package/dist/upstreams/index.d.ts +2 -6
  164. package/dist/upstreams/index.js +0 -4
  165. package/dist/upstreams/openai.d.ts +0 -5
  166. package/dist/upstreams/openai.js +1 -13
  167. package/package.json +12 -4
  168. package/dist/agent/gatewaySignIns.d.ts +0 -5
  169. package/dist/agent/gatewaySignIns.js +0 -11
  170. package/dist/agent/session/codex/home.d.ts +0 -2
  171. package/dist/agent/session/codex/home.js +0 -4
  172. package/dist/agent/session/codex/messages.d.ts +0 -30
  173. package/dist/agent/session/codex/messages.js +0 -135
  174. package/dist/agent/session/codex/transcript.d.ts +0 -12
  175. package/dist/agent/session/codex/transcript.js +0 -22
  176. package/dist/agent/session/sdkMessages.d.ts +0 -15
  177. package/dist/agent/session/sdkMessages.js +0 -28
  178. package/dist/agent/session/workerSseConnection.d.ts +0 -31
  179. package/dist/agent/session/workerSseConnection.js +0 -76
  180. package/dist/agent/vendor-types/codex/app-server/AbsolutePathBuf.d.ts +0 -10
  181. package/dist/agent/vendor-types/codex/app-server/AbsolutePathBuf.js +0 -2
  182. package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputBody.d.ts +0 -2
  183. package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputBody.js +0 -2
  184. package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputContentItem.d.ts +0 -22
  185. package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputContentItem.js +0 -2
  186. package/dist/agent/vendor-types/codex/app-server/ImageDetail.d.ts +0 -1
  187. package/dist/agent/vendor-types/codex/app-server/ImageDetail.js +0 -2
  188. package/dist/agent/vendor-types/codex/app-server/ImageGenerationFailure.d.ts +0 -5
  189. package/dist/agent/vendor-types/codex/app-server/ImageGenerationFailure.js +0 -2
  190. package/dist/agent/vendor-types/codex/app-server/ImageGenerationItem.d.ts +0 -11
  191. package/dist/agent/vendor-types/codex/app-server/ImageGenerationItem.js +0 -2
  192. package/dist/agent/vendor-types/codex/app-server/LegacyAppPathString.d.ts +0 -23
  193. package/dist/agent/vendor-types/codex/app-server/LegacyAppPathString.js +0 -2
  194. package/dist/agent/vendor-types/codex/app-server/MessagePhase.d.ts +0 -7
  195. package/dist/agent/vendor-types/codex/app-server/MessagePhase.js +0 -2
  196. package/dist/agent/vendor-types/codex/app-server/ReasoningEffort.d.ts +0 -4
  197. package/dist/agent/vendor-types/codex/app-server/ReasoningEffort.js +0 -2
  198. package/dist/agent/vendor-types/codex/app-server/SleepItem.d.ts +0 -7
  199. package/dist/agent/vendor-types/codex/app-server/SleepItem.js +0 -2
  200. package/dist/agent/vendor-types/codex/app-server/WebSearchItem.d.ts +0 -14
  201. package/dist/agent/vendor-types/codex/app-server/WebSearchItem.js +0 -2
  202. package/dist/agent/vendor-types/codex/app-server/serde_json/JsonValue.d.ts +0 -3
  203. package/dist/agent/vendor-types/codex/app-server/serde_json/JsonValue.js +0 -2
  204. package/dist/agent/vendor-types/codex/app-server/v2/AgentMessageDelivery.d.ts +0 -1
  205. package/dist/agent/vendor-types/codex/app-server/v2/AgentMessageDelivery.js +0 -2
  206. package/dist/agent/vendor-types/codex/app-server/v2/AsyncUserInputQuestion.d.ts +0 -4
  207. package/dist/agent/vendor-types/codex/app-server/v2/AsyncUserInputQuestion.js +0 -2
  208. package/dist/agent/vendor-types/codex/app-server/v2/ByteRange.d.ts +0 -4
  209. package/dist/agent/vendor-types/codex/app-server/v2/ByteRange.js +0 -2
  210. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentState.d.ts +0 -5
  211. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentState.js +0 -2
  212. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentStatus.d.ts +0 -1
  213. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentStatus.js +0 -2
  214. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentTool.d.ts +0 -1
  215. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentTool.js +0 -2
  216. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentToolCallStatus.d.ts +0 -1
  217. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentToolCallStatus.js +0 -2
  218. package/dist/agent/vendor-types/codex/app-server/v2/CommandAction.d.ts +0 -19
  219. package/dist/agent/vendor-types/codex/app-server/v2/CommandAction.js +0 -2
  220. package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionSource.d.ts +0 -1
  221. package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionSource.js +0 -2
  222. package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionStatus.d.ts +0 -1
  223. package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionStatus.js +0 -2
  224. package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallOutputContentItem.d.ts +0 -10
  225. package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallOutputContentItem.js +0 -2
  226. package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallStatus.d.ts +0 -1
  227. package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallStatus.js +0 -2
  228. package/dist/agent/vendor-types/codex/app-server/v2/FileUpdateChange.d.ts +0 -6
  229. package/dist/agent/vendor-types/codex/app-server/v2/FileUpdateChange.js +0 -2
  230. package/dist/agent/vendor-types/codex/app-server/v2/HookPromptFragment.d.ts +0 -4
  231. package/dist/agent/vendor-types/codex/app-server/v2/HookPromptFragment.js +0 -2
  232. package/dist/agent/vendor-types/codex/app-server/v2/McpAppDisplayMode.d.ts +0 -1
  233. package/dist/agent/vendor-types/codex/app-server/v2/McpAppDisplayMode.js +0 -2
  234. package/dist/agent/vendor-types/codex/app-server/v2/McpAppUi.d.ts +0 -8
  235. package/dist/agent/vendor-types/codex/app-server/v2/McpAppUi.js +0 -2
  236. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallAppContext.d.ts +0 -7
  237. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallAppContext.js +0 -2
  238. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallError.d.ts +0 -3
  239. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallError.js +0 -2
  240. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallResult.d.ts +0 -6
  241. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallResult.js +0 -2
  242. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallStatus.d.ts +0 -1
  243. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallStatus.js +0 -2
  244. package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitation.d.ts +0 -5
  245. package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitation.js +0 -2
  246. package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitationEntry.d.ts +0 -6
  247. package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitationEntry.js +0 -2
  248. package/dist/agent/vendor-types/codex/app-server/v2/PatchApplyStatus.d.ts +0 -1
  249. package/dist/agent/vendor-types/codex/app-server/v2/PatchApplyStatus.js +0 -2
  250. package/dist/agent/vendor-types/codex/app-server/v2/PatchChangeKind.d.ts +0 -8
  251. package/dist/agent/vendor-types/codex/app-server/v2/PatchChangeKind.js +0 -2
  252. package/dist/agent/vendor-types/codex/app-server/v2/SubAgentActivityKind.d.ts +0 -1
  253. package/dist/agent/vendor-types/codex/app-server/v2/SubAgentActivityKind.js +0 -2
  254. package/dist/agent/vendor-types/codex/app-server/v2/TextElement.d.ts +0 -11
  255. package/dist/agent/vendor-types/codex/app-server/v2/TextElement.js +0 -2
  256. package/dist/agent/vendor-types/codex/app-server/v2/ThreadItem.d.ts +0 -215
  257. package/dist/agent/vendor-types/codex/app-server/v2/ThreadItem.js +0 -2
  258. package/dist/agent/vendor-types/codex/app-server/v2/UserInput.d.ts +0 -35
  259. package/dist/agent/vendor-types/codex/app-server/v2/UserInput.js +0 -2
  260. package/dist/agent/vendor-types/codex/app-server/v2/WebSearchAction.d.ts +0 -14
  261. package/dist/agent/vendor-types/codex/app-server/v2/WebSearchAction.js +0 -2
  262. package/dist/cli/services.d.ts +0 -19
  263. package/dist/cli/services.js +0 -34
  264. package/dist/dimension/http.d.ts +0 -7
  265. package/dist/dimension/http.js +0 -77
  266. package/dist/dimension/storage.d.ts +0 -39
  267. package/dist/dimension/storage.js +0 -79
  268. package/dist/portal-gateway/fakes.d.ts +0 -3
  269. package/dist/portal-gateway/fakes.js +0 -11
  270. package/dist/portal-gateway/gatewayStorage.d.ts +0 -66
  271. package/dist/portal-gateway/gatewayStorage.js +0 -125
  272. package/dist/portal-gateway/portalStorage.d.ts +0 -34
  273. package/dist/portal-gateway/portalStorage.js +0 -44
  274. package/dist/sandbox/boatService.d.ts +0 -52
  275. package/dist/sandbox/boatService.js +0 -343
  276. package/dist/sandbox/containerService.d.ts +0 -15
  277. package/dist/sandbox/containerService.js +0 -184
  278. package/dist/sandbox/endpointProxy.d.ts +0 -90
  279. package/dist/sandbox/endpointProxy.js +0 -716
  280. package/dist/sandbox/exeDevAuth.d.ts +0 -16
  281. package/dist/sandbox/exeDevAuth.js +0 -49
  282. package/dist/sandbox/localService.d.ts +0 -19
  283. package/dist/sandbox/localService.js +0 -200
  284. package/dist/sandbox/sseConnection.d.ts +0 -41
  285. package/dist/sandbox/sseConnection.js +0 -166
  286. package/dist/sandbox/sshService.d.ts +0 -30
  287. package/dist/sandbox/sshService.js +0 -680
@@ -1,140 +1,61 @@
1
- import { dimensionStateDirectory } from "../dimension/paths.js";
2
- import { RUN_PATH, STATE_PATH, containerName as dimensionContainerName } from "../dimension/service.js";
3
- import { createContainerSandbox } from "../sandbox/containerService.js";
1
+ import { dimensionLayout } from "../dimension/api.js";
2
+ import { createDockerClient } from "../lib/docker.js";
4
3
  import { InvalidNameError, checkName } from "../lib/jsonDirectory.js";
5
- import { sleep } from "../lib/sleep.js";
6
- import { OS_CA_BUNDLE } from "../portal-gateway/portalService.js";
4
+ import { createAgentStorage } from "./agentStorage.js";
7
5
  import { AgentConflictError, AgentInvalidError, AgentNotFoundError, } from "./api.js";
8
6
  import { createWorkerClient } from "./session/client.js";
9
7
  import { workerSocketConnector } from "./session/workerConnection.js";
10
8
  import { readHostTranscript } from "./hostTranscript.js";
11
9
  import { workerSocketPath } from "./session/sessionStorage.js";
10
+ import { WORKER_PROTOCOL } from "./session/worker/protocol.js";
11
+ import { replacingWorkerClient } from "./workerReplacement.js";
12
+ import { agentsDownScript, createAgentScript, installScript, listAgentsScript, listBackendsScript, workerSocketScript, listSessionsScript, loginStateScript, stopAgentScript, } from "./control.js";
12
13
  import { WORKER_PARAMS } from "./session/worker/workerParams.js";
13
- import { workerBundle } from "./workerBundle.js";
14
- /**
15
- * In the dimension: where the backends are installed (a package with its node_modules). Shared by
16
- * agents. In the state directory, so they last as long as the dimension: a container recreated (or a
17
- * machine whose docker kept nothing) doesn't take them, and nothing is installed twice.
18
- */
19
- export const WORKER_DIR = `${STATE_PATH}/worker`;
20
- /** Where they were before: moved to WORKER_DIR when a worker starts (ensureWorker). */
21
- const OLD_WORKER_DIR = "/opt/hyperbrane/worker";
22
- /**
23
- * In the dimension: the worker's script. Outside WORKER_DIR on purpose: bun caches the listing of the
24
- * directory a script is loaded from, and packages installed there afterwards didn't resolve.
25
- */
26
- export const WORKER_SCRIPT = "/opt/hyperbrane/worker.js";
27
- const START_TIMEOUT_MS = 30_000;
28
- const STOP_TIMEOUT_MS = 10_000;
29
- /** The gateway upstream each backend's CLI signs in to (upstreams/). */
30
- const AUTH_UPSTREAMS = { claude: "anthropic", codex: "openai" };
31
- /** The worker's log in the dimension's run directory (RUN_PATH or its host side), next to its socket. */
32
- function workerLogPath(runDirectory, agentName) {
33
- return `${runDirectory}/${agentName}.log`;
34
- }
35
- // Runs as the dimension's user and returns at once: the worker is detached from the exec, so it
36
- // outlives it (and every later command), until the container stops.
37
- const START_WORKER_SCRIPT = `set -eu
38
- nohup setsid bun "$HYB_WORKER_SCRIPT" >> "$HYB_LOG" 2>&1 < /dev/null &
39
- `;
40
- // Run when a worker won't stop, as the dimension's user, whom the worker and the CLIs it started run
41
- // as: they carry its agent in their environment, which root can't read in a container (docker gives
42
- // it no ptrace capability, and reading another user's environ takes one). The name comes in as
43
- // AGENT: this script itself mustn't match.
44
- const KILL_WORKER_SCRIPT = `for process in /proc/[0-9]*; do
45
- if tr '\\0' '\\n' < "$process/environ" 2>/dev/null | grep -qx "HYB_AGENT=$AGENT"; then kill -9 "\${process#/proc/}" 2>/dev/null || true; fi
46
- done
47
- `;
48
- /**
49
- * Runs each agent as a session worker inside its dimension (agent/session/main.ts) and talks
50
- * JSON-RPC to it over the worker's unix socket, which the host reaches through the dimension's
51
- * mounted run directory (through `host`). Keeps no state between calls beyond the agent records: every call finds
52
- * the worker again.
53
- */
54
- /**
55
- * `gatewaySignIns` tells whether a dimension's gateway already holds a backend's sign-in, so a
56
- * session's login can complete without anyone; without it every login waits for the user.
57
- */
58
- /**
59
- * `host` is the machine docker runs on: workers' sockets and logs are reached through it, and agents
60
- * are exposed through its endpoints when it declares them.
61
- */
62
- export function createAgentService(storage, docker, host, gatewaySignIns,
63
- /** Re-wires a dimension's portals: what a restart (its container's, the machine's) took. */
64
- restoring) {
65
- const endpoints = "exposeEndpoint" in host ? host : null;
14
+ import { createPortalClient } from "../portal-gateway/portalClient.js";
15
+ export function createAgentService(host, { dataDirectory } = {}) {
16
+ const storage = createAgentStorage(host, dataDirectory);
17
+ const docker = createDockerClient(host);
18
+ const restoring = createPortalClient(host, { dataDirectory });
19
+ const controlOptions = { dataDirectory };
66
20
  async function runningDimension(dimensionName) {
67
- const container = dimensionContainerName(dimensionName);
21
+ const container = dimensionLayout.containerName(dimensionName);
68
22
  const inspected = await docker.inspectContainer(container);
69
23
  if (inspected?.Config?.Labels?.["hyperbrane.dimension"] !== dimensionName) {
70
24
  throw new AgentNotFoundError(`dimension ${dimensionName} does not exist`);
71
25
  }
72
26
  if (!inspected.State?.Running)
73
27
  throw new AgentConflictError(`dimension ${dimensionName} is not running`);
74
- const runDirectory = inspected.Mounts?.find((mount) => mount.Destination === RUN_PATH)?.Source;
75
- const hasState = inspected.Mounts?.some((mount) => mount.Destination === STATE_PATH);
28
+ const runDirectory = inspected.Mounts?.find((mount) => mount.Destination === dimensionLayout.runPath())?.Source;
29
+ const hasState = inspected.Mounts?.some((mount) => mount.Destination === dimensionLayout.statePath());
76
30
  if (!runDirectory || !hasState) {
77
- throw new AgentConflictError(`dimension ${dimensionName} lacks the ${RUN_PATH} and ${STATE_PATH} mounts agents need (it predates them); recreate it`);
31
+ throw new AgentConflictError(`dimension ${dimensionName} lacks the ${dimensionLayout.runPath()} and ${dimensionLayout.statePath()} mounts agents need (it predates them); recreate it`);
78
32
  }
79
33
  return { name: dimensionName, container, owner: inspected.Config.User || "0:0", runDirectory };
80
34
  }
81
- /** The worker as it is: for starting, stopping and checking on it. */
82
35
  function rawWorkerClient(dimension, agentName) {
83
36
  return createWorkerClient(workerSocketConnector(host, workerSocketPath(dimension.runDirectory, agentName)));
84
37
  }
85
- /**
86
- * The worker, started again first if it isn't running: it doesn't come back with its container
87
- * (a restart, a host reboot, a Boat sandbox resumed after its auto-stop). A connection that can't
88
- * be made starts it, once, and is tried again.
89
- */
90
- function workerClient(dimension, agentName) {
91
- const connect = workerSocketConnector(host, workerSocketPath(dimension.runDirectory, agentName));
92
- const reviving = async (onNotification) => {
93
- try {
94
- return await connect(onNotification);
95
- }
96
- catch {
97
- await reviveWorker(agentName, dimension);
98
- return connect(onNotification);
99
- }
100
- };
101
- return createWorkerClient(reviving);
102
- }
103
- // One start at a time per agent, however many calls find its worker gone.
104
38
  const reviving = new Map();
105
- function reviveWorker(agentName, dimension) {
39
+ function reviveWorker(agentName, dimensionName, { replaceBuild = false } = {}) {
106
40
  let started = reviving.get(agentName);
107
41
  if (!started) {
108
- // Whatever took the worker down (a restart, a reboot, a Boat resume) took the dimension's
109
- // portals too: without them the worker, and the backends it installs, can't reach anything.
110
- const restorePortals = () => restoring?.restorePortals({ dimensionName: dimension.name });
111
- started = Promise.resolve(restorePortals())
112
- .then(() => ensureWorker(agentName, dimension))
42
+ const restorePortals = async () => restoring.restorePortals({ dimensionName });
43
+ const start = () => createAgentScript(host, { name: agentName, dimensionName }, { ...controlOptions, replaceBuild });
44
+ started = Promise.all([restorePortals(), start()])
45
+ .then(() => { })
113
46
  .catch(async () => {
114
- // Unreachable even freshly started: the container's mounts may no longer be the host's
115
- // directories (it started while they were something else). A restart binds them again.
116
- await docker.restartContainer(dimension.container);
117
- await restorePortals();
118
- await ensureWorker(agentName, dimension);
47
+ await docker.restartContainer(dimensionLayout.containerName(dimensionName));
48
+ await Promise.all([restorePortals(), start()]);
119
49
  })
120
50
  .finally(() => reviving.delete(agentName));
121
51
  reviving.set(agentName, started);
122
52
  }
123
53
  return started;
124
54
  }
125
- /**
126
- * Starts the worker of every agent whose dimension runs but whose worker doesn't answer (after a
127
- * restart took them down); returns their names. Agents whose dimension isn't running are left.
128
- */
129
55
  async function restoreAgents() {
130
- const restored = await Promise.all((await storage.listAgents()).map(async ({ name, dimensionName }) => {
131
- const dimension = await runningDimension(dimensionName).catch(() => null);
132
- if (!dimension)
133
- return null;
134
- if (await rawWorkerClient(dimension, name).call("worker/health", {}).then(() => true, () => false))
135
- return null;
56
+ const restored = await Promise.all((await agentsDownScript(host, controlOptions)).map(async ({ name, dimensionName }) => {
136
57
  try {
137
- await reviveWorker(name, dimension);
58
+ await reviveWorker(name, dimensionName);
138
59
  return name;
139
60
  }
140
61
  catch (error) {
@@ -152,254 +73,72 @@ restoring) {
152
73
  return agent;
153
74
  }
154
75
  async function clientFor(agentName) {
155
- const agent = await requireAgent(agentName);
156
- return workerClient(await runningDimension(agent.dimensionName), agentName);
76
+ assertValidName("agent", agentName);
77
+ const { dimensionName, socket } = await workerSocketScript(host, agentName, controlOptions);
78
+ return replacingWorkerClient(workerSocketConnector(host, socket), async (replaceBuild) => {
79
+ await runningDimension(dimensionName);
80
+ await reviveWorker(agentName, dimensionName, { replaceBuild });
81
+ });
157
82
  }
158
- // Re-running replaces a worker running older code, and starts one that stopped with its container.
159
83
  async function createAgent(agent) {
160
84
  assertValidName("agent", agent.name);
161
85
  assertValidName("dimension", agent.dimensionName);
162
- const dimension = await runningDimension(agent.dimensionName);
163
- const record = await storage.insertAgent({ name: agent.name, dimensionName: agent.dimensionName });
164
- if (record.dimensionName !== agent.dimensionName) {
165
- throw new AgentConflictError(`agent ${agent.name} already exists in dimension ${record.dimensionName}`);
166
- }
167
- await ensureWorker(agent.name, dimension);
86
+ await createAgentScript(host, agent, controlOptions);
168
87
  return { name: agent.name, dimensionName: agent.dimensionName };
169
88
  }
170
- async function ensureWorker(agentName, dimension) {
171
- const { container, owner } = dimension;
172
- const bundle = await workerBundle();
173
- const client = rawWorkerClient(dimension, agentName);
174
- const health = await client.call("worker/health", {}).catch(() => null);
175
- if (health?.build === bundle.build)
176
- return;
177
- const root = createContainerSandbox(docker, container, { user: "root" });
178
- // The run and state directories too: the host created them, possibly as another user than the dimension's.
179
- // Backends installed where they used to be move along, once.
180
- const setup = await root.runCommand([
181
- "sh",
182
- "-c",
183
- 'mkdir -p "$W" "$(dirname "$SCRIPT")" && chown "$OWNER" "$(dirname "$SCRIPT")" "$W" "$R" "$S" && ' +
184
- '{ [ -e "$W/node_modules" ] || [ ! -d "$OLD/node_modules" ] || { cp -a "$OLD/." "$W/" && rm -rf "$OLD"; }; }',
185
- ], { env: { W: WORKER_DIR, SCRIPT: WORKER_SCRIPT, OLD: OLD_WORKER_DIR, R: RUN_PATH, S: STATE_PATH, OWNER: owner } });
186
- if (setup.exitCode !== 0)
187
- throw new Error(`preparing ${container} for agent ${agentName} failed: ${setup.stderr.trim()}`);
188
- const sandbox = createContainerSandbox(docker, container);
189
- await sandbox.writeFile(WORKER_SCRIPT, bundle.source);
190
- await sandbox.writeFileExclusive(`${WORKER_DIR}/package.json`, JSON.stringify({ name: "hyperbrane-worker", private: true }, null, 2) + "\n");
191
- // The gateway intercepts every connection, so installs must trust the bundle portals put its CA in.
192
- await sandbox.writeFile(`${WORKER_DIR}/bunfig.toml`, `[install]\ncafile = "${OS_CA_BUNDLE}"\n`);
193
- if (health)
194
- await stopWorker(agentName, dimension);
195
- // One that doesn't answer may still run (unreachable): it goes, rather than piling up.
196
- else
197
- await killWorker(agentName, dimension);
198
- await startWorker(agentName, dimension, bundle, await socketOwner(agentName, dimension));
199
- }
200
- // The host user (uid:gid on the machine), asked once: it connects to workers' sockets.
201
- let hostUser = null;
202
- function hostUserIds() {
203
- hostUser ??= host.runCommand(["sh", "-c", 'printf "%s:%s" "$(id -u)" "$(id -g)"']).then((result) => {
204
- const [uid, gid] = result.stdout.trim().split(":").map(Number);
205
- if (result.exitCode !== 0 || !Number.isInteger(uid) || !Number.isInteger(gid)) {
206
- throw new Error(`reading the host user's ids failed: ${result.stderr.trim() || result.stdout.trim()}`);
207
- }
208
- return { uid: uid, gid: gid };
209
- });
210
- hostUser.catch(() => (hostUser = null));
211
- return hostUser;
212
- }
213
- /**
214
- * Who the worker's socket must belong to for the host user to connect (a unix socket takes write
215
- * permission): no one else when the dimension runs as the host user; the host user when it runs
216
- * as root (its directory belongs to root), which the worker then hands the socket to. A dimension
217
- * of a third user can't be reached.
218
- */
219
- async function socketOwner(agentName, dimension) {
220
- const runsAs = Number(dimension.owner.split(":")[0]);
221
- const { uid, gid } = await hostUserIds();
222
- if (runsAs === uid)
223
- return undefined;
224
- if (runsAs === 0)
225
- return `${uid}:${gid}`;
226
- throw new AgentConflictError(`agent ${agentName}'s worker would run as uid ${runsAs} (its dimension's directory belongs to that user), whose socket the host user (uid ${uid}) can't connect to; use a directory owned by uid ${uid} or root`);
227
- }
228
- async function startWorker(agentName, dimension, bundle, socketOwnerIds) {
229
- const sandbox = createContainerSandbox(docker, dimension.container);
230
- const home = await sandbox.homeDirectory();
231
- const started = await sandbox.runCommand(["sh", "-c", START_WORKER_SCRIPT], {
232
- cwd: home,
233
- env: {
234
- HOME: home,
235
- HYB_AGENT: agentName,
236
- HYB_LOG: workerLogPath(RUN_PATH, agentName),
237
- HYB_INSTALL_DIR: WORKER_DIR,
238
- HYB_WORKER_SCRIPT: WORKER_SCRIPT,
239
- HYB_WORKER_BUILD: bundle.build,
240
- ...(socketOwnerIds ? { HYB_SOCKET_OWNER: socketOwnerIds } : {}),
241
- NODE_EXTRA_CA_CERTS: OS_CA_BUNDLE,
242
- SSL_CERT_FILE: OS_CA_BUNDLE,
243
- },
244
- });
245
- if (started.exitCode !== 0)
246
- throw new Error(`starting agent ${agentName}'s worker failed: ${started.stderr.trim()}`);
247
- const client = rawWorkerClient(dimension, agentName);
248
- const deadline = Date.now() + START_TIMEOUT_MS;
249
- let lastError = "";
250
- while (Date.now() < deadline) {
251
- const health = await client.call("worker/health", {}).catch((error) => {
252
- lastError = error.message;
253
- return null;
254
- });
255
- if (health?.build === bundle.build)
256
- return;
257
- await sleep(250);
258
- }
259
- const log = await host
260
- .readFile(workerLogPath(dimension.runDirectory, agentName))
261
- .then((bytes) => new TextDecoder().decode(bytes))
262
- .catch(() => "");
263
- // A socket the host user can't write to refuses it; Bun reports that as ENOENT, so say who owns it.
264
- const socket = await host.stat(workerSocketPath(dimension.runDirectory, agentName)).catch(() => null);
265
- const { uid } = await hostUserIds();
266
- const owner = socket && socket.uid !== uid ? `; its socket belongs to uid ${socket.uid}, not the host user's ${uid}` : "";
267
- throw new Error(`agent ${agentName}'s worker did not come up${lastError ? ` (reaching it: ${lastError}${owner})` : owner}:\n${log.slice(-2000)}`);
268
- }
269
- // Asked first, so sessions end cleanly; killed if it doesn't go.
270
- async function stopWorker(agentName, dimension) {
271
- const client = rawWorkerClient(dimension, agentName);
272
- await client.call("worker/shutdown", {}).catch(() => { });
273
- const deadline = Date.now() + STOP_TIMEOUT_MS;
274
- while (Date.now() < deadline) {
275
- if ((await client.call("worker/health", {}).catch(() => null)) === null)
276
- return;
277
- await sleep(250);
278
- }
279
- await killWorker(agentName, dimension);
280
- }
281
- async function killWorker(agentName, dimension) {
282
- await createContainerSandbox(docker, dimension.container).runCommand(["sh", "-c", KILL_WORKER_SCRIPT], { env: { AGENT: agentName } });
283
- }
284
89
  async function stopAgent(agentName) {
285
90
  assertValidName("agent", agentName);
286
- const agent = await storage.readAgent(agentName);
287
- if (!agent)
288
- return;
289
- await closeAgentEndpoints(agentName);
290
- const dimension = await runningDimension(agent.dimensionName).catch(() => null);
291
- if (dimension)
292
- await stopWorker(agentName, dimension);
293
- await storage.removeAgent(agentName);
91
+ await stopAgentScript(host, agentName, controlOptions);
294
92
  }
295
- // Lazy: the stored endpoint is served again if its proxy stopped (a reboot), with the same URL and
296
- // credentials. The proxy connects to the worker's socket per stream, so a worker restart is fine.
297
- // Each way the host is reached (its accessible host) has an endpoint of its own: several server
298
- // records may drive the same machine. Origins only add up: the endpoint is shared, and one caller's
299
- // origins must not cut off another's pages (a narrower set closes nothing). What can run at once does.
300
- async function exposeAgent(agentName, scheme, allowedOrigins = []) {
301
- if (!endpoints)
302
- throw new AgentInvalidError("this host doesn't declare accessible endpoints");
303
- const key = endpoints.accessibleHost;
304
- const [agent, stored] = await Promise.all([requireAgent(agentName), storage.readEndpoints(agentName).then((all) => all[key])]);
93
+ async function workerSocket(agentName) {
94
+ const agent = await requireAgent(agentName);
305
95
  const dimension = await runningDimension(agent.dimensionName);
306
- const [healthy, served] = await Promise.all([
307
- rawWorkerClient(dimension, agentName).call("worker/health", {}).then(() => true, () => false),
308
- stored && stored.credentials.scheme === scheme ? endpoints.ensureEndpoint(stored.id) : false,
309
- ]);
310
- // The client connects to the worker's socket through the endpoint: nothing on its way could start
311
- // a worker a restart took down, so it's started here.
312
- if (!healthy)
313
- await reviveWorker(agentName, dimension);
314
- const origins = [...new Set([...(stored?.allowedOrigins ?? []), ...allowedOrigins])];
315
- if (stored && served) {
316
- if (origins.every((origin) => stored.allowedOrigins.includes(origin)))
317
- return stored;
318
- // Same URL and credentials, so pages already connected stay connected.
319
- if (await endpoints.updateEndpoint(stored.id, { allowedOrigins: origins })) {
320
- const updated = { ...stored, allowedOrigins: origins };
321
- await storage.saveEndpoint(agentName, key, updated);
322
- return updated;
323
- }
324
- }
325
- // Another scheme, or it can't be served as it was given out (closed, its port taken).
326
- if (stored) {
327
- await endpoints.closeEndpoint(stored.id);
328
- await storage.removeEndpoint(agentName, key);
329
- }
330
- const endpoint = await endpoints.exposeEndpoint({
331
- target: { unix: workerSocketPath(dimension.runDirectory, agentName) },
332
- scheme,
333
- transport: "sse",
334
- allowedOrigins: origins,
335
- });
336
- await storage.saveEndpoint(agentName, key, endpoint);
337
- return endpoint;
338
- }
339
- // Every way the host is reached: any sandbox on the machine can close them.
340
- async function closeAgentEndpoints(agentName) {
341
- const stored = Object.values(await storage.readEndpoints(agentName));
342
- if (endpoints)
343
- for (const endpoint of stored)
344
- await endpoints.closeEndpoint(endpoint.id);
345
- await storage.removeEndpoints(agentName);
96
+ const health = await rawWorkerClient(dimension, agentName).call("worker/health", {}).catch(() => null);
97
+ if (health?.protocol !== WORKER_PROTOCOL)
98
+ await reviveWorker(agentName, dimension.name);
99
+ return workerSocketPath(dimension.runDirectory, agentName);
346
100
  }
347
101
  async function listAgents() {
348
- return (await storage.listAgents()).sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
102
+ return (await listAgentsScript(host, controlOptions)).sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
349
103
  }
350
- /** The client of the worker running `ref`'s agent. */
351
104
  async function sessionClient({ agentName }) {
352
105
  return clientFor(agentName);
353
106
  }
354
- /**
355
- * Read on the host from the dimension's state directory (hostTranscript.ts), so nothing has to run,
356
- * neither the worker nor the dimension; only a session whose backend's home is elsewhere asks its
357
- * worker.
358
- */
359
107
  async function getTranscript(ref, request = {}) {
360
108
  const { session, limit, before } = WORKER_PARAMS["session/transcript"]({ session: ref.sessionName, ...request });
109
+ const { toolUseId } = request;
361
110
  const agent = await requireAgent(ref.agentName);
362
- const stateDirectory = dimensionStateDirectory(await storage.dataDirectory(), agent.dimensionName);
363
- const page = await readHostTranscript(host, { stateDirectory, agentName: agent.name, sessionName: session }, { limit, before });
364
- return page ?? (await sessionClient(ref)).call("session/transcript", { session, limit, before });
365
- }
366
- // Agents whose dimension or worker is down have no sessions to show right now.
367
- async function listSessions() {
368
- const perAgent = await Promise.all((await storage.listAgents()).map(async (agent) => {
369
- try {
370
- return await (await clientFor(agent.name)).call("session/list", {});
371
- }
372
- catch (error) {
373
- console.warn(`listing agent ${agent.name}'s sessions failed:`, error.message);
374
- return [];
375
- }
376
- }));
377
- return perAgent.flat();
111
+ const stateDirectory = dimensionLayout.stateDirectory(await storage.dataDirectory(), agent.dimensionName);
112
+ const page = toolUseId === undefined ? await readHostTranscript(host, { stateDirectory, agentName: agent.name, sessionName: session }, { limit, before }) : null;
113
+ if (page)
114
+ return page;
115
+ const worker = await sessionClient(ref);
116
+ if (toolUseId !== undefined)
117
+ return worker.call("session/subagentTranscript", { session, toolUseId, limit, before });
118
+ return worker.call("session/transcript", { session, limit, before });
119
+ }
120
+ function listSessions() {
121
+ return listSessionsScript(host, controlOptions);
378
122
  }
379
123
  async function* getStream(ref, request) {
380
124
  yield* (await sessionClient(ref)).stream(ref.sessionName, request.after_id);
381
125
  }
382
- // Only the host knows whether the dimension's gateway is signed in.
383
126
  async function login(ref) {
384
- const agent = await requireAgent(ref.agentName);
385
- const client = workerClient(await runningDimension(agent.dimensionName), ref.agentName);
386
- const session = (await client.call("session/list", {})).find((candidate) => candidate.name === ref.sessionName);
387
- if (!session)
388
- throw new AgentNotFoundError(`session ${ref.sessionName} does not exist`);
389
- const gatewaySignedIn = (await gatewaySignIns?.(agent.dimensionName, AUTH_UPSTREAMS[session.backend])) ?? false;
390
- return client.call("auth/login", { session: ref.sessionName, gatewaySignedIn });
127
+ assertValidName("session", ref.sessionName);
128
+ const { gatewaySignedIn } = await loginStateScript(host, ref, controlOptions);
129
+ return (await clientFor(ref.agentName)).call("auth/login", { session: ref.sessionName, gatewaySignedIn });
391
130
  }
392
131
  return {
393
- createAgent,
394
- stopAgent,
132
+ createAgent: (agent) => createAgent(agent),
133
+ stopAgent: (agentName) => stopAgent(agentName),
395
134
  listAgents,
396
135
  listSessions,
397
136
  getStream,
398
- login,
399
- exposeAgent,
137
+ login: (ref) => login(ref),
138
+ workerSocket,
400
139
  restoreAgents,
401
- install: async (agentName, backend) => void (await (await clientFor(agentName)).call("backend/install", { backend })),
402
- listBackends: async (agentName) => (await clientFor(agentName)).call("backend/list", {}),
140
+ install: (agentName, backend) => installScript(host, agentName, backend, controlOptions),
141
+ listBackends: (agentName) => listBackendsScript(host, agentName, controlOptions),
403
142
  createSession: async (agentName, session) => (await clientFor(agentName)).call("session/create", { session }),
404
143
  updateSession: async (ref, limits) => (await sessionClient(ref)).call("session/update", { session: ref.sessionName, limits }),
405
144
  stopSession: async (ref) => void (await (await sessionClient(ref)).call("session/stop", { session: ref.sessionName })),
@@ -414,6 +153,7 @@ restoring) {
414
153
  respondQuestionRequest: async (ref, request, response) => void (await (await sessionClient(ref)).call("question/respond", { session: ref.sessionName, request, response })),
415
154
  listModels: async (ref) => (await sessionClient(ref)).call("model/list", { session: ref.sessionName }),
416
155
  setModel: async (ref, model) => void (await (await sessionClient(ref)).call("model/set", { session: ref.sessionName, model })),
156
+ setPermissionMode: async (ref, mode) => void (await (await sessionClient(ref)).call("permission/mode", { session: ref.sessionName, mode })),
417
157
  listBackgroundTasks: async (ref) => (await sessionClient(ref)).call("task/list", { session: ref.sessionName }),
418
158
  stopTask: async (ref, task) => void (await (await sessionClient(ref)).call("task/stop", { session: ref.sessionName, task })),
419
159
  getAuthStatus: async (ref) => (await sessionClient(ref)).call("auth/status", { session: ref.sessionName }),
@@ -1,29 +1,10 @@
1
- import type { Sandbox, SandboxEndpoint } from "../sandbox/api.ts";
1
+ import type { Sandbox } from "../sandbox/api.ts";
2
2
  import type { Agent } from "./api.ts";
3
- /** Every name must pass checkName; operations throw InvalidNameError otherwise. */
4
3
  export interface AgentStorage {
5
- /** Stores the record unless one with that name exists; returns whichever record is stored afterwards. */
6
4
  insertAgent(agent: Agent): Promise<Agent>;
7
- /** Null if missing. */
8
5
  readAgent(agentName: string): Promise<Agent | null>;
9
- /** No-op if missing. */
10
6
  removeAgent(agentName: string): Promise<void>;
11
7
  listAgents(): Promise<Agent[]>;
12
- /** Hyperbrane's data directory these are kept in, with the dimensions' (see dimension/paths.ts). */
13
8
  dataDirectory(): Promise<string>;
14
- /**
15
- * The agent's accessible endpoints (AgentService.exposeAgent), credentials included, by the
16
- * accessible host each was exposed for: one per way the machine is reached.
17
- */
18
- readEndpoints(agentName: string): Promise<Record<string, SandboxEndpoint>>;
19
- saveEndpoint(agentName: string, accessibleHost: string, endpoint: SandboxEndpoint): Promise<void>;
20
- /** No-op if missing. */
21
- removeEndpoint(agentName: string, accessibleHost: string): Promise<void>;
22
- /** All of them. No-op if none. */
23
- removeEndpoints(agentName: string): Promise<void>;
24
9
  }
25
- /**
26
- * `dir` is a sandbox path; defaults to ~/.local/share/hyperbrane of the sandbox's user. Layout:
27
- * agents/<agent>.json, agent-endpoints/<agent>.json.
28
- */
29
10
  export declare function createAgentStorage(sandbox: Sandbox, dir?: string): AgentStorage;
@@ -1,33 +1,7 @@
1
1
  import { createJsonDirectory, defaultDataDir } from "../lib/jsonDirectory.js";
2
- /**
3
- * `dir` is a sandbox path; defaults to ~/.local/share/hyperbrane of the sandbox's user. Layout:
4
- * agents/<agent>.json, agent-endpoints/<agent>.json.
5
- */
6
2
  export function createAgentStorage(sandbox, dir) {
7
3
  const dataDirectory = async () => dir ?? (await defaultDataDir(sandbox));
8
4
  const agents = createJsonDirectory(sandbox, async () => `${await dataDirectory()}/agents`);
9
- // A record used to hold one endpoint; it is read as the entry for its URL's host:port.
10
- const endpoints = createJsonDirectory(sandbox, async () => `${await dataDirectory()}/agent-endpoints`);
11
- async function readEndpoints(agentName) {
12
- const record = await endpoints.read(agentName);
13
- if (!record)
14
- return {};
15
- if (typeof record.id === "string")
16
- return { [new URL(record.url).host]: record };
17
- return record;
18
- }
19
- async function saveEndpoint(agentName, accessibleHost, endpoint) {
20
- await endpoints.save(agentName, { ...(await readEndpoints(agentName)), [accessibleHost]: endpoint });
21
- }
22
- async function removeEndpoint(agentName, accessibleHost) {
23
- const { [accessibleHost]: removed, ...rest } = await readEndpoints(agentName);
24
- if (!removed)
25
- return;
26
- if (Object.keys(rest).length === 0)
27
- await endpoints.remove(agentName);
28
- else
29
- await endpoints.save(agentName, rest);
30
- }
31
5
  async function insertAgent(agent) {
32
6
  return agents.insert(agent.name, agent);
33
7
  }
@@ -46,9 +20,5 @@ export function createAgentStorage(sandbox, dir) {
46
20
  removeAgent,
47
21
  listAgents,
48
22
  dataDirectory,
49
- readEndpoints,
50
- saveEndpoint,
51
- removeEndpoint,
52
- removeEndpoints: (agentName) => endpoints.remove(agentName),
53
23
  };
54
24
  }