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,6 +1,5 @@
1
1
  import type { components, operations } from "./docker-api.ts";
2
2
  import type { Sandbox } from "../sandbox/api.ts";
3
- /** Engine API version the client speaks. After changing it, run `bun run generate:docker-api`. */
4
3
  export declare const DOCKER_API_VERSION = "v1.44";
5
4
  type Schemas = components["schemas"];
6
5
  export type ContainerInspect = operations["ContainerInspect"]["responses"][200]["content"]["application/json"];
@@ -11,16 +10,13 @@ export type ImageSummary = Schemas["ImageSummary"];
11
10
  export type DockerEvent = Schemas["EventMessage"];
12
11
  export type ContainerResources = Pick<Schemas["Resources"], "Memory" | "MemorySwap" | "NanoCpus">;
13
12
  export type ContainerStats = {
14
- /** Bytes, excluding reclaimable page cache (same number `docker stats` shows). */
15
13
  memoryUsage: number;
16
- /** Cores in use, e.g. 0.25 = a quarter of one core. */
17
14
  cpuUsage: number;
18
15
  };
19
16
  export type ExecOptions = {
20
17
  cmd: string[];
21
18
  user?: string;
22
19
  env?: Record<string, string>;
23
- /** Defaults to the container's working directory. */
24
20
  workdir?: string;
25
21
  };
26
22
  export type ExecResult = {
@@ -30,7 +26,6 @@ export type ExecResult = {
30
26
  };
31
27
  export type BuildOptions = {
32
28
  tag: string;
33
- /** Build context: file path inside the context -> file contents. Must include "Dockerfile". */
34
29
  context: Record<string, string>;
35
30
  labels?: Record<string, string>;
36
31
  };
@@ -38,41 +33,24 @@ export type BuildOptions = {
38
33
  export interface DockerClient {
39
34
  imageExists(image: string): Promise<boolean>;
40
35
  buildImage(options: BuildOptions): Promise<void>;
41
- /** Filters use docker's syntax, e.g. { label: ["hyperbrane"], reference: ["hyperbrane-dimension"] }. */
42
36
  listImages(filters: Record<string, string[]>): Promise<ImageSummary[]>;
43
- /** Removes a tag (and the image, once untagged). No-op if missing; DockerError 409 while a container uses it. */
44
37
  removeImage(image: string): Promise<void>;
45
38
  inspectContainer(name: string): Promise<ContainerInspect | null>;
46
39
  listContainers(label: string): Promise<ContainerSummary[]>;
47
- /** Throws DockerError with status 409 if a container with this name already exists. */
48
40
  createContainer(name: string, spec: ContainerCreateSpec): Promise<void>;
49
- /** No-op if the container is already running. */
50
41
  startContainer(name: string): Promise<void>;
51
- /** No-op if the container is already stopped. */
52
42
  stopContainer(name: string): Promise<void>;
53
- /** Stops the container (if running) and starts it again. */
54
43
  restartContainer(name: string): Promise<void>;
55
- /** Force-removes the container and its anonymous volumes. No-op if it doesn't exist. */
56
44
  removeContainer(name: string): Promise<void>;
57
45
  containerStats(name: string): Promise<ContainerStats>;
58
- /** Changes a container's resource limits in place: no restart. */
59
46
  updateContainer(name: string, resources: ContainerResources): Promise<void>;
60
47
  exec(name: string, options: ExecOptions): Promise<ExecResult>;
61
- /**
62
- * Extracts `files` (path relative to `directory` -> contents) into the container, owned by root.
63
- * `directory` must exist. DockerError 404 if the container or the directory doesn't.
64
- */
65
48
  putArchive(name: string, directory: string, files: Record<string, string | Uint8Array>): Promise<void>;
66
- /**
67
- * Streams events as they happen, e.g. filters { type: ["container"], event: ["start"] }. Ends when
68
- * `signal` aborts (quietly) or the daemon drops the connection.
69
- */
70
49
  events(filters: Record<string, string[]>, signal: AbortSignal): AsyncIterable<DockerEvent>;
71
50
  }
72
51
  export declare class DockerError extends Error {
73
52
  readonly status: number;
74
53
  constructor(status: number, message: string);
75
54
  }
76
- /** Talks to the docker daemon whose API socket is at `socketPath` inside `sandbox`. */
77
55
  export declare function createDockerClient(sandbox: Sandbox, socketPath?: string): DockerClient;
78
56
  export {};
@@ -1,6 +1,5 @@
1
1
  import createClient from "openapi-fetch";
2
2
  import { createTar } from "./tar.js";
3
- /** Engine API version the client speaks. After changing it, run `bun run generate:docker-api`. */
4
3
  export const DOCKER_API_VERSION = "v1.44";
5
4
  export class DockerError extends Error {
6
5
  status;
@@ -10,14 +9,12 @@ export class DockerError extends Error {
10
9
  this.name = "DockerError";
11
10
  }
12
11
  }
13
- /** Returns the data of a successful call (or of one whose status is listed in `allowed`), else throws DockerError. */
14
12
  function unwrap({ data, error, response }, allowed = []) {
15
13
  if (response.ok || allowed.includes(response.status))
16
14
  return data;
17
15
  const message = error?.message ?? response.statusText;
18
16
  throw new DockerError(response.status, message);
19
17
  }
20
- /** Talks to the docker daemon whose API socket is at `socketPath` inside `sandbox`. */
21
18
  export function createDockerClient(sandbox, socketPath = "/var/run/docker.sock") {
22
19
  function sandboxFetch(timeout) {
23
20
  return async (request) => sandbox.fetch(request.url, {
@@ -40,12 +37,10 @@ export function createDockerClient(sandbox, socketPath = "/var/run/docker.sock")
40
37
  const result = await api.POST("/build", {
41
38
  params: { query: { t: tag, labels: JSON.stringify(labels), rm: true, forcerm: true } },
42
39
  headers: { "Content-type": "application/x-tar" },
43
- // The spec models the binary tar stream as a string.
44
40
  body: tar,
45
41
  bodySerializer: (body) => body,
46
42
  parseAs: "text",
47
43
  });
48
- // The build streams JSON lines; failures arrive in-band with HTTP 200.
49
44
  const log = [];
50
45
  for (const line of (unwrap(result) ?? "").split("\n").filter(Boolean)) {
51
46
  const event = JSON.parse(line);
@@ -93,7 +88,6 @@ export function createDockerClient(sandbox, socketPath = "/var/run/docker.sock")
93
88
  unwrap(await api.POST("/containers/{id}/update", { params: { path: { id: name } }, body: resources }));
94
89
  }
95
90
  async function containerStats(name) {
96
- // stream=false without one-shot waits for two samples, which the CPU delta needs.
97
91
  const result = await api.GET("/containers/{id}/stats", {
98
92
  params: { path: { id: name }, query: { stream: false } },
99
93
  });
@@ -126,7 +120,6 @@ export function createDockerClient(sandbox, socketPath = "/var/run/docker.sock")
126
120
  const result = await api.PUT("/containers/{id}/archive", {
127
121
  params: { path: { id: name }, query: { path: directory } },
128
122
  headers: { "Content-type": "application/x-tar" },
129
- // The spec models the binary tar stream as a string.
130
123
  body: tar,
131
124
  bodySerializer: (body) => body,
132
125
  parseAs: "text",
@@ -139,13 +132,11 @@ export function createDockerClient(sandbox, socketPath = "/var/run/docker.sock")
139
132
  params: { query: { filters: JSON.stringify(filters) } },
140
133
  parseAs: "stream",
141
134
  signal,
142
- // The stream stays silent while nothing happens; an idle timeout would cut it.
143
135
  fetch: sandboxFetch(false),
144
136
  });
145
137
  const body = unwrap(result);
146
138
  if (!body)
147
139
  return;
148
- // One JSON object per line.
149
140
  const decoder = new TextDecoder();
150
141
  let buffered = "";
151
142
  for await (const chunk of body) {
@@ -193,7 +184,6 @@ function toContainerStats(raw) {
193
184
  const cpuUsage = systemDelta > 0 && cpuDelta > 0 ? (cpuDelta / systemDelta) * cpus : 0;
194
185
  return { memoryUsage, cpuUsage };
195
186
  }
196
- /** Splits docker's multiplexed attach stream: 8-byte header (stream id, 3 pad, uint32 BE size) per frame. */
197
187
  function demuxStream(bytes) {
198
188
  const decoder = new TextDecoder();
199
189
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
@@ -1,14 +1,11 @@
1
1
  import { type DockerClient } from "./docker.ts";
2
2
  export type ImageSpec = {
3
3
  repository: string;
4
- /** Bump whenever the build context changes; the tag is how an existing build is recognised. */
5
4
  version: string;
6
- /** Build context: file path -> contents. Must include "Dockerfile". */
7
5
  context: Record<string, string>;
8
6
  };
9
7
  export interface ManagedImage {
10
8
  readonly tag: string;
11
- /** Builds the image if it isn't present yet, removes outdated versions, and returns its tag. */
12
9
  ensure(): Promise<string>;
13
10
  }
14
11
  export declare function createManagedImage(docker: DockerClient, { repository, version, context }: ImageSpec): ManagedImage;
package/dist/lib/image.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { DockerError } from "./docker.js";
2
2
  export function createManagedImage(docker, { repository, version, context }) {
3
3
  const tag = `${repository}:${version}`;
4
- // Concurrent builds of the same tag (from any process) are harmless: they produce the same image.
5
4
  async function ensure() {
6
5
  if (!(await docker.imageExists(tag))) {
7
6
  await docker.buildImage({ tag, context, labels: { hyperbrane: "true", "hyperbrane.image.version": version } });
@@ -9,7 +8,6 @@ export function createManagedImage(docker, { repository, version, context }) {
9
8
  await removeOutdatedImages().catch((error) => console.warn(`removing outdated ${repository} images failed:`, error));
10
9
  return tag;
11
10
  }
12
- // Images still used by a container are skipped (docker answers 409) and retried on a later ensure().
13
11
  async function removeOutdatedImages() {
14
12
  const images = await docker.listImages({ label: ["hyperbrane"], reference: [repository] });
15
13
  const outdated = images
@@ -1,28 +1,15 @@
1
1
  import { type Sandbox } from "../sandbox/api.ts";
2
- /**
3
- * One JSON file per record in a sandbox directory, so no operation rewrites a shared file and every
4
- * write is a single atomic sandbox operation. Safe to use from several processes at once.
5
- */
6
2
  export interface JsonDirectory<T> {
7
- /** Null if missing. */
8
3
  read(name: string): Promise<T | null>;
9
- /** Stores the record unless one with that name exists; returns whichever record is stored afterwards. */
10
4
  insert(name: string, record: T): Promise<T>;
11
- /** Replaces the record. */
12
5
  save(name: string, record: T): Promise<void>;
13
- /** No-op if missing. */
14
6
  remove(name: string): Promise<void>;
15
- /** Empty if the directory doesn't exist yet. */
16
7
  list(): Promise<T[]>;
17
- /** list, each record with its name. */
18
8
  entries(): Promise<[string, T][]>;
19
9
  }
20
10
  export declare class InvalidNameError extends Error {
21
11
  name: string;
22
12
  }
23
- /** Throws InvalidNameError unless `name` is safe to use as a path segment. */
24
13
  export declare function checkName(name: string): string;
25
- /** `dir` resolves to a sandbox path; it's re-resolved per call so it can depend on the sandbox's home. */
26
14
  export declare function createJsonDirectory<T>(sandbox: Sandbox, dir: () => Promise<string>): JsonDirectory<T>;
27
- /** ~/.local/share/hyperbrane of the sandbox's user. */
28
15
  export declare function defaultDataDir(sandbox: Sandbox): Promise<string>;
@@ -1,18 +1,16 @@
1
1
  import { SandboxNotFoundError } from "../sandbox/api.js";
2
+ import { sh } from "./sh.js";
2
3
  const EXTENSION = ".json";
3
- // Same alphabet as dimension names; keeps names safe to use as file and directory names.
4
4
  const NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}$/;
5
5
  export class InvalidNameError extends Error {
6
6
  name = "InvalidNameError";
7
7
  }
8
- /** Throws InvalidNameError unless `name` is safe to use as a path segment. */
9
8
  export function checkName(name) {
10
9
  if (!NAME_PATTERN.test(name)) {
11
10
  throw new InvalidNameError(`Invalid name "${name}": use letters, digits, "_", "." and "-", up to 63 characters`);
12
11
  }
13
12
  return name;
14
13
  }
15
- /** `dir` resolves to a sandbox path; it's re-resolved per call so it can depend on the sandbox's home. */
16
14
  export function createJsonDirectory(sandbox, dir) {
17
15
  async function pathFor(name) {
18
16
  return `${await dir()}/${checkName(name)}${EXTENSION}`;
@@ -37,7 +35,6 @@ export function createJsonDirectory(sandbox, dir) {
37
35
  const path = await pathFor(name);
38
36
  if (await sandbox.writeFileExclusive(path, serialize(record)))
39
37
  return record;
40
- // Lost to an existing record; if that one was removed in the meantime, try again.
41
38
  return (await readPath(path)) ?? insert(name, record);
42
39
  }
43
40
  async function save(name, record) {
@@ -46,11 +43,9 @@ export function createJsonDirectory(sandbox, dir) {
46
43
  async function remove(name) {
47
44
  await sandbox.removeFile(await pathFor(name));
48
45
  }
49
- // One command for the whole directory: over SSH or Boat, a call per record adds up. Falls back to
50
- // listing and reading where the command can't run.
51
46
  async function entries() {
52
47
  const path = await dir();
53
- const all = await sandbox.runCommand(["sh", "-c", LIST_SCRIPT, "sh", path]).catch(() => null);
48
+ const all = await sandbox.runCommand(["sh", "-c", LIST_SCRIPT, "sh", path], { env: { MISSING: String(MISSING), EXTENSION } }).catch(() => null);
54
49
  if (all?.exitCode === MISSING)
55
50
  return [];
56
51
  if (all?.exitCode === 0) {
@@ -76,7 +71,6 @@ export function createJsonDirectory(sandbox, dir) {
76
71
  return [];
77
72
  throw error;
78
73
  }
79
- // A record removed between listing and reading is simply skipped.
80
74
  const records = await Promise.all(files
81
75
  .filter((file) => file.endsWith(EXTENSION))
82
76
  .map(async (file) => [file.slice(0, -EXTENSION.length), await readPath(`${path}/${file}`)]));
@@ -87,16 +81,17 @@ export function createJsonDirectory(sandbox, dir) {
87
81
  }
88
82
  return { read, insert, save, remove, list, entries };
89
83
  }
90
- /** list's command exits with it when the directory doesn't exist. */
91
84
  const MISSING = 44;
92
- /** Ends each record list's command prints (its file name, a newline, its JSON): a record separator, which JSON escapes inside strings. */
93
85
  const RECORD_END = "\n\x1e\n";
94
- const LIST_SCRIPT = `cd "$1" 2>/dev/null || exit ${MISSING}
95
- for file in *${EXTENSION}; do
86
+ const LIST_SCRIPT = sh `
87
+ cd "$1" 2>/dev/null || exit "$MISSING"
88
+ for file in *"$EXTENSION"; do
96
89
  [ -f "$file" ] || continue
97
- printf '%s\\n' "$file" && cat "$file" && printf '\\n\\036\\n'
98
- done`;
99
- /** ~/.local/share/hyperbrane of the sandbox's user. */
90
+ printf '%s\n' "$file"
91
+ cat "$file"
92
+ printf '\n\036\n'
93
+ done
94
+ `;
100
95
  export async function defaultDataDir(sandbox) {
101
96
  return `${await sandbox.homeDirectory()}/.local/share/hyperbrane`;
102
97
  }
@@ -0,0 +1,6 @@
1
+ export type ScriptBundle = {
2
+ source: string;
3
+ build: string;
4
+ };
5
+ export declare function scriptBundle(entry: () => string, generated: () => Promise<string>): () => Promise<ScriptBundle>;
6
+ export declare function buildScript(entry: string): Promise<string>;
@@ -0,0 +1,28 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync } from "node:fs";
3
+ export function scriptBundle(entry, generated) {
4
+ let bundled;
5
+ async function load() {
6
+ const fromSources = "Bun" in globalThis && existsSync(entry());
7
+ const source = fromSources ? await buildScript(entry()) : await generated();
8
+ return { source, build: createHash("sha256").update(source).digest("hex").slice(0, 16) };
9
+ }
10
+ return () => {
11
+ bundled ??= load().catch((error) => {
12
+ bundled = undefined;
13
+ throw error;
14
+ });
15
+ return bundled;
16
+ };
17
+ }
18
+ export async function buildScript(entry) {
19
+ for (let attempt = 1;; attempt++) {
20
+ const result = await Bun.build({ entrypoints: [entry], target: "bun" }).catch((error) => error);
21
+ if (!(result instanceof Error))
22
+ return result.outputs[0].text();
23
+ const logs = (result.errors ?? [result]).map(String).join("\n");
24
+ if (attempt >= BUILD_ATTEMPTS || !/Could not resolve|Unseekable reading file/.test(logs))
25
+ throw new Error(`bundling ${entry} failed:\n${logs}`);
26
+ }
27
+ }
28
+ const BUILD_ATTEMPTS = 3;
@@ -0,0 +1,2 @@
1
+ export declare function sh(strings: TemplateStringsArray, ...values: never[]): string;
2
+ export declare function scriptText(raw: string): string;
package/dist/lib/sh.js ADDED
@@ -0,0 +1,8 @@
1
+ export function sh(strings, ...values) {
2
+ if (values.length > 0)
3
+ throw new Error("sh`…` takes no interpolations: pass inputs as environment variables or arguments");
4
+ return scriptText(strings.raw[0]);
5
+ }
6
+ export function scriptText(raw) {
7
+ return raw.replace(/\\(\$\{|`)/g, "$1").replace(/^\n/, "");
8
+ }
@@ -1,2 +1 @@
1
- /** Resolves after `ms` milliseconds. Portable stand-in for Bun.sleep, so the services run under Node too. */
2
1
  export declare function sleep(ms: number): Promise<void>;
package/dist/lib/sleep.js CHANGED
@@ -1,4 +1,3 @@
1
- /** Resolves after `ms` milliseconds. Portable stand-in for Bun.sleep, so the services run under Node too. */
2
1
  export function sleep(ms) {
3
2
  return new Promise((resolve) => setTimeout(resolve, ms));
4
3
  }
@@ -0,0 +1,15 @@
1
+ export type HttpStream = {
2
+ write(data: string | Uint8Array): unknown;
3
+ on(event: "data", listener: (chunk: Buffer) => void): unknown;
4
+ on(event: "error", listener: (error: Error) => void): unknown;
5
+ on(event: "end" | "close", listener: () => void): unknown;
6
+ destroy(): unknown;
7
+ };
8
+ export type StreamFetchInit = {
9
+ method?: string;
10
+ headers?: Record<string, string>;
11
+ body?: string | Uint8Array;
12
+ signal?: AbortSignal;
13
+ timeout?: number | false;
14
+ };
15
+ export declare function fetchOverStream(stream: HttpStream, url: string, init: StreamFetchInit, where: string): Promise<Response>;
@@ -0,0 +1,150 @@
1
+ export function fetchOverStream(stream, url, init, where) {
2
+ if (init.signal?.aborted) {
3
+ stream.destroy();
4
+ return Promise.reject(init.signal.reason);
5
+ }
6
+ const target = new URL(url);
7
+ const body = init.body === undefined ? undefined : typeof init.body === "string" ? Buffer.from(init.body) : Buffer.from(init.body);
8
+ const head = [`${init.method ?? "GET"} ${target.pathname}${target.search} HTTP/1.1`, `Host: ${target.host}`, "Connection: close"];
9
+ for (const [name, value] of Object.entries(init.headers ?? {}))
10
+ head.push(`${name}: ${value}`);
11
+ if (body)
12
+ head.push(`Content-Length: ${body.length}`);
13
+ stream.write(`${head.join("\r\n")}\r\n\r\n`);
14
+ if (body)
15
+ stream.write(body);
16
+ let idle;
17
+ let fail = () => { };
18
+ const touch = () => {
19
+ if (typeof init.timeout !== "number")
20
+ return;
21
+ clearTimeout(idle);
22
+ idle = setTimeout(() => fail(new Error(`fetch ${url} on ${where}: no data for ${init.timeout}ms`)), init.timeout);
23
+ };
24
+ const abort = () => fail(init.signal.reason instanceof Error ? init.signal.reason : new Error("aborted"));
25
+ init.signal?.addEventListener("abort", abort, { once: true });
26
+ const finish = () => {
27
+ clearTimeout(idle);
28
+ init.signal?.removeEventListener("abort", abort);
29
+ stream.destroy();
30
+ };
31
+ touch();
32
+ return new Promise((resolve, reject) => {
33
+ let buffer = Buffer.alloc(0);
34
+ let responded = false;
35
+ let controller = null;
36
+ let framing = { kind: "close" };
37
+ let done = false;
38
+ const end = () => {
39
+ if (done)
40
+ return;
41
+ done = true;
42
+ finish();
43
+ try {
44
+ controller?.close();
45
+ }
46
+ catch {
47
+ }
48
+ };
49
+ fail = (error) => {
50
+ if (done)
51
+ return;
52
+ done = true;
53
+ finish();
54
+ if (!responded)
55
+ reject(error);
56
+ else
57
+ controller?.error(error);
58
+ };
59
+ const pumpBody = () => {
60
+ if (framing.kind === "close") {
61
+ if (buffer.length)
62
+ controller?.enqueue(new Uint8Array(buffer));
63
+ buffer = Buffer.alloc(0);
64
+ return false;
65
+ }
66
+ if (framing.kind === "length") {
67
+ const take = buffer.subarray(0, framing.left);
68
+ if (take.length)
69
+ controller?.enqueue(new Uint8Array(take));
70
+ framing.left -= take.length;
71
+ buffer = buffer.subarray(take.length);
72
+ return framing.left === 0;
73
+ }
74
+ for (;;) {
75
+ const lineEnd = buffer.indexOf("\r\n");
76
+ if (lineEnd < 0)
77
+ return false;
78
+ const size = parseInt(buffer.subarray(0, lineEnd).toString(), 16);
79
+ if (Number.isNaN(size))
80
+ throw new Error("malformed chunked body");
81
+ if (size === 0)
82
+ return true;
83
+ if (buffer.length < lineEnd + 2 + size + 2)
84
+ return false;
85
+ controller?.enqueue(new Uint8Array(buffer.subarray(lineEnd + 2, lineEnd + 2 + size)));
86
+ buffer = buffer.subarray(lineEnd + 2 + size + 2);
87
+ }
88
+ };
89
+ const onData = (chunk) => {
90
+ if (done)
91
+ return;
92
+ touch();
93
+ buffer = buffer.length ? Buffer.concat([buffer, chunk]) : chunk;
94
+ try {
95
+ if (!responded) {
96
+ const headEnd = buffer.indexOf("\r\n\r\n");
97
+ if (headEnd < 0)
98
+ return;
99
+ const [statusLine = "", ...lines] = buffer.subarray(0, headEnd).toString("latin1").split("\r\n");
100
+ buffer = buffer.subarray(headEnd + 4);
101
+ const status = Number(statusLine.split(" ")[1]);
102
+ if (status >= 100 && status < 200)
103
+ return onData(Buffer.alloc(0));
104
+ const headers = new Headers();
105
+ for (const line of lines) {
106
+ const colon = line.indexOf(":");
107
+ if (colon > 0)
108
+ headers.append(line.slice(0, colon).trim(), line.slice(colon + 1).trim());
109
+ }
110
+ responded = true;
111
+ if (status === 204 || status === 304 || init.method === "HEAD") {
112
+ resolve(new Response(null, { status, headers }));
113
+ return end();
114
+ }
115
+ if (headers.get("transfer-encoding")?.toLowerCase().includes("chunked"))
116
+ framing = { kind: "chunked" };
117
+ else if (headers.has("content-length"))
118
+ framing = { kind: "length", left: Number(headers.get("content-length")) };
119
+ const responseBody = new ReadableStream({
120
+ start(c) {
121
+ controller = c;
122
+ },
123
+ cancel() {
124
+ done = true;
125
+ finish();
126
+ },
127
+ });
128
+ resolve(new Response(responseBody, { status, headers }));
129
+ }
130
+ if (pumpBody())
131
+ end();
132
+ }
133
+ catch (error) {
134
+ fail(error);
135
+ }
136
+ };
137
+ stream.on("data", onData);
138
+ stream.on("error", (error) => fail(new Error(`fetch ${url} on ${where} failed: ${error.message}`)));
139
+ const ended = () => {
140
+ if (!responded)
141
+ return fail(new Error(`fetch ${url} on ${where} failed: the socket closed before a response`));
142
+ if (framing.kind === "close")
143
+ end();
144
+ else
145
+ fail(new Error(`fetch ${url} on ${where} failed: the response was cut short`));
146
+ };
147
+ stream.on("end", ended);
148
+ stream.on("close", ended);
149
+ });
150
+ }
package/dist/lib/tar.d.ts CHANGED
@@ -1,2 +1 @@
1
- /** Packs `files` (path -> contents) into a tar archive. Files are mode 0644, owned by root. */
2
1
  export declare function createTar(files: Record<string, string | Uint8Array>): Uint8Array<ArrayBuffer>;
package/dist/lib/tar.js CHANGED
@@ -1,9 +1,5 @@
1
- // A minimal tar (ustar) writer: regular files only, which is all a docker build context or
2
- // putArchive needs (docker creates missing parent directories). Portable stand-in for Bun.Archive,
3
- // so the services run under Node too.
4
1
  const BLOCK = 512;
5
2
  function octal(value, width) {
6
- // width includes the trailing NUL.
7
3
  return value.toString(8).padStart(width - 1, "0") + "\0";
8
4
  }
9
5
  function header(name, size, mode, mtime) {
@@ -18,7 +14,6 @@ function header(name, size, mode, mtime) {
18
14
  let path = name;
19
15
  let prefix = "";
20
16
  if (encoder.encode(name).length > 100) {
21
- // ustar splits long paths at a "/" into prefix (155) and name (100).
22
17
  const cut = name.lastIndexOf("/", 155);
23
18
  if (cut <= 0)
24
19
  throw new Error(`tar: "${name}" is too long`);
@@ -27,12 +22,12 @@ function header(name, size, mode, mtime) {
27
22
  }
28
23
  put(path, 0, 100);
29
24
  put(octal(mode, 8), 100, 8);
30
- put(octal(0, 8), 108, 8); // uid
31
- put(octal(0, 8), 116, 8); // gid
25
+ put(octal(0, 8), 108, 8);
26
+ put(octal(0, 8), 116, 8);
32
27
  put(octal(size, 12), 124, 12);
33
28
  put(octal(mtime, 12), 136, 12);
34
- put(" ", 148, 8); // checksum placeholder: eight spaces while summing
35
- put("0", 156, 1); // regular file
29
+ put(" ", 148, 8);
30
+ put("0", 156, 1);
36
31
  put("ustar\0", 257, 6);
37
32
  put("00", 263, 2);
38
33
  put(prefix, 345, 155);
@@ -42,7 +37,6 @@ function header(name, size, mode, mtime) {
42
37
  put(sum.toString(8).padStart(6, "0") + "\0 ", 148, 8);
43
38
  return block;
44
39
  }
45
- /** Packs `files` (path -> contents) into a tar archive. Files are mode 0644, owned by root. */
46
40
  export function createTar(files) {
47
41
  const encoder = new TextEncoder();
48
42
  const mtime = Math.floor(Date.now() / 1000);
@@ -54,7 +48,7 @@ export function createTar(files) {
54
48
  if (padding)
55
49
  parts.push(new Uint8Array(padding));
56
50
  }
57
- parts.push(new Uint8Array(BLOCK * 2)); // end of archive
51
+ parts.push(new Uint8Array(BLOCK * 2));
58
52
  const out = new Uint8Array(parts.reduce((total, part) => total + part.length, 0));
59
53
  let offset = 0;
60
54
  for (const part of parts) {