hyperbrane 0.1.1 → 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 +81 -11
  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 -30
  5. package/dist/agent/agentService.js +62 -293
  6. package/dist/agent/agentStorage.d.ts +1 -20
  7. package/dist/agent/agentStorage.js +0 -30
  8. package/dist/agent/api.d.ts +89 -133
  9. package/dist/agent/api.js +4 -4
  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 -30
  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 +27 -18
  40. package/dist/agent/session/worker/protocol.js +32 -16
  41. package/dist/agent/session/worker/workerParams.d.ts +0 -5
  42. package/dist/agent/session/worker/workerParams.js +15 -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 +44 -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 -21
  75. package/dist/dimension/service.js +60 -224
  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 -8
  87. package/dist/index.js +5 -16
  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 -35
  113. package/dist/portal-gateway/gatewayService.js +79 -253
  114. package/dist/portal-gateway/image.d.ts +1 -2
  115. package/dist/portal-gateway/image.js +1 -3
  116. package/dist/portal-gateway/imageFiles.generated.js +3 -3
  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 -317
  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 +12 -82
  128. package/dist/portal-gateway/upstream.js +0 -1
  129. package/dist/sandbox/api.d.ts +121 -178
  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 -15
  179. package/dist/agent/session/workerSseConnection.js +0 -26
  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 -33
  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 -701
  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,657 @@
1
+ import { dimensionLayout } from "../dimension/api.js";
2
+ import { exitEnv, records, runControl } from "../lib/control.js";
3
+ import { sh } from "../lib/sh.js";
4
+ import { HYPERBRANE_VERSION } from "../lib/version.js";
5
+ import { GatewayConflictError, GatewayInvalidError, GatewayNotFoundError } from "./api.js";
6
+ import { GATEWAY_IMAGE_REPOSITORY, GATEWAY_IMAGE_VERSION } from "./image.js";
7
+ import { transportBundle } from "./transportBundle.js";
8
+ export const GATEWAY_STATE_PATH = "/state";
9
+ const GATEWAY_IMAGE = `${GATEWAY_IMAGE_REPOSITORY}:${GATEWAY_IMAGE_VERSION}`;
10
+ const PROXY_PORT = 8080;
11
+ const DNS_PORT = 5353;
12
+ const READY_TIMEOUT_S = 60;
13
+ const SUBNET_COUNT = 1 << 14;
14
+ const TRANSPORT_BUN_VERSION = "1.4.3";
15
+ const EXIT = { invalid: 40, conflict: 41, notFound: 42, needImage: 43, needBundle: 44 };
16
+ export function gatewayContainerName(gatewayName) {
17
+ return `hybgw-${gatewayName}`;
18
+ }
19
+ export function gatewayCaCertificatePath(stateDirectory) {
20
+ return `${stateDirectory}/mitmproxy/mitmproxy-ca-cert.pem`;
21
+ }
22
+ async function run(host, script, env, options) {
23
+ const result = await runControl(host, PORTAL_FUNCTIONS, script, { ...portalEnv(), ...env }, options);
24
+ if (result.exitCode === EXIT.needImage)
25
+ return false;
26
+ return { result };
27
+ }
28
+ export function portalEnv() {
29
+ return {
30
+ DIMENSION_PREFIX: dimensionLayout.containerName(""),
31
+ GATEWAY_PREFIX: gatewayContainerName(""),
32
+ GATEWAY_IMAGE,
33
+ CA_BUNDLE: dimensionLayout.caBundlePath(),
34
+ TRUST_SCRIPT: TRUST_GATEWAY_SCRIPT,
35
+ ...exitEnv(EXIT),
36
+ };
37
+ }
38
+ function check(ran, what) {
39
+ if (!ran)
40
+ throw new Error(`${what}: the gateway image is missing`);
41
+ const { result } = ran;
42
+ const said = result.stderr.trim();
43
+ switch (result.exitCode) {
44
+ case 0:
45
+ return result;
46
+ case EXIT.invalid:
47
+ throw new GatewayInvalidError(said);
48
+ case EXIT.conflict:
49
+ throw new GatewayConflictError(said);
50
+ case EXIT.notFound:
51
+ throw new GatewayNotFoundError(said);
52
+ default:
53
+ throw new Error(`${what} failed (exit ${result.exitCode}): ${said || result.stdout.trim()}`);
54
+ }
55
+ }
56
+ async function withImage(ensureImage, attempt, what) {
57
+ let ran = await attempt();
58
+ if (!ran) {
59
+ await ensureImage();
60
+ ran = await attempt();
61
+ }
62
+ return check(ran, what);
63
+ }
64
+ export function gatewayControl(host, ensureImage, options = {}) {
65
+ const gatewayEnv = (gatewayName) => ({ GATEWAY: gatewayName });
66
+ const call = (script, env, what) => withImage(ensureImage, () => run(host, script, env, options), what);
67
+ return {
68
+ async createGateway(gatewayName, upstreams) {
69
+ const lines = upstreams.map((upstream) => `${upstream.name}\t${JSON.stringify(upstream)}`).join("\n");
70
+ const env = { ...gatewayEnv(gatewayName), UPSTREAMS: lines, VERSION: HYPERBRANE_VERSION, STATE_PATH: GATEWAY_STATE_PATH, ...readyEnv() };
71
+ await call(CREATE_GATEWAY, env, `creating gateway ${gatewayName}`);
72
+ },
73
+ async destroyGateway(gatewayName) {
74
+ await call(DESTROY_GATEWAY, gatewayEnv(gatewayName), `destroying gateway ${gatewayName}`);
75
+ },
76
+ async restartGateway(gatewayName) {
77
+ await call(RESTART_GATEWAY, { ...gatewayEnv(gatewayName), ...readyEnv() }, `restarting gateway ${gatewayName}`);
78
+ },
79
+ async startTransport(gatewayName, endpoint, { restart, }) {
80
+ const { source, build } = await transportBundle();
81
+ const env = {
82
+ ...gatewayEnv(gatewayName),
83
+ PORT: String(endpoint.port),
84
+ SECRET: endpoint.secret,
85
+ BUILD: build,
86
+ BUN_VERSION: TRANSPORT_BUN_VERSION,
87
+ RESTART: restart ? "1" : "",
88
+ };
89
+ const what = `starting gateway ${gatewayName}'s transport worker`;
90
+ let ran = await run(host, START_TRANSPORT, env, options);
91
+ if (ran && ran.result.exitCode === EXIT.needBundle) {
92
+ await host.writeFile(`${ran.result.stdout.trim()}/transport-${build}.js`, source);
93
+ ran = await run(host, START_TRANSPORT, env, options);
94
+ }
95
+ check(ran, what);
96
+ },
97
+ async listGateways() {
98
+ const result = await call(PRINT_RECORDS, { DIRECTORY: "gateways" }, "listing gateways");
99
+ return records(result.stdout).map(([name]) => ({ name }));
100
+ },
101
+ async registerUpstream(gatewayName, upstream) {
102
+ const env = { ...gatewayEnv(gatewayName), FILE: `upstreams/${upstream.name}.json`, CONTENT: `${JSON.stringify(upstream, null, 2)}\n` };
103
+ await call(WRITE_STATE, env, `registering upstream ${upstream.name}`);
104
+ },
105
+ async listUpstreams(gatewayName) {
106
+ const result = await call(LIST_STATE, { ...gatewayEnv(gatewayName), DIRECTORY: "upstreams" }, `listing gateway ${gatewayName}'s upstreams`);
107
+ return records(result.stdout).map(([, upstream]) => upstream);
108
+ },
109
+ async removeUpstream(gatewayName, upstreamName) {
110
+ await call(REMOVE_UPSTREAM, { ...gatewayEnv(gatewayName), UPSTREAM: upstreamName }, `removing upstream ${upstreamName}`);
111
+ },
112
+ async listCredentials(gatewayName) {
113
+ const result = await call(LIST_CREDENTIALS, gatewayEnv(gatewayName), `listing gateway ${gatewayName}'s credentials`);
114
+ return records(result.stdout).map(([header, apiKeys]) => {
115
+ const [upstream = "", oauth] = header.split(" ");
116
+ return { upstream, oauth: oauth === "1", apiKeys };
117
+ });
118
+ },
119
+ async removeOAuthCredential(gatewayName, upstreamName) {
120
+ await call(REMOVE_STATE, { ...gatewayEnv(gatewayName), FILE: `credentials/${upstreamName}.oauth.json` }, `removing ${upstreamName}'s sign-in`);
121
+ },
122
+ async setApiKey(gatewayName, upstreamName, keyName, key, fakeCharacters) {
123
+ const env = { ...gatewayEnv(gatewayName), UPSTREAM: upstreamName, KEY_NAME: keyName, KEY: key, FAKE_CHARACTERS: fakeCharacters };
124
+ return (await call(SET_API_KEY, env, `setting API key ${keyName}`)).stdout.trim();
125
+ },
126
+ async deleteApiKey(gatewayName, upstreamName, keyName) {
127
+ await call(DELETE_API_KEY, { ...gatewayEnv(gatewayName), UPSTREAM: upstreamName, KEY_NAME: keyName }, `deleting API key ${keyName}`);
128
+ },
129
+ async createTrafficRule(gatewayName, rule) {
130
+ await call(CREATE_TRAFFIC_RULE, { ...gatewayEnv(gatewayName), RULE: rule.name, CONTENT: trafficRuleFile(rule) }, `creating traffic rule ${rule.name}`);
131
+ },
132
+ async updateTrafficRule(gatewayName, rule) {
133
+ await call(UPDATE_TRAFFIC_RULE, { ...gatewayEnv(gatewayName), RULE: rule.name, CONTENT: trafficRuleFile(rule) }, `updating traffic rule ${rule.name}`);
134
+ },
135
+ async deleteTrafficRule(gatewayName, ruleName) {
136
+ await call(DELETE_TRAFFIC_RULE, { ...gatewayEnv(gatewayName), RULE: ruleName }, `deleting traffic rule ${ruleName}`);
137
+ },
138
+ async listTrafficRules(gatewayName) {
139
+ const result = await call(LIST_STATE, { ...gatewayEnv(gatewayName), DIRECTORY: "traffic-rules" }, `listing gateway ${gatewayName}'s traffic rules`);
140
+ return records(result.stdout).map(([, rule]) => rule);
141
+ },
142
+ async trafficRuleRespond(gatewayName, ruleName, action) {
143
+ const response = `${JSON.stringify({ name: ruleName, action, at: Date.now() }, null, 2)}\n`;
144
+ await call(RESPOND_TRAFFIC_RULE, { ...gatewayEnv(gatewayName), RULE: ruleName, CONTENT: response }, `answering traffic rule ${ruleName}`);
145
+ },
146
+ async createPortal(portal) {
147
+ const env = { PORTAL: portal.name, DIMENSION: portal.dimensionName, GATEWAY: portal.gatewayName, SUBNET_COUNT: String(SUBNET_COUNT) };
148
+ await call(CREATE_PORTAL, env, `creating portal ${portal.name}`);
149
+ },
150
+ async destroyPortal(portalName) {
151
+ await call(DESTROY_PORTAL, { PORTAL: portalName }, `destroying portal ${portalName}`);
152
+ },
153
+ async listPortals() {
154
+ const result = await call(PRINT_RECORDS, { DIRECTORY: "portals" }, "listing portals");
155
+ return records(result.stdout).map(([, { name, dimensionName, gatewayName }]) => ({ name, dimensionName, gatewayName }));
156
+ },
157
+ async restorePortals({ gatewayName = "", dimensionName = "" }) {
158
+ return (await call(RESTORE_PORTALS, { GATEWAY: gatewayName, DIMENSION: dimensionName }, "restoring portals")).stderr.trim();
159
+ },
160
+ };
161
+ }
162
+ function readyEnv() {
163
+ return { PROXY_PORT: String(PROXY_PORT), DNS_PORT: String(DNS_PORT), READY_TIMEOUT: String(READY_TIMEOUT_S) };
164
+ }
165
+ function trafficRuleFile({ name, urlMask, action, delayMs }) {
166
+ return `${JSON.stringify({ name, urlMask, action, delayMs }, null, 2)}\n`;
167
+ }
168
+ const TRUST_GATEWAY_SCRIPT = sh `
169
+ set -eu
170
+ exec 9>/run/hyperbrane-trust.lock
171
+ flock 9
172
+ local_cas=/usr/local/share/ca-certificates
173
+ mkdir -p "$local_cas" /etc/ssl/certs /etc/profile.d
174
+ cert="$local_cas/hyperbrane-gateway-$GATEWAY.crt"
175
+ if [ -n "$GATEWAY_CA" ]; then printf '%s\n' "$GATEWAY_CA" > "$cert"; else rm -f "$cert"; fi
176
+
177
+ {
178
+ if [ -f /etc/ca-certificates.conf ]; then
179
+ grep -v '^[#!]' /etc/ca-certificates.conf | grep . | sed 's|^|/usr/share/ca-certificates/|' | xargs -r -d '\n' cat
180
+ fi
181
+ find "$local_cas" -type f -name '*.crt' | sort | xargs -r -d '\n' cat
182
+ } > "$CA_BUNDLE.hyperbrane"
183
+ mv "$CA_BUNDLE.hyperbrane" "$CA_BUNDLE"
184
+
185
+ find /etc/ssl/certs -maxdepth 1 -type l -lname '*hyperbrane-gateway-*' -delete
186
+ for gateway_cert in "$local_cas"/hyperbrane-gateway-*.crt; do
187
+ [ -f "$gateway_cert" ] || continue
188
+ name="\${gateway_cert##*/}"
189
+ pem="/etc/ssl/certs/\${name%.crt}.pem"
190
+ ln -sf "$gateway_cert" "$pem"
191
+ command -v openssl > /dev/null || continue
192
+ hash="$(openssl x509 -hash -noout -in "$gateway_cert")"
193
+ index=0
194
+ while [ -e "/etc/ssl/certs/$hash.$index" ]; do index=$((index + 1)); done
195
+ ln -s "$pem" "/etc/ssl/certs/$hash.$index"
196
+ done
197
+
198
+ { grep -v '^NODE_EXTRA_CA_CERTS=' /etc/environment 2>/dev/null || true; } > /etc/environment.hyperbrane
199
+ if ls "$local_cas"/hyperbrane-gateway-*.crt >/dev/null 2>&1; then
200
+ echo "NODE_EXTRA_CA_CERTS=$CA_BUNDLE" >> /etc/environment.hyperbrane
201
+ echo "export NODE_EXTRA_CA_CERTS=$CA_BUNDLE" > /etc/profile.d/hyperbrane-gateway.sh
202
+ else
203
+ rm -f /etc/profile.d/hyperbrane-gateway.sh
204
+ fi
205
+ mv /etc/environment.hyperbrane /etc/environment
206
+ `;
207
+ export const PORTAL_FUNCTIONS = sh `
208
+ running_pid() {
209
+ docker inspect -f '{{if .State.Running}}{{.State.Pid}}{{end}}' "$1" 2>/dev/null || true
210
+ }
211
+
212
+ # shellcheck disable=SC2034
213
+ portal_link() {
214
+ hash="$(printf '%s' "$1" | sha256sum | cut -c1-8)"
215
+ DIMENSION_IF="hvd$hash"
216
+ GATEWAY_IF="hvg$hash"
217
+ base="10.77.$(($2 >> 6))"
218
+ offset=$((($2 & 63) << 2))
219
+ GATEWAY_IP="$base.$((offset + 1))"
220
+ DIMENSION_IP="$base.$((offset + 2))"
221
+ ROUTE_METRIC=$((100 + $2))
222
+ }
223
+
224
+ wire_portal() {
225
+ portal_link "$1" "$4"
226
+ for attempt in 1 2; do
227
+ dimension_pid="$(running_pid "$DIMENSION_PREFIX$2")"
228
+ gateway_pid="$(running_pid "$GATEWAY_PREFIX$3")"
229
+ if [ -z "$dimension_pid" ] || [ -z "$gateway_pid" ]; then
230
+ echo "dimension $2 or gateway $3 is not running" >&2
231
+ return 1
232
+ fi
233
+ if docker run --rm --privileged --pid host --network host --volume /run:/hostrun --init --user root \
234
+ --label hyperbrane=true --label hyperbrane.helper=wire-portal \
235
+ -e PORTAL="$1" -e DIMENSION_PID="$dimension_pid" -e GATEWAY_PID="$gateway_pid" \
236
+ -e DIMENSION_IF="$DIMENSION_IF" -e GATEWAY_IF="$GATEWAY_IF" -e DIMENSION_IP="$DIMENSION_IP" -e GATEWAY_IP="$GATEWAY_IP" \
237
+ -e ROUTE_METRIC="$ROUTE_METRIC" \
238
+ "$GATEWAY_IMAGE" hyb-wire-portal > /dev/null; then
239
+ return 0
240
+ fi
241
+ [ "$attempt" = 2 ] || sleep 1
242
+ done
243
+ return 1
244
+ }
245
+
246
+ unwire_portal() {
247
+ portal_link "$1" "$3"
248
+ docker exec -u root "$GATEWAY_PREFIX$2" ip link del "$GATEWAY_IF" > /dev/null 2>&1 || true
249
+ }
250
+
251
+ trust_gateway() {
252
+ [ -n "$(running_pid "$DIMENSION_PREFIX$1")" ] || return 0
253
+ ca=""
254
+ if [ -n "$3" ]; then ca="$(cat "$DATA/gateway-state/$2/mitmproxy/mitmproxy-ca-cert.pem")"; fi
255
+ docker exec -u root -e GATEWAY="$2" -e GATEWAY_CA="$ca" -e CA_BUNDLE "$DIMENSION_PREFIX$1" sh -c "$TRUST_SCRIPT"
256
+ }
257
+
258
+ untrust_unless_shared() {
259
+ for other in "$DATA"/portals/*.json; do
260
+ [ -f "$other" ] && [ "$other" != "$DATA/portals/$1.json" ] || continue
261
+ if [ "$(record_text "$other" dimensionName)" = "$2" ] && [ "$(record_text "$other" gatewayName)" = "$3" ]; then return 0; fi
262
+ done
263
+ trust_gateway "$2" "$3" ""
264
+ }
265
+
266
+ ready_portals() {
267
+ for record in "$DATA"/portals/*.json; do
268
+ [ -f "$record" ] && [ "$(record_text "$record" state)" = ready ] || continue
269
+ subnet="$(record_field "$record" subnet)"
270
+ [ -n "$subnet" ] || continue
271
+ printf '%s %s %s %s\n' "$(record_text "$record" name)" "$(record_text "$record" dimensionName)" "$(record_text "$record" gatewayName)" "$subnet"
272
+ done
273
+ }
274
+
275
+ lock_portal() {
276
+ mkdir -p "$DATA/portal-locks"
277
+ exec 8> "$DATA/portal-locks/$1.lock"
278
+ flock 8
279
+ }
280
+
281
+ restore_portal() {
282
+ lock_portal "$1"
283
+ [ "$(record_text "$DATA/portals/$1.json" state 2> /dev/null)" = ready ] || return 0
284
+ pids="$(running_pid "$DIMENSION_PREFIX$2") $(running_pid "$GATEWAY_PREFIX$3")"
285
+ wire_portal "$@" && trust_gateway "$2" "$3" yes && return 0
286
+ sleep 1
287
+ [ "$(running_pid "$DIMENSION_PREFIX$2") $(running_pid "$GATEWAY_PREFIX$3")" != "$pids" ] || return 1
288
+ }
289
+
290
+ restore_portals() {
291
+ looked="$(mktemp -d)"
292
+ ready_portals | while read -r portal dimension gateway subnet; do
293
+ [ -z "$1" ] || [ "$gateway" = "$1" ] || continue
294
+ [ -z "$2" ] || [ "$dimension" = "$2" ] || continue
295
+ [ -n "$(running_pid "$DIMENSION_PREFIX$dimension")" ] || continue
296
+ links="$looked/$gateway.links"
297
+ [ -f "$links" ] || docker exec "$GATEWAY_PREFIX$gateway" ip -br link > "$links" 2> /dev/null || echo down > "$links"
298
+ [ "$(cat "$links")" != down ] || continue
299
+ portal_link "$portal" "$subnet"
300
+ grep -q "^\${GATEWAY_IF}[@ ]" "$links" && continue
301
+ echo "$portal $dimension $gateway $subnet" >> "$looked/missing"
302
+ done
303
+ if [ -f "$looked/missing" ]; then
304
+ docker image inspect "$GATEWAY_IMAGE" > /dev/null 2>&1 || exit "$E_NEED_IMAGE"
305
+ while read -r portal dimension gateway subnet; do
306
+ (restore_portal "$portal" "$dimension" "$gateway" "$subnet" 2> "$looked/$portal.failed" && rm -f "$looked/$portal.failed") &
307
+ done < "$looked/missing"
308
+ wait
309
+ fi
310
+ failed=0
311
+ for failure in "$looked"/*.failed; do
312
+ [ -f "$failure" ] || continue
313
+ name="\${failure##*/}"
314
+ echo "re-wiring portal \${name%.failed} failed: $(cat "$failure")" >&2
315
+ failed=1
316
+ done
317
+ rm -rf "$looked"
318
+ return "$failed"
319
+ }
320
+
321
+ # shellcheck disable=SC2034,SC2153
322
+ gateway_paths() {
323
+ RECORD="$DATA/gateways/$GATEWAY.json"
324
+ CONTAINER="$GATEWAY_PREFIX$GATEWAY"
325
+ STATE="$DATA/gateway-state/$GATEWAY"
326
+ TRANSPORT="$DATA/gateway-transport/$GATEWAY"
327
+ }
328
+
329
+ # shellcheck disable=SC2034
330
+ transport_running() {
331
+ TRANSPORT_PID=""
332
+ TRANSPORT_BUILD=""
333
+ [ -f "$TRANSPORT/ready" ] || return 1
334
+ read -r pid build _ < "$TRANSPORT/ready" || return 1
335
+ grep -q transport "/proc/$pid/cmdline" 2> /dev/null || return 1
336
+ TRANSPORT_PID="$pid"
337
+ TRANSPORT_BUILD="$build"
338
+ }
339
+
340
+ stop_transport() {
341
+ if transport_running; then
342
+ kill "$TRANSPORT_PID" 2> /dev/null || true
343
+ for _ in 1 2 3 4 5 6 7 8 9 10; do kill -0 "$TRANSPORT_PID" 2> /dev/null || break; sleep 0.5; done
344
+ kill -9 "$TRANSPORT_PID" 2> /dev/null || true
345
+ fi
346
+ rm -f "$TRANSPORT/ready"
347
+ }
348
+
349
+ require_gateway() {
350
+ gateway_paths
351
+ [ -f "$RECORD" ] || refuse "$E_NOT_FOUND" "gateway $GATEWAY does not exist"
352
+ }
353
+
354
+ # shellcheck disable=SC2034
355
+ gateway_container_exists() {
356
+ info="$(docker inspect -f '{{index .Config.Labels "hyperbrane.gateway"}}|{{.Config.Image}}' "$CONTAINER" 2>/dev/null)" || return 1
357
+ [ "\${info%%|*}" = "$GATEWAY" ] || refuse "$E_CONFLICT" "container $CONTAINER exists but is not managed by hyperbrane"
358
+ IMAGE="\${info#*|}"
359
+ }
360
+
361
+ await_ready() {
362
+ waited=0
363
+ until [ -f "$STATE/mitmproxy/mitmproxy-ca-cert.pem" ] && listening="$(docker exec -u root "$CONTAINER" ss -Hln "( sport = :$PROXY_PORT or sport = :$DNS_PORT )" 2> /dev/null)" &&
364
+ printf '%s\n' "$listening" | grep -q '^tcp' && printf '%s\n' "$listening" | grep -q '^udp'; do
365
+ if [ "$waited" -ge "$((READY_TIMEOUT * 4))" ]; then
366
+ refuse 1 "gateway $GATEWAY did not become ready: container $(docker inspect -f '{{.State.Status}}, exit code {{.State.ExitCode}}' "$CONTAINER" 2> /dev/null || echo missing)"
367
+ fi
368
+ sleep 0.25
369
+ waited=$((waited + 1))
370
+ done
371
+ }
372
+
373
+ gateway_jq() {
374
+ if [ -n "$(running_pid "$CONTAINER")" ]; then
375
+ docker exec -i -e KEY -e FAKE_CHARACTERS "$CONTAINER" jq "$@"
376
+ else
377
+ docker image inspect "$GATEWAY_IMAGE" > /dev/null 2>&1 || exit "$E_NEED_IMAGE"
378
+ docker run --rm -i --network none -e KEY -e FAKE_CHARACTERS "$GATEWAY_IMAGE" jq "$@"
379
+ fi
380
+ }
381
+ `;
382
+ const PRINT_RECORDS = sh `
383
+ print_records "$DATA/$DIRECTORY"
384
+ `;
385
+ const CREATE_GATEWAY = sh `
386
+ set -eu
387
+ gateway_paths
388
+ if gateway_container_exists; then :; fi
389
+ mkdir -p "$DATA/gateways"
390
+ [ -f "$RECORD" ] || printf '{\n "name": "%s",\n "state": "creating"\n}\n' "$GATEWAY" | write_new "$RECORD"
391
+ mkdir -p "$STATE/upstreams" "$STATE/credentials" "$STATE/traffic-rules" "$STATE/traffic-responses" "$STATE/mitmproxy"
392
+ tab="$(printf '\t')"
393
+ printf '%s\n' "$UPSTREAMS" | while IFS="$tab" read -r name definition; do
394
+ [ -z "$name" ] || printf '%s\n' "$definition" | write_whole "$STATE/upstreams/$name.json"
395
+ done
396
+ docker image inspect "$GATEWAY_IMAGE" > /dev/null 2>&1 || exit "$E_NEED_IMAGE"
397
+
398
+ if gateway_container_exists && [ "$IMAGE" != "$GATEWAY_IMAGE" ]; then docker rm -f "$CONTAINER" > /dev/null; fi
399
+ if ! gateway_container_exists; then
400
+ if ! created="$(docker create --name "$CONTAINER" --hostname "$CONTAINER" \
401
+ --label hyperbrane=true --label "hyperbrane.version=$VERSION" --label "hyperbrane.gateway=$GATEWAY" \
402
+ --volume "$STATE:$STATE_PATH" --cap-add NET_ADMIN --network bridge --init --restart unless-stopped \
403
+ "$GATEWAY_IMAGE" 2>&1)"; then
404
+ case "$created" in
405
+ *"already in use"*) ;;
406
+ *) refuse 1 "$created" ;;
407
+ esac
408
+ waited=0
409
+ until gateway_container_exists; do
410
+ [ "$waited" -lt 50 ] || refuse 1 "$created"
411
+ sleep 0.1
412
+ waited=$((waited + 1))
413
+ done
414
+ fi
415
+ fi
416
+ docker start "$CONTAINER" > /dev/null
417
+ await_ready
418
+ grep -q '"state": "ready"' "$RECORD" || printf '{\n "name": "%s",\n "state": "ready"\n}\n' "$GATEWAY" | write_whole "$RECORD"
419
+ restore_portals "$GATEWAY" ""
420
+ `;
421
+ const DESTROY_GATEWAY = sh `
422
+ set -eu
423
+ gateway_paths
424
+ attached=""
425
+ for portal in "$DATA"/portals/*.json; do
426
+ [ -f "$portal" ] && [ "$(record_text "$portal" gatewayName)" = "$GATEWAY" ] || continue
427
+ attached="$attached\${attached:+, }$(record_text "$portal" name)"
428
+ done
429
+ [ -z "$attached" ] || refuse "$E_CONFLICT" "gateway $GATEWAY still has portals: $attached"
430
+ stop_transport
431
+ rm -rf "$TRANSPORT"
432
+ if gateway_container_exists; then
433
+ if ! removed="$(docker rm -f "$CONTAINER" 2>&1)" && docker inspect "$CONTAINER" > /dev/null 2>&1; then refuse 1 "$removed"; fi
434
+ fi
435
+ rm -rf "$STATE"
436
+ rm -f "$RECORD"
437
+ `;
438
+ const RESTART_GATEWAY = sh `
439
+ set -eu
440
+ require_gateway
441
+ gateway_container_exists || refuse "$E_NOT_FOUND" "gateway $GATEWAY has no container; create it again"
442
+ if [ -n "$(ready_portals)" ]; then docker image inspect "$GATEWAY_IMAGE" > /dev/null 2>&1 || exit "$E_NEED_IMAGE"; fi
443
+ docker restart "$CONTAINER" > /dev/null
444
+ await_ready
445
+ restore_portals "$GATEWAY" ""
446
+ `;
447
+ const START_TRANSPORT = sh `
448
+ set -eu
449
+ require_gateway
450
+ mkdir -p "$TRANSPORT"
451
+ chmod 700 "$TRANSPORT"
452
+ exec 9> "$TRANSPORT/lock"
453
+ flock -w 120 9
454
+ if [ -z "$RESTART" ] && transport_running && [ "$TRANSPORT_BUILD" = "$BUILD" ] &&
455
+ [ "$(cat "$TRANSPORT/port" 2> /dev/null)" = "$PORT" ] && [ "$(cat "$TRANSPORT/secret" 2> /dev/null)" = "$SECRET" ]; then
456
+ exit 0
457
+ fi
458
+ [ -f "$TRANSPORT/transport-$BUILD.js" ] || { printf '%s\n' "$TRANSPORT"; exit "$E_NEED_BUNDLE"; }
459
+
460
+ bun=""
461
+ if command -v bun > /dev/null 2>&1 && [ "$(bun --version 2> /dev/null)" = "$BUN_VERSION" ]; then bun="$(command -v bun)"; fi
462
+ if [ -z "$bun" ]; then
463
+ bun="$DATA/bun/$BUN_VERSION/bun"
464
+ if [ ! -x "$bun" ]; then
465
+ case "$(uname -m)" in
466
+ x86_64 | amd64) arch=x64 ;;
467
+ aarch64 | arm64) arch=aarch64 ;;
468
+ *) refuse 1 "there is no bun build for $(uname -m)" ;;
469
+ esac
470
+ mkdir -p "\${bun%/*}"
471
+ zip="$bun.$$.zip"
472
+ url="https://github.com/oven-sh/bun/releases/download/bun-v$BUN_VERSION/bun-linux-$arch.zip"
473
+ if command -v curl > /dev/null 2>&1; then curl -fsSL -o "$zip" "$url"; else wget -qO "$zip" "$url"; fi
474
+ if command -v unzip > /dev/null 2>&1; then
475
+ unzip -p "$zip" "bun-linux-$arch/bun" > "$bun.$$"
476
+ else
477
+ python3 -c 'import sys, zipfile; sys.stdout.buffer.write(zipfile.ZipFile(sys.argv[1]).read(sys.argv[2]))' "$zip" "bun-linux-$arch/bun" > "$bun.$$"
478
+ fi
479
+ chmod 755 "$bun.$$"
480
+ mv -f "$bun.$$" "$bun"
481
+ rm -f "$zip"
482
+ fi
483
+ fi
484
+
485
+ umask 077
486
+ printf '%s' "$SECRET" > "$TRANSPORT/secret.$$"
487
+ mv -f "$TRANSPORT/secret.$$" "$TRANSPORT/secret"
488
+ printf '%s' "$PORT" > "$TRANSPORT/port"
489
+ stop_transport
490
+ HYB_TRANSPORT_DIR="$TRANSPORT" HYB_TRANSPORT_BUILD="$BUILD" HYB_TRANSPORT_PORT="$PORT" HYB_GATEWAY="$GATEWAY" HYB_DATA="$DATA" \
491
+ setsid nohup "$bun" "$TRANSPORT/transport-$BUILD.js" >> "$TRANSPORT/log" 2>&1 < /dev/null 9>&- &
492
+ waited=0
493
+ until transport_running && [ "$TRANSPORT_BUILD" = "$BUILD" ]; do
494
+ if [ "$waited" -ge 100 ]; then
495
+ echo "gateway $GATEWAY's transport worker did not come up:" >&2
496
+ tail -c 2000 "$TRANSPORT/log" >&2
497
+ exit 1
498
+ fi
499
+ sleep 0.2
500
+ waited=$((waited + 1))
501
+ done
502
+ find "$TRANSPORT" -maxdepth 1 -name 'transport-*.js' ! -name "transport-$BUILD.js" -exec rm -f {} + 2> /dev/null || true
503
+ `;
504
+ const WRITE_STATE = sh `
505
+ set -eu
506
+ require_gateway
507
+ printf '%s' "$CONTENT" | write_whole "$STATE/$FILE"
508
+ `;
509
+ const REMOVE_STATE = sh `
510
+ set -eu
511
+ gateway_paths
512
+ rm -f "$STATE/$FILE"
513
+ `;
514
+ const LIST_STATE = sh `
515
+ set -eu
516
+ require_gateway
517
+ print_records "$STATE/$DIRECTORY"
518
+ `;
519
+ const REMOVE_UPSTREAM = sh `
520
+ set -eu
521
+ gateway_paths
522
+ rm -f "$STATE/credentials/$UPSTREAM.oauth.json" "$STATE/credentials/$UPSTREAM.api-keys.json"
523
+ rm -f "$STATE/upstreams/$UPSTREAM.json"
524
+ `;
525
+ const LIST_CREDENTIALS = sh `
526
+ set -eu
527
+ require_gateway
528
+ for definition in "$STATE"/upstreams/*.json; do
529
+ [ -f "$definition" ] || continue
530
+ name="\${definition##*/}"
531
+ name="\${name%.json}"
532
+ oauth=0
533
+ if [ -f "$STATE/credentials/$name.oauth.json" ]; then oauth=1; fi
534
+ printf '%s %s\n' "$name" "$oauth"
535
+ cat "$STATE/credentials/$name.api-keys.json" 2> /dev/null || echo null
536
+ printf '\n\036\n'
537
+ done
538
+ `;
539
+ const SET_API_KEY = sh `
540
+ set -eu
541
+ require_gateway
542
+ definition="$STATE/upstreams/$UPSTREAM.json"
543
+ [ -f "$definition" ] || refuse "$E_NOT_FOUND" "upstream $UPSTREAM does not exist on gateway $GATEWAY"
544
+ keys="$STATE/credentials/$UPSTREAM.api-keys.json"
545
+ stored="$(cat "$keys" 2> /dev/null || echo null)"
546
+ # shellcheck disable=SC2016
547
+ merged="$(printf '%s\n%s\n' "$(cat "$definition")" "$stored" | gateway_jq -rs --arg name "$KEY_NAME" '
548
+ if .[0].modes.apiKey == null then "none" else
549
+ (.[0].modes.apiKey.fake.prefix // "") as $prefix
550
+ | (.[1] // {keys: {}}) as $stored
551
+ | ($stored.keys[$name].fake // ($prefix + $ENV.FAKE_CHARACTERS[0:([($ENV.KEY | length) - ($prefix | length), 16] | max)])) as $fake
552
+ | $fake, ($stored | .keys[$name] = {key: $ENV.KEY, fake: $fake} | tojson)
553
+ end')"
554
+ fake="$(printf '%s\n' "$merged" | head -n 1)"
555
+ [ "$fake" != none ] || refuse "$E_INVALID" "upstream $UPSTREAM has no API key mode"
556
+ mkdir -p "$STATE/credentials"
557
+ printf '%s\n' "$merged" | tail -n +2 | write_whole "$keys"
558
+ printf '%s\n' "$fake"
559
+ `;
560
+ const DELETE_API_KEY = sh `
561
+ set -eu
562
+ require_gateway
563
+ [ -f "$STATE/upstreams/$UPSTREAM.json" ] || refuse "$E_NOT_FOUND" "upstream $UPSTREAM does not exist on gateway $GATEWAY"
564
+ keys="$STATE/credentials/$UPSTREAM.api-keys.json"
565
+ [ -f "$keys" ] || exit 0
566
+ # shellcheck disable=SC2016
567
+ left="$(gateway_jq -r --arg name "$KEY_NAME" 'del(.keys[$name]) | if (.keys | length) == 0 then "" else tojson end' < "$keys")"
568
+ if [ -z "$left" ]; then rm -f "$keys"; else printf '%s\n' "$left" | write_whole "$keys"; fi
569
+ `;
570
+ const CREATE_TRAFFIC_RULE = sh `
571
+ set -eu
572
+ require_gateway
573
+ rule_file="$STATE/traffic-rules/$RULE.json"
574
+ mkdir -p "$STATE/traffic-rules"
575
+ printf '%s' "$CONTENT" | write_new "$rule_file"
576
+ printf '%s' "$CONTENT" | cmp -s - "$rule_file" || refuse "$E_CONFLICT" "traffic rule $RULE already exists with a different spec"
577
+ `;
578
+ const UPDATE_TRAFFIC_RULE = sh `
579
+ set -eu
580
+ require_gateway
581
+ rule_file="$STATE/traffic-rules/$RULE.json"
582
+ [ -f "$rule_file" ] || refuse "$E_NOT_FOUND" "traffic rule $RULE does not exist on gateway $GATEWAY"
583
+ printf '%s' "$CONTENT" | write_whole "$rule_file"
584
+ `;
585
+ const DELETE_TRAFFIC_RULE = sh `
586
+ set -eu
587
+ gateway_paths
588
+ rm -f "$STATE/traffic-rules/$RULE.json" "$STATE/traffic-responses/$RULE.json"
589
+ `;
590
+ const RESPOND_TRAFFIC_RULE = sh `
591
+ set -eu
592
+ require_gateway
593
+ rule_file="$STATE/traffic-rules/$RULE.json"
594
+ [ -f "$rule_file" ] || refuse "$E_NOT_FOUND" "traffic rule $RULE does not exist on gateway $GATEWAY"
595
+ [ "$(record_text "$rule_file" action)" = ASK ] || refuse "$E_INVALID" "traffic rule $RULE is not an ASK rule"
596
+ mkdir -p "$STATE/traffic-responses"
597
+ printf '%s' "$CONTENT" | write_whole "$STATE/traffic-responses/$RULE.json"
598
+ `;
599
+ const CREATE_PORTAL = sh `
600
+ set -eu
601
+ require_running() {
602
+ info="$(docker inspect -f "{{index .Config.Labels \"hyperbrane.$1\"}}|{{.State.Running}}" "$2" 2> /dev/null)" || refuse "$E_NOT_FOUND" "$1 $3 does not exist"
603
+ [ "\${info%|*}" = "$3" ] || refuse "$E_NOT_FOUND" "$1 $3 does not exist"
604
+ [ "\${info#*|}" = true ] || refuse "$E_CONFLICT" "$1 $3 is not running"
605
+ }
606
+ gateway_record="$DATA/gateways/$GATEWAY.json"
607
+ [ -f "$gateway_record" ] && [ "$(record_text "$gateway_record" state)" = ready ] || refuse "$E_NOT_FOUND" "gateway $GATEWAY is not ready"
608
+ require_running gateway "$GATEWAY_PREFIX$GATEWAY" "$GATEWAY"
609
+ require_running dimension "$DIMENSION_PREFIX$DIMENSION" "$DIMENSION"
610
+ docker image inspect "$GATEWAY_IMAGE" > /dev/null 2>&1 || exit "$E_NEED_IMAGE"
611
+
612
+ record="$DATA/portals/$PORTAL.json"
613
+ lock_portal "$PORTAL"
614
+ mkdir -p "$DATA/portals" "$DATA/portal-subnets"
615
+ printf '{\n "name": "%s",\n "dimensionName": "%s",\n "gatewayName": "%s",\n "state": "creating"\n}\n' "$PORTAL" "$DIMENSION" "$GATEWAY" | write_new "$record"
616
+ [ "$(record_text "$record" state)" != destroying ] || refuse "$E_CONFLICT" "portal $PORTAL is being destroyed"
617
+ if [ "$(record_text "$record" dimensionName)" != "$DIMENSION" ] || [ "$(record_text "$record" gatewayName)" != "$GATEWAY" ]; then
618
+ refuse "$E_CONFLICT" "portal $PORTAL already exists with a different spec"
619
+ fi
620
+
621
+ subnet="$(record_field "$record" subnet)"
622
+ if [ -z "$subnet" ]; then
623
+ subnet=0
624
+ until [ "$(record_text "$DATA/portal-subnets/$subnet.json" portalName 2> /dev/null)" = "$PORTAL" ]; do
625
+ if [ -e "$DATA/portal-subnets/$subnet.json" ]; then
626
+ subnet=$((subnet + 1))
627
+ continue
628
+ fi
629
+ [ "$subnet" -lt "$SUBNET_COUNT" ] || refuse "$E_CONFLICT" "no portal subnets left"
630
+ printf '{\n "subnet": %s,\n "portalName": "%s"\n}\n' "$subnet" "$PORTAL" | write_new "$DATA/portal-subnets/$subnet.json"
631
+ done
632
+ sed 's/^ "state": \(.*\)$/ "state": \1,\n "subnet": '"$subnet"'/' "$record" | write_whole "$record"
633
+ fi
634
+
635
+ wire_portal "$PORTAL" "$DIMENSION" "$GATEWAY" "$subnet" || refuse 1 "wiring portal $PORTAL failed"
636
+ trust_gateway "$DIMENSION" "$GATEWAY" yes
637
+ sed 's/^ "state": "creating"/ "state": "ready"/' "$record" | write_whole "$record"
638
+ `;
639
+ const DESTROY_PORTAL = sh `
640
+ set -eu
641
+ record="$DATA/portals/$PORTAL.json"
642
+ [ -f "$record" ] || exit 0
643
+ lock_portal "$PORTAL"
644
+ dimension="$(record_text "$record" dimensionName)"
645
+ gateway="$(record_text "$record" gatewayName)"
646
+ subnet="$(record_field "$record" subnet)"
647
+ sed 's/^ "state": "[a-z]*"/ "state": "destroying"/' "$record" | write_whole "$record"
648
+ if [ -n "$subnet" ]; then
649
+ unwire_portal "$PORTAL" "$gateway" "$subnet"
650
+ rm -f "$DATA/portal-subnets/$subnet.json"
651
+ fi
652
+ untrust_unless_shared "$PORTAL" "$dimension" "$gateway"
653
+ rm -f "$record" "$DATA/portal-locks/$PORTAL.lock"
654
+ `;
655
+ const RESTORE_PORTALS = sh `
656
+ restore_portals "$GATEWAY" "$DIMENSION" || true
657
+ `;
@@ -0,0 +1,5 @@
1
+ import type { Sandbox, SandboxDataEndpoint } from "../sandbox/api.ts";
2
+ import type { GatewayClient } from "./api.ts";
3
+ export declare function createGatewayClient(sandbox: Sandbox & SandboxDataEndpoint, options?: {
4
+ dataDirectory?: string;
5
+ }): GatewayClient;
@@ -0,0 +1,4 @@
1
+ import { createGatewayService } from "./gatewayService.js";
2
+ export function createGatewayClient(sandbox, options = {}) {
3
+ return createGatewayService(sandbox, options);
4
+ }