@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.
- package/README.md +39 -52
- package/dist/client/index.d.ts +12 -11
- package/dist/client/index.js +64 -65
- package/dist/config/index.d.ts +0 -1
- package/dist/config/index.js +2 -4
- package/dist/generated/proto.js +0 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +2 -2
- package/dist/network-policy.d.ts +41 -0
- package/dist/network-policy.js +132 -0
- package/dist/node/attached_process.d.ts +1 -2
- package/dist/node/attached_process.js +12 -26
- package/dist/node/client.d.ts +4 -6
- package/dist/node/client.js +2 -9
- package/dist/node/exec.d.ts +1 -3
- package/dist/node/exec.js +0 -43
- package/dist/proto/axern/control/admin/v1/access.proto +12 -5
- package/dist/proto/axern/control/admin/v1/audit.proto +14 -16
- package/dist/proto/axern/control/admin/v1/node.proto +23 -67
- package/dist/proto/axern/control/admin/v1/reliability.proto +11 -78
- package/dist/proto/axern/control/capability/v1/capability.proto +35 -90
- package/dist/proto/axern/control/common/v1/common.proto +72 -102
- package/dist/proto/axern/control/environment/v1/environment.proto +42 -26
- package/dist/proto/axern/control/gateway/v1/gateway.proto +18 -59
- package/dist/proto/axern/control/identity/v1/identity.proto +1 -1
- package/dist/proto/axern/control/namespace/v1/namespace.proto +2 -3
- package/dist/proto/axern/control/quota/v1/quota.proto +22 -32
- package/dist/proto/axern/control/run/v1/run.proto +26 -22
- package/dist/proto/axern/control/secret/v1/secret.proto +1 -3
- package/dist/proto/axern/control/tunnel/v1/tunnel.proto +18 -25
- package/dist/proto/axern/node/sandbox/v1/node.proto +59 -375
- package/dist/sandbox/index.d.ts +5 -9
- package/dist/sandbox/index.js +14 -22
- package/dist/sandbox/lifecycle.d.ts +1 -1
- package/dist/sandbox/lifecycle.js +55 -17
- package/dist/tunnel/connector.js +1 -1
- package/dist/tunnel/control.d.ts +0 -1
- package/dist/tunnel/control.js +0 -1
- package/dist/tunnel/runtime.d.ts +0 -1
- package/dist/tunnel/runtime.js +0 -1
- package/dist/tunnel/types.d.ts +0 -1
- package/dist/types.d.ts +4 -21
- package/dist/types.js +1 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/proto/axern/control/admin/v1/allocation_lifecycle.proto +0 -91
- package/dist/proto/axern/control/admin/v1/service.proto +0 -18
- package/dist/proto/axern/control/admin/v1/storage.proto +0 -81
- package/dist/proto/axern/control/agentprofile/v1/agent_profile.proto +0 -169
- package/dist/proto/axern/control/catalog/v1/catalog.proto +0 -127
- package/dist/proto/axern/control/function/v1/function.proto +0 -130
- package/dist/proto/axern/control/function/v1/function_types.proto +0 -233
- package/dist/proto/axern/control/node/v1/node_control.proto +0 -443
- package/dist/proto/axern/control/rollout/v1/rollout.proto +0 -344
- package/dist/proto/axern/control/service/v1/service.proto +0 -96
- package/dist/proto/axern/control/service/v1/service_event.proto +0 -47
- package/dist/proto/axern/control/service/v1/service_replica.proto +0 -78
- package/dist/proto/axern/control/service/v1/service_types.proto +0 -155
- package/dist/proto/axern/control/storage/v1/storage.proto +0 -107
- package/dist/proto/axern/control/storage/v1/storage_types.proto +0 -125
- 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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
- `
|
|
104
|
-
- `
|
|
105
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
-
|
|
132
|
-
|
|
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.
|
|
169
|
-
|
|
170
|
-
|
|
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.
|
package/dist/client/index.d.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/dist/client/index.js
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
199
|
-
|
|
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
|
-
|
|
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 !== "");
|
package/dist/config/index.d.ts
CHANGED
package/dist/config/index.js
CHANGED
|
@@ -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"
|
|
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", "
|
|
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),
|
package/dist/generated/proto.js
CHANGED
|
@@ -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,
|
|
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 {
|
|
12
|
-
export type {
|
|
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
|
|
18
|
-
export {
|
|
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 {
|
|
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 {
|
|
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 {};
|