@cofy-x/axern-sdk 0.6.2 → 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 +26 -65
- package/dist/client/index.d.ts +10 -11
- package/dist/client/index.js +64 -68
- 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 +4 -5
- package/dist/index.js +1 -2
- 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 +34 -94
- package/dist/proto/axern/control/common/v1/common.proto +29 -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 +3 -9
- package/dist/sandbox/index.js +13 -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,
|
|
@@ -70,9 +66,7 @@ try {
|
|
|
70
66
|
|
|
71
67
|
## Network Policies
|
|
72
68
|
|
|
73
|
-
Omitting `networkPolicy`
|
|
74
|
-
fail-closed; `denyDns` only refuses matching traditional UDP/TCP DNS queries and
|
|
75
|
-
does not block direct IP traffic, DoH, DoT, or already-resolved addresses.
|
|
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.
|
|
76
70
|
|
|
77
71
|
```ts
|
|
78
72
|
import { NetworkPolicy, Sandbox } from "@cofy-x/axern-sdk";
|
|
@@ -80,26 +74,13 @@ import { NetworkPolicy, Sandbox } from "@cofy-x/axern-sdk";
|
|
|
80
74
|
const sandbox = await new Sandbox({
|
|
81
75
|
client,
|
|
82
76
|
image: "docker.io/library/python:3.12-slim",
|
|
83
|
-
networkPolicy: NetworkPolicy.denyDns(
|
|
84
|
-
"github.com",
|
|
85
|
-
"*.github.com",
|
|
86
|
-
"githubusercontent.com",
|
|
87
|
-
"*.githubusercontent.com",
|
|
88
|
-
),
|
|
77
|
+
networkPolicy: NetworkPolicy.denyDns("github.com", "*.github.com", "githubusercontent.com", "*.githubusercontent.com"),
|
|
89
78
|
}).start();
|
|
90
79
|
```
|
|
91
80
|
|
|
92
|
-
`NetworkPolicy.allowDomains("example.com", "*.example.com")` allows only
|
|
93
|
-
strict HTTP/HTTPS destinations validated by DNS plus HTTP Host or TLS SNI.
|
|
94
|
-
`NetworkPolicy.strict({ cidrRules: [...] })` adds explicit TCP/UDP CIDR and port
|
|
95
|
-
grants; `NetworkPolicy.denyAll()` allows no egress.
|
|
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.
|
|
96
82
|
|
|
97
|
-
Run a tool from a separate image with `execImage` or `processImage`. OCI and
|
|
98
|
-
Nydus refs use the same image field. When `mounts` is omitted, the SDK requests
|
|
99
|
-
`/workspace -> /workspace`; pass `mounts: []` for no shared paths. Use
|
|
100
|
-
`new Sandbox({ image })` when the image should be the sandbox rootfs with normal
|
|
101
|
-
files, exec, process, tunnel, and lifecycle APIs; image-backed processes are
|
|
102
|
-
temporary side processes attached to an existing sandbox.
|
|
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.
|
|
103
84
|
|
|
104
85
|
```ts
|
|
105
86
|
import { workspaceMount } from "@cofy-x/axern-sdk";
|
|
@@ -111,58 +92,40 @@ const result = await sandbox.execImage("ghcr.io/cofy-x/agent:latest", "tool run"
|
|
|
111
92
|
console.log(result.stdoutText());
|
|
112
93
|
```
|
|
113
94
|
|
|
114
|
-
`AxernClient` requires an explicit endpoint. Use `AxernClient.fromContext()` in
|
|
115
|
-
interactive examples or `AxernClient.fromEnv()` in environment-driven
|
|
116
|
-
automation. Neither the client constructor nor `Sandbox` silently reads the
|
|
117
|
-
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.
|
|
118
96
|
|
|
119
97
|
## Configuration
|
|
120
98
|
|
|
121
|
-
`Sandbox` requires exactly one source: `templateId`, `image`, or
|
|
122
|
-
`environmentId`.
|
|
99
|
+
`Sandbox` requires exactly one source: `templateId`, `image`, or `environmentId`.
|
|
123
100
|
|
|
124
101
|
Common options:
|
|
125
102
|
|
|
126
103
|
- `namespace`: control-plane namespace, default `default`
|
|
127
104
|
- `client`: explicit `AxernClient` shared by the sandbox
|
|
128
105
|
- `argv`, `env`, `cwd`: initial sandbox process configuration
|
|
129
|
-
- `
|
|
130
|
-
- `
|
|
131
|
-
|
|
132
|
-
and numeric memory/storage values are bytes
|
|
133
|
-
- `limitCpu`, `limitMemory`, `limitEphemeralStorage`: runtime hard limits;
|
|
134
|
-
numeric CPU values are cores and numeric memory/storage values are bytes
|
|
135
|
-
- `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
|
|
136
109
|
|
|
137
110
|
Tunnel options:
|
|
138
111
|
|
|
139
|
-
- `tunnel.upstream`: local TCP address reached by the SDK connector, for
|
|
140
|
-
example `127.0.0.1:8080`
|
|
112
|
+
- `tunnel.upstream`: local TCP address reached by the SDK connector, for example `127.0.0.1:8080`
|
|
141
113
|
- `tunnel.proxyPort`: sandbox-local port bound by Axern, default `8786`
|
|
142
114
|
- `tunnel.ttlSeconds`: control-plane tunnel session TTL, renewed by the SDK
|
|
143
115
|
|
|
144
|
-
Tunnel traffic reuses the client gateway endpoint, mTLS identity, server name,
|
|
145
|
-
and proxy policy.
|
|
116
|
+
Tunnel traffic reuses the client gateway endpoint, mTLS identity, server name, and proxy policy.
|
|
146
117
|
|
|
147
|
-
After `start()`, use `sandbox.metadata.tunnel?.boundAddr` from inside the
|
|
148
|
-
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`.
|
|
149
119
|
|
|
150
120
|
## Sandbox API
|
|
151
121
|
|
|
152
122
|
- Lifecycle: `start()`, `close()`, `state`, `metadata`
|
|
153
123
|
- Execution: `exec(command, options)`, `process(command, options)`
|
|
154
|
-
- Agent sandbox: `capabilityStatus`, `computerUseStatus`,
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
-
|
|
158
|
-
|
|
159
|
-
- Directories: `uploadDir(localPath, remotePath)`,
|
|
160
|
-
`downloadDir(remotePath, localPath)`
|
|
161
|
-
- Tunnel: `new Sandbox({ tunnel: { upstream, proxyPort } })`,
|
|
162
|
-
`metadata.tunnel`
|
|
163
|
-
- Errors: `AxernRpcError`, `SandboxExecError`, `SandboxStateError`,
|
|
164
|
-
`SandboxValidationError`, `isNotFound`, `isPermissionDenied`, `isTimeout`,
|
|
165
|
-
`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`
|
|
166
129
|
|
|
167
130
|
## Local Smoke
|
|
168
131
|
|
|
@@ -173,9 +136,7 @@ pnpm --filter @cofy-x/axern-sdk run smoke:local
|
|
|
173
136
|
pnpm --filter @cofy-x/axern-sdk run smoke:tunnel
|
|
174
137
|
```
|
|
175
138
|
|
|
176
|
-
The smoke loads `deploy/local/state/compose/axern.env` when present. It uses the
|
|
177
|
-
`python311` template by default; set `AXERN_TS_SMOKE_IMAGE` to verify an image
|
|
178
|
-
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.
|
|
179
140
|
|
|
180
141
|
Check package contents with:
|
|
181
142
|
|
|
@@ -185,12 +146,12 @@ pnpm --filter @cofy-x/axern-sdk run pack:dry-run
|
|
|
185
146
|
|
|
186
147
|
## Proto Boundary
|
|
187
148
|
|
|
188
|
-
The SDK loads protobuf definitions through `@grpc/proto-loader`. Dynamic proto
|
|
189
|
-
access remains isolated under `src/generated`; public callers depend only on
|
|
190
|
-
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.
|
|
191
150
|
|
|
192
151
|
## Scope
|
|
193
152
|
|
|
194
|
-
This SDK is Node.js-first.
|
|
195
|
-
|
|
196
|
-
|
|
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,11 +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
9
|
import type { NetworkPolicy } from "../network-policy.js";
|
|
10
10
|
import { TunnelControlClient } from "../tunnel/control.js";
|
|
11
|
-
import type { VolumeMount } from "../types.js";
|
|
12
11
|
import type { GatewayTransportOptions } from "../tunnel/relay.js";
|
|
13
12
|
export interface AxernClientOptions {
|
|
14
13
|
endpoint: string;
|
|
@@ -27,16 +26,14 @@ export interface CreateEnvironmentOptions {
|
|
|
27
26
|
rootfsReadonly?: boolean;
|
|
28
27
|
labels?: Record<string, string>;
|
|
29
28
|
}
|
|
30
|
-
export interface
|
|
29
|
+
export interface CreateRunOptions {
|
|
31
30
|
namespace?: string;
|
|
32
31
|
environmentId: string;
|
|
33
32
|
argv?: string[];
|
|
34
33
|
env?: Record<string, string>;
|
|
35
34
|
cwd?: string;
|
|
36
|
-
runtimeClass?: string;
|
|
37
35
|
networkPolicy?: NetworkPolicy;
|
|
38
36
|
extensionCapabilities?: readonly ExtensionCapability[];
|
|
39
|
-
volumes?: readonly VolumeMount[];
|
|
40
37
|
requestCpu?: ResourceQuantity;
|
|
41
38
|
requestMemory?: ResourceQuantity;
|
|
42
39
|
requestEphemeralStorage?: ResourceQuantity;
|
|
@@ -53,13 +50,16 @@ export interface ReadRunOutputOptions {
|
|
|
53
50
|
cursor?: string;
|
|
54
51
|
follow?: boolean;
|
|
55
52
|
}
|
|
53
|
+
export interface WatchRunOptions {
|
|
54
|
+
afterVersion?: number;
|
|
55
|
+
signal?: AbortSignal;
|
|
56
|
+
}
|
|
56
57
|
export declare class AxernClient {
|
|
57
58
|
readonly endpoint: string;
|
|
58
59
|
private readonly credentials;
|
|
59
60
|
private readonly controlOptions;
|
|
60
61
|
private readonly environmentControl;
|
|
61
62
|
private readonly runControl;
|
|
62
|
-
private readonly serviceControl;
|
|
63
63
|
private readonly tunnelControl;
|
|
64
64
|
private readonly gatewayTransport;
|
|
65
65
|
constructor(options: AxernClientOptions);
|
|
@@ -68,12 +68,11 @@ export declare class AxernClient {
|
|
|
68
68
|
close(): void;
|
|
69
69
|
createEnvironment(options: CreateEnvironmentOptions): Promise<Record<string, unknown>>;
|
|
70
70
|
deleteEnvironment(environmentId: string): Promise<void>;
|
|
71
|
-
|
|
71
|
+
createRun(options: CreateRunOptions): Promise<Record<string, unknown>>;
|
|
72
|
+
cancelRun(runId: string): Promise<void>;
|
|
73
|
+
watchRun(runId: string, options?: WatchRunOptions): AsyncGenerator<Record<string, unknown>>;
|
|
72
74
|
readRunOutput(runId: string, options?: ReadRunOutputOptions): AsyncGenerator<Record<string, unknown>>;
|
|
73
|
-
|
|
74
|
-
deleteService(serviceId: string): Promise<void>;
|
|
75
|
-
listServiceReplicas(serviceId: string): Promise<Record<string, unknown>[]>;
|
|
76
|
-
nodeSandbox(allocationId: string): NodeSandboxClient;
|
|
75
|
+
allocation(allocationId: string): AllocationClient;
|
|
77
76
|
tunnelClient(): TunnelControlClient;
|
|
78
77
|
tunnelTransport(): GatewayTransportOptions;
|
|
79
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,57 +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
|
-
...(options.networkPolicy === undefined
|
|
211
|
-
? {}
|
|
212
|
-
: { network: { egress_policy: options.networkPolicy.toWire() } }),
|
|
213
|
-
extension_capability_requirements: (options.extensionCapabilities ?? []).map((capability) => ({
|
|
214
|
-
capability: { name: capability.name, value: capability.value ?? "" },
|
|
215
|
-
})),
|
|
216
|
-
volume_mounts: serviceVolumeMounts(options.volumes),
|
|
217
|
-
resources,
|
|
218
|
-
},
|
|
219
|
-
labels: options.labels ?? {},
|
|
220
|
-
});
|
|
221
|
-
return response.service;
|
|
222
|
-
}
|
|
223
|
-
catch (error) {
|
|
224
|
-
throw mapRpcError(error, "create service");
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
async deleteService(serviceId) {
|
|
228
|
-
try {
|
|
229
|
-
await unary(this.serviceControl, "DeleteService", { service_id: required("serviceId", serviceId) });
|
|
230
|
-
}
|
|
231
|
-
catch (error) {
|
|
232
|
-
throw mapRpcError(error, "delete service");
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
async listServiceReplicas(serviceId) {
|
|
236
|
-
try {
|
|
237
|
-
const response = await unary(this.serviceControl, "ListServiceReplicas", {
|
|
238
|
-
service_id: required("serviceId", serviceId),
|
|
239
|
-
filter: { view: 2 },
|
|
240
|
-
});
|
|
241
|
-
return response.replicas ?? [];
|
|
242
|
-
}
|
|
243
|
-
catch (error) {
|
|
244
|
-
throw mapRpcError(error, "list service replicas");
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
nodeSandbox(allocationId) {
|
|
248
|
-
return new NodeSandboxClient({
|
|
241
|
+
allocation(allocationId) {
|
|
242
|
+
return new AllocationClient({
|
|
249
243
|
allocationId: required("allocationId", allocationId),
|
|
250
244
|
target: this.endpoint,
|
|
251
245
|
credentials: this.credentials,
|
|
@@ -259,14 +253,6 @@ export class AxernClient {
|
|
|
259
253
|
return { ...this.gatewayTransport };
|
|
260
254
|
}
|
|
261
255
|
}
|
|
262
|
-
function serviceVolumeMounts(mounts) {
|
|
263
|
-
return (mounts ?? []).map((mount) => ({
|
|
264
|
-
name: mount.name,
|
|
265
|
-
target: mount.target,
|
|
266
|
-
readonly: mount.readonly ?? false,
|
|
267
|
-
options: [...(mount.options ?? [])],
|
|
268
|
-
}));
|
|
269
|
-
}
|
|
270
256
|
function serverStream(client, method, request) {
|
|
271
257
|
const fn = client[method];
|
|
272
258
|
return fn.call(client, request);
|
|
@@ -275,8 +261,18 @@ function transientReadError(error) {
|
|
|
275
261
|
const code = error.code;
|
|
276
262
|
return code === grpc.status.UNAVAILABLE || code === grpc.status.DEADLINE_EXCEEDED;
|
|
277
263
|
}
|
|
278
|
-
function sleep(milliseconds) {
|
|
279
|
-
|
|
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
|
+
});
|
|
280
276
|
}
|
|
281
277
|
function controlCredentials(options) {
|
|
282
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,18 +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
17
|
export { NetworkPolicy, cidrRule, portRange } from "./network-policy.js";
|
|
18
18
|
export type { CIDRRule, PortRange, StrictNetworkPolicyOptions } from "./network-policy.js";
|
|
19
|
-
export type { ChmodOptions,
|
|
20
|
-
export { workspaceMount } from "./types.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";
|
|
21
20
|
export { AXERN_VERSION, platformName } from "./version.js";
|
package/dist/index.js
CHANGED
|
@@ -6,9 +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
12
|
export { NetworkPolicy, cidrRule, portRange } from "./network-policy.js";
|
|
13
|
-
export { workspaceMount } from "./types.js";
|
|
14
13
|
export { AXERN_VERSION, platformName } from "./version.js";
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Copyright 2026 cofy-x
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import type { Command,
|
|
6
|
+
import type { Command, ProcessOptions } from "../types.js";
|
|
7
7
|
import { NodeClientContext } from "./context.js";
|
|
8
8
|
import { SandboxProcess } from "./process.js";
|
|
9
9
|
export declare function process(ctx: NodeClientContext, command: Command, options?: ProcessOptions): Promise<SandboxProcess>;
|
|
10
|
-
export declare function processImage(ctx: NodeClientContext, image: string, command: Command, options?: ImageProcessOptions): Promise<SandboxProcess>;
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
* Copyright 2026 cofy-x
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { mapRpcError } from "../errors/index.js";
|
|
6
|
+
import { mapRpcError, SandboxValidationError } from "../errors/index.js";
|
|
7
7
|
import { normalizeCommand } from "../validation.js";
|
|
8
|
-
import { execSpec
|
|
8
|
+
import { execSpec } from "./exec.js";
|
|
9
9
|
import { SandboxProcess } from "./process.js";
|
|
10
10
|
export async function process(ctx, command, options = {}) {
|
|
11
11
|
const argv = normalizeCommand(command);
|
|
12
|
+
const initialSize = processInitialSize(options);
|
|
12
13
|
const client = ctx.rpcClient();
|
|
13
14
|
const call = client
|
|
14
15
|
.Process();
|
|
@@ -20,6 +21,7 @@ export async function process(ctx, command, options = {}) {
|
|
|
20
21
|
call.write({
|
|
21
22
|
open: ctx.authRequest({
|
|
22
23
|
spec: execSpec(argv, options),
|
|
24
|
+
...(initialSize === undefined ? {} : { initial_size: initialSize }),
|
|
23
25
|
}),
|
|
24
26
|
});
|
|
25
27
|
try {
|
|
@@ -31,30 +33,14 @@ export async function process(ctx, command, options = {}) {
|
|
|
31
33
|
throw mapRpcError(error, "sandbox process", ctx.allocationId);
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
function processInitialSize(options) {
|
|
37
|
+
const cols = options.initialCols ?? 0;
|
|
38
|
+
const rows = options.initialRows ?? 0;
|
|
39
|
+
if ((cols === 0) !== (rows === 0)) {
|
|
40
|
+
throw new SandboxValidationError("initialCols and initialRows must both be zero or both be positive");
|
|
37
41
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const call = client
|
|
41
|
-
.ProcessImage();
|
|
42
|
-
const sandboxProcess = new SandboxProcess({
|
|
43
|
-
allocationId: ctx.allocationId,
|
|
44
|
-
call,
|
|
45
|
-
closeClient: () => client.close(),
|
|
46
|
-
});
|
|
47
|
-
call.write({
|
|
48
|
-
open: ctx.authRequest({
|
|
49
|
-
spec: imageProcessSpec(image, argv, options),
|
|
50
|
-
}),
|
|
51
|
-
});
|
|
52
|
-
try {
|
|
53
|
-
await sandboxProcess.waitReady();
|
|
54
|
-
return sandboxProcess;
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
await sandboxProcess.close();
|
|
58
|
-
throw mapRpcError(error, "sandbox image process", ctx.allocationId);
|
|
42
|
+
if (cols < 0 || rows < 0 || !Number.isInteger(cols) || !Number.isInteger(rows)) {
|
|
43
|
+
throw new SandboxValidationError("initialCols and initialRows must be non-negative integers");
|
|
59
44
|
}
|
|
45
|
+
return cols === 0 ? undefined : { cols, rows };
|
|
60
46
|
}
|
package/dist/node/client.d.ts
CHANGED
|
@@ -4,22 +4,20 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
import type * as grpc from "@grpc/grpc-js";
|
|
7
|
-
import type { ChmodOptions, CapabilityStatus, Command, ComputerUseDisplay, ComputerUseKeyboardOptions, ComputerUseMouseOptions, ComputerUseScreenshot, ComputerUseScreenshotOptions, ComputerUseStatus, CopyOptions, DownloadArchiveOptions, ExecOptions, ExecResult,
|
|
7
|
+
import type { ChmodOptions, CapabilityStatus, Command, ComputerUseDisplay, ComputerUseKeyboardOptions, ComputerUseMouseOptions, ComputerUseScreenshot, ComputerUseScreenshotOptions, ComputerUseStatus, CopyOptions, DownloadArchiveOptions, ExecOptions, ExecResult, MkdirOptions, MoveOptions, NodeCallOptions, ProcessOptions, RemoveOptions, SandboxFileInfo, TouchOptions, UploadArchiveOptions, WriteFileOptions } from "../types.js";
|
|
8
8
|
import type { SandboxProcess } from "./process.js";
|
|
9
|
-
export interface
|
|
9
|
+
export interface AllocationClientOptions {
|
|
10
10
|
allocationId: string;
|
|
11
11
|
target: string;
|
|
12
12
|
credentials: grpc.ChannelCredentials;
|
|
13
13
|
channelOptions?: grpc.ChannelOptions;
|
|
14
14
|
}
|
|
15
|
-
export declare class
|
|
15
|
+
export declare class AllocationClient {
|
|
16
16
|
readonly allocationId: string;
|
|
17
17
|
private readonly ctx;
|
|
18
|
-
constructor(options:
|
|
18
|
+
constructor(options: AllocationClientOptions);
|
|
19
19
|
exec(command: Command, options?: ExecOptions): Promise<ExecResult>;
|
|
20
20
|
process(command: Command, options?: ProcessOptions): Promise<SandboxProcess>;
|
|
21
|
-
execImage(image: string, command: Command, options?: ImageExecOptions): Promise<ExecResult>;
|
|
22
|
-
processImage(image: string, command: Command, options?: ImageProcessOptions): Promise<SandboxProcess>;
|
|
23
21
|
capabilityStatus(options?: NodeCallOptions): Promise<CapabilityStatus>;
|
|
24
22
|
computerUseStatus(options?: NodeCallOptions): Promise<ComputerUseStatus>;
|
|
25
23
|
computerUseScreenshot(options?: ComputerUseScreenshotOptions): Promise<ComputerUseScreenshot>;
|