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,221 +1,155 @@
1
- export type RunCommandOptions = {
2
- /** Sandbox path; defaults to the sandbox root. */
3
- cwd?: string;
4
- env?: Record<string, string>;
5
- stdin?: string | Uint8Array;
6
- };
7
- export type RunCommandResult = {
8
- readonly exitCode: number;
9
- readonly stdout: string;
10
- readonly stderr: string;
11
- };
1
+ /** @sealed */
2
+ export interface SandboxFileOperations {
3
+ readFile(path: string): Promise<Uint8Array>;
4
+ readFileRange(path: string, start: number, end: number): Promise<Uint8Array>;
5
+ writeFile(path: string, data: string | Uint8Array): Promise<void>;
6
+ writeFileExclusive(path: string, data: string | Uint8Array): Promise<boolean>;
7
+ removeFile(path: string): Promise<void>;
8
+ createDirectory(path: string): Promise<void>;
9
+ removeDirectory(path: string): Promise<void>;
10
+ listDirectory(path: string): Promise<string[]>;
11
+ stat(path: string): Promise<SandboxFileInfo>;
12
+ homeDirectory(): Promise<string>;
13
+ }
14
+ /** @sealed */
15
+ export interface SandboxLifecycle {
16
+ create(spec: SandboxSpec): Promise<[SandboxAuthContext, SandboxDescriptor]>;
17
+ open(): Promise<SandboxDescriptor>;
18
+ get(): Promise<SandboxDescriptor>;
19
+ state(): Promise<"running" | "stopped">;
20
+ start(): Promise<void>;
21
+ destroy(): Promise<void>;
22
+ }
23
+ /** @sealed */
24
+ export interface SandboxCommands {
25
+ runCommand(cmd: string[], options?: RunCommandOptions): Promise<RunCommandResult>;
26
+ }
27
+ /** @sealed */
28
+ export interface SandboxDataEndpoint {
29
+ openEndpoint(name: string): Promise<SandboxEndpoint>;
30
+ closeEndpoint(name: string): Promise<void>;
31
+ listEndpoints(): Promise<SandboxEndpoint[]>;
32
+ credentials(name: string, options?: {
33
+ ttlSeconds?: number;
34
+ }): Promise<SandboxCredentials>;
35
+ }
36
+ /** @sealed */
37
+ export interface SandboxExpiration {
38
+ hasExpiration(): boolean;
39
+ getExpiration(): Promise<Date | null>;
40
+ setExpiration(seconds: number | null): Promise<Date | null>;
41
+ }
42
+ /** @sealed */
43
+ export interface SandboxShell {
44
+ hasShell(): boolean;
45
+ sshEndpoint(): Promise<SandboxSshEndpoint>;
46
+ }
47
+ /** @sealed */
48
+ export interface SandboxNetwork {
49
+ fetch(url: string, init?: SandboxFetchInit): Promise<Response>;
50
+ connect(path: string): Promise<SandboxConnection>;
51
+ }
52
+ /** @sealed */
53
+ export interface SandboxClient extends SandboxFileOperations, SandboxCommands, SandboxNetwork, SandboxLifecycle, SandboxDataEndpoint, SandboxShell, SandboxExpiration {
54
+ }
55
+ /** @sealed */
56
+ export interface SandboxService extends SandboxFileOperations, SandboxCommands, SandboxNetwork, SandboxLifecycle, SandboxDataEndpoint, SandboxShell, SandboxExpiration {
57
+ }
12
58
  export type SandboxFileInfo = {
13
59
  readonly type: "file" | "directory" | "other";
14
60
  readonly uid: number;
15
61
  readonly gid: number;
16
- /** In bytes; what the system reports for anything that isn't a file. */
17
62
  readonly size: number;
18
63
  };
19
64
  export type SandboxFetchInit = {
20
65
  method?: string;
21
66
  headers?: Record<string, string>;
22
67
  body?: string | Uint8Array<ArrayBuffer>;
23
- /** Sandbox path of a unix socket to send the request over (e.g. the docker API socket). */
24
68
  unix?: string;
25
69
  signal?: AbortSignal;
26
- /**
27
- * Idle timeout in milliseconds: the request fails once nothing has been sent or received for this
28
- * long. `false` disables it, for streams that can stay quiet indefinitely. Defaults to the backend's.
29
- */
30
70
  timeout?: number | false;
31
71
  };
32
- /**
33
- * A stream connection to a unix socket inside a sandbox (see Sandbox.connect).
34
- *
35
- * @sealed
36
- */
72
+ /** @sealed */
37
73
  export type SandboxConnection = {
38
74
  write(data: Uint8Array): void;
39
- /** Receives every chunk read from the socket, in order. */
40
75
  onData(listener: (data: Uint8Array) => void): void;
41
- /** Resolves once the connection is closed, by either side or by an error. */
42
76
  closed: Promise<void>;
43
77
  close(): void;
44
78
  };
45
- /** What an accessible endpoint forwards to, inside the sandbox. */
46
- export type SandboxEndpointTarget = {
47
- port: number;
48
- } | {
49
- unix: string;
50
- };
51
- /**
52
- * What a client must present to an accessible endpoint. There is deliberately no unauthenticated
53
- * variant: every endpoint requires one of these.
54
- */
55
- export type SandboxEndpointCredentials =
56
- /** `Authorization: Bearer <token>`. */
57
- {
58
- readonly scheme: "bearer";
79
+ /** @sealed */
80
+ export type SandboxCredentials = {
59
81
  readonly token: string;
60
- }
61
- /** `Authorization: Basic ...`; browsers prompt for it on their own. */
62
- | {
63
- readonly scheme: "basic";
64
- readonly username: string;
65
- readonly password: string;
66
- }
67
- /**
68
- * Logged in with exe.dev, as a user the VM is shared with; exe.dev's proxy checks it (exeDevAuth.ts).
69
- * The login is per port: a browser that isn't logged in on the endpoint's goes through `loginUrl`
70
- * first (exeDevLoginUrl in agent/session/workerSseConnection.ts).
71
- */
72
- | {
73
- readonly scheme: "exe.dev";
74
- readonly loginUrl: string;
75
- };
76
- export type SandboxEndpointScheme = SandboxEndpointCredentials["scheme"];
77
- export type SandboxEndpointRequest = {
78
- target: SandboxEndpointTarget;
79
- /** Required: the endpoint is never open to unauthenticated clients. */
80
- scheme: SandboxEndpointScheme;
81
- /** Port the endpoint listens on; a free one (that its auth allows) if omitted. */
82
- port?: number;
83
- /**
84
- * "http" (the default): the target speaks HTTP, and requests go through to it. "sse": the target
85
- * is a stream socket speaking lines of text (an agent worker's JSON-RPC); each `GET /` opens a
86
- * connection to it and streams its lines back as server-sent events, and `POST /<connection>`
87
- * writes the request body to it (see endpointProxy.ts).
88
- */
89
- transport?: "http" | "sse";
90
- /**
91
- * Web origins (e.g. "https://app.example.com") whose pages may call the endpoint from a browser:
92
- * the proxy answers their CORS preflights and lets them read "sse" responses. None by default.
93
- */
94
- allowedOrigins?: string[];
82
+ readonly exeDev?: {
83
+ readonly token: string;
84
+ };
95
85
  };
96
86
  export type SandboxEndpoint = {
97
- readonly id: string;
98
- /**
99
- * Where the client reaches the endpoint, directly over the network (not through the backend's own
100
- * connection), at the host the sandbox was created with. It lasts until the endpoint is closed,
101
- * but what serves it may stop (a reboot, an archived Boat sandbox): ensureEndpoint starts it again.
102
- * Until then a browser reports a request to it as a CORS error, since nothing of the endpoint's answers.
103
- */
87
+ readonly name: string;
104
88
  readonly url: string;
105
- /** What the client presents; requests without it are refused before reaching the target. */
106
- readonly credentials: SandboxEndpointCredentials;
107
- /** As requested, or as updateEndpoint changed them since. */
108
- readonly allowedOrigins: string[];
89
+ readonly port: number;
90
+ readonly secret: string;
109
91
  };
110
- /**
111
- * Paths are sandbox paths: relative or absolute, both resolved against the sandbox root.
112
- * Operations that report a missing path throw SandboxNotFoundError, whatever the backend.
113
- *
114
- * @sealed
115
- */
116
- export interface SandboxFileOperations {
117
- /** Throws SandboxNotFoundError if missing. */
118
- readFile(path: string): Promise<Uint8Array>;
119
- /**
120
- * Bytes `start` up to `end` (exclusive) of the file, fewer where the file ends sooner, so a large
121
- * file can be read a part at a time. Throws SandboxNotFoundError if missing.
122
- */
123
- readFileRange(path: string, start: number, end: number): Promise<Uint8Array>;
124
- /** Atomically replaces the file (readers never see partial content). Creates missing parent directories. */
125
- writeFile(path: string, data: string | Uint8Array): Promise<void>;
126
- /**
127
- * Writes the file only if it doesn't exist yet; returns false if it does. Atomic across concurrent
128
- * callers and never exposes partial content. Creates missing parent directories.
129
- */
130
- writeFileExclusive(path: string, data: string | Uint8Array): Promise<boolean>;
131
- /** No-op if missing. */
132
- removeFile(path: string): Promise<void>;
133
- /** Creates missing parents; no-op if it exists. */
134
- createDirectory(path: string): Promise<void>;
135
- /** Removes the directory and everything in it. No-op if missing. */
136
- removeDirectory(path: string): Promise<void>;
137
- /** Entry names, unsorted. Throws SandboxNotFoundError if missing. */
138
- listDirectory(path: string): Promise<string[]>;
139
- /** Throws SandboxNotFoundError if missing. */
140
- stat(path: string): Promise<SandboxFileInfo>;
141
- /** Sandbox path of the home directory of the user operations run as. */
142
- homeDirectory(): Promise<string>;
143
- }
144
- /** @sealed */
145
- export interface SandboxCommands {
146
- runCommand(cmd: string[], options?: RunCommandOptions): Promise<RunCommandResult>;
147
- }
148
- /**
149
- * Reaching into the sandbox from the caller's side.
150
- *
151
- * @sealed
152
- */
153
- export interface SandboxNetwork {
154
- /** HTTP request made from inside the sandbox. */
155
- fetch(url: string, init?: SandboxFetchInit): Promise<Response>;
156
- /** Opens a stream connection to the unix socket at `path` (e.g. an agent worker's). */
157
- connect(path: string): Promise<SandboxConnection>;
158
- }
159
- /**
160
- * Makes something listening in the sandbox reachable from the SDK's client over the network, without
161
- * going through the backend's own connection. Declared when the sandbox is created (with the host
162
- * clients reach it at), by the backends that can. Always authenticated: a scheme the sandbox's auth
163
- * can't enforce is refused with SandboxEndpointError. An endpoint lasts until it is closed, past the
164
- * sandbox object that exposed it.
165
- *
166
- * @sealed
167
- */
168
- export interface SandboxAccessibleEndpoint {
169
- /**
170
- * How clients reach the machine, as the sandbox was created with ("host" or "host:port"). Several
171
- * sandboxes may drive the same machine, each reached its own way: their endpoints are their own.
172
- */
173
- readonly accessibleHost: string;
174
- exposeEndpoint(request: SandboxEndpointRequest): Promise<SandboxEndpoint>;
175
- /** Stops accepting connections and revokes its credentials. No-op if already closed. */
176
- closeEndpoint(endpointId: string): Promise<void>;
177
- /**
178
- * Makes sure it is served, starting its proxy again if that stopped (a reboot), with the same URL
179
- * and credentials; false if it was closed, or its port has since been taken (expose another).
180
- */
181
- ensureEndpoint(endpointId: string): Promise<boolean>;
182
- /**
183
- * Changes which web origins may call the endpoint, in place: its URL and credentials stay, and so
184
- * do connections already open. False if it was closed.
185
- */
186
- updateEndpoint(endpointId: string, update: {
187
- allowedOrigins: string[];
188
- }): Promise<boolean>;
189
- }
190
- /**
191
- * A sandbox that stops itself on a timer (Boat).
192
- *
193
- * @sealed
194
- */
195
- export interface SandboxExpiration {
196
- /** When it stops itself; null if it never does. */
197
- getExpiration(): Promise<Date | null>;
198
- /** Makes it stop itself `seconds` from now; null: never. Returns when it now will. */
199
- setExpiration(seconds: number | null): Promise<Date | null>;
200
- }
201
- /** Whether the sandbox has the SandboxExpiration capability. */
202
- export declare function hasExpiration<T extends object>(sandbox: T): sandbox is T & SandboxExpiration;
203
- /**
204
- * What every backend provides.
205
- *
206
- * @sealed
207
- */
208
- export type Sandbox = SandboxFileOperations & SandboxCommands & SandboxNetwork & {
209
- /** Releases what the sandbox holds open (a remote connection). Optional: most hold nothing. */
210
- close?(): void;
92
+ export type SandboxAccess = {
93
+ readonly url: string;
94
+ readonly credentials: SandboxCredentials;
95
+ };
96
+ export type RunCommandOptions = {
97
+ cwd?: string;
98
+ env?: Record<string, string>;
99
+ stdin?: string | Uint8Array;
100
+ };
101
+ export type RunCommandResult = {
102
+ readonly exitCode: number;
103
+ readonly stdout: string;
104
+ readonly stderr: string;
105
+ };
106
+ export type SandboxSshEndpoint = {
107
+ readonly host: string;
108
+ readonly port: number;
109
+ readonly user: string;
110
+ readonly privateKey: string;
111
+ };
112
+ export type SandboxAuthContext = {
113
+ kind: "boat" | "exe.dev" | "ssh" | "local";
114
+ id?: string;
115
+ sshHost?: string;
116
+ sshUser?: string;
117
+ sshPort?: number;
118
+ sshKey?: string;
119
+ apikey?: string;
120
+ };
121
+ export type SandboxDataEndpointOptions = {
122
+ url?: string;
123
+ ports?: {
124
+ from: number;
125
+ to: number;
126
+ };
127
+ needsFirewall?: boolean;
128
+ };
129
+ export type SandboxSpec = {
130
+ name: string;
131
+ dataEndpointOptions?: SandboxDataEndpointOptions;
132
+ };
133
+ export type SandboxDescriptor = {
134
+ id: string;
135
+ kind: SandboxAuthContext["kind"];
136
+ name: string;
137
+ sshHost?: string;
138
+ sshUser?: string;
139
+ sshPort?: number;
140
+ dataEndpointOptions?: SandboxDataEndpointOptions;
211
141
  };
142
+ /** @sealed */
143
+ export type Sandbox = SandboxFileOperations & SandboxCommands & SandboxNetwork & {};
212
144
  export declare class SandboxPathError extends Error {
213
145
  name: string;
214
146
  }
215
147
  export declare class SandboxNotFoundError extends Error {
216
148
  name: string;
217
149
  }
218
- /** The endpoint can't be exposed as asked (a scheme or port its auth doesn't allow, ...). */
219
- export declare class SandboxEndpointError extends Error {
150
+ export declare class SandboxStoppedError extends Error {
151
+ name: string;
152
+ }
153
+ export declare class SandboxConnectionRefusedError extends Error {
220
154
  name: string;
221
155
  }
@@ -1,14 +1,12 @@
1
- /** Whether the sandbox has the SandboxExpiration capability. */
2
- export function hasExpiration(sandbox) {
3
- return "getExpiration" in sandbox && "setExpiration" in sandbox;
4
- }
5
1
  export class SandboxPathError extends Error {
6
2
  name = "SandboxPathError";
7
3
  }
8
4
  export class SandboxNotFoundError extends Error {
9
5
  name = "SandboxNotFoundError";
10
6
  }
11
- /** The endpoint can't be exposed as asked (a scheme or port its auth doesn't allow, ...). */
12
- export class SandboxEndpointError extends Error {
13
- name = "SandboxEndpointError";
7
+ export class SandboxStoppedError extends Error {
8
+ name = "SandboxStoppedError";
9
+ }
10
+ export class SandboxConnectionRefusedError extends Error {
11
+ name = "SandboxConnectionRefusedError";
14
12
  }
@@ -0,0 +1,2 @@
1
+ import type { SandboxAuthContext, SandboxClient } from "./api.ts";
2
+ export declare function createSandboxClient(ctx: SandboxAuthContext): SandboxClient;
@@ -0,0 +1,4 @@
1
+ import { createSandboxService } from "./service.js";
2
+ export function createSandboxClient(ctx) {
3
+ return createSandboxService(ctx);
4
+ }
@@ -0,0 +1,13 @@
1
+ import { type Sandbox, type SandboxCredentials, type SandboxDataEndpoint, type SandboxEndpoint } from "../api.ts";
2
+ import type { SandboxStorage } from "../storage/sandboxStorage.ts";
3
+ export type EndpointPorts = {
4
+ ports(): Promise<{
5
+ from: number;
6
+ to: number;
7
+ }>;
8
+ expose(port: number): Promise<string>;
9
+ conceal(port: number): Promise<void>;
10
+ credentials?(endpoint: SandboxEndpoint, ttlSeconds: number): Promise<Omit<SandboxCredentials, "token">>;
11
+ };
12
+ export declare function signToken(secret: string, ttlSeconds: number): Promise<string>;
13
+ export declare function createEndpoints(sandbox: Sandbox, storage: SandboxStorage, ports: EndpointPorts): SandboxDataEndpoint;
@@ -0,0 +1,100 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { SignJWT } from "jose";
3
+ import { sh } from "../../lib/sh.js";
4
+ import { SandboxNotFoundError } from "../api.js";
5
+ const DEFAULT_TTL_S = 12 * 3600;
6
+ export function signToken(secret, ttlSeconds) {
7
+ return new SignJWT()
8
+ .setProtectedHeader({ alg: "HS256" })
9
+ .setExpirationTime(`${ttlSeconds}s`)
10
+ .sign(new TextEncoder().encode(secret));
11
+ }
12
+ async function listeningPorts(sandbox) {
13
+ const listed = await sandbox.runCommand(["sh", "-c", LISTENING_PORTS]);
14
+ if (listed.exitCode !== 0)
15
+ throw new Error(`listing the ports in use failed: ${listed.stderr.trim()}`);
16
+ return listed.stdout.split("\n").filter(Boolean).map(Number);
17
+ }
18
+ async function withLock(sandbox, body) {
19
+ const lock = `${await sandbox.homeDirectory()}/.local/state/hyperbrane/endpoints.lock`;
20
+ const taken = await sandbox.runCommand(["sh", "-c", TAKE_LOCK, "sh", lock]);
21
+ if (taken.exitCode !== 0)
22
+ throw new Error(`${lock} stayed locked: ${taken.stderr.trim()}`);
23
+ try {
24
+ return await body();
25
+ }
26
+ finally {
27
+ await sandbox.runCommand(["rm", "-rf", lock]);
28
+ }
29
+ }
30
+ function under(base, endpoint) {
31
+ if (base === undefined || new URL(base).pathname === "/")
32
+ return endpoint;
33
+ return { ...endpoint, url: `${base.replace(/\/+$/, "")}/${endpoint.name}` };
34
+ }
35
+ export function createEndpoints(sandbox, storage, ports) {
36
+ const located = async (endpoints) => {
37
+ const base = (await storage.readDescriptor())?.dataEndpointOptions?.url;
38
+ return endpoints.map((endpoint) => under(base, endpoint));
39
+ };
40
+ async function openEndpoint(name) {
41
+ const [endpoint] = await located([await openStored(name)]);
42
+ return endpoint;
43
+ }
44
+ async function openStored(name) {
45
+ return withLock(sandbox, async () => {
46
+ const existing = await storage.readEndpoint(name);
47
+ if (existing)
48
+ return existing;
49
+ const { from, to } = await ports.ports();
50
+ const used = new Set([...(await storage.listEndpoints()).map((endpoint) => endpoint.port), ...(await listeningPorts(sandbox))]);
51
+ let port = from;
52
+ while (port <= to && used.has(port))
53
+ port++;
54
+ if (port > to)
55
+ throw new Error(`no data endpoint ports are left in ${from}-${to}`);
56
+ const endpoint = { name, url: await ports.expose(port), port, secret: randomBytes(32).toString("base64url") };
57
+ await storage.saveEndpoint(endpoint);
58
+ return endpoint;
59
+ });
60
+ }
61
+ async function closeEndpoint(name) {
62
+ await withLock(sandbox, async () => {
63
+ const endpoint = await storage.readEndpoint(name);
64
+ if (!endpoint)
65
+ return;
66
+ await ports.conceal(endpoint.port);
67
+ await storage.removeEndpoint(name);
68
+ });
69
+ }
70
+ async function credentials(name, { ttlSeconds = DEFAULT_TTL_S, } = {}) {
71
+ const endpoint = await storage.readEndpoint(name);
72
+ if (!endpoint)
73
+ throw new SandboxNotFoundError(`there is no data endpoint ${name}`);
74
+ return { token: await signToken(endpoint.secret, ttlSeconds), ...(await ports.credentials?.(endpoint, ttlSeconds)) };
75
+ }
76
+ return {
77
+ openEndpoint,
78
+ closeEndpoint,
79
+ listEndpoints: async () => located(await storage.listEndpoints()),
80
+ credentials,
81
+ };
82
+ }
83
+ const LISTENING_PORTS = sh `
84
+ awk 'FNR > 1 && $4 == "0A" { n = split($2, local, ":"); print local[n] }' /proc/net/tcp /proc/net/tcp6 2> /dev/null | while read -r hex; do
85
+ printf '%d\n' "0x$hex"
86
+ done
87
+ `;
88
+ const TAKE_LOCK = sh `
89
+ mkdir -p "$(dirname "$1")"
90
+ waited=0
91
+ until mkdir "$1" 2> /dev/null; do
92
+ if [ -n "$(find "$1" -maxdepth 0 -mmin +2 2> /dev/null)" ]; then
93
+ rm -rf "$1"
94
+ continue
95
+ fi
96
+ [ "$waited" -lt 1200 ] || exit 1
97
+ sleep 0.1
98
+ waited=$((waited + 1))
99
+ done
100
+ `;
@@ -0,0 +1,4 @@
1
+ import type { Sandbox } from "../api.ts";
2
+ import type { SandboxStorage } from "../storage/sandboxStorage.ts";
3
+ import type { EndpointPorts } from "./dataEndpoints.ts";
4
+ export declare function firewalledPorts(sandbox: Sandbox, storage: SandboxStorage): EndpointPorts;
@@ -0,0 +1,28 @@
1
+ export function firewalledPorts(sandbox, storage) {
2
+ const options = async () => (await storage.readDescriptor())?.dataEndpointOptions;
3
+ async function firewall(rule) {
4
+ const result = await sandbox.runCommand(["sudo", "-n", "ufw", ...rule]);
5
+ if (result.exitCode !== 0)
6
+ throw new Error(`ufw ${rule.join(" ")} failed: ${result.stderr.trim()}`);
7
+ }
8
+ return {
9
+ async ports() {
10
+ const ports = (await options())?.ports;
11
+ if (!ports)
12
+ throw new Error("this machine has no data endpoints: it's created with dataEndpointOptions.ports");
13
+ return ports;
14
+ },
15
+ async expose(port) {
16
+ const found = await options();
17
+ if (found?.needsFirewall)
18
+ await firewall(["allow", `${port}/tcp`]);
19
+ const url = new URL(found?.url ?? "http://127.0.0.1");
20
+ url.port = String(port);
21
+ return url.toString().replace(/\/$/, "");
22
+ },
23
+ async conceal(port) {
24
+ if ((await options())?.needsFirewall)
25
+ await firewall(["delete", "allow", `${port}/tcp`]);
26
+ },
27
+ };
28
+ }
@@ -0,0 +1,3 @@
1
+ import { type Duplex } from "node:stream";
2
+ import type { SandboxFetchInit } from "../api.ts";
3
+ export declare function httpOver(url: string, connection: () => Duplex, { method, headers, body, signal, timeout }: Omit<SandboxFetchInit, "unix">): Promise<Response>;
@@ -0,0 +1,21 @@
1
+ import { request as httpRequest } from "node:http";
2
+ import { Readable } from "node:stream";
3
+ export function httpOver(url, connection, { method = "GET", headers = {}, body, signal, timeout }) {
4
+ return new Promise((resolve, reject) => {
5
+ const target = new URL(url);
6
+ const request = httpRequest({ createConnection: connection, path: `${target.pathname}${target.search}`, method, headers: { host: target.host, ...headers }, signal }, (response) => {
7
+ const responseHeaders = new Headers();
8
+ for (let i = 0; i + 1 < response.rawHeaders.length; i += 2)
9
+ responseHeaders.append(response.rawHeaders[i], response.rawHeaders[i + 1]);
10
+ const status = response.statusCode ?? 502;
11
+ const nullBody = status === 204 || status === 304 || method === "HEAD";
12
+ if (nullBody)
13
+ response.resume();
14
+ resolve(new Response(nullBody ? null : Readable.toWeb(response), { status, headers: responseHeaders }));
15
+ });
16
+ request.once("error", reject);
17
+ if (typeof timeout === "number")
18
+ request.setTimeout(timeout, () => request.destroy(new Error(`fetch ${url}: no data for ${timeout}ms`)));
19
+ request.end(body === undefined ? undefined : typeof body === "string" ? body : Buffer.from(body));
20
+ });
21
+ }
@@ -0,0 +1,10 @@
1
+ import { type RunCommandOptions, type RunCommandResult, type SandboxFileOperations } from "../api.ts";
2
+ export declare const MISSING = 44;
3
+ export type CommandMachine = {
4
+ runCommand(cmd: string[], options?: RunCommandOptions): Promise<RunCommandResult>;
5
+ readBytes(path: string, start?: number, end?: number): Promise<Uint8Array>;
6
+ putFile(path: string, data: Uint8Array, exclusive: boolean): Promise<boolean>;
7
+ };
8
+ export declare function commandFileOperations(machine: CommandMachine): SandboxFileOperations;
9
+ export declare const PUT_FILE: string;
10
+ export declare const READ_BYTES: string;
@@ -0,0 +1,70 @@
1
+ import { posix } from "node:path";
2
+ import { sh } from "../../lib/sh.js";
3
+ import { SandboxNotFoundError } from "../api.js";
4
+ export const MISSING = 44;
5
+ function remote(path) {
6
+ return posix.resolve("/", path);
7
+ }
8
+ function bytes(data) {
9
+ return typeof data === "string" ? new TextEncoder().encode(data) : data;
10
+ }
11
+ async function script(machine, what, path, body) {
12
+ const result = await machine.runCommand(["sh", "-c", body, "sh", path], { env: { MISSING: String(MISSING) } });
13
+ if (result.exitCode === MISSING)
14
+ throw new SandboxNotFoundError(`Not found: "${path}"`);
15
+ if (result.exitCode !== 0)
16
+ throw new Error(`${what} ${path} failed: ${result.stderr.trim()}`);
17
+ return result.stdout;
18
+ }
19
+ export function commandFileOperations(machine) {
20
+ return {
21
+ readFile: (path) => machine.readBytes(remote(path)),
22
+ readFileRange: (path, start, end) => machine.readBytes(remote(path), start, end),
23
+ writeFile: async (path, data) => void (await machine.putFile(remote(path), bytes(data), false)),
24
+ writeFileExclusive: (path, data) => machine.putFile(remote(path), bytes(data), true),
25
+ removeFile: async (path) => void (await script(machine, "removing", remote(path), REMOVE_FILE)),
26
+ createDirectory: async (path) => void (await script(machine, "creating", remote(path), CREATE_DIRECTORY)),
27
+ removeDirectory: async (path) => void (await script(machine, "removing", remote(path), REMOVE_DIRECTORY)),
28
+ listDirectory: async (path) => (await script(machine, "listing", remote(path), LIST_DIRECTORY)).split("\0").filter(Boolean),
29
+ async stat(path) {
30
+ const [type, uid, gid, size] = (await script(machine, "reading", remote(path), STAT)).trim().split(" ");
31
+ return { type: type, uid: Number(uid), gid: Number(gid), size: Number(size) };
32
+ },
33
+ homeDirectory: async () => (await script(machine, "reading", "~", HOME)).trim(),
34
+ };
35
+ }
36
+ export const PUT_FILE = sh `
37
+ set -eu
38
+ mkdir -p "$(dirname "$1")"
39
+ staged="$(dirname "$1")/.$(basename "$1").$$.tmp"
40
+ trap 'rm -f "$staged"' EXIT
41
+ cat > "$staged"
42
+ if [ "$EXCLUSIVE" = 1 ]; then
43
+ ln "$staged" "$1" 2> /dev/null || exit "$EXISTS"
44
+ else
45
+ mv -f "$staged" "$1"
46
+ fi
47
+ `;
48
+ export const READ_BYTES = sh `
49
+ [ -f "$1" ] || exit "$MISSING"
50
+ if [ -z "$START" ]; then exec cat "$1"; fi
51
+ tail -c "+$((START + 1))" "$1" | head -c "$((END - START))"
52
+ `;
53
+ const REMOVE_FILE = sh `rm -f "$1"`;
54
+ const CREATE_DIRECTORY = sh `mkdir -p "$1"`;
55
+ const REMOVE_DIRECTORY = sh `rm -rf "$1"`;
56
+ const LIST_DIRECTORY = sh `
57
+ [ -e "$1" ] || exit "$MISSING"
58
+ cd "$1" || exit 1
59
+ for name in .* *; do
60
+ case "$name" in . | ..) continue ;; esac
61
+ [ -e "$name" ] || [ -L "$name" ] || continue
62
+ printf '%s\0' "$name"
63
+ done
64
+ `;
65
+ const STAT = sh `
66
+ [ -e "$1" ] || exit "$MISSING"
67
+ if [ -f "$1" ]; then kind="file"; elif [ -d "$1" ]; then kind="directory"; else kind="other"; fi
68
+ printf '%s %s\n' "$kind" "$(stat -c '%u %g %s' "$1")"
69
+ `;
70
+ const HOME = sh `printf '%s\n' "$HOME"`;
@@ -0,0 +1,3 @@
1
+ import type { RunCommandOptions } from "../api.ts";
2
+ export declare function quote(word: string): string;
3
+ export declare function commandLine(cmd: string[], { cwd, env }?: Pick<RunCommandOptions, "cwd" | "env">): string;
@@ -0,0 +1,7 @@
1
+ export function quote(word) {
2
+ return `'${word.replaceAll("'", `'\\''`)}'`;
3
+ }
4
+ export function commandLine(cmd, { cwd, env = {} } = {}) {
5
+ const words = ["env", ...Object.entries(env).map(([name, value]) => `${name}=${value}`), ...cmd].map(quote).join(" ");
6
+ return cwd === undefined ? `exec ${words}` : `cd ${quote(cwd)} && exec ${words}`;
7
+ }
@@ -0,0 +1,2 @@
1
+ import { type SandboxAuthContext, type SandboxService } from "../../api.ts";
2
+ export declare function createBoatService(ctx: SandboxAuthContext): SandboxService;