hyperbrane 0.2.0 → 0.2.1

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 (50) hide show
  1. package/README.md +43 -3
  2. package/dist/agent/agentService.js +3 -2
  3. package/dist/agent/api.d.ts +26 -2
  4. package/dist/agent/control.js +1 -1
  5. package/dist/agent/dimensionAgentClient.d.ts +8 -0
  6. package/dist/agent/dimensionAgentClient.js +99 -0
  7. package/dist/agent/session/backend.d.ts +2 -1
  8. package/dist/agent/session/claude/transcript.d.ts +1 -0
  9. package/dist/agent/session/claude/transcript.js +1 -1
  10. package/dist/agent/session/worker/protocol.d.ts +6 -3
  11. package/dist/agent/session/worker/protocol.js +1 -1
  12. package/dist/agent/session/worker/workerParams.js +10 -3
  13. package/dist/agent/vendor-types/manifest.json +5 -60
  14. package/dist/agent/workerBundle.generated.js +1 -1
  15. package/dist/dimension/api.d.ts +3 -0
  16. package/dist/dimension/control.d.ts +2 -1
  17. package/dist/dimension/control.js +18 -0
  18. package/dist/dimension/service.js +27 -1
  19. package/dist/dimension/transportBundle.generated.js +1 -1
  20. package/dist/index.d.ts +3 -1
  21. package/dist/index.js +3 -1
  22. package/dist/lib/control.d.ts +4 -1
  23. package/dist/lib/control.js +2 -2
  24. package/dist/lib/jsonDirectory.js +24 -3
  25. package/dist/lib/unixSocket.d.ts +2 -0
  26. package/dist/lib/unixSocket.js +28 -0
  27. package/dist/portal-gateway/api.d.ts +1 -0
  28. package/dist/portal-gateway/gatewayService.js +14 -0
  29. package/dist/portal-gateway/transportBundle.generated.js +1 -1
  30. package/dist/restore.d.ts +9 -0
  31. package/dist/restore.js +10 -0
  32. package/dist/sandbox/api.d.ts +5 -0
  33. package/dist/sandbox/client.d.ts +2 -1
  34. package/dist/sandbox/client.js +4 -1
  35. package/dist/sandbox/lib/sshKey.d.ts +6 -0
  36. package/dist/sandbox/lib/sshKey.js +75 -0
  37. package/dist/sandbox/providers/boat/boatService.d.ts +2 -1
  38. package/dist/sandbox/providers/boat/boatService.js +72 -20
  39. package/dist/sandbox/providers/exedev/exeDevService.js +4 -2
  40. package/dist/sandbox/providers/exedev/exeDevToken.js +2 -6
  41. package/dist/sandbox/providers/local/localService.js +4 -26
  42. package/dist/sandbox/providers/ssh/sshService.js +2 -1
  43. package/dist/sandbox/service.d.ts +2 -1
  44. package/dist/sandbox/service.js +9 -1
  45. package/dist/vendor-types/claude/agent-sdk/bridge.d.ts +20 -4
  46. package/dist/vendor-types/claude/agent-sdk/browser-sdk.d.ts +24 -0
  47. package/dist/vendor-types/claude/agent-sdk/core.d.ts +2 -0
  48. package/dist/vendor-types/claude/agent-sdk/sdk-tools.d.ts +75 -6
  49. package/dist/vendor-types/claude/agent-sdk/sdk.d.ts +569 -100
  50. package/package.json +2 -1
@@ -1,3 +1,4 @@
1
+ import type { RunCommandOptions, RunCommandResult } from "../sandbox/api.ts";
1
2
  export { dimensionLayout } from "./paths.ts";
2
3
  export type MemoryLimit = number & {
3
4
  readonly __brand: "MemoryLimit";
@@ -59,6 +60,7 @@ export interface DimensionClient {
59
60
  createDimension(dimension: Dimension): Promise<Dimension>;
60
61
  destroyDimension(dimensionName: string): Promise<void>;
61
62
  restartDimension(dimensionName: string): Promise<void>;
63
+ restoreDimensions(): Promise<string[]>;
62
64
  updateDimension(dimensionName: string, limits: DimensionLimits): Promise<Dimension>;
63
65
  listDimensions(): Promise<DimensionInfo[]>;
64
66
  getDimensionUsage(): Promise<DimensionUsage[]>;
@@ -68,6 +70,7 @@ export interface DimensionClient {
68
70
  }): Promise<ReadableStream<Uint8Array>>;
69
71
  writeFile(dimensionName: string, path: string, data: Uint8Array | ReadableStream<Uint8Array>): Promise<void>;
70
72
  statFile(dimensionName: string, path: string): Promise<DimensionFileInfo>;
73
+ runCommand(dimensionName: string, cmd: string[], options?: RunCommandOptions): Promise<RunCommandResult>;
71
74
  }
72
75
  export declare class DimensionInvalidError extends Error {
73
76
  name: string;
@@ -1,5 +1,5 @@
1
1
  import { type ControlOptions } from "../lib/control.ts";
2
- import type { Sandbox } from "../sandbox/api.ts";
2
+ import type { RunCommandOptions, RunCommandResult, Sandbox } from "../sandbox/api.ts";
3
3
  import { type Dimension, type DimensionLimits, type DimensionStatus, type DimensionUsage } from "./api.ts";
4
4
  export type { ControlOptions };
5
5
  export type DimensionRecord = Dimension & {
@@ -19,3 +19,4 @@ export declare function dimensionUsageScript(host: Sandbox, options?: ControlOpt
19
19
  export declare function startTransportScript(host: Sandbox, dimensionName: string, options: ControlOptions, { restart, }: {
20
20
  restart: boolean;
21
21
  }): Promise<void>;
22
+ export declare function runInDimensionScript(host: Sandbox, dimensionName: string, cmd: string[], { cwd, env, stdin }: RunCommandOptions, options?: ControlOptions): Promise<RunCommandResult>;
@@ -1,3 +1,4 @@
1
+ import { posix } from "node:path";
1
2
  import { exitEnv, records, runControl } from "../lib/control.js";
2
3
  import { sh } from "../lib/sh.js";
3
4
  import { HYPERBRANE_VERSION } from "../lib/version.js";
@@ -96,6 +97,13 @@ export async function startTransportScript(host, dimensionName, options, { resta
96
97
  }
97
98
  check(result, `starting dimension ${dimensionName}'s transport worker`);
98
99
  }
100
+ export async function runInDimensionScript(host, dimensionName, cmd, { cwd, env = {}, stdin }, options = {}) {
101
+ const envFlags = Object.entries(env).flatMap(([name, value]) => ["-e", `${name}=${value}`]);
102
+ const result = await runControl(host, PRELUDE, RUN_IN_DIMENSION, { ...exitEnv(EXIT), ...dimensionEnv(dimensionName, options), WORKDIR: posix.resolve(WORKSPACE_PATH, cwd ?? "."), INTERACTIVE: stdin === undefined ? "" : "1" }, {}, { args: [...envFlags, containerName(dimensionName), ...cmd], stdin });
103
+ if (!result.stdout.startsWith(COMMAND_START))
104
+ check(result, `running a command in dimension ${dimensionName}`);
105
+ return { ...result, stdout: result.stdout.slice(COMMAND_START.length) };
106
+ }
99
107
  function dataEnv({ dataDirectory }) {
100
108
  return { ...(dataDirectory === undefined ? {} : { DATA: dataDirectory }), CONTAINER_PREFIX: containerName(""), RUN_PATH, STATE_PATH };
101
109
  }
@@ -121,6 +129,7 @@ function check(result, what) {
121
129
  }
122
130
  }
123
131
  const CONTAINERS_START = "\n\x1d\n";
132
+ const COMMAND_START = "\x1e";
124
133
  const PRELUDE = sh `
125
134
  # shellcheck disable=SC2034
126
135
  dimension_paths() {
@@ -326,3 +335,12 @@ done
326
335
  # shellcheck disable=SC2086
327
336
  [ -z "$containers" ] || docker stats --no-stream --format '{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}' $containers
328
337
  `;
338
+ const RUN_IN_DIMENSION = sh `
339
+ set -eu
340
+ dimension_paths
341
+ require_dimension
342
+ [ "$RUNNING" = true ] || refuse "$E_CONFLICT" "dimension $DIMENSION isn't running"
343
+ user="$(docker inspect -f '{{.Config.User}}' "$CONTAINER")"
344
+ printf '\036'
345
+ exec docker exec \${INTERACTIVE:+-i} -u "\${user:-0:0}" -w "$WORKDIR" "$@"
346
+ `;
@@ -1,5 +1,5 @@
1
1
  import { DimensionInvalidError, isDimensionName } from "./api.js";
2
- import { createDimensionScript, destroyDimensionScript, dimensionUsageScript, listDimensionsScript, restartDimensionScript, startTransportScript, updateDimensionScript, } from "./control.js";
2
+ import { createDimensionScript, destroyDimensionScript, dimensionUsageScript, listDimensionsScript, restartDimensionScript, runInDimensionScript, startTransportScript, updateDimensionScript, } from "./control.js";
3
3
  import { createDimensionImage } from "./image.js";
4
4
  import { RUN_PATH, STATE_PATH, WORKSPACE_PATH, containerName, dimensionLayout } from "./paths.js";
5
5
  import { transportFiles } from "./transportClient.js";
@@ -41,6 +41,20 @@ export function createDimensionService(host, { dataDirectory } = {}) {
41
41
  await portals.restorePortals({ dimensionName });
42
42
  await startTransportScript(host, dimensionName, options, { restart: true });
43
43
  }
44
+ async function restoreDimensions() {
45
+ const running = (await listDimensionsScript(host, options)).filter(({ status }) => status === "running");
46
+ const restored = await Promise.all(running.map(async ({ record: { name } }) => {
47
+ try {
48
+ await startTransportScript(host, name, options, { restart: false });
49
+ return name;
50
+ }
51
+ catch (error) {
52
+ console.warn(`starting dimension ${name}'s transport worker failed:`, error.message);
53
+ return null;
54
+ }
55
+ }));
56
+ return restored.filter((name) => name !== null);
57
+ }
44
58
  async function transportSocket(dimensionName) {
45
59
  assertValidName(dimensionName);
46
60
  const data = dataDirectory ?? `${await host.homeDirectory()}/.local/share/hyperbrane`;
@@ -53,6 +67,16 @@ export function createDimensionService(host, { dataDirectory } = {}) {
53
67
  const { state: _state, ...updated } = await updateDimensionScript(host, dimensionName, limits, options);
54
68
  return updated;
55
69
  }
70
+ async function runCommand(dimensionName, cmd, commandOptions = {}) {
71
+ assertValidName(dimensionName);
72
+ if (cmd.length === 0)
73
+ throw new DimensionInvalidError("a command needs at least its program");
74
+ for (const name of Object.keys(commandOptions.env ?? {})) {
75
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
76
+ throw new DimensionInvalidError(`not an environment variable name: "${name}"`);
77
+ }
78
+ return runInDimensionScript(host, dimensionName, cmd, commandOptions, options);
79
+ }
56
80
  return {
57
81
  listDimensions,
58
82
  getDimensionUsage: () => dimensionUsageScript(host, options),
@@ -62,7 +86,9 @@ export function createDimensionService(host, { dataDirectory } = {}) {
62
86
  createDimension: (dimension) => createDimension(dimension),
63
87
  destroyDimension: (dimensionName) => destroyDimension(dimensionName),
64
88
  restartDimension: (dimensionName) => restartDimension(dimensionName),
89
+ restoreDimensions,
65
90
  updateDimension: (dimensionName, limits) => updateDimension(dimensionName, limits),
91
+ runCommand: (dimensionName, cmd, commandOptions) => runCommand(dimensionName, cmd, commandOptions),
66
92
  };
67
93
  }
68
94
  function assertLimits(limits) {