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
@@ -0,0 +1,219 @@
1
+ import { spawn } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import * as fs from "node:fs/promises";
4
+ import { connect as netConnect, Socket } from "node:net";
5
+ import { homedir, userInfo } from "node:os";
6
+ import { basename, dirname, join, resolve } from "node:path";
7
+ import { SandboxConnectionRefusedError, SandboxNotFoundError, } from "../../api.js";
8
+ import { createDescriptor, getDescriptor, openDescriptor } from "../../storage/descriptors.js";
9
+ import { createEndpoints } from "../../data-endpoints/dataEndpoints.js";
10
+ import { firewalledPorts } from "../../data-endpoints/firewalledPorts.js";
11
+ import { httpOver } from "../../lib/httpOverStream.js";
12
+ import { createSandboxStorage } from "../../storage/sandboxStorage.js";
13
+ function hostPath(path) {
14
+ return resolve("/", path);
15
+ }
16
+ async function notFoundAs(path, operation) {
17
+ try {
18
+ return await operation;
19
+ }
20
+ catch (error) {
21
+ if (error.code === "ENOENT")
22
+ throw new SandboxNotFoundError(`Not found: "${path}"`);
23
+ throw error;
24
+ }
25
+ }
26
+ async function writeTemp(full, data, mode = 0o666) {
27
+ await fs.mkdir(dirname(full), { recursive: true });
28
+ const tmp = join(dirname(full), `.${basename(full)}.${randomUUID()}.tmp`);
29
+ await fs.writeFile(tmp, data, { mode });
30
+ return tmp;
31
+ }
32
+ async function readFile(path) {
33
+ return new Uint8Array(await notFoundAs(path, fs.readFile(hostPath(path))));
34
+ }
35
+ async function readFileRange(path, start, end) {
36
+ const file = await notFoundAs(path, fs.open(hostPath(path), "r"));
37
+ try {
38
+ const buffer = new Uint8Array(Math.max(0, end - start));
39
+ let filled = 0;
40
+ while (filled < buffer.length) {
41
+ const { bytesRead } = await file.read(buffer, filled, buffer.length - filled, start + filled);
42
+ if (bytesRead === 0)
43
+ break;
44
+ filled += bytesRead;
45
+ }
46
+ return buffer.subarray(0, filled);
47
+ }
48
+ finally {
49
+ await file.close();
50
+ }
51
+ }
52
+ async function writeFile(path, data) {
53
+ const full = hostPath(path);
54
+ await fs.rename(await writeTemp(full, data), full);
55
+ }
56
+ async function writeFileExclusive(path, data) {
57
+ const full = hostPath(path);
58
+ const tmp = await writeTemp(full, data);
59
+ try {
60
+ await fs.link(tmp, full);
61
+ return true;
62
+ }
63
+ catch (error) {
64
+ if (error.code === "EEXIST")
65
+ return false;
66
+ throw error;
67
+ }
68
+ finally {
69
+ await fs.unlink(tmp);
70
+ }
71
+ }
72
+ async function removeFile(path) {
73
+ await fs.rm(hostPath(path), { force: true });
74
+ }
75
+ async function createDirectory(path) {
76
+ await fs.mkdir(hostPath(path), { recursive: true });
77
+ }
78
+ async function removeDirectory(path) {
79
+ await fs.rm(hostPath(path), { recursive: true, force: true });
80
+ }
81
+ async function listDirectory(path) {
82
+ return notFoundAs(path, fs.readdir(hostPath(path)));
83
+ }
84
+ async function stat(path) {
85
+ const stats = await notFoundAs(path, fs.stat(hostPath(path)));
86
+ const type = stats.isFile() ? "file" : stats.isDirectory() ? "directory" : "other";
87
+ return { type, uid: stats.uid, gid: stats.gid, size: stats.size };
88
+ }
89
+ async function homeDirectory() {
90
+ return process.env.HOME || homedir();
91
+ }
92
+ function runCommand(cmd, { cwd, env = {}, stdin } = {}) {
93
+ return new Promise((resolve, reject) => {
94
+ const [command, ...args] = cmd;
95
+ if (!command)
96
+ return reject(new Error("no command to run"));
97
+ const child = spawn(command, args, {
98
+ cwd: hostPath(cwd ?? "."),
99
+ env: { ...process.env, ...env },
100
+ stdio: [stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
101
+ });
102
+ const stdout = [];
103
+ const stderr = [];
104
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
105
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
106
+ child.once("error", reject);
107
+ child.once("close", (code) => resolve({ exitCode: code ?? -1, stdout: Buffer.concat(stdout).toString(), stderr: Buffer.concat(stderr).toString() }));
108
+ if (stdin !== undefined)
109
+ child.stdin.end(stdin);
110
+ });
111
+ }
112
+ async function state() {
113
+ return "running";
114
+ }
115
+ async function start() { }
116
+ async function destroy() { }
117
+ function hasExpiration() {
118
+ return false;
119
+ }
120
+ async function getExpiration() {
121
+ return null;
122
+ }
123
+ async function setExpiration() {
124
+ return null;
125
+ }
126
+ function hasShell(ctx) {
127
+ return ctx.sshKey !== undefined;
128
+ }
129
+ async function sshEndpoint(ctx) {
130
+ if (ctx.sshKey === undefined)
131
+ throw new Error("this machine has no shell: its context has no SSH key (sshKey)");
132
+ return {
133
+ host: ctx.sshHost ?? "127.0.0.1",
134
+ port: ctx.sshPort ?? 22,
135
+ user: ctx.sshUser ?? userInfo().username,
136
+ privateKey: ctx.sshKey,
137
+ };
138
+ }
139
+ async function fetch(url, { unix, ...init } = {}) {
140
+ if (unix === undefined || "Bun" in globalThis)
141
+ return globalThis.fetch(url, { ...init, unix: unix === undefined ? undefined : hostPath(unix) });
142
+ return httpOver(url, () => netConnect(hostPath(unix)), init);
143
+ }
144
+ function connect(path) {
145
+ return new Promise((resolve, reject) => {
146
+ const socket = new Socket();
147
+ let markClosed;
148
+ const closed = new Promise((done) => (markClosed = done));
149
+ const listeners = [];
150
+ socket.on("data", (chunk) => {
151
+ for (const listener of listeners)
152
+ listener(new Uint8Array(chunk));
153
+ });
154
+ socket.once("close", () => markClosed());
155
+ socket.once("end", () => markClosed());
156
+ socket.on("error", (error) => {
157
+ markClosed();
158
+ const message = `connecting to ${path} failed: ${error.message}`;
159
+ reject(error.code === "ENOENT" || error.code === "ECONNREFUSED" ? new SandboxConnectionRefusedError(message) : new Error(message));
160
+ });
161
+ socket.once("connect", () => resolve({
162
+ write: (data) => void socket.write(data),
163
+ onData: (listener) => void listeners.push(listener),
164
+ closed,
165
+ close: () => socket.end(),
166
+ }));
167
+ socket.connect({ path: hostPath(path) });
168
+ });
169
+ }
170
+ export function createLocalService(ctx) {
171
+ const fileOps = {
172
+ readFile,
173
+ readFileRange,
174
+ writeFile,
175
+ writeFileExclusive,
176
+ removeFile,
177
+ createDirectory,
178
+ removeDirectory,
179
+ listDirectory,
180
+ stat,
181
+ homeDirectory,
182
+ };
183
+ const cmdOps = {
184
+ runCommand,
185
+ };
186
+ const netOps = {
187
+ fetch,
188
+ connect,
189
+ };
190
+ const machine = { ...fileOps, ...cmdOps, ...netOps };
191
+ const storage = createSandboxStorage(machine);
192
+ const lifecycleOps = {
193
+ create: (spec) => createDescriptor(ctx, storage, ctx.id ?? spec.name, spec),
194
+ open: () => openDescriptor(ctx, storage, ctx.id ?? "this machine"),
195
+ get: () => getDescriptor(storage, "this machine"),
196
+ state,
197
+ start,
198
+ destroy,
199
+ };
200
+ const dataOps = createEndpoints(machine, storage, firewalledPorts(machine, storage));
201
+ const expirationOps = {
202
+ hasExpiration,
203
+ getExpiration,
204
+ setExpiration,
205
+ };
206
+ const shellOps = {
207
+ hasShell: () => hasShell(ctx),
208
+ sshEndpoint: () => sshEndpoint(ctx),
209
+ };
210
+ return {
211
+ ...fileOps,
212
+ ...lifecycleOps,
213
+ ...cmdOps,
214
+ ...dataOps,
215
+ ...expirationOps,
216
+ ...shellOps,
217
+ ...netOps,
218
+ };
219
+ }
@@ -0,0 +1,2 @@
1
+ import type { SandboxAuthContext, SandboxService } from "../../api.ts";
2
+ export declare function createSshService(ctx: SandboxAuthContext): SandboxService;
@@ -0,0 +1,82 @@
1
+ import { userInfo } from "node:os";
2
+ import { commandFileOperations } from "../../lib/remoteFileOperations.js";
3
+ import { createDescriptor, getDescriptor, openDescriptor } from "../../storage/descriptors.js";
4
+ import { createEndpoints } from "../../data-endpoints/dataEndpoints.js";
5
+ import { firewalledPorts } from "../../data-endpoints/firewalledPorts.js";
6
+ import { createSandboxStorage } from "../../storage/sandboxStorage.js";
7
+ import { sshCommand, sshConnect, sshFetch, sshMachine } from "./sshTransport.js";
8
+ function target(ctx) {
9
+ if (!ctx.sshHost)
10
+ throw new Error("an SSH machine needs its host (sshHost)");
11
+ if (!ctx.sshKey)
12
+ throw new Error(`SSH to ${ctx.sshHost} needs a key (sshKey)`);
13
+ return { host: ctx.sshHost, port: ctx.sshPort ?? 22, user: ctx.sshUser ?? userInfo().username, privateKey: ctx.sshKey };
14
+ }
15
+ function where(ctx) {
16
+ return `SSH machine ${ctx.sshHost ?? "(no host)"}`;
17
+ }
18
+ async function create(ctx, storage, spec) {
19
+ return createDescriptor(ctx, storage, ctx.id ?? spec.name, spec);
20
+ }
21
+ async function open(ctx, storage) {
22
+ return openDescriptor(ctx, storage, ctx.id ?? ctx.sshHost ?? "ssh machine");
23
+ }
24
+ async function state() {
25
+ return "running";
26
+ }
27
+ async function start() { }
28
+ async function destroy() { }
29
+ function hasExpiration() {
30
+ return false;
31
+ }
32
+ async function getExpiration() {
33
+ return null;
34
+ }
35
+ async function setExpiration() {
36
+ return null;
37
+ }
38
+ function hasShell(ctx) {
39
+ return ctx.sshHost !== undefined && ctx.sshKey !== undefined;
40
+ }
41
+ async function sshEndpoint(ctx) {
42
+ return target(ctx);
43
+ }
44
+ export function createSshService(ctx) {
45
+ const fileOps = commandFileOperations(sshMachine(() => target(ctx)));
46
+ const cmdOps = {
47
+ runCommand: (cmd, options) => sshCommand(target(ctx), cmd, options),
48
+ };
49
+ const netOps = {
50
+ fetch: (url, init) => sshFetch(target(ctx), url, init),
51
+ connect: (path) => sshConnect(target(ctx), path),
52
+ };
53
+ const machine = { ...fileOps, ...cmdOps, ...netOps };
54
+ const storage = createSandboxStorage(machine);
55
+ const lifecycleOps = {
56
+ create: (spec) => create(ctx, storage, spec),
57
+ open: () => open(ctx, storage),
58
+ get: () => getDescriptor(storage, where(ctx)),
59
+ state,
60
+ start,
61
+ destroy,
62
+ };
63
+ const dataOps = createEndpoints(machine, storage, firewalledPorts(machine, storage));
64
+ const expirationOps = {
65
+ hasExpiration,
66
+ getExpiration,
67
+ setExpiration,
68
+ };
69
+ const shellOps = {
70
+ hasShell: () => hasShell(ctx),
71
+ sshEndpoint: () => sshEndpoint(ctx),
72
+ };
73
+ return {
74
+ ...fileOps,
75
+ ...lifecycleOps,
76
+ ...cmdOps,
77
+ ...dataOps,
78
+ ...expirationOps,
79
+ ...shellOps,
80
+ ...netOps,
81
+ };
82
+ }
@@ -0,0 +1,12 @@
1
+ import { type RunCommandOptions, type RunCommandResult, type SandboxConnection, type SandboxFetchInit } from "../../api.ts";
2
+ import { type CommandMachine } from "../../lib/remoteFileOperations.ts";
3
+ export type SshTarget = {
4
+ host: string;
5
+ port: number;
6
+ user: string;
7
+ privateKey: string;
8
+ };
9
+ export declare function sshCommand(target: SshTarget, cmd: string[], { cwd, env, stdin }?: RunCommandOptions): Promise<RunCommandResult>;
10
+ export declare function sshMachine(target: () => SshTarget): CommandMachine;
11
+ export declare function sshConnect(target: SshTarget, path: string): Promise<SandboxConnection>;
12
+ export declare function sshFetch(target: SshTarget, url: string, { unix, ...init }?: SandboxFetchInit): Promise<Response>;
@@ -0,0 +1,216 @@
1
+ import { posix } from "node:path";
2
+ import { Client } from "ssh2";
3
+ import { SandboxConnectionRefusedError, SandboxNotFoundError } from "../../api.js";
4
+ import { commandLine, quote } from "../../lib/shellCommand.js";
5
+ import { MISSING, PUT_FILE, READ_BYTES } from "../../lib/remoteFileOperations.js";
6
+ import { httpOver } from "../../lib/httpOverStream.js";
7
+ import { sleep } from "../../../lib/sleep.js";
8
+ const READY_TIMEOUT_MS = 15_000;
9
+ const EXISTS = 45;
10
+ const ATTEMPTS = 4;
11
+ class NotRunError extends Error {
12
+ }
13
+ function where({ host, port, user }) {
14
+ return `${user}@${host}:${port}`;
15
+ }
16
+ function openClient(target) {
17
+ return new Promise((resolve, reject) => {
18
+ const client = new Client();
19
+ let ready = false;
20
+ client
21
+ .on("ready", () => {
22
+ ready = true;
23
+ resolve(client);
24
+ })
25
+ .on("error", (error) => {
26
+ if (!ready)
27
+ reject(new Error(`ssh to ${where(target)} failed: ${error.message}`));
28
+ })
29
+ .on("close", () => {
30
+ if (!ready)
31
+ reject(new Error(`ssh to ${where(target)} closed before it was ready`));
32
+ })
33
+ .connect({ host: target.host, port: target.port, username: target.user, privateKey: target.privateKey, readyTimeout: READY_TIMEOUT_MS });
34
+ });
35
+ }
36
+ async function withSsh(target, body) {
37
+ const client = await openClient(target);
38
+ try {
39
+ return await body(client);
40
+ }
41
+ finally {
42
+ client.end();
43
+ }
44
+ }
45
+ function exec(client, command, stdin) {
46
+ return new Promise((resolve, reject) => client.exec(command, (error, channel) => {
47
+ if (error)
48
+ return reject(error);
49
+ const stdout = [];
50
+ const stderr = [];
51
+ let exit;
52
+ channel.on("data", (chunk) => stdout.push(chunk));
53
+ channel.stderr.on("data", (chunk) => stderr.push(chunk));
54
+ channel.once("exit", (code, signal) => (exit = code ?? signal ?? "no status"));
55
+ channel.once("close", () => {
56
+ const said = stdout.length > 0 || stderr.length > 0;
57
+ if (exit === undefined && !said)
58
+ return reject(new NotRunError(`${command.slice(0, 80)} wasn't run: the channel closed with no exit code and no output`));
59
+ if (typeof exit !== "number")
60
+ return reject(new Error(`${command.slice(0, 80)} ended without an exit code (${exit ?? "the channel closed first"})`));
61
+ resolve({ exitCode: exit, stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr).toString() });
62
+ });
63
+ channel.end(stdin === undefined ? undefined : typeof stdin === "string" ? stdin : Buffer.from(stdin));
64
+ }));
65
+ }
66
+ async function run(target, command, stdin) {
67
+ for (let attempt = 1;; attempt++) {
68
+ try {
69
+ return await withSsh(target, (client) => exec(client, command, stdin));
70
+ }
71
+ catch (error) {
72
+ const again = error instanceof NotRunError || /Channel open failure/.test(error.message);
73
+ if (!again || attempt >= ATTEMPTS)
74
+ throw error;
75
+ }
76
+ await sleep(50 * 2 ** attempt);
77
+ }
78
+ }
79
+ export async function sshCommand(target, cmd, { cwd, env, stdin } = {}) {
80
+ const line = commandLine(cmd, { env, cwd: cwd === undefined ? undefined : posix.resolve("/", cwd) });
81
+ const { exitCode, stdout, stderr } = await run(target, line, stdin);
82
+ return { exitCode, stdout: stdout.toString(), stderr };
83
+ }
84
+ export function sshMachine(target) {
85
+ async function readBytes(path, start, end) {
86
+ const env = { MISSING: String(MISSING), START: start === undefined ? "" : String(start), END: end === undefined ? "" : String(end) };
87
+ const ran = await run(target(), commandLine(["sh", "-c", READ_BYTES, "sh", path], { env }));
88
+ if (ran.exitCode === MISSING)
89
+ throw new SandboxNotFoundError(`Not found: "${path}"`);
90
+ if (ran.exitCode !== 0)
91
+ throw new Error(`reading ${path} on ${where(target())} failed: ${ran.stderr.trim()}`);
92
+ return new Uint8Array(ran.stdout);
93
+ }
94
+ async function putFile(path, data, exclusive) {
95
+ const env = { EXCLUSIVE: exclusive ? "1" : "", EXISTS: String(EXISTS) };
96
+ const ran = await run(target(), commandLine(["sh", "-c", PUT_FILE, "sh", path], { env }), data);
97
+ if (ran.exitCode === EXISTS)
98
+ return false;
99
+ if (ran.exitCode !== 0)
100
+ throw new Error(`writing ${path} on ${where(target())} failed: ${ran.stderr.trim()}`);
101
+ return true;
102
+ }
103
+ return {
104
+ runCommand: (cmd, options) => sshCommand(target(), cmd, options),
105
+ readBytes,
106
+ putFile,
107
+ };
108
+ }
109
+ function relay(client, path) {
110
+ return new Promise((resolve, reject) => client.exec(`exec python3 -c ${quote(RELAY)} ${quote(path)}`, (error, channel) => {
111
+ if (error)
112
+ return reject(error);
113
+ let said = "";
114
+ const failed = () => reject(new Error(said.trim() || "the relay exited"));
115
+ const onStderr = (chunk) => {
116
+ said += chunk.toString();
117
+ if (!said.includes("\n"))
118
+ return;
119
+ channel.stderr.off("data", onStderr);
120
+ channel.off("close", failed);
121
+ if (said.startsWith("connected\n"))
122
+ resolve(channel);
123
+ else
124
+ failed();
125
+ };
126
+ channel.stderr.on("data", onStderr);
127
+ channel.once("close", failed);
128
+ }));
129
+ }
130
+ async function socketChannel(client, path) {
131
+ try {
132
+ return await new Promise((resolve, reject) => client.openssh_forwardOutStreamLocal(path, (error, channel) => (error ? reject(error) : resolve(channel))));
133
+ }
134
+ catch (forwarding) {
135
+ return relay(client, path).catch((error) => {
136
+ const message = `connecting to ${path} failed: ${forwarding.message}; through python3: ${error.message}`;
137
+ throw /^(ENOENT|ECONNREFUSED) /.test(error.message) ? new SandboxConnectionRefusedError(message) : new Error(message);
138
+ });
139
+ }
140
+ }
141
+ function tcpChannel(client, host, port) {
142
+ return new Promise((resolve, reject) => client.forwardOut("127.0.0.1", 0, host, port, (error, channel) => (error ? reject(error) : resolve(channel))));
143
+ }
144
+ function connectionOf(stream, onClosed) {
145
+ const listeners = [];
146
+ const early = [];
147
+ const closed = new Promise((done) => stream.once("close", done));
148
+ void closed.then(onClosed);
149
+ stream.on("data", (chunk) => {
150
+ const data = new Uint8Array(chunk);
151
+ if (listeners.length === 0)
152
+ early.push(data);
153
+ for (const listener of listeners)
154
+ listener(data);
155
+ });
156
+ stream.on("error", () => stream.destroy());
157
+ return {
158
+ write: (data) => void stream.write(data),
159
+ onData(listener) {
160
+ listeners.push(listener);
161
+ for (const data of early.splice(0))
162
+ listener(data);
163
+ },
164
+ closed,
165
+ close: () => void stream.end(),
166
+ };
167
+ }
168
+ export async function sshConnect(target, path) {
169
+ const client = await openClient(target);
170
+ try {
171
+ return connectionOf(await socketChannel(client, posix.resolve("/", path)), () => client.end());
172
+ }
173
+ catch (error) {
174
+ client.end();
175
+ const message = `connecting to ${path} on ${where(target)} failed: ${error.message}`;
176
+ throw error instanceof SandboxConnectionRefusedError ? new SandboxConnectionRefusedError(message) : new Error(message);
177
+ }
178
+ }
179
+ export async function sshFetch(target, url, { unix, ...init } = {}) {
180
+ const client = await openClient(target);
181
+ try {
182
+ const parsed = new URL(url);
183
+ const port = Number(parsed.port || (parsed.protocol === "https:" ? 443 : 80));
184
+ const channel = unix === undefined ? await tcpChannel(client, parsed.hostname, port) : await socketChannel(client, posix.resolve("/", unix));
185
+ channel.once("close", () => client.end());
186
+ return await httpOver(url, () => channel, init);
187
+ }
188
+ catch (error) {
189
+ client.end();
190
+ throw error;
191
+ }
192
+ }
193
+ const RELAY = `
194
+ import errno, os, socket, sys, threading
195
+ s = socket.socket(socket.AF_UNIX)
196
+ try:
197
+ s.connect(sys.argv[1])
198
+ except OSError as e:
199
+ sys.stderr.write(errno.errorcode.get(e.errno, "E?") + " " + e.strerror + "\\n")
200
+ sys.exit(1)
201
+ sys.stderr.write("connected\\n")
202
+ sys.stderr.flush()
203
+ def up():
204
+ while True:
205
+ data = os.read(0, 65536)
206
+ if not data:
207
+ break
208
+ s.sendall(data)
209
+ s.shutdown(socket.SHUT_WR)
210
+ threading.Thread(target=up, daemon=True).start()
211
+ while True:
212
+ data = s.recv(65536)
213
+ if not data:
214
+ break
215
+ os.write(1, data)
216
+ `;
@@ -0,0 +1,2 @@
1
+ import type { SandboxAuthContext, SandboxService } from "./api.ts";
2
+ export declare function createSandboxService(ctx: SandboxAuthContext): SandboxService;
@@ -0,0 +1,18 @@
1
+ import { createBoatService } from "./providers/boat/boatService.js";
2
+ import { createExeDevService } from "./providers/exedev/exeDevService.js";
3
+ import { createSshService } from "./providers/ssh/sshService.js";
4
+ import { createLocalService } from "./providers/local/localService.js";
5
+ export function createSandboxService(ctx) {
6
+ // Create service is a factory that initializes the specific sandbox operations client
7
+ // It does nothing, just creates the client
8
+ switch (ctx.kind) {
9
+ case "boat":
10
+ return createBoatService(ctx);
11
+ case "exe.dev":
12
+ return createExeDevService(ctx);
13
+ case "ssh":
14
+ return createSshService(ctx);
15
+ case "local":
16
+ return createLocalService(ctx);
17
+ }
18
+ }
@@ -0,0 +1,6 @@
1
+ import type { SandboxAuthContext, SandboxDescriptor, SandboxSpec } from "../api.ts";
2
+ import type { SandboxStorage } from "./sandboxStorage.ts";
3
+ export declare function makeDescriptor(ctx: SandboxAuthContext, id: string, spec: SandboxSpec): SandboxDescriptor;
4
+ export declare function createDescriptor(ctx: SandboxAuthContext, storage: SandboxStorage, id: string, spec: SandboxSpec): Promise<[SandboxAuthContext, SandboxDescriptor]>;
5
+ export declare function openDescriptor(ctx: SandboxAuthContext, storage: SandboxStorage, id: string): Promise<SandboxDescriptor>;
6
+ export declare function getDescriptor(storage: SandboxStorage, where: string): Promise<SandboxDescriptor>;
@@ -0,0 +1,25 @@
1
+ export function makeDescriptor(ctx, id, spec) {
2
+ return {
3
+ id,
4
+ kind: ctx.kind,
5
+ name: spec.name,
6
+ ...(ctx.sshHost === undefined ? {} : { sshHost: ctx.sshHost }),
7
+ ...(ctx.sshUser === undefined ? {} : { sshUser: ctx.sshUser }),
8
+ ...(ctx.sshPort === undefined ? {} : { sshPort: ctx.sshPort }),
9
+ ...(spec.dataEndpointOptions === undefined ? {} : { dataEndpointOptions: spec.dataEndpointOptions }),
10
+ };
11
+ }
12
+ export async function createDescriptor(ctx, storage, id, spec) {
13
+ const descriptor = makeDescriptor(ctx, id, spec);
14
+ await storage.saveDescriptor(descriptor);
15
+ return [{ ...ctx, id }, descriptor];
16
+ }
17
+ export async function openDescriptor(ctx, storage, id) {
18
+ return (await storage.readDescriptor()) ?? storage.insertDescriptor(makeDescriptor(ctx, id, { name: id }));
19
+ }
20
+ export async function getDescriptor(storage, where) {
21
+ const descriptor = await storage.readDescriptor();
22
+ if (!descriptor)
23
+ throw new Error(`${where} has no descriptor: opening it saves one`);
24
+ return descriptor;
25
+ }
@@ -0,0 +1,11 @@
1
+ import type { Sandbox, SandboxDescriptor, SandboxEndpoint } from "../api.ts";
2
+ export interface SandboxStorage {
3
+ readDescriptor(): Promise<SandboxDescriptor | null>;
4
+ insertDescriptor(descriptor: SandboxDescriptor): Promise<SandboxDescriptor>;
5
+ saveDescriptor(descriptor: SandboxDescriptor): Promise<void>;
6
+ readEndpoint(name: string): Promise<SandboxEndpoint | null>;
7
+ saveEndpoint(endpoint: SandboxEndpoint): Promise<void>;
8
+ removeEndpoint(name: string): Promise<void>;
9
+ listEndpoints(): Promise<SandboxEndpoint[]>;
10
+ }
11
+ export declare function createSandboxStorage(sandbox: Sandbox): SandboxStorage;
@@ -0,0 +1,42 @@
1
+ import { createJsonDirectory } from "../../lib/jsonDirectory.js";
2
+ const DESCRIPTOR = "sandbox";
3
+ export function createSandboxStorage(sandbox) {
4
+ const stateDirectory = async () => `${await sandbox.homeDirectory()}/.local/state/hyperbrane`;
5
+ const endpointsDirectory = async () => `${await stateDirectory()}/endpoints`;
6
+ const descriptors = createJsonDirectory(sandbox, stateDirectory);
7
+ const endpoints = createJsonDirectory(sandbox, endpointsDirectory);
8
+ async function readDescriptor() {
9
+ return descriptors.read(DESCRIPTOR);
10
+ }
11
+ async function insertDescriptor(descriptor) {
12
+ return descriptors.insert(DESCRIPTOR, descriptor);
13
+ }
14
+ async function saveDescriptor(descriptor) {
15
+ await descriptors.save(DESCRIPTOR, descriptor);
16
+ }
17
+ async function readEndpoint(name) {
18
+ return endpoints.read(name);
19
+ }
20
+ async function saveEndpoint(endpoint) {
21
+ const directory = await endpointsDirectory();
22
+ const made = await sandbox.runCommand(["sh", "-c", 'mkdir -p "$1" && chmod 700 "$1"', "sh", directory]);
23
+ if (made.exitCode !== 0)
24
+ throw new Error(`making ${directory} private failed: ${made.stderr.trim()}`);
25
+ await endpoints.save(endpoint.name, endpoint);
26
+ }
27
+ async function removeEndpoint(name) {
28
+ await endpoints.remove(name);
29
+ }
30
+ async function listEndpoints() {
31
+ return endpoints.list();
32
+ }
33
+ return {
34
+ readDescriptor,
35
+ insertDescriptor,
36
+ saveDescriptor,
37
+ readEndpoint,
38
+ saveEndpoint,
39
+ removeEndpoint,
40
+ listEndpoints,
41
+ };
42
+ }
@@ -1,7 +1,2 @@
1
- /**
2
- * Claude: a Claude subscription through `claude auth login`, or an Anthropic API key. Once the
3
- * gateway holds a sign-in, `claude auth login` in any dimension completes with any pasted code in
4
- * claude's code#state format (e.g. "x#x"): the gateway answers the exchange itself.
5
- */
6
1
  declare const _default: import("../portal-gateway/upstream.ts").UpstreamDefinition;
7
2
  export default _default;
@@ -1,17 +1,10 @@
1
- import { defineUpstream } from "../portal-gateway/upstream.js";
2
- /**
3
- * Claude: a Claude subscription through `claude auth login`, or an Anthropic API key. Once the
4
- * gateway holds a sign-in, `claude auth login` in any dimension completes with any pasted code in
5
- * claude's code#state format (e.g. "x#x"): the gateway answers the exchange itself.
6
- */
1
+ import { defineUpstream } from "../portal-gateway/api.js";
7
2
  export default defineUpstream({
8
3
  name: "anthropic",
9
4
  modes: {
10
5
  oauth: {
11
- // Every host claude presents its token to; one missing here would get the fake verbatim.
12
6
  hosts: ["api.anthropic.com", "mcp-proxy.anthropic.com", "platform.claude.com"],
13
7
  present: { header: "authorization", scheme: "Bearer" },
14
- // Both the sign-in and the refresh go here, with JSON bodies.
15
8
  tokenUrl: "https://platform.claude.com/v1/oauth/token",
16
9
  fakes: {
17
10
  access: { prefix: "sk-ant-oat01-" },