@computesdk/archil 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/index.d.mts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +324 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +299 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 computesdk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# @computesdk/archil
|
|
2
|
+
|
|
3
|
+
[Archil](https://archil.com) provider for [ComputeSDK](https://www.computesdk.com).
|
|
4
|
+
|
|
5
|
+
`create` provisions a new Archil disk (no mounts — archil-managed storage)
|
|
6
|
+
and `runCommand` executes shell commands in a managed container with that
|
|
7
|
+
disk attached via the control-plane `exec` endpoint. `destroy` deletes the
|
|
8
|
+
disk. `getById` accepts either a disk id or a disk name.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @computesdk/archil
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The provider talks to the Archil control plane directly over HTTP — no
|
|
17
|
+
additional Archil SDK is required.
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
| Option | Env var | Required | Description |
|
|
22
|
+
| --------- | ------------------ | -------- | ------------------------------------------ |
|
|
23
|
+
| `apiKey` | `ARCHIL_API_KEY` | yes | Archil control-plane API key |
|
|
24
|
+
| `region` | `ARCHIL_REGION` | yes | Archil region (e.g. `aws-us-east-1`) |
|
|
25
|
+
| `baseUrl` | — | no | Override control-plane URL (for testing) |
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { archil } from '@computesdk/archil';
|
|
31
|
+
|
|
32
|
+
const provider = archil();
|
|
33
|
+
|
|
34
|
+
// Create a fresh disk. Pass a name to control it; omit for auto-generated.
|
|
35
|
+
const { sandbox } = await provider.sandbox.create({ name: 'my-workspace' });
|
|
36
|
+
|
|
37
|
+
const result = await provider.sandbox.runCommand(sandbox, 'echo hello > /mnt/note && cat /mnt/note');
|
|
38
|
+
console.log(result.stdout); // "hello"
|
|
39
|
+
|
|
40
|
+
// Look up later by name or by id:
|
|
41
|
+
const byName = await provider.sandbox.getById('my-workspace');
|
|
42
|
+
const byId = await provider.sandbox.getById(sandbox.sandboxId);
|
|
43
|
+
|
|
44
|
+
await provider.sandbox.destroy(sandbox.sandboxId);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Disk names must match `^[a-zA-Z0-9_-]+$` and be 1–100 characters.
|
|
48
|
+
|
|
49
|
+
## Supported operations
|
|
50
|
+
|
|
51
|
+
| Method | Supported | Notes |
|
|
52
|
+
| ------------- | --------- | ----------------------------------------------------------- |
|
|
53
|
+
| `create` | ✅ | Creates a new disk with archil-managed storage (no mounts). |
|
|
54
|
+
| `getById` | ✅ | Accepts either the disk id or the disk name. |
|
|
55
|
+
| `list` | ✅ | Lists all disks visible to the API key. |
|
|
56
|
+
| `destroy` | ✅ | Deletes the disk. |
|
|
57
|
+
| `runCommand` | ✅ | Calls Archil's HTTP `exec` endpoint and waits for completion. |
|
|
58
|
+
| `runCode` | ✅ | Wraps code in `node -e` or `python3 -c`. Requires explicit `runtime`. |
|
|
59
|
+
| `getInfo` | ✅ | |
|
|
60
|
+
| `getUrl` | ❌ | Each exec runs in a fresh ephemeral container — no port to expose. |
|
|
61
|
+
| `filesystem` | ✅ | Implemented via shell commands (`cat`, `find`, `mkdir`, etc.). |
|
|
62
|
+
|
|
63
|
+
## Limitations
|
|
64
|
+
|
|
65
|
+
- Each `exec` call provisions a fresh container — there is no persistent
|
|
66
|
+
state between calls beyond what is written to the disk.
|
|
67
|
+
- Responses are truncated to ~5 MB by the Archil control plane.
|
|
68
|
+
- `getUrl` is not supported — each exec runs in a fresh ephemeral container,
|
|
69
|
+
so there is no long-lived process to expose a port on.
|
|
70
|
+
- Filesystem operations are implemented as shell commands, so each call
|
|
71
|
+
costs one HTTP round trip.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as _computesdk_provider from '@computesdk/provider';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Archil Provider
|
|
5
|
+
*
|
|
6
|
+
* Executes commands against an Archil disk via the Archil control-plane HTTP
|
|
7
|
+
* API. Archil is exec-only — each command runs in an Archil-managed container
|
|
8
|
+
* with the configured disk mounted, then returns stdout, stderr, and exit code.
|
|
9
|
+
* There is no sandbox lifecycle to manage; "create" just resolves a handle to
|
|
10
|
+
* the target disk.
|
|
11
|
+
*/
|
|
12
|
+
interface ArchilConfig {
|
|
13
|
+
/** Archil API key. Falls back to ARCHIL_API_KEY env var. */
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
/** Archil region (e.g. "aws-us-east-1"). Falls back to ARCHIL_REGION env var. */
|
|
16
|
+
region?: string;
|
|
17
|
+
/** Override the control-plane base URL (useful for testing). */
|
|
18
|
+
baseUrl?: string;
|
|
19
|
+
}
|
|
20
|
+
interface DiskResponse {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
organization: string;
|
|
24
|
+
status: string;
|
|
25
|
+
provider: string;
|
|
26
|
+
region: string;
|
|
27
|
+
createdAt: string;
|
|
28
|
+
}
|
|
29
|
+
interface ResolvedConfig {
|
|
30
|
+
apiKey: string;
|
|
31
|
+
baseUrl: string;
|
|
32
|
+
}
|
|
33
|
+
interface ArchilSandbox {
|
|
34
|
+
disk: DiskResponse;
|
|
35
|
+
resolved: ResolvedConfig;
|
|
36
|
+
createdAt: Date;
|
|
37
|
+
}
|
|
38
|
+
declare const archil: (config?: ArchilConfig) => _computesdk_provider.Provider<ArchilSandbox, any, any>;
|
|
39
|
+
|
|
40
|
+
export { type ArchilConfig, type ArchilSandbox, archil };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as _computesdk_provider from '@computesdk/provider';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Archil Provider
|
|
5
|
+
*
|
|
6
|
+
* Executes commands against an Archil disk via the Archil control-plane HTTP
|
|
7
|
+
* API. Archil is exec-only — each command runs in an Archil-managed container
|
|
8
|
+
* with the configured disk mounted, then returns stdout, stderr, and exit code.
|
|
9
|
+
* There is no sandbox lifecycle to manage; "create" just resolves a handle to
|
|
10
|
+
* the target disk.
|
|
11
|
+
*/
|
|
12
|
+
interface ArchilConfig {
|
|
13
|
+
/** Archil API key. Falls back to ARCHIL_API_KEY env var. */
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
/** Archil region (e.g. "aws-us-east-1"). Falls back to ARCHIL_REGION env var. */
|
|
16
|
+
region?: string;
|
|
17
|
+
/** Override the control-plane base URL (useful for testing). */
|
|
18
|
+
baseUrl?: string;
|
|
19
|
+
}
|
|
20
|
+
interface DiskResponse {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
organization: string;
|
|
24
|
+
status: string;
|
|
25
|
+
provider: string;
|
|
26
|
+
region: string;
|
|
27
|
+
createdAt: string;
|
|
28
|
+
}
|
|
29
|
+
interface ResolvedConfig {
|
|
30
|
+
apiKey: string;
|
|
31
|
+
baseUrl: string;
|
|
32
|
+
}
|
|
33
|
+
interface ArchilSandbox {
|
|
34
|
+
disk: DiskResponse;
|
|
35
|
+
resolved: ResolvedConfig;
|
|
36
|
+
createdAt: Date;
|
|
37
|
+
}
|
|
38
|
+
declare const archil: (config?: ArchilConfig) => _computesdk_provider.Provider<ArchilSandbox, any, any>;
|
|
39
|
+
|
|
40
|
+
export { type ArchilConfig, type ArchilSandbox, archil };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
archil: () => archil
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
var import_provider = require("@computesdk/provider");
|
|
27
|
+
var REGION_COLORS = {
|
|
28
|
+
"gcp-us-central1": "blue"
|
|
29
|
+
};
|
|
30
|
+
function regionToBaseUrl(region) {
|
|
31
|
+
const dash = region.indexOf("-");
|
|
32
|
+
if (dash <= 0 || dash === region.length - 1) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Invalid Archil region "${region}". Expected "{cloud}-{suffix}", e.g. "aws-us-east-1".`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
const cloud = region.slice(0, dash);
|
|
38
|
+
const suffix = region.slice(dash + 1);
|
|
39
|
+
const color = REGION_COLORS[region] ?? "green";
|
|
40
|
+
return `https://control.${color}.${suffix}.${cloud}.prod.archil.com`;
|
|
41
|
+
}
|
|
42
|
+
function resolveConfig(config) {
|
|
43
|
+
const apiKey = config.apiKey ?? process.env.ARCHIL_API_KEY;
|
|
44
|
+
const region = config.region ?? process.env.ARCHIL_REGION;
|
|
45
|
+
if (!apiKey) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
'Missing API key for Archil.\n\nPass it: archil({ apiKey: "..." })\nOr set ARCHIL_API_KEY in your environment.'
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
let baseUrl = config.baseUrl;
|
|
51
|
+
if (!baseUrl) {
|
|
52
|
+
if (!region) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
'Missing region for Archil.\n\nPass it: archil({ region: "..." })\nOr set ARCHIL_REGION in your environment.\nExamples: "aws-us-east-1", "aws-eu-west-1", "gcp-us-central1".'
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
baseUrl = regionToBaseUrl(region);
|
|
58
|
+
}
|
|
59
|
+
return { apiKey, baseUrl };
|
|
60
|
+
}
|
|
61
|
+
function authHeader(apiKey) {
|
|
62
|
+
return `key-${apiKey.replace(/^key-/, "")}`;
|
|
63
|
+
}
|
|
64
|
+
async function callApi(resolved, method, path, body) {
|
|
65
|
+
const response = await fetch(`${resolved.baseUrl}${path}`, {
|
|
66
|
+
method,
|
|
67
|
+
headers: {
|
|
68
|
+
Authorization: authHeader(resolved.apiKey),
|
|
69
|
+
...body ? { "Content-Type": "application/json" } : {}
|
|
70
|
+
},
|
|
71
|
+
body: body ? JSON.stringify(body) : void 0
|
|
72
|
+
});
|
|
73
|
+
let payload = null;
|
|
74
|
+
try {
|
|
75
|
+
payload = await response.json();
|
|
76
|
+
} catch {
|
|
77
|
+
payload = null;
|
|
78
|
+
}
|
|
79
|
+
if (!response.ok || !payload || payload.success === false) {
|
|
80
|
+
const message = payload && payload.error || `Archil API ${method} ${path} failed with status ${response.status}`;
|
|
81
|
+
throw new Error(message);
|
|
82
|
+
}
|
|
83
|
+
return payload.data;
|
|
84
|
+
}
|
|
85
|
+
function generateDiskName() {
|
|
86
|
+
const random = Math.random().toString(36).slice(2, 10);
|
|
87
|
+
return `computesdk-${Date.now()}-${random}`;
|
|
88
|
+
}
|
|
89
|
+
async function lookupDiskByName(resolved, name) {
|
|
90
|
+
const disks = await callApi(resolved, "GET", "/api/disks");
|
|
91
|
+
return disks.find((d) => d.name === name) ?? null;
|
|
92
|
+
}
|
|
93
|
+
function shellEscape(value) {
|
|
94
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
95
|
+
}
|
|
96
|
+
function wrapCommand(command, options) {
|
|
97
|
+
let wrapped = command;
|
|
98
|
+
const envEntries = Object.entries(options?.env ?? {});
|
|
99
|
+
if (!envEntries.some(([key]) => key === "HOME")) {
|
|
100
|
+
envEntries.unshift(["HOME", process.env.HOME || "/tmp"]);
|
|
101
|
+
}
|
|
102
|
+
const envPrefix = envEntries.map(([k, v]) => `${k}=${shellEscape(String(v))}`).join(" ");
|
|
103
|
+
wrapped = `${envPrefix} ${wrapped}`;
|
|
104
|
+
if (options?.cwd) {
|
|
105
|
+
wrapped = `cd ${shellEscape(options.cwd)} && ${wrapped}`;
|
|
106
|
+
}
|
|
107
|
+
if (options?.background) {
|
|
108
|
+
wrapped = `nohup sh -c ${shellEscape(wrapped)} > /dev/null 2>&1 &`;
|
|
109
|
+
}
|
|
110
|
+
return wrapped;
|
|
111
|
+
}
|
|
112
|
+
async function execOnDisk(sandbox, command) {
|
|
113
|
+
return callApi(
|
|
114
|
+
sandbox.resolved,
|
|
115
|
+
"POST",
|
|
116
|
+
`/api/disks/${encodeURIComponent(sandbox.disk.id)}/exec`,
|
|
117
|
+
{ command }
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
var _provider = (0, import_provider.defineProvider)({
|
|
121
|
+
name: "archil",
|
|
122
|
+
methods: {
|
|
123
|
+
sandbox: {
|
|
124
|
+
create: async (config, options) => {
|
|
125
|
+
const resolved = resolveConfig(config);
|
|
126
|
+
const name = (options?.name ?? generateDiskName()).trim();
|
|
127
|
+
const created = await callApi(
|
|
128
|
+
resolved,
|
|
129
|
+
"POST",
|
|
130
|
+
"/api/disks",
|
|
131
|
+
{ name }
|
|
132
|
+
);
|
|
133
|
+
const disk = await callApi(
|
|
134
|
+
resolved,
|
|
135
|
+
"GET",
|
|
136
|
+
`/api/disks/${encodeURIComponent(created.diskId)}`
|
|
137
|
+
);
|
|
138
|
+
return {
|
|
139
|
+
sandbox: { disk, resolved, createdAt: /* @__PURE__ */ new Date() },
|
|
140
|
+
sandboxId: disk.id
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
getById: async (config, sandboxId) => {
|
|
144
|
+
const resolved = resolveConfig(config);
|
|
145
|
+
try {
|
|
146
|
+
const disk = await callApi(
|
|
147
|
+
resolved,
|
|
148
|
+
"GET",
|
|
149
|
+
`/api/disks/${encodeURIComponent(sandboxId)}`
|
|
150
|
+
);
|
|
151
|
+
return {
|
|
152
|
+
sandbox: { disk, resolved, createdAt: /* @__PURE__ */ new Date() },
|
|
153
|
+
sandboxId: disk.id
|
|
154
|
+
};
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const disk = await lookupDiskByName(resolved, sandboxId);
|
|
159
|
+
if (!disk) return null;
|
|
160
|
+
return {
|
|
161
|
+
sandbox: { disk, resolved, createdAt: /* @__PURE__ */ new Date() },
|
|
162
|
+
sandboxId: disk.id
|
|
163
|
+
};
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
list: async (config) => {
|
|
169
|
+
const resolved = resolveConfig(config);
|
|
170
|
+
const disks = await callApi(resolved, "GET", "/api/disks");
|
|
171
|
+
return disks.map((disk) => ({
|
|
172
|
+
sandbox: { disk, resolved, createdAt: /* @__PURE__ */ new Date() },
|
|
173
|
+
sandboxId: disk.id
|
|
174
|
+
}));
|
|
175
|
+
},
|
|
176
|
+
destroy: async (config, sandboxId) => {
|
|
177
|
+
const resolved = resolveConfig(config);
|
|
178
|
+
await callApi(
|
|
179
|
+
resolved,
|
|
180
|
+
"DELETE",
|
|
181
|
+
`/api/disks/${encodeURIComponent(sandboxId)}`
|
|
182
|
+
);
|
|
183
|
+
},
|
|
184
|
+
runCommand: async (sandbox, command, options) => {
|
|
185
|
+
const startTime = Date.now();
|
|
186
|
+
try {
|
|
187
|
+
const result = await execOnDisk(sandbox, wrapCommand(command, options));
|
|
188
|
+
return {
|
|
189
|
+
stdout: result.stdout ?? "",
|
|
190
|
+
stderr: result.stderr ?? "",
|
|
191
|
+
exitCode: result.exitCode,
|
|
192
|
+
durationMs: Date.now() - startTime
|
|
193
|
+
};
|
|
194
|
+
} catch (error) {
|
|
195
|
+
return {
|
|
196
|
+
stdout: "",
|
|
197
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
198
|
+
exitCode: 1,
|
|
199
|
+
durationMs: Date.now() - startTime
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
runCode: async (sandbox, code, runtime, _config) => {
|
|
204
|
+
if (!runtime) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
'Archil runCode requires an explicit runtime. Pass runtime: "node" or runtime: "python".'
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
if (runtime !== "node" && runtime !== "python") {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`Archil runCode does not support runtime "${runtime}". Supported runtimes: "node", "python".`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
const interpreter = runtime === "python" ? "python3" : "node";
|
|
215
|
+
const flag = runtime === "python" ? "-c" : "-e";
|
|
216
|
+
const command = `${interpreter} ${flag} ${shellEscape(code)}`;
|
|
217
|
+
const result = await execOnDisk(sandbox, command);
|
|
218
|
+
const stdout = result.stdout ?? "";
|
|
219
|
+
const stderr = result.stderr ?? "";
|
|
220
|
+
const output = stderr ? `${stdout}${stdout && stderr ? "\n" : ""}${stderr}` : stdout;
|
|
221
|
+
if (result.exitCode !== 0) {
|
|
222
|
+
const syntaxErrorPattern = runtime === "python" ? /\bSyntaxError\b|invalid syntax/i : /\bSyntaxError\b|Unexpected token|Unexpected identifier/i;
|
|
223
|
+
if (syntaxErrorPattern.test(output)) {
|
|
224
|
+
throw new Error(output || `${runtime} syntax error`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
output,
|
|
229
|
+
exitCode: result.exitCode,
|
|
230
|
+
language: runtime
|
|
231
|
+
};
|
|
232
|
+
},
|
|
233
|
+
getInfo: async (sandbox) => {
|
|
234
|
+
return {
|
|
235
|
+
id: sandbox.disk.id,
|
|
236
|
+
provider: "archil",
|
|
237
|
+
runtime: "node",
|
|
238
|
+
status: sandbox.disk.status === "ready" ? "running" : "stopped",
|
|
239
|
+
createdAt: new Date(sandbox.disk.createdAt),
|
|
240
|
+
timeout: 0,
|
|
241
|
+
metadata: {
|
|
242
|
+
name: sandbox.disk.name,
|
|
243
|
+
organization: sandbox.disk.organization,
|
|
244
|
+
region: sandbox.disk.region,
|
|
245
|
+
provider: sandbox.disk.provider
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
},
|
|
249
|
+
getUrl: async (_sandbox, options) => {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`Archil exec runs each command in a fresh ephemeral container that exits when the command returns, so there is no long-lived process to expose port ${options.port} on. getUrl is not supported.`
|
|
252
|
+
);
|
|
253
|
+
},
|
|
254
|
+
filesystem: {
|
|
255
|
+
readFile: async (sandbox, path, runCommand) => {
|
|
256
|
+
const result = await runCommand(sandbox, `cat ${shellEscape(path)}`);
|
|
257
|
+
if (result.exitCode !== 0) {
|
|
258
|
+
throw new Error(`Failed to read ${path}: ${result.stderr}`);
|
|
259
|
+
}
|
|
260
|
+
return result.stdout;
|
|
261
|
+
},
|
|
262
|
+
writeFile: async (sandbox, path, content, runCommand) => {
|
|
263
|
+
const parent = path.substring(0, path.lastIndexOf("/"));
|
|
264
|
+
if (parent) {
|
|
265
|
+
await runCommand(sandbox, `mkdir -p ${shellEscape(parent)}`);
|
|
266
|
+
}
|
|
267
|
+
const encoded = Buffer.from(content, "utf8").toString("base64");
|
|
268
|
+
const result = await runCommand(
|
|
269
|
+
sandbox,
|
|
270
|
+
`printf %s ${shellEscape(encoded)} | base64 -d > ${shellEscape(path)}`
|
|
271
|
+
);
|
|
272
|
+
if (result.exitCode !== 0) {
|
|
273
|
+
throw new Error(`Failed to write ${path}: ${result.stderr}`);
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
mkdir: async (sandbox, path, runCommand) => {
|
|
277
|
+
const result = await runCommand(sandbox, `mkdir -p ${shellEscape(path)}`);
|
|
278
|
+
if (result.exitCode !== 0) {
|
|
279
|
+
throw new Error(`Failed to create directory ${path}: ${result.stderr}`);
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
readdir: async (sandbox, path, runCommand) => {
|
|
283
|
+
const result = await runCommand(
|
|
284
|
+
sandbox,
|
|
285
|
+
`find ${shellEscape(path)} -mindepth 1 -maxdepth 1 -printf '%y\\t%s\\t%T@\\t%f\\n'`
|
|
286
|
+
);
|
|
287
|
+
if (result.exitCode !== 0) {
|
|
288
|
+
throw new Error(`Failed to list directory ${path}: ${result.stderr}`);
|
|
289
|
+
}
|
|
290
|
+
const entries = [];
|
|
291
|
+
for (const line of result.stdout.split("\n")) {
|
|
292
|
+
if (!line) continue;
|
|
293
|
+
const [typeChar, sizeStr, mtimeStr, ...nameParts] = line.split(" ");
|
|
294
|
+
const name = nameParts.join(" ");
|
|
295
|
+
entries.push({
|
|
296
|
+
name,
|
|
297
|
+
type: typeChar === "d" ? "directory" : "file",
|
|
298
|
+
size: parseInt(sizeStr, 10) || 0,
|
|
299
|
+
modified: new Date(parseFloat(mtimeStr) * 1e3)
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return entries;
|
|
303
|
+
},
|
|
304
|
+
exists: async (sandbox, path, runCommand) => {
|
|
305
|
+
const result = await runCommand(sandbox, `test -e ${shellEscape(path)}`);
|
|
306
|
+
return result.exitCode === 0;
|
|
307
|
+
},
|
|
308
|
+
remove: async (sandbox, path, runCommand) => {
|
|
309
|
+
const result = await runCommand(sandbox, `rm -rf ${shellEscape(path)}`);
|
|
310
|
+
if (result.exitCode !== 0) {
|
|
311
|
+
throw new Error(`Failed to remove ${path}: ${result.stderr}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
getInstance: (sandbox) => sandbox
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
var archil = (config = {}) => _provider(config);
|
|
320
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
321
|
+
0 && (module.exports = {
|
|
322
|
+
archil
|
|
323
|
+
});
|
|
324
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Archil Provider\n *\n * Executes commands against an Archil disk via the Archil control-plane HTTP\n * API. Archil is exec-only — each command runs in an Archil-managed container\n * with the configured disk mounted, then returns stdout, stderr, and exit code.\n * There is no sandbox lifecycle to manage; \"create\" just resolves a handle to\n * the target disk.\n */\n\nimport { defineProvider } from '@computesdk/provider';\nimport type {\n CodeResult,\n CommandResult,\n SandboxInfo,\n Runtime,\n CreateSandboxOptions,\n FileEntry,\n RunCommandOptions,\n} from 'computesdk';\n\n// Per-region color overrides. Default is \"green\" for any region not listed here.\nconst REGION_COLORS: Record<string, string> = {\n 'gcp-us-central1': 'blue',\n};\n\nfunction regionToBaseUrl(region: string): string {\n const dash = region.indexOf('-');\n if (dash <= 0 || dash === region.length - 1) {\n throw new Error(\n `Invalid Archil region \"${region}\". Expected \"{cloud}-{suffix}\", e.g. \"aws-us-east-1\".`,\n );\n }\n const cloud = region.slice(0, dash);\n const suffix = region.slice(dash + 1);\n const color = REGION_COLORS[region] ?? 'green';\n return `https://control.${color}.${suffix}.${cloud}.prod.archil.com`;\n}\n\nexport interface ArchilConfig {\n /** Archil API key. Falls back to ARCHIL_API_KEY env var. */\n apiKey?: string;\n /** Archil region (e.g. \"aws-us-east-1\"). Falls back to ARCHIL_REGION env var. */\n region?: string;\n /** Override the control-plane base URL (useful for testing). */\n baseUrl?: string;\n}\n\ninterface DiskResponse {\n id: string;\n name: string;\n organization: string;\n status: string;\n provider: string;\n region: string;\n createdAt: string;\n}\n\ninterface ExecTiming {\n totalMs: number;\n queueMs: number;\n executeMs: number;\n}\n\ninterface ExecResponse {\n exitCode: number;\n stdout: string;\n stderr: string;\n timing: ExecTiming;\n}\n\ninterface ResolvedConfig {\n apiKey: string;\n baseUrl: string;\n}\n\ninterface ArchilSandbox {\n disk: DiskResponse;\n resolved: ResolvedConfig;\n createdAt: Date;\n}\n\nfunction resolveConfig(config: ArchilConfig): ResolvedConfig {\n const apiKey = config.apiKey ?? process.env.ARCHIL_API_KEY;\n const region = config.region ?? process.env.ARCHIL_REGION;\n\n if (!apiKey) {\n throw new Error(\n 'Missing API key for Archil.\\n\\n' +\n 'Pass it: archil({ apiKey: \"...\" })\\n' +\n 'Or set ARCHIL_API_KEY in your environment.',\n );\n }\n\n let baseUrl = config.baseUrl;\n if (!baseUrl) {\n if (!region) {\n throw new Error(\n 'Missing region for Archil.\\n\\n' +\n 'Pass it: archil({ region: \"...\" })\\n' +\n 'Or set ARCHIL_REGION in your environment.\\n' +\n 'Examples: \"aws-us-east-1\", \"aws-eu-west-1\", \"gcp-us-central1\".',\n );\n }\n baseUrl = regionToBaseUrl(region);\n }\n\n return { apiKey, baseUrl };\n}\n\nfunction authHeader(apiKey: string): string {\n return `key-${apiKey.replace(/^key-/, '')}`;\n}\n\nasync function callApi<T>(\n resolved: ResolvedConfig,\n method: 'GET' | 'POST' | 'DELETE',\n path: string,\n body?: unknown,\n): Promise<T> {\n const response = await fetch(`${resolved.baseUrl}${path}`, {\n method,\n headers: {\n Authorization: authHeader(resolved.apiKey),\n ...(body ? { 'Content-Type': 'application/json' } : {}),\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n type Envelope = { success?: boolean; data?: T; error?: string };\n let payload: Envelope | null = null;\n try {\n payload = (await response.json()) as Envelope;\n } catch {\n payload = null;\n }\n\n if (!response.ok || !payload || payload.success === false) {\n const message =\n (payload && payload.error) ||\n `Archil API ${method} ${path} failed with status ${response.status}`;\n throw new Error(message);\n }\n\n return payload.data as T;\n}\n\n// Archil disk names must match /^[a-zA-Z0-9_-]+$/ and be 1–100 chars.\nfunction generateDiskName(): string {\n const random = Math.random().toString(36).slice(2, 10);\n return `computesdk-${Date.now()}-${random}`;\n}\n\nasync function lookupDiskByName(\n resolved: ResolvedConfig,\n name: string,\n): Promise<DiskResponse | null> {\n const disks = await callApi<DiskResponse[]>(resolved, 'GET', '/api/disks');\n return disks.find((d) => d.name === name) ?? null;\n}\n\nfunction shellEscape(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction wrapCommand(command: string, options?: RunCommandOptions): string {\n let wrapped = command;\n\n const envEntries = Object.entries(options?.env ?? {});\n if (!envEntries.some(([key]) => key === 'HOME')) {\n envEntries.unshift(['HOME', process.env.HOME || '/tmp']);\n }\n const envPrefix = envEntries\n .map(([k, v]) => `${k}=${shellEscape(String(v))}`)\n .join(' ');\n wrapped = `${envPrefix} ${wrapped}`;\n\n if (options?.cwd) {\n wrapped = `cd ${shellEscape(options.cwd)} && ${wrapped}`;\n }\n\n if (options?.background) {\n wrapped = `nohup sh -c ${shellEscape(wrapped)} > /dev/null 2>&1 &`;\n }\n\n return wrapped;\n}\n\nasync function execOnDisk(sandbox: ArchilSandbox, command: string): Promise<ExecResponse> {\n return callApi<ExecResponse>(\n sandbox.resolved,\n 'POST',\n `/api/disks/${encodeURIComponent(sandbox.disk.id)}/exec`,\n { command },\n );\n}\n\nconst _provider = defineProvider<ArchilSandbox, ArchilConfig>({\n name: 'archil',\n methods: {\n sandbox: {\n create: async (config: ArchilConfig, options?: CreateSandboxOptions) => {\n const resolved = resolveConfig(config);\n const name = ((options?.name as string | undefined) ?? generateDiskName()).trim();\n const created = await callApi<{ diskId: string }>(\n resolved,\n 'POST',\n '/api/disks',\n { name },\n );\n const disk = await callApi<DiskResponse>(\n resolved,\n 'GET',\n `/api/disks/${encodeURIComponent(created.diskId)}`,\n );\n return {\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n };\n },\n\n getById: async (config: ArchilConfig, sandboxId: string) => {\n const resolved = resolveConfig(config);\n // Try as disk id first; fall back to name lookup via list.\n try {\n const disk = await callApi<DiskResponse>(\n resolved,\n 'GET',\n `/api/disks/${encodeURIComponent(sandboxId)}`,\n );\n return {\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n };\n } catch {\n // not an id — try name\n }\n try {\n const disk = await lookupDiskByName(resolved, sandboxId);\n if (!disk) return null;\n return {\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n };\n } catch {\n return null;\n }\n },\n\n list: async (config: ArchilConfig) => {\n const resolved = resolveConfig(config);\n const disks = await callApi<DiskResponse[]>(resolved, 'GET', '/api/disks');\n return disks.map((disk) => ({\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n }));\n },\n\n destroy: async (config: ArchilConfig, sandboxId: string) => {\n const resolved = resolveConfig(config);\n await callApi<null>(\n resolved,\n 'DELETE',\n `/api/disks/${encodeURIComponent(sandboxId)}`,\n );\n },\n\n runCommand: async (\n sandbox: ArchilSandbox,\n command: string,\n options?: RunCommandOptions,\n ): Promise<CommandResult> => {\n const startTime = Date.now();\n try {\n const result = await execOnDisk(sandbox, wrapCommand(command, options));\n return {\n stdout: result.stdout ?? '',\n stderr: result.stderr ?? '',\n exitCode: result.exitCode,\n durationMs: Date.now() - startTime,\n };\n } catch (error) {\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 1,\n durationMs: Date.now() - startTime,\n };\n }\n },\n\n runCode: async (\n sandbox: ArchilSandbox,\n code: string,\n runtime?: Runtime,\n _config?: ArchilConfig,\n ): Promise<CodeResult> => {\n if (!runtime) {\n throw new Error(\n 'Archil runCode requires an explicit runtime. Pass runtime: \"node\" or runtime: \"python\".',\n );\n }\n if (runtime !== 'node' && runtime !== 'python') {\n throw new Error(\n `Archil runCode does not support runtime \"${runtime}\". Supported runtimes: \"node\", \"python\".`,\n );\n }\n const interpreter = runtime === 'python' ? 'python3' : 'node';\n const flag = runtime === 'python' ? '-c' : '-e';\n const command = `${interpreter} ${flag} ${shellEscape(code)}`;\n\n const result = await execOnDisk(sandbox, command);\n const stdout = result.stdout ?? '';\n const stderr = result.stderr ?? '';\n const output = stderr ? `${stdout}${stdout && stderr ? '\\n' : ''}${stderr}` : stdout;\n\n if (result.exitCode !== 0) {\n const syntaxErrorPattern =\n runtime === 'python'\n ? /\\bSyntaxError\\b|invalid syntax/i\n : /\\bSyntaxError\\b|Unexpected token|Unexpected identifier/i;\n if (syntaxErrorPattern.test(output)) {\n throw new Error(output || `${runtime} syntax error`);\n }\n }\n\n return {\n output,\n exitCode: result.exitCode,\n language: runtime,\n };\n },\n\n getInfo: async (sandbox: ArchilSandbox): Promise<SandboxInfo> => {\n return {\n id: sandbox.disk.id,\n provider: 'archil',\n runtime: 'node',\n status: sandbox.disk.status === 'ready' ? 'running' : 'stopped',\n createdAt: new Date(sandbox.disk.createdAt),\n timeout: 0,\n metadata: {\n name: sandbox.disk.name,\n organization: sandbox.disk.organization,\n region: sandbox.disk.region,\n provider: sandbox.disk.provider,\n },\n };\n },\n\n getUrl: async (\n _sandbox: ArchilSandbox,\n options: { port: number; protocol?: string },\n ): Promise<string> => {\n throw new Error(\n `Archil exec runs each command in a fresh ephemeral container that exits when the command returns, ` +\n `so there is no long-lived process to expose port ${options.port} on. ` +\n `getUrl is not supported.`,\n );\n },\n\n filesystem: {\n readFile: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `cat ${shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to read ${path}: ${result.stderr}`);\n }\n return result.stdout;\n },\n\n writeFile: async (sandbox, path, content, runCommand) => {\n const parent = path.substring(0, path.lastIndexOf('/'));\n if (parent) {\n await runCommand(sandbox, `mkdir -p ${shellEscape(parent)}`);\n }\n // base64-pipe to avoid heredoc/quoting hazards on arbitrary content.\n const encoded = Buffer.from(content, 'utf8').toString('base64');\n const result = await runCommand(\n sandbox,\n `printf %s ${shellEscape(encoded)} | base64 -d > ${shellEscape(path)}`,\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to write ${path}: ${result.stderr}`);\n }\n },\n\n mkdir: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `mkdir -p ${shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to create directory ${path}: ${result.stderr}`);\n }\n },\n\n readdir: async (sandbox, path, runCommand) => {\n // Tab-separated: type<TAB>size<TAB>mtime-iso<TAB>name. Robust to spaces in names.\n const result = await runCommand(\n sandbox,\n `find ${shellEscape(path)} -mindepth 1 -maxdepth 1 -printf '%y\\\\t%s\\\\t%T@\\\\t%f\\\\n'`,\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to list directory ${path}: ${result.stderr}`);\n }\n const entries: FileEntry[] = [];\n for (const line of result.stdout.split('\\n')) {\n if (!line) continue;\n const [typeChar, sizeStr, mtimeStr, ...nameParts] = line.split('\\t');\n const name = nameParts.join('\\t');\n entries.push({\n name,\n type: typeChar === 'd' ? 'directory' : 'file',\n size: parseInt(sizeStr, 10) || 0,\n modified: new Date(parseFloat(mtimeStr) * 1000),\n });\n }\n return entries;\n },\n\n exists: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `test -e ${shellEscape(path)}`);\n return result.exitCode === 0;\n },\n\n remove: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `rm -rf ${shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to remove ${path}: ${result.stderr}`);\n }\n },\n },\n\n getInstance: (sandbox: ArchilSandbox): ArchilSandbox => sandbox,\n },\n },\n});\n\nexport const archil = (config: ArchilConfig = {}) => _provider(config);\n\nexport type { ArchilSandbox };\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { defineProvider } from "@computesdk/provider";
|
|
3
|
+
var REGION_COLORS = {
|
|
4
|
+
"gcp-us-central1": "blue"
|
|
5
|
+
};
|
|
6
|
+
function regionToBaseUrl(region) {
|
|
7
|
+
const dash = region.indexOf("-");
|
|
8
|
+
if (dash <= 0 || dash === region.length - 1) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`Invalid Archil region "${region}". Expected "{cloud}-{suffix}", e.g. "aws-us-east-1".`
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
const cloud = region.slice(0, dash);
|
|
14
|
+
const suffix = region.slice(dash + 1);
|
|
15
|
+
const color = REGION_COLORS[region] ?? "green";
|
|
16
|
+
return `https://control.${color}.${suffix}.${cloud}.prod.archil.com`;
|
|
17
|
+
}
|
|
18
|
+
function resolveConfig(config) {
|
|
19
|
+
const apiKey = config.apiKey ?? process.env.ARCHIL_API_KEY;
|
|
20
|
+
const region = config.region ?? process.env.ARCHIL_REGION;
|
|
21
|
+
if (!apiKey) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
'Missing API key for Archil.\n\nPass it: archil({ apiKey: "..." })\nOr set ARCHIL_API_KEY in your environment.'
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
let baseUrl = config.baseUrl;
|
|
27
|
+
if (!baseUrl) {
|
|
28
|
+
if (!region) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
'Missing region for Archil.\n\nPass it: archil({ region: "..." })\nOr set ARCHIL_REGION in your environment.\nExamples: "aws-us-east-1", "aws-eu-west-1", "gcp-us-central1".'
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
baseUrl = regionToBaseUrl(region);
|
|
34
|
+
}
|
|
35
|
+
return { apiKey, baseUrl };
|
|
36
|
+
}
|
|
37
|
+
function authHeader(apiKey) {
|
|
38
|
+
return `key-${apiKey.replace(/^key-/, "")}`;
|
|
39
|
+
}
|
|
40
|
+
async function callApi(resolved, method, path, body) {
|
|
41
|
+
const response = await fetch(`${resolved.baseUrl}${path}`, {
|
|
42
|
+
method,
|
|
43
|
+
headers: {
|
|
44
|
+
Authorization: authHeader(resolved.apiKey),
|
|
45
|
+
...body ? { "Content-Type": "application/json" } : {}
|
|
46
|
+
},
|
|
47
|
+
body: body ? JSON.stringify(body) : void 0
|
|
48
|
+
});
|
|
49
|
+
let payload = null;
|
|
50
|
+
try {
|
|
51
|
+
payload = await response.json();
|
|
52
|
+
} catch {
|
|
53
|
+
payload = null;
|
|
54
|
+
}
|
|
55
|
+
if (!response.ok || !payload || payload.success === false) {
|
|
56
|
+
const message = payload && payload.error || `Archil API ${method} ${path} failed with status ${response.status}`;
|
|
57
|
+
throw new Error(message);
|
|
58
|
+
}
|
|
59
|
+
return payload.data;
|
|
60
|
+
}
|
|
61
|
+
function generateDiskName() {
|
|
62
|
+
const random = Math.random().toString(36).slice(2, 10);
|
|
63
|
+
return `computesdk-${Date.now()}-${random}`;
|
|
64
|
+
}
|
|
65
|
+
async function lookupDiskByName(resolved, name) {
|
|
66
|
+
const disks = await callApi(resolved, "GET", "/api/disks");
|
|
67
|
+
return disks.find((d) => d.name === name) ?? null;
|
|
68
|
+
}
|
|
69
|
+
function shellEscape(value) {
|
|
70
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
71
|
+
}
|
|
72
|
+
function wrapCommand(command, options) {
|
|
73
|
+
let wrapped = command;
|
|
74
|
+
const envEntries = Object.entries(options?.env ?? {});
|
|
75
|
+
if (!envEntries.some(([key]) => key === "HOME")) {
|
|
76
|
+
envEntries.unshift(["HOME", process.env.HOME || "/tmp"]);
|
|
77
|
+
}
|
|
78
|
+
const envPrefix = envEntries.map(([k, v]) => `${k}=${shellEscape(String(v))}`).join(" ");
|
|
79
|
+
wrapped = `${envPrefix} ${wrapped}`;
|
|
80
|
+
if (options?.cwd) {
|
|
81
|
+
wrapped = `cd ${shellEscape(options.cwd)} && ${wrapped}`;
|
|
82
|
+
}
|
|
83
|
+
if (options?.background) {
|
|
84
|
+
wrapped = `nohup sh -c ${shellEscape(wrapped)} > /dev/null 2>&1 &`;
|
|
85
|
+
}
|
|
86
|
+
return wrapped;
|
|
87
|
+
}
|
|
88
|
+
async function execOnDisk(sandbox, command) {
|
|
89
|
+
return callApi(
|
|
90
|
+
sandbox.resolved,
|
|
91
|
+
"POST",
|
|
92
|
+
`/api/disks/${encodeURIComponent(sandbox.disk.id)}/exec`,
|
|
93
|
+
{ command }
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
var _provider = defineProvider({
|
|
97
|
+
name: "archil",
|
|
98
|
+
methods: {
|
|
99
|
+
sandbox: {
|
|
100
|
+
create: async (config, options) => {
|
|
101
|
+
const resolved = resolveConfig(config);
|
|
102
|
+
const name = (options?.name ?? generateDiskName()).trim();
|
|
103
|
+
const created = await callApi(
|
|
104
|
+
resolved,
|
|
105
|
+
"POST",
|
|
106
|
+
"/api/disks",
|
|
107
|
+
{ name }
|
|
108
|
+
);
|
|
109
|
+
const disk = await callApi(
|
|
110
|
+
resolved,
|
|
111
|
+
"GET",
|
|
112
|
+
`/api/disks/${encodeURIComponent(created.diskId)}`
|
|
113
|
+
);
|
|
114
|
+
return {
|
|
115
|
+
sandbox: { disk, resolved, createdAt: /* @__PURE__ */ new Date() },
|
|
116
|
+
sandboxId: disk.id
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
getById: async (config, sandboxId) => {
|
|
120
|
+
const resolved = resolveConfig(config);
|
|
121
|
+
try {
|
|
122
|
+
const disk = await callApi(
|
|
123
|
+
resolved,
|
|
124
|
+
"GET",
|
|
125
|
+
`/api/disks/${encodeURIComponent(sandboxId)}`
|
|
126
|
+
);
|
|
127
|
+
return {
|
|
128
|
+
sandbox: { disk, resolved, createdAt: /* @__PURE__ */ new Date() },
|
|
129
|
+
sandboxId: disk.id
|
|
130
|
+
};
|
|
131
|
+
} catch {
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const disk = await lookupDiskByName(resolved, sandboxId);
|
|
135
|
+
if (!disk) return null;
|
|
136
|
+
return {
|
|
137
|
+
sandbox: { disk, resolved, createdAt: /* @__PURE__ */ new Date() },
|
|
138
|
+
sandboxId: disk.id
|
|
139
|
+
};
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
list: async (config) => {
|
|
145
|
+
const resolved = resolveConfig(config);
|
|
146
|
+
const disks = await callApi(resolved, "GET", "/api/disks");
|
|
147
|
+
return disks.map((disk) => ({
|
|
148
|
+
sandbox: { disk, resolved, createdAt: /* @__PURE__ */ new Date() },
|
|
149
|
+
sandboxId: disk.id
|
|
150
|
+
}));
|
|
151
|
+
},
|
|
152
|
+
destroy: async (config, sandboxId) => {
|
|
153
|
+
const resolved = resolveConfig(config);
|
|
154
|
+
await callApi(
|
|
155
|
+
resolved,
|
|
156
|
+
"DELETE",
|
|
157
|
+
`/api/disks/${encodeURIComponent(sandboxId)}`
|
|
158
|
+
);
|
|
159
|
+
},
|
|
160
|
+
runCommand: async (sandbox, command, options) => {
|
|
161
|
+
const startTime = Date.now();
|
|
162
|
+
try {
|
|
163
|
+
const result = await execOnDisk(sandbox, wrapCommand(command, options));
|
|
164
|
+
return {
|
|
165
|
+
stdout: result.stdout ?? "",
|
|
166
|
+
stderr: result.stderr ?? "",
|
|
167
|
+
exitCode: result.exitCode,
|
|
168
|
+
durationMs: Date.now() - startTime
|
|
169
|
+
};
|
|
170
|
+
} catch (error) {
|
|
171
|
+
return {
|
|
172
|
+
stdout: "",
|
|
173
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
174
|
+
exitCode: 1,
|
|
175
|
+
durationMs: Date.now() - startTime
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
runCode: async (sandbox, code, runtime, _config) => {
|
|
180
|
+
if (!runtime) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
'Archil runCode requires an explicit runtime. Pass runtime: "node" or runtime: "python".'
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
if (runtime !== "node" && runtime !== "python") {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Archil runCode does not support runtime "${runtime}". Supported runtimes: "node", "python".`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
const interpreter = runtime === "python" ? "python3" : "node";
|
|
191
|
+
const flag = runtime === "python" ? "-c" : "-e";
|
|
192
|
+
const command = `${interpreter} ${flag} ${shellEscape(code)}`;
|
|
193
|
+
const result = await execOnDisk(sandbox, command);
|
|
194
|
+
const stdout = result.stdout ?? "";
|
|
195
|
+
const stderr = result.stderr ?? "";
|
|
196
|
+
const output = stderr ? `${stdout}${stdout && stderr ? "\n" : ""}${stderr}` : stdout;
|
|
197
|
+
if (result.exitCode !== 0) {
|
|
198
|
+
const syntaxErrorPattern = runtime === "python" ? /\bSyntaxError\b|invalid syntax/i : /\bSyntaxError\b|Unexpected token|Unexpected identifier/i;
|
|
199
|
+
if (syntaxErrorPattern.test(output)) {
|
|
200
|
+
throw new Error(output || `${runtime} syntax error`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
output,
|
|
205
|
+
exitCode: result.exitCode,
|
|
206
|
+
language: runtime
|
|
207
|
+
};
|
|
208
|
+
},
|
|
209
|
+
getInfo: async (sandbox) => {
|
|
210
|
+
return {
|
|
211
|
+
id: sandbox.disk.id,
|
|
212
|
+
provider: "archil",
|
|
213
|
+
runtime: "node",
|
|
214
|
+
status: sandbox.disk.status === "ready" ? "running" : "stopped",
|
|
215
|
+
createdAt: new Date(sandbox.disk.createdAt),
|
|
216
|
+
timeout: 0,
|
|
217
|
+
metadata: {
|
|
218
|
+
name: sandbox.disk.name,
|
|
219
|
+
organization: sandbox.disk.organization,
|
|
220
|
+
region: sandbox.disk.region,
|
|
221
|
+
provider: sandbox.disk.provider
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
},
|
|
225
|
+
getUrl: async (_sandbox, options) => {
|
|
226
|
+
throw new Error(
|
|
227
|
+
`Archil exec runs each command in a fresh ephemeral container that exits when the command returns, so there is no long-lived process to expose port ${options.port} on. getUrl is not supported.`
|
|
228
|
+
);
|
|
229
|
+
},
|
|
230
|
+
filesystem: {
|
|
231
|
+
readFile: async (sandbox, path, runCommand) => {
|
|
232
|
+
const result = await runCommand(sandbox, `cat ${shellEscape(path)}`);
|
|
233
|
+
if (result.exitCode !== 0) {
|
|
234
|
+
throw new Error(`Failed to read ${path}: ${result.stderr}`);
|
|
235
|
+
}
|
|
236
|
+
return result.stdout;
|
|
237
|
+
},
|
|
238
|
+
writeFile: async (sandbox, path, content, runCommand) => {
|
|
239
|
+
const parent = path.substring(0, path.lastIndexOf("/"));
|
|
240
|
+
if (parent) {
|
|
241
|
+
await runCommand(sandbox, `mkdir -p ${shellEscape(parent)}`);
|
|
242
|
+
}
|
|
243
|
+
const encoded = Buffer.from(content, "utf8").toString("base64");
|
|
244
|
+
const result = await runCommand(
|
|
245
|
+
sandbox,
|
|
246
|
+
`printf %s ${shellEscape(encoded)} | base64 -d > ${shellEscape(path)}`
|
|
247
|
+
);
|
|
248
|
+
if (result.exitCode !== 0) {
|
|
249
|
+
throw new Error(`Failed to write ${path}: ${result.stderr}`);
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
mkdir: async (sandbox, path, runCommand) => {
|
|
253
|
+
const result = await runCommand(sandbox, `mkdir -p ${shellEscape(path)}`);
|
|
254
|
+
if (result.exitCode !== 0) {
|
|
255
|
+
throw new Error(`Failed to create directory ${path}: ${result.stderr}`);
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
readdir: async (sandbox, path, runCommand) => {
|
|
259
|
+
const result = await runCommand(
|
|
260
|
+
sandbox,
|
|
261
|
+
`find ${shellEscape(path)} -mindepth 1 -maxdepth 1 -printf '%y\\t%s\\t%T@\\t%f\\n'`
|
|
262
|
+
);
|
|
263
|
+
if (result.exitCode !== 0) {
|
|
264
|
+
throw new Error(`Failed to list directory ${path}: ${result.stderr}`);
|
|
265
|
+
}
|
|
266
|
+
const entries = [];
|
|
267
|
+
for (const line of result.stdout.split("\n")) {
|
|
268
|
+
if (!line) continue;
|
|
269
|
+
const [typeChar, sizeStr, mtimeStr, ...nameParts] = line.split(" ");
|
|
270
|
+
const name = nameParts.join(" ");
|
|
271
|
+
entries.push({
|
|
272
|
+
name,
|
|
273
|
+
type: typeChar === "d" ? "directory" : "file",
|
|
274
|
+
size: parseInt(sizeStr, 10) || 0,
|
|
275
|
+
modified: new Date(parseFloat(mtimeStr) * 1e3)
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return entries;
|
|
279
|
+
},
|
|
280
|
+
exists: async (sandbox, path, runCommand) => {
|
|
281
|
+
const result = await runCommand(sandbox, `test -e ${shellEscape(path)}`);
|
|
282
|
+
return result.exitCode === 0;
|
|
283
|
+
},
|
|
284
|
+
remove: async (sandbox, path, runCommand) => {
|
|
285
|
+
const result = await runCommand(sandbox, `rm -rf ${shellEscape(path)}`);
|
|
286
|
+
if (result.exitCode !== 0) {
|
|
287
|
+
throw new Error(`Failed to remove ${path}: ${result.stderr}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
getInstance: (sandbox) => sandbox
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
var archil = (config = {}) => _provider(config);
|
|
296
|
+
export {
|
|
297
|
+
archil
|
|
298
|
+
};
|
|
299
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Archil Provider\n *\n * Executes commands against an Archil disk via the Archil control-plane HTTP\n * API. Archil is exec-only — each command runs in an Archil-managed container\n * with the configured disk mounted, then returns stdout, stderr, and exit code.\n * There is no sandbox lifecycle to manage; \"create\" just resolves a handle to\n * the target disk.\n */\n\nimport { defineProvider } from '@computesdk/provider';\nimport type {\n CodeResult,\n CommandResult,\n SandboxInfo,\n Runtime,\n CreateSandboxOptions,\n FileEntry,\n RunCommandOptions,\n} from 'computesdk';\n\n// Per-region color overrides. Default is \"green\" for any region not listed here.\nconst REGION_COLORS: Record<string, string> = {\n 'gcp-us-central1': 'blue',\n};\n\nfunction regionToBaseUrl(region: string): string {\n const dash = region.indexOf('-');\n if (dash <= 0 || dash === region.length - 1) {\n throw new Error(\n `Invalid Archil region \"${region}\". Expected \"{cloud}-{suffix}\", e.g. \"aws-us-east-1\".`,\n );\n }\n const cloud = region.slice(0, dash);\n const suffix = region.slice(dash + 1);\n const color = REGION_COLORS[region] ?? 'green';\n return `https://control.${color}.${suffix}.${cloud}.prod.archil.com`;\n}\n\nexport interface ArchilConfig {\n /** Archil API key. Falls back to ARCHIL_API_KEY env var. */\n apiKey?: string;\n /** Archil region (e.g. \"aws-us-east-1\"). Falls back to ARCHIL_REGION env var. */\n region?: string;\n /** Override the control-plane base URL (useful for testing). */\n baseUrl?: string;\n}\n\ninterface DiskResponse {\n id: string;\n name: string;\n organization: string;\n status: string;\n provider: string;\n region: string;\n createdAt: string;\n}\n\ninterface ExecTiming {\n totalMs: number;\n queueMs: number;\n executeMs: number;\n}\n\ninterface ExecResponse {\n exitCode: number;\n stdout: string;\n stderr: string;\n timing: ExecTiming;\n}\n\ninterface ResolvedConfig {\n apiKey: string;\n baseUrl: string;\n}\n\ninterface ArchilSandbox {\n disk: DiskResponse;\n resolved: ResolvedConfig;\n createdAt: Date;\n}\n\nfunction resolveConfig(config: ArchilConfig): ResolvedConfig {\n const apiKey = config.apiKey ?? process.env.ARCHIL_API_KEY;\n const region = config.region ?? process.env.ARCHIL_REGION;\n\n if (!apiKey) {\n throw new Error(\n 'Missing API key for Archil.\\n\\n' +\n 'Pass it: archil({ apiKey: \"...\" })\\n' +\n 'Or set ARCHIL_API_KEY in your environment.',\n );\n }\n\n let baseUrl = config.baseUrl;\n if (!baseUrl) {\n if (!region) {\n throw new Error(\n 'Missing region for Archil.\\n\\n' +\n 'Pass it: archil({ region: \"...\" })\\n' +\n 'Or set ARCHIL_REGION in your environment.\\n' +\n 'Examples: \"aws-us-east-1\", \"aws-eu-west-1\", \"gcp-us-central1\".',\n );\n }\n baseUrl = regionToBaseUrl(region);\n }\n\n return { apiKey, baseUrl };\n}\n\nfunction authHeader(apiKey: string): string {\n return `key-${apiKey.replace(/^key-/, '')}`;\n}\n\nasync function callApi<T>(\n resolved: ResolvedConfig,\n method: 'GET' | 'POST' | 'DELETE',\n path: string,\n body?: unknown,\n): Promise<T> {\n const response = await fetch(`${resolved.baseUrl}${path}`, {\n method,\n headers: {\n Authorization: authHeader(resolved.apiKey),\n ...(body ? { 'Content-Type': 'application/json' } : {}),\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n type Envelope = { success?: boolean; data?: T; error?: string };\n let payload: Envelope | null = null;\n try {\n payload = (await response.json()) as Envelope;\n } catch {\n payload = null;\n }\n\n if (!response.ok || !payload || payload.success === false) {\n const message =\n (payload && payload.error) ||\n `Archil API ${method} ${path} failed with status ${response.status}`;\n throw new Error(message);\n }\n\n return payload.data as T;\n}\n\n// Archil disk names must match /^[a-zA-Z0-9_-]+$/ and be 1–100 chars.\nfunction generateDiskName(): string {\n const random = Math.random().toString(36).slice(2, 10);\n return `computesdk-${Date.now()}-${random}`;\n}\n\nasync function lookupDiskByName(\n resolved: ResolvedConfig,\n name: string,\n): Promise<DiskResponse | null> {\n const disks = await callApi<DiskResponse[]>(resolved, 'GET', '/api/disks');\n return disks.find((d) => d.name === name) ?? null;\n}\n\nfunction shellEscape(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction wrapCommand(command: string, options?: RunCommandOptions): string {\n let wrapped = command;\n\n const envEntries = Object.entries(options?.env ?? {});\n if (!envEntries.some(([key]) => key === 'HOME')) {\n envEntries.unshift(['HOME', process.env.HOME || '/tmp']);\n }\n const envPrefix = envEntries\n .map(([k, v]) => `${k}=${shellEscape(String(v))}`)\n .join(' ');\n wrapped = `${envPrefix} ${wrapped}`;\n\n if (options?.cwd) {\n wrapped = `cd ${shellEscape(options.cwd)} && ${wrapped}`;\n }\n\n if (options?.background) {\n wrapped = `nohup sh -c ${shellEscape(wrapped)} > /dev/null 2>&1 &`;\n }\n\n return wrapped;\n}\n\nasync function execOnDisk(sandbox: ArchilSandbox, command: string): Promise<ExecResponse> {\n return callApi<ExecResponse>(\n sandbox.resolved,\n 'POST',\n `/api/disks/${encodeURIComponent(sandbox.disk.id)}/exec`,\n { command },\n );\n}\n\nconst _provider = defineProvider<ArchilSandbox, ArchilConfig>({\n name: 'archil',\n methods: {\n sandbox: {\n create: async (config: ArchilConfig, options?: CreateSandboxOptions) => {\n const resolved = resolveConfig(config);\n const name = ((options?.name as string | undefined) ?? generateDiskName()).trim();\n const created = await callApi<{ diskId: string }>(\n resolved,\n 'POST',\n '/api/disks',\n { name },\n );\n const disk = await callApi<DiskResponse>(\n resolved,\n 'GET',\n `/api/disks/${encodeURIComponent(created.diskId)}`,\n );\n return {\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n };\n },\n\n getById: async (config: ArchilConfig, sandboxId: string) => {\n const resolved = resolveConfig(config);\n // Try as disk id first; fall back to name lookup via list.\n try {\n const disk = await callApi<DiskResponse>(\n resolved,\n 'GET',\n `/api/disks/${encodeURIComponent(sandboxId)}`,\n );\n return {\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n };\n } catch {\n // not an id — try name\n }\n try {\n const disk = await lookupDiskByName(resolved, sandboxId);\n if (!disk) return null;\n return {\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n };\n } catch {\n return null;\n }\n },\n\n list: async (config: ArchilConfig) => {\n const resolved = resolveConfig(config);\n const disks = await callApi<DiskResponse[]>(resolved, 'GET', '/api/disks');\n return disks.map((disk) => ({\n sandbox: { disk, resolved, createdAt: new Date() },\n sandboxId: disk.id,\n }));\n },\n\n destroy: async (config: ArchilConfig, sandboxId: string) => {\n const resolved = resolveConfig(config);\n await callApi<null>(\n resolved,\n 'DELETE',\n `/api/disks/${encodeURIComponent(sandboxId)}`,\n );\n },\n\n runCommand: async (\n sandbox: ArchilSandbox,\n command: string,\n options?: RunCommandOptions,\n ): Promise<CommandResult> => {\n const startTime = Date.now();\n try {\n const result = await execOnDisk(sandbox, wrapCommand(command, options));\n return {\n stdout: result.stdout ?? '',\n stderr: result.stderr ?? '',\n exitCode: result.exitCode,\n durationMs: Date.now() - startTime,\n };\n } catch (error) {\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 1,\n durationMs: Date.now() - startTime,\n };\n }\n },\n\n runCode: async (\n sandbox: ArchilSandbox,\n code: string,\n runtime?: Runtime,\n _config?: ArchilConfig,\n ): Promise<CodeResult> => {\n if (!runtime) {\n throw new Error(\n 'Archil runCode requires an explicit runtime. Pass runtime: \"node\" or runtime: \"python\".',\n );\n }\n if (runtime !== 'node' && runtime !== 'python') {\n throw new Error(\n `Archil runCode does not support runtime \"${runtime}\". Supported runtimes: \"node\", \"python\".`,\n );\n }\n const interpreter = runtime === 'python' ? 'python3' : 'node';\n const flag = runtime === 'python' ? '-c' : '-e';\n const command = `${interpreter} ${flag} ${shellEscape(code)}`;\n\n const result = await execOnDisk(sandbox, command);\n const stdout = result.stdout ?? '';\n const stderr = result.stderr ?? '';\n const output = stderr ? `${stdout}${stdout && stderr ? '\\n' : ''}${stderr}` : stdout;\n\n if (result.exitCode !== 0) {\n const syntaxErrorPattern =\n runtime === 'python'\n ? /\\bSyntaxError\\b|invalid syntax/i\n : /\\bSyntaxError\\b|Unexpected token|Unexpected identifier/i;\n if (syntaxErrorPattern.test(output)) {\n throw new Error(output || `${runtime} syntax error`);\n }\n }\n\n return {\n output,\n exitCode: result.exitCode,\n language: runtime,\n };\n },\n\n getInfo: async (sandbox: ArchilSandbox): Promise<SandboxInfo> => {\n return {\n id: sandbox.disk.id,\n provider: 'archil',\n runtime: 'node',\n status: sandbox.disk.status === 'ready' ? 'running' : 'stopped',\n createdAt: new Date(sandbox.disk.createdAt),\n timeout: 0,\n metadata: {\n name: sandbox.disk.name,\n organization: sandbox.disk.organization,\n region: sandbox.disk.region,\n provider: sandbox.disk.provider,\n },\n };\n },\n\n getUrl: async (\n _sandbox: ArchilSandbox,\n options: { port: number; protocol?: string },\n ): Promise<string> => {\n throw new Error(\n `Archil exec runs each command in a fresh ephemeral container that exits when the command returns, ` +\n `so there is no long-lived process to expose port ${options.port} on. ` +\n `getUrl is not supported.`,\n );\n },\n\n filesystem: {\n readFile: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `cat ${shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to read ${path}: ${result.stderr}`);\n }\n return result.stdout;\n },\n\n writeFile: async (sandbox, path, content, runCommand) => {\n const parent = path.substring(0, path.lastIndexOf('/'));\n if (parent) {\n await runCommand(sandbox, `mkdir -p ${shellEscape(parent)}`);\n }\n // base64-pipe to avoid heredoc/quoting hazards on arbitrary content.\n const encoded = Buffer.from(content, 'utf8').toString('base64');\n const result = await runCommand(\n sandbox,\n `printf %s ${shellEscape(encoded)} | base64 -d > ${shellEscape(path)}`,\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to write ${path}: ${result.stderr}`);\n }\n },\n\n mkdir: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `mkdir -p ${shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to create directory ${path}: ${result.stderr}`);\n }\n },\n\n readdir: async (sandbox, path, runCommand) => {\n // Tab-separated: type<TAB>size<TAB>mtime-iso<TAB>name. Robust to spaces in names.\n const result = await runCommand(\n sandbox,\n `find ${shellEscape(path)} -mindepth 1 -maxdepth 1 -printf '%y\\\\t%s\\\\t%T@\\\\t%f\\\\n'`,\n );\n if (result.exitCode !== 0) {\n throw new Error(`Failed to list directory ${path}: ${result.stderr}`);\n }\n const entries: FileEntry[] = [];\n for (const line of result.stdout.split('\\n')) {\n if (!line) continue;\n const [typeChar, sizeStr, mtimeStr, ...nameParts] = line.split('\\t');\n const name = nameParts.join('\\t');\n entries.push({\n name,\n type: typeChar === 'd' ? 'directory' : 'file',\n size: parseInt(sizeStr, 10) || 0,\n modified: new Date(parseFloat(mtimeStr) * 1000),\n });\n }\n return entries;\n },\n\n exists: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `test -e ${shellEscape(path)}`);\n return result.exitCode === 0;\n },\n\n remove: async (sandbox, path, runCommand) => {\n const result = await runCommand(sandbox, `rm -rf ${shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to remove ${path}: ${result.stderr}`);\n }\n },\n },\n\n getInstance: (sandbox: ArchilSandbox): ArchilSandbox => sandbox,\n },\n },\n});\n\nexport const archil = (config: ArchilConfig = {}) => _provider(config);\n\nexport type { ArchilSandbox };\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@computesdk/archil",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Archil provider for ComputeSDK - exec commands against an Archil disk",
|
|
5
|
+
"author": "ComputeSDK",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"computesdk": "2.6.0",
|
|
22
|
+
"@computesdk/provider": "1.3.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^20.0.0",
|
|
26
|
+
"@vitest/coverage-v8": "^1.0.0",
|
|
27
|
+
"eslint": "^8.37.0",
|
|
28
|
+
"rimraf": "^5.0.0",
|
|
29
|
+
"tsup": "^8.0.0",
|
|
30
|
+
"typescript": "^5.0.0",
|
|
31
|
+
"vitest": "^1.0.0",
|
|
32
|
+
"@computesdk/test-utils": "1.6.1"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"computesdk",
|
|
36
|
+
"archil",
|
|
37
|
+
"exec",
|
|
38
|
+
"code-execution",
|
|
39
|
+
"provider",
|
|
40
|
+
"disk"
|
|
41
|
+
],
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/computesdk/computesdk.git",
|
|
45
|
+
"directory": "packages/archil"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://www.computesdk.com",
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/computesdk/computesdk/issues"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup",
|
|
53
|
+
"clean": "rimraf dist",
|
|
54
|
+
"dev": "tsup --watch",
|
|
55
|
+
"test": "vitest run",
|
|
56
|
+
"test:watch": "vitest watch",
|
|
57
|
+
"test:coverage": "vitest run --coverage",
|
|
58
|
+
"typecheck": "tsc --noEmit",
|
|
59
|
+
"lint": "eslint"
|
|
60
|
+
}
|
|
61
|
+
}
|