@cofy-x/axern-sdk 0.5.1 → 0.7.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 (62) hide show
  1. package/README.md +39 -52
  2. package/dist/client/index.d.ts +12 -11
  3. package/dist/client/index.js +64 -65
  4. package/dist/config/index.d.ts +0 -1
  5. package/dist/config/index.js +2 -4
  6. package/dist/generated/proto.js +0 -1
  7. package/dist/index.d.ts +6 -5
  8. package/dist/index.js +2 -2
  9. package/dist/network-policy.d.ts +41 -0
  10. package/dist/network-policy.js +132 -0
  11. package/dist/node/attached_process.d.ts +1 -2
  12. package/dist/node/attached_process.js +12 -26
  13. package/dist/node/client.d.ts +4 -6
  14. package/dist/node/client.js +2 -9
  15. package/dist/node/exec.d.ts +1 -3
  16. package/dist/node/exec.js +0 -43
  17. package/dist/proto/axern/control/admin/v1/access.proto +12 -5
  18. package/dist/proto/axern/control/admin/v1/audit.proto +14 -16
  19. package/dist/proto/axern/control/admin/v1/node.proto +23 -67
  20. package/dist/proto/axern/control/admin/v1/reliability.proto +11 -78
  21. package/dist/proto/axern/control/capability/v1/capability.proto +35 -90
  22. package/dist/proto/axern/control/common/v1/common.proto +72 -102
  23. package/dist/proto/axern/control/environment/v1/environment.proto +42 -26
  24. package/dist/proto/axern/control/gateway/v1/gateway.proto +18 -59
  25. package/dist/proto/axern/control/identity/v1/identity.proto +1 -1
  26. package/dist/proto/axern/control/namespace/v1/namespace.proto +2 -3
  27. package/dist/proto/axern/control/quota/v1/quota.proto +22 -32
  28. package/dist/proto/axern/control/run/v1/run.proto +26 -22
  29. package/dist/proto/axern/control/secret/v1/secret.proto +1 -3
  30. package/dist/proto/axern/control/tunnel/v1/tunnel.proto +18 -25
  31. package/dist/proto/axern/node/sandbox/v1/node.proto +59 -375
  32. package/dist/sandbox/index.d.ts +5 -9
  33. package/dist/sandbox/index.js +14 -22
  34. package/dist/sandbox/lifecycle.d.ts +1 -1
  35. package/dist/sandbox/lifecycle.js +55 -17
  36. package/dist/tunnel/connector.js +1 -1
  37. package/dist/tunnel/control.d.ts +0 -1
  38. package/dist/tunnel/control.js +0 -1
  39. package/dist/tunnel/runtime.d.ts +0 -1
  40. package/dist/tunnel/runtime.js +0 -1
  41. package/dist/tunnel/types.d.ts +0 -1
  42. package/dist/types.d.ts +4 -21
  43. package/dist/types.js +1 -3
  44. package/dist/version.d.ts +1 -1
  45. package/dist/version.js +1 -1
  46. package/package.json +1 -1
  47. package/dist/proto/axern/control/admin/v1/allocation_lifecycle.proto +0 -91
  48. package/dist/proto/axern/control/admin/v1/service.proto +0 -18
  49. package/dist/proto/axern/control/admin/v1/storage.proto +0 -81
  50. package/dist/proto/axern/control/agentprofile/v1/agent_profile.proto +0 -169
  51. package/dist/proto/axern/control/catalog/v1/catalog.proto +0 -127
  52. package/dist/proto/axern/control/function/v1/function.proto +0 -130
  53. package/dist/proto/axern/control/function/v1/function_types.proto +0 -233
  54. package/dist/proto/axern/control/node/v1/node_control.proto +0 -443
  55. package/dist/proto/axern/control/rollout/v1/rollout.proto +0 -344
  56. package/dist/proto/axern/control/service/v1/service.proto +0 -96
  57. package/dist/proto/axern/control/service/v1/service_event.proto +0 -47
  58. package/dist/proto/axern/control/service/v1/service_replica.proto +0 -78
  59. package/dist/proto/axern/control/service/v1/service_types.proto +0 -155
  60. package/dist/proto/axern/control/storage/v1/storage.proto +0 -107
  61. package/dist/proto/axern/control/storage/v1/storage_types.proto +0 -125
  62. package/dist/proto/axern/data/artifact/v1/artifact.proto +0 -19
package/README.md CHANGED
@@ -28,18 +28,14 @@ pnpm install
28
28
  make sdk-typescript-verify
29
29
  ```
30
30
 
31
- The SDK dynamically loads Axern proto definitions from `sdk/proto`. Set
32
- `AXERN_PROTO_ROOT` when running from a different layout.
31
+ The SDK dynamically loads Axern proto definitions from `sdk/proto`. Set `AXERN_PROTO_ROOT` when running from a different layout.
33
32
 
34
33
  ## Basic Usage
35
34
 
36
35
  ```ts
37
36
  import { AxernClient, Sandbox } from "@cofy-x/axern-sdk";
38
37
 
39
- const client = AxernClient.fromContext(
40
- process.env.AXERN_CONFIG ?? `${process.env.HOME}/.config/axern/config.json`,
41
- process.env.AXERN_CONTEXT,
42
- );
38
+ const client = AxernClient.fromContext(process.env.AXERN_CONFIG ?? `${process.env.HOME}/.config/axern/config.json`, process.env.AXERN_CONTEXT);
43
39
 
44
40
  const sandbox = await new Sandbox({
45
41
  client,
@@ -68,12 +64,23 @@ try {
68
64
  }
69
65
  ```
70
66
 
71
- Run a tool from a separate image with `execImage` or `processImage`. OCI and
72
- Nydus refs use the same image field. When `mounts` is omitted, the SDK requests
73
- `/workspace -> /workspace`; pass `mounts: []` for no shared paths. Use
74
- `new Sandbox({ image })` when the image should be the sandbox rootfs with normal
75
- files, exec, process, tunnel, and lifecycle APIs; image-backed processes are
76
- temporary side processes attached to an existing sandbox.
67
+ ## Network Policies
68
+
69
+ Omitting `networkPolicy` requests unrestricted networking. Strict policies are fail-closed; `denyDns` only refuses matching traditional UDP/TCP DNS queries and does not block direct IP traffic, DoH, DoT, or already-resolved addresses.
70
+
71
+ ```ts
72
+ import { NetworkPolicy, Sandbox } from "@cofy-x/axern-sdk";
73
+
74
+ const sandbox = await new Sandbox({
75
+ client,
76
+ image: "docker.io/library/python:3.12-slim",
77
+ networkPolicy: NetworkPolicy.denyDns("github.com", "*.github.com", "githubusercontent.com", "*.githubusercontent.com"),
78
+ }).start();
79
+ ```
80
+
81
+ `NetworkPolicy.allowDomains("example.com", "*.example.com")` allows only strict HTTP/HTTPS destinations validated by DNS plus HTTP Host or TLS SNI. `NetworkPolicy.strict({ cidrRules: [...] })` adds explicit TCP/UDP CIDR and port grants; `NetworkPolicy.denyAll()` allows no egress.
82
+
83
+ Run a tool from a separate image with `execImage` or `processImage`. OCI and Nydus refs use the same image field. When `mounts` is omitted, the SDK requests `/workspace -> /workspace`; pass `mounts: []` for no shared paths. Use `new Sandbox({ image })` when the image should be the sandbox rootfs with normal files, exec, process, tunnel, and lifecycle APIs; image-backed processes are temporary side processes attached to an existing sandbox.
77
84
 
78
85
  ```ts
79
86
  import { workspaceMount } from "@cofy-x/axern-sdk";
@@ -85,58 +92,40 @@ const result = await sandbox.execImage("ghcr.io/cofy-x/agent:latest", "tool run"
85
92
  console.log(result.stdoutText());
86
93
  ```
87
94
 
88
- `AxernClient` requires an explicit endpoint. Use `AxernClient.fromContext()` in
89
- interactive examples or `AxernClient.fromEnv()` in environment-driven
90
- automation. Neither the client constructor nor `Sandbox` silently reads the
91
- user directory.
95
+ `AxernClient` requires an explicit endpoint. Use `AxernClient.fromContext()` in interactive examples or `AxernClient.fromEnv()` in environment-driven automation. Neither the client constructor nor `Sandbox` silently reads the user directory.
92
96
 
93
97
  ## Configuration
94
98
 
95
- `Sandbox` requires exactly one source: `templateId`, `image`, or
96
- `environmentId`.
99
+ `Sandbox` requires exactly one source: `templateId`, `image`, or `environmentId`.
97
100
 
98
101
  Common options:
99
102
 
100
103
  - `namespace`: control-plane namespace, default `default`
101
104
  - `client`: explicit `AxernClient` shared by the sandbox
102
105
  - `argv`, `env`, `cwd`: initial sandbox process configuration
103
- - `runtimeClass`: runtime selector, for example `runsc` or `runc`
104
- - `requestCpu`, `requestMemory`, `requestEphemeralStorage`: scheduler resource
105
- requests such as `500m`, `512MiB`, and `1GiB`; numeric CPU values are cores
106
- and numeric memory/storage values are bytes
107
- - `limitCpu`, `limitMemory`, `limitEphemeralStorage`: runtime hard limits;
108
- numeric CPU values are cores and numeric memory/storage values are bytes
109
- - `readyTimeoutMs`: service replica readiness timeout
106
+ - `requestCpu`, `requestMemory`, `requestEphemeralStorage`: scheduler resource requests such as `500m`, `512MiB`, and `1GiB`; numeric CPU values are cores and numeric memory/storage values are bytes
107
+ - `limitCpu`, `limitMemory`, `limitEphemeralStorage`: runtime hard limits; numeric CPU values are cores and numeric memory/storage values are bytes
108
+ - `readyTimeoutMs`: Run allocation startup timeout
110
109
 
111
110
  Tunnel options:
112
111
 
113
- - `tunnel.upstream`: local TCP address reached by the SDK connector, for
114
- example `127.0.0.1:8080`
112
+ - `tunnel.upstream`: local TCP address reached by the SDK connector, for example `127.0.0.1:8080`
115
113
  - `tunnel.proxyPort`: sandbox-local port bound by Axern, default `8786`
116
114
  - `tunnel.ttlSeconds`: control-plane tunnel session TTL, renewed by the SDK
117
115
 
118
- Tunnel traffic reuses the client gateway endpoint, mTLS identity, server name,
119
- and proxy policy.
116
+ Tunnel traffic reuses the client gateway endpoint, mTLS identity, server name, and proxy policy.
120
117
 
121
- After `start()`, use `sandbox.metadata.tunnel?.boundAddr` from inside the
122
- sandbox, for example `http://127.0.0.1:8786`.
118
+ After `start()`, use `sandbox.metadata.tunnel?.boundAddr` from inside the sandbox, for example `http://127.0.0.1:8786`.
123
119
 
124
120
  ## Sandbox API
125
121
 
126
122
  - Lifecycle: `start()`, `close()`, `state`, `metadata`
127
123
  - Execution: `exec(command, options)`, `process(command, options)`
128
- - Agent sandbox: `capabilityStatus`, `computerUseStatus`,
129
- `computerUseScreenshot`, `computerUseDisplay`, `computerUseMouse`,
130
- `computerUseKeyboard`
131
- - Files: `readFile`, `readText`, `writeFile`, `writeText`, `stat`, `listDir`,
132
- `exists`, `mkdir`, `remove`, `copy`, `move`, `chmod`, `touch`
133
- - Directories: `uploadDir(localPath, remotePath)`,
134
- `downloadDir(remotePath, localPath)`
135
- - Tunnel: `new Sandbox({ tunnel: { upstream, proxyPort } })`,
136
- `metadata.tunnel`
137
- - Errors: `AxernRpcError`, `SandboxExecError`, `SandboxStateError`,
138
- `SandboxValidationError`, `isNotFound`, `isPermissionDenied`, `isTimeout`,
139
- `rpcCode`
124
+ - Agent sandbox: `capabilityStatus`, `computerUseStatus`, `computerUseScreenshot`, `computerUseDisplay`, `computerUseMouse`, `computerUseKeyboard`
125
+ - Files: `readFile`, `readText`, `writeFile`, `writeText`, `stat`, `listDir`, `exists`, `mkdir`, `remove`, `copy`, `move`, `chmod`, `touch`
126
+ - Directories: `uploadDir(localPath, remotePath)`, `downloadDir(remotePath, localPath)`
127
+ - Tunnel: `new Sandbox({ tunnel: { upstream, proxyPort } })`, `metadata.tunnel`
128
+ - Errors: `AxernRpcError`, `SandboxExecError`, `SandboxStateError`, `SandboxValidationError`, `isNotFound`, `isPermissionDenied`, `isTimeout`, `rpcCode`
140
129
 
141
130
  ## Local Smoke
142
131
 
@@ -147,9 +136,7 @@ pnpm --filter @cofy-x/axern-sdk run smoke:local
147
136
  pnpm --filter @cofy-x/axern-sdk run smoke:tunnel
148
137
  ```
149
138
 
150
- The smoke loads `deploy/local/state/compose/axern.env` when present. It uses the
151
- `python311` template by default; set `AXERN_TS_SMOKE_IMAGE` to verify an image
152
- source instead.
139
+ The smoke loads `deploy/local/state/compose/axern.env` when present. It uses the `python311` template by default; set `AXERN_TS_SMOKE_IMAGE` to verify an image source instead.
153
140
 
154
141
  Check package contents with:
155
142
 
@@ -159,12 +146,12 @@ pnpm --filter @cofy-x/axern-sdk run pack:dry-run
159
146
 
160
147
  ## Proto Boundary
161
148
 
162
- The SDK loads protobuf definitions through `@grpc/proto-loader`. Dynamic proto
163
- access remains isolated under `src/generated`; public callers depend only on
164
- the stable TypeScript DTOs and error types.
149
+ The SDK loads protobuf definitions through `@grpc/proto-loader`. Dynamic proto access remains isolated under `src/generated`; public callers depend only on the stable TypeScript DTOs and error types.
165
150
 
166
151
  ## Scope
167
152
 
168
- This SDK is Node.js-first. The higher-level Browser API, generated TypeScript
169
- proto stubs, and full control-plane administration APIs remain outside the
170
- v0.2 public contract.
153
+ This SDK is Node.js-first. Browser automation runs as caller-owned workload software through process and Computer Use operations; generated TypeScript proto stubs and full control-plane administration APIs remain outside its public contract.
154
+
155
+ ## Run Output Retention
156
+
157
+ Run output reads expose Allocation-local stdout/stderr after runtime cleanup until `output_expires_at`, fixed at 15 minutes after cleanup begins. The combined readable limit is 64 MiB, with an explicit truncation signal. Node-process restart preserves sealed output; node-disk loss does not. Ordinary writable files still require explicit download before termination. No durable output object or persistent workspace is created.
@@ -4,10 +4,10 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import * as grpc from "@grpc/grpc-js";
7
- import { NodeSandboxClient } from "../node/client.js";
7
+ import { AllocationClient } from "../node/client.js";
8
8
  import type { ResourceQuantity } from "../resources.js";
9
+ import type { NetworkPolicy } from "../network-policy.js";
9
10
  import { TunnelControlClient } from "../tunnel/control.js";
10
- import type { VolumeMount } from "../types.js";
11
11
  import type { GatewayTransportOptions } from "../tunnel/relay.js";
12
12
  export interface AxernClientOptions {
13
13
  endpoint: string;
@@ -26,15 +26,14 @@ export interface CreateEnvironmentOptions {
26
26
  rootfsReadonly?: boolean;
27
27
  labels?: Record<string, string>;
28
28
  }
29
- export interface CreateServiceOptions {
29
+ export interface CreateRunOptions {
30
30
  namespace?: string;
31
31
  environmentId: string;
32
32
  argv?: string[];
33
33
  env?: Record<string, string>;
34
34
  cwd?: string;
35
- runtimeClass?: string;
35
+ networkPolicy?: NetworkPolicy;
36
36
  extensionCapabilities?: readonly ExtensionCapability[];
37
- volumes?: readonly VolumeMount[];
38
37
  requestCpu?: ResourceQuantity;
39
38
  requestMemory?: ResourceQuantity;
40
39
  requestEphemeralStorage?: ResourceQuantity;
@@ -51,13 +50,16 @@ export interface ReadRunOutputOptions {
51
50
  cursor?: string;
52
51
  follow?: boolean;
53
52
  }
53
+ export interface WatchRunOptions {
54
+ afterVersion?: number;
55
+ signal?: AbortSignal;
56
+ }
54
57
  export declare class AxernClient {
55
58
  readonly endpoint: string;
56
59
  private readonly credentials;
57
60
  private readonly controlOptions;
58
61
  private readonly environmentControl;
59
62
  private readonly runControl;
60
- private readonly serviceControl;
61
63
  private readonly tunnelControl;
62
64
  private readonly gatewayTransport;
63
65
  constructor(options: AxernClientOptions);
@@ -66,12 +68,11 @@ export declare class AxernClient {
66
68
  close(): void;
67
69
  createEnvironment(options: CreateEnvironmentOptions): Promise<Record<string, unknown>>;
68
70
  deleteEnvironment(environmentId: string): Promise<void>;
69
- watchRun(runId: string, afterVersion?: number): AsyncGenerator<Record<string, unknown>>;
71
+ createRun(options: CreateRunOptions): Promise<Record<string, unknown>>;
72
+ cancelRun(runId: string): Promise<void>;
73
+ watchRun(runId: string, options?: WatchRunOptions): AsyncGenerator<Record<string, unknown>>;
70
74
  readRunOutput(runId: string, options?: ReadRunOutputOptions): AsyncGenerator<Record<string, unknown>>;
71
- createService(options: CreateServiceOptions): Promise<Record<string, unknown>>;
72
- deleteService(serviceId: string): Promise<void>;
73
- listServiceReplicas(serviceId: string): Promise<Record<string, unknown>[]>;
74
- nodeSandbox(allocationId: string): NodeSandboxClient;
75
+ allocation(allocationId: string): AllocationClient;
75
76
  tunnelClient(): TunnelControlClient;
76
77
  tunnelTransport(): GatewayTransportOptions;
77
78
  }
@@ -8,7 +8,7 @@ import { readFileSync } from "node:fs";
8
8
  import { loadAxernContext, loadAxernEnv, normalizeProxyMode } from "../config/index.js";
9
9
  import { mapRpcError } from "../errors/index.js";
10
10
  import { serviceConstructor, unary } from "../generated/proto.js";
11
- import { NodeSandboxClient } from "../node/client.js";
11
+ import { AllocationClient } from "../node/client.js";
12
12
  import { buildResourceSpec } from "../resources.js";
13
13
  import { TunnelControlClient } from "../tunnel/control.js";
14
14
  import { required } from "../validation.js";
@@ -18,7 +18,6 @@ export class AxernClient {
18
18
  controlOptions;
19
19
  environmentControl;
20
20
  runControl;
21
- serviceControl;
22
21
  tunnelControl;
23
22
  gatewayTransport;
24
23
  constructor(options) {
@@ -57,11 +56,9 @@ export class AxernClient {
57
56
  "EnvironmentControl",
58
57
  ]);
59
58
  const RunControl = serviceConstructor(["axern", "control", "run", "v1", "RunControl"]);
60
- const ServiceControl = serviceConstructor(["axern", "control", "service", "v1", "ServiceControl"]);
61
59
  const TunnelControl = serviceConstructor(["axern", "control", "tunnel", "v1", "TunnelControl"]);
62
60
  this.environmentControl = new EnvironmentControl(this.endpoint, this.credentials, this.controlOptions);
63
61
  this.runControl = new RunControl(this.endpoint, this.credentials, this.controlOptions);
64
- this.serviceControl = new ServiceControl(this.endpoint, this.credentials, this.controlOptions);
65
62
  this.tunnelControl = new TunnelControl(this.endpoint, this.credentials, this.controlOptions);
66
63
  }
67
64
  static fromEnv(overrides = {}) {
@@ -82,7 +79,6 @@ export class AxernClient {
82
79
  close() {
83
80
  this.environmentControl.close();
84
81
  this.runControl.close();
85
- this.serviceControl.close();
86
82
  this.tunnelControl.close();
87
83
  }
88
84
  async createEnvironment(options) {
@@ -118,17 +114,57 @@ export class AxernClient {
118
114
  throw mapRpcError(error, "delete environment");
119
115
  }
120
116
  }
121
- async *watchRun(runId, afterVersion = 0) {
117
+ async createRun(options) {
118
+ const resources = buildResourceSpec(options);
119
+ try {
120
+ const response = await unary(this.runControl, "CreateRun", {
121
+ namespace: options.namespace ?? "default",
122
+ environment_id: required("environmentId", options.environmentId),
123
+ config: {
124
+ argv: options.argv ?? [],
125
+ env: options.env ?? {},
126
+ cwd: options.cwd ?? "",
127
+ ...(options.networkPolicy === undefined
128
+ ? {}
129
+ : { network: { egress_policy: options.networkPolicy.toWire() } }),
130
+ extension_capability_requirements: (options.extensionCapabilities ?? []).map((capability) => ({
131
+ capability: { name: capability.name, value: capability.value ?? "" },
132
+ })),
133
+ resources,
134
+ },
135
+ labels: options.labels ?? {},
136
+ });
137
+ return response.run;
138
+ }
139
+ catch (error) {
140
+ throw mapRpcError(error, "create run");
141
+ }
142
+ }
143
+ async cancelRun(runId) {
144
+ try {
145
+ await unary(this.runControl, "CancelRun", { run_id: required("runId", runId) });
146
+ }
147
+ catch (error) {
148
+ throw mapRpcError(error, "cancel run");
149
+ }
150
+ }
151
+ async *watchRun(runId, options = {}) {
152
+ const afterVersion = options.afterVersion ?? 0;
153
+ const signal = options.signal;
122
154
  if (afterVersion < 0) {
123
155
  throw new Error("afterVersion must be non-negative");
124
156
  }
125
157
  let version = afterVersion;
126
158
  let retryDelayMs = 100;
127
159
  for (;;) {
160
+ if (signal?.aborted)
161
+ return;
128
162
  const stream = serverStream(this.runControl, "WatchRun", {
129
163
  run_id: required("runId", runId),
130
164
  after_version: version,
131
165
  });
166
+ const cancel = () => stream.cancel();
167
+ signal?.addEventListener("abort", cancel, { once: true });
132
168
  try {
133
169
  for await (const response of stream) {
134
170
  const run = response.run;
@@ -144,14 +180,21 @@ export class AxernClient {
144
180
  return;
145
181
  }
146
182
  catch (error) {
183
+ if (signal?.aborted)
184
+ return;
147
185
  if (!transientReadError(error))
148
186
  throw mapRpcError(error, "watch run");
149
187
  }
150
- await sleep(retryDelayMs);
188
+ finally {
189
+ signal?.removeEventListener("abort", cancel);
190
+ }
191
+ await sleep(retryDelayMs, signal);
151
192
  retryDelayMs = Math.min(retryDelayMs * 2, 2_000);
152
193
  }
153
194
  }
154
195
  async *readRunOutput(runId, options = {}) {
196
+ // Output is Allocation-local and may be unavailable after cleanup; callers
197
+ // that need durable bytes must consume and persist them before then.
155
198
  const response = await unary(this.runControl, "GetRun", { run_id: required("runId", runId) });
156
199
  const allocationId = String(response.run?.allocation_id ?? "");
157
200
  if (allocationId === "")
@@ -195,54 +238,8 @@ export class AxernClient {
195
238
  retryDelayMs = Math.min(retryDelayMs * 2, 2_000);
196
239
  }
197
240
  }
198
- async createService(options) {
199
- const resources = buildResourceSpec(options);
200
- try {
201
- const response = await unary(this.serviceControl, "CreateService", {
202
- namespace: options.namespace ?? "default",
203
- environment_id: required("environmentId", options.environmentId),
204
- replicas: 1,
205
- config: {
206
- argv: options.argv ?? [],
207
- env: options.env ?? {},
208
- cwd: options.cwd ?? "",
209
- runtime_class: options.runtimeClass ?? "",
210
- extension_capability_requirements: (options.extensionCapabilities ?? []).map((capability) => ({
211
- capability: { name: capability.name, value: capability.value ?? "" },
212
- })),
213
- volume_mounts: serviceVolumeMounts(options.volumes),
214
- resources,
215
- },
216
- labels: options.labels ?? {},
217
- });
218
- return response.service;
219
- }
220
- catch (error) {
221
- throw mapRpcError(error, "create service");
222
- }
223
- }
224
- async deleteService(serviceId) {
225
- try {
226
- await unary(this.serviceControl, "DeleteService", { service_id: required("serviceId", serviceId) });
227
- }
228
- catch (error) {
229
- throw mapRpcError(error, "delete service");
230
- }
231
- }
232
- async listServiceReplicas(serviceId) {
233
- try {
234
- const response = await unary(this.serviceControl, "ListServiceReplicas", {
235
- service_id: required("serviceId", serviceId),
236
- filter: { view: 2 },
237
- });
238
- return response.replicas ?? [];
239
- }
240
- catch (error) {
241
- throw mapRpcError(error, "list service replicas");
242
- }
243
- }
244
- nodeSandbox(allocationId) {
245
- return new NodeSandboxClient({
241
+ allocation(allocationId) {
242
+ return new AllocationClient({
246
243
  allocationId: required("allocationId", allocationId),
247
244
  target: this.endpoint,
248
245
  credentials: this.credentials,
@@ -256,14 +253,6 @@ export class AxernClient {
256
253
  return { ...this.gatewayTransport };
257
254
  }
258
255
  }
259
- function serviceVolumeMounts(mounts) {
260
- return (mounts ?? []).map((mount) => ({
261
- name: mount.name,
262
- target: mount.target,
263
- readonly: mount.readonly ?? false,
264
- options: [...(mount.options ?? [])],
265
- }));
266
- }
267
256
  function serverStream(client, method, request) {
268
257
  const fn = client[method];
269
258
  return fn.call(client, request);
@@ -272,8 +261,18 @@ function transientReadError(error) {
272
261
  const code = error.code;
273
262
  return code === grpc.status.UNAVAILABLE || code === grpc.status.DEADLINE_EXCEEDED;
274
263
  }
275
- function sleep(milliseconds) {
276
- return new Promise((resolve) => setTimeout(resolve, milliseconds));
264
+ function sleep(milliseconds, signal) {
265
+ if (signal?.aborted)
266
+ return Promise.resolve();
267
+ return new Promise((resolve) => {
268
+ const complete = () => {
269
+ clearTimeout(timer);
270
+ signal?.removeEventListener("abort", complete);
271
+ resolve();
272
+ };
273
+ const timer = setTimeout(complete, milliseconds);
274
+ signal?.addEventListener("abort", complete, { once: true });
275
+ });
277
276
  }
278
277
  function controlCredentials(options) {
279
278
  const configured = [options.tlsCaCert, options.tlsCert, options.tlsKey].filter((value) => value !== undefined && value !== "");
@@ -1,7 +1,6 @@
1
1
  /** Explicit environment and context loaders for the Axern SDK. */
2
2
  export interface AxernConfig {
3
3
  endpoint: string;
4
- serviceUrl?: string;
5
4
  sshEndpoint?: string;
6
5
  sshIdentityFile?: string;
7
6
  tlsCaCert?: string;
@@ -3,7 +3,6 @@ import { readFileSync } from "node:fs";
3
3
  export function loadAxernEnv(overrides = {}) {
4
4
  return {
5
5
  endpoint: overrides.endpoint ?? process.env.AXERN_ENDPOINT ?? "127.0.0.1:25000",
6
- serviceUrl: overrides.serviceUrl ?? process.env.AXERN_SERVICE_URL,
7
6
  sshEndpoint: overrides.sshEndpoint ?? process.env.AXERN_SSH_ENDPOINT,
8
7
  sshIdentityFile: overrides.sshIdentityFile ?? process.env.AXERN_SSH_IDENTITY_FILE,
9
8
  tlsCaCert: overrides.tlsCaCert ?? process.env.AXERN_TLS_CA_CERT,
@@ -15,18 +14,17 @@ export function loadAxernEnv(overrides = {}) {
15
14
  }
16
15
  export function loadAxernContext(path, name = "") {
17
16
  const file = JSON.parse(readFileSync(path, "utf8"));
18
- rejectUnknown(file, ["current_context", "contexts", "agent_profiles"], "config");
17
+ rejectUnknown(file, ["current_context", "contexts"], "config");
19
18
  const contextName = name || stringValue(file.current_context);
20
19
  if (!contextName)
21
20
  throw new Error("Axern context name is required");
22
21
  const contexts = objectValue(file.contexts, "contexts");
23
22
  const context = objectValue(contexts[contextName], `context ${JSON.stringify(contextName)}`);
24
- rejectUnknown(context, ["endpoint", "service_url", "ssh_endpoint", "ssh_identity_file", "tls", "proxy_mode"], "context");
23
+ rejectUnknown(context, ["endpoint", "ssh_endpoint", "ssh_identity_file", "tls", "proxy_mode"], "context");
25
24
  const tls = objectValue(context.tls, "context.tls");
26
25
  rejectUnknown(tls, ["ca_cert", "cert", "key", "server_name"], "context.tls");
27
26
  const config = {
28
27
  endpoint: stringValue(context.endpoint),
29
- serviceUrl: stringValue(context.service_url) || undefined,
30
28
  sshEndpoint: stringValue(context.ssh_endpoint) || undefined,
31
29
  sshIdentityFile: stringValue(context.ssh_identity_file) || undefined,
32
30
  tlsCaCert: stringValue(tls.ca_cert),
@@ -16,7 +16,6 @@ const defaultProtoRootCandidates = [
16
16
  const protoFiles = [
17
17
  "axern/control/environment/v1/environment.proto",
18
18
  "axern/control/run/v1/run.proto",
19
- "axern/control/service/v1/service.proto",
20
19
  "axern/control/tunnel/v1/tunnel.proto",
21
20
  "axern/node/sandbox/v1/node.proto",
22
21
  "axern/tunnel/v1/tunnel.proto",
package/dist/index.d.ts CHANGED
@@ -4,16 +4,17 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  export { AxernClient } from "./client/index.js";
7
- export type { AxernClientOptions, CreateEnvironmentOptions, CreateServiceOptions, } from "./client/index.js";
7
+ export type { AxernClientOptions, CreateEnvironmentOptions, CreateRunOptions, } from "./client/index.js";
8
8
  export { loadAxernContext, loadAxernEnv } from "./config/index.js";
9
9
  export type { AxernConfig } from "./config/index.js";
10
10
  export { AxernError, AxernRpcError, isNotFound, isCancelled, isUnavailable, errorRetryable, isPermissionDenied, isTimeout, rpcCode, SandboxExecError, SandboxStateError, SandboxTimeoutError, SandboxValidationError, } from "./errors/index.js";
11
- export { NodeSandboxClient } from "./node/client.js";
12
- export type { NodeSandboxClientOptions } from "./node/client.js";
11
+ export { AllocationClient } from "./node/client.js";
12
+ export type { AllocationClientOptions } from "./node/client.js";
13
13
  export { SandboxProcess } from "./node/process.js";
14
14
  export type { ResourceQuantity } from "./resources.js";
15
15
  export { Sandbox } from "./sandbox/index.js";
16
16
  export type { SandboxMetadata, SandboxOptions, SandboxState } from "./sandbox/index.js";
17
- export type { ChmodOptions, CapabilityDependencyStatus, CapabilityProviderStatus, CapabilityProviderSummary, CapabilityStatus, Command, ComputerUseDependencyStatus, ComputerUseDisplay, ComputerUseKeyboardOptions, ComputerUseMouseOptions, ComputerUseRegion, ComputerUseScreenshot, ComputerUseScreenshotOptions, ComputerUseStatus, CopyOptions, DownloadArchiveOptions, DownloadDirOptions, ExecOptions, ExecResult, ImageExecOptions, ImageProcessMount, ImageProcessOptions, MkdirOptions, MoveOptions, NodeCallOptions, ProcessEvent, ProcessOptions, ProcessResult, RemoveOptions, SandboxFileInfo, SandboxFileKind, TouchOptions, TunnelConnectorOptions, TunnelMetadata, TunnelOptions, UploadArchiveOptions, UploadDirOptions, VolumeMount, WriteFileOptions, } from "./types.js";
18
- export { workspaceMount } from "./types.js";
17
+ export { NetworkPolicy, cidrRule, portRange } from "./network-policy.js";
18
+ export type { CIDRRule, PortRange, StrictNetworkPolicyOptions } from "./network-policy.js";
19
+ export type { ChmodOptions, CapabilityProviderDependencyStatus, CapabilityProviderStatus, CapabilityProviderSummary, CapabilityStatus, Command, ComputerUseDependencyStatus, ComputerUseDisplay, ComputerUseKeyboardOptions, ComputerUseMouseOptions, ComputerUseRegion, ComputerUseScreenshot, ComputerUseScreenshotOptions, ComputerUseStatus, CopyOptions, DownloadArchiveOptions, DownloadDirOptions, ExecOptions, ExecResult, MkdirOptions, MoveOptions, NodeCallOptions, ProcessEvent, ProcessOptions, ProcessResult, RemoveOptions, SandboxFileInfo, SandboxFileKind, TouchOptions, TunnelConnectorOptions, TunnelMetadata, TunnelOptions, UploadArchiveOptions, UploadDirOptions, WriteFileOptions, } from "./types.js";
19
20
  export { AXERN_VERSION, platformName } from "./version.js";
package/dist/index.js CHANGED
@@ -6,8 +6,8 @@
6
6
  export { AxernClient } from "./client/index.js";
7
7
  export { loadAxernContext, loadAxernEnv } from "./config/index.js";
8
8
  export { AxernError, AxernRpcError, isNotFound, isCancelled, isUnavailable, errorRetryable, isPermissionDenied, isTimeout, rpcCode, SandboxExecError, SandboxStateError, SandboxTimeoutError, SandboxValidationError, } from "./errors/index.js";
9
- export { NodeSandboxClient } from "./node/client.js";
9
+ export { AllocationClient } from "./node/client.js";
10
10
  export { SandboxProcess } from "./node/process.js";
11
11
  export { Sandbox } from "./sandbox/index.js";
12
- export { workspaceMount } from "./types.js";
12
+ export { NetworkPolicy, cidrRule, portRange } from "./network-policy.js";
13
13
  export { AXERN_VERSION, platformName } from "./version.js";
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 cofy-x
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ export interface PortRange {
7
+ readonly start: number;
8
+ readonly end: number;
9
+ }
10
+ export interface CIDRRule {
11
+ readonly cidr: string;
12
+ readonly protocol: "tcp" | "udp";
13
+ readonly ports: readonly PortRange[];
14
+ }
15
+ export interface StrictNetworkPolicyOptions {
16
+ readonly domains?: readonly string[];
17
+ readonly cidrRules?: readonly CIDRRule[];
18
+ }
19
+ type NetworkPolicyWire = {
20
+ strict: {
21
+ allowed_domains: string[];
22
+ allowed_cidrs: Record<string, unknown>[];
23
+ };
24
+ } | {
25
+ dns_deny: {
26
+ denied_domains: string[];
27
+ };
28
+ };
29
+ export declare class NetworkPolicy {
30
+ private readonly wire;
31
+ private constructor();
32
+ static strict(options?: StrictNetworkPolicyOptions): NetworkPolicy;
33
+ static allowDomains(...domains: string[]): NetworkPolicy;
34
+ static denyDns(...domains: string[]): NetworkPolicy;
35
+ static denyAll(): NetworkPolicy;
36
+ /** @internal */
37
+ toWire(): NetworkPolicyWire;
38
+ }
39
+ export declare function portRange(start: number, end?: number): PortRange;
40
+ export declare function cidrRule(cidr: string, protocol: "tcp" | "udp", ...ports: PortRange[]): CIDRRule;
41
+ export {};