@slicervm/sdk 0.1.7 → 0.1.9
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 +37 -1
- package/dist/index.cjs +237 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +146 -5
- package/dist/index.d.ts +146 -5
- package/dist/index.js +235 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
TypeScript SDK for the [Slicer](https://slicervm.com) VM control-plane API.
|
|
4
4
|
|
|
5
|
-
Mirrors the [Go SDK](https://github.com/slicervm/sdk) semantically. The top-level `SlicerClient` exposes `hostGroups`, `vms`, and `secrets` namespaces for control-plane operations; per-VM operations live on a `VM` handle returned from `client.vms.create()` / `client.vms.attach()`.
|
|
5
|
+
Mirrors the [Go SDK](https://github.com/slicervm/sdk) semantically. The top-level `SlicerClient` exposes `hostGroups`, `vms`, `commits`, and `secrets` namespaces for control-plane operations; per-VM operations live on a `VM` handle returned from `client.vms.create()` / `client.vms.attach()`.
|
|
6
6
|
|
|
7
7
|
Supports Unix socket and HTTP(S) transports.
|
|
8
8
|
|
|
@@ -33,6 +33,42 @@ for await (const f of vm.bg.logs(bg.execId, { follow: true })) {
|
|
|
33
33
|
await vm.delete();
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
## Cold forks
|
|
37
|
+
|
|
38
|
+
A stopped persistent VM can be committed as an immutable disk parent, then
|
|
39
|
+
forked into cold-booted children. Fork calls wait for agent readiness and guest
|
|
40
|
+
identity finalisation by default. `wait: 'none'` acknowledges launch only; if
|
|
41
|
+
requested asynchronous finalisation fails, the daemon removes the child rather
|
|
42
|
+
than expose cloned identity or secrets with partially applied policy.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
await vm.shutdown();
|
|
46
|
+
const parent = await vm.commit({
|
|
47
|
+
tags: ['node-build'],
|
|
48
|
+
cacheKey: 'node-build-v1',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const child = await parent.fork({
|
|
52
|
+
wait: 'agent',
|
|
53
|
+
waitTimeoutSec: 120,
|
|
54
|
+
persistent: false,
|
|
55
|
+
vcpu: 1,
|
|
56
|
+
ramBytes: 512 * 1024 * 1024,
|
|
57
|
+
network: { allow: [], drop: ['0.0.0.0/0'] },
|
|
58
|
+
tagMode: 'replace',
|
|
59
|
+
tags: ['job=node-build'],
|
|
60
|
+
secrets: [],
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const description = await child.describe();
|
|
64
|
+
console.log(description.parentCommitId, description.network.effective);
|
|
65
|
+
|
|
66
|
+
const cached = await client.commits.list({ cacheKey: 'node-build-v1' });
|
|
67
|
+
await child.delete();
|
|
68
|
+
await vm.delete();
|
|
69
|
+
await client.commits.delete(parent.commitId);
|
|
70
|
+
```
|
|
71
|
+
|
|
36
72
|
## Status
|
|
37
73
|
|
|
38
74
|
Pre-release. See `ts-plan.md`.
|
package/dist/index.cjs
CHANGED
|
@@ -284,7 +284,72 @@ function agentHealthFromWire(w) {
|
|
|
284
284
|
...w.agent_uptime !== void 0 && { agentUptime: w.agent_uptime },
|
|
285
285
|
...w.agent_version !== void 0 && { agentVersion: w.agent_version },
|
|
286
286
|
...w.system_uptime !== void 0 && { systemUptime: w.system_uptime },
|
|
287
|
-
...w.userdata_ran !== void 0 && { userdataRan: w.userdata_ran }
|
|
287
|
+
...w.userdata_ran !== void 0 && { userdataRan: w.userdata_ran },
|
|
288
|
+
...w.userdata_exit_code !== void 0 && { userdataExitCode: w.userdata_exit_code }
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function vmCommitFromWire(w) {
|
|
292
|
+
return {
|
|
293
|
+
hostname: w.hostname,
|
|
294
|
+
commitId: w.commit_id,
|
|
295
|
+
status: w.status,
|
|
296
|
+
...w.parent_status !== void 0 && { parentStatus: w.parent_status },
|
|
297
|
+
mode: w.mode ?? "",
|
|
298
|
+
...w.tags !== void 0 && { tags: w.tags },
|
|
299
|
+
...w.labels !== void 0 && { labels: w.labels },
|
|
300
|
+
...w.cache_key !== void 0 && { cacheKey: w.cache_key }
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function vmCommitInfoFromWire(w) {
|
|
304
|
+
return {
|
|
305
|
+
commitId: w.commit_id,
|
|
306
|
+
sourceHostname: w.source_hostname,
|
|
307
|
+
sourceHostGroup: w.source_host_group,
|
|
308
|
+
createdAt: w.created_at,
|
|
309
|
+
mode: w.mode,
|
|
310
|
+
...w.tags !== void 0 && { tags: w.tags },
|
|
311
|
+
...w.labels !== void 0 && { labels: w.labels },
|
|
312
|
+
...w.cache_key !== void 0 && { cacheKey: w.cache_key }
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function vmCommitDeleteFromWire(w) {
|
|
316
|
+
return { commitId: w.commit_id, status: w.status };
|
|
317
|
+
}
|
|
318
|
+
function vmForkFromWire(w) {
|
|
319
|
+
return {
|
|
320
|
+
hostname: w.hostname,
|
|
321
|
+
sourceHostname: w.source_hostname,
|
|
322
|
+
...w.commit_id !== void 0 && { commitId: w.commit_id },
|
|
323
|
+
status: w.status,
|
|
324
|
+
...w.parent_status !== void 0 && { parentStatus: w.parent_status },
|
|
325
|
+
...w.child_status !== void 0 && { childStatus: w.child_status },
|
|
326
|
+
mode: w.mode ?? "",
|
|
327
|
+
...w.persistent !== void 0 && { persistent: w.persistent }
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function vmDescriptionFromWire(w) {
|
|
331
|
+
return {
|
|
332
|
+
hostname: w.hostname,
|
|
333
|
+
...w.hostgroup !== void 0 && { hostGroup: w.hostgroup },
|
|
334
|
+
...w.ip !== void 0 && { ip: w.ip },
|
|
335
|
+
...w.ram_bytes !== void 0 && { ramBytes: w.ram_bytes },
|
|
336
|
+
...w.cpus !== void 0 && { cpus: w.cpus },
|
|
337
|
+
...w.created_at !== void 0 && { createdAt: w.created_at },
|
|
338
|
+
...w.arch !== void 0 && { arch: w.arch },
|
|
339
|
+
...w.tags !== void 0 && { tags: w.tags },
|
|
340
|
+
...w.status !== void 0 && { status: w.status },
|
|
341
|
+
...w.persistent !== void 0 && { persistent: w.persistent },
|
|
342
|
+
...w.storage !== void 0 && { storage: w.storage },
|
|
343
|
+
...w.image !== void 0 && { image: w.image },
|
|
344
|
+
...w.commit_id !== void 0 && { commitId: w.commit_id },
|
|
345
|
+
...w.parent_commit_id !== void 0 && { parentCommitId: w.parent_commit_id },
|
|
346
|
+
network: {
|
|
347
|
+
...w.network.mode !== void 0 && { mode: w.network.mode },
|
|
348
|
+
...w.network.policy_source !== void 0 && { policySource: w.network.policy_source },
|
|
349
|
+
hostGroup: w.network.host_group,
|
|
350
|
+
...w.network.override !== void 0 && { override: w.network.override },
|
|
351
|
+
effective: w.network.effective
|
|
352
|
+
}
|
|
288
353
|
};
|
|
289
354
|
}
|
|
290
355
|
function fsEntryFromWire(w) {
|
|
@@ -309,6 +374,31 @@ function secretFromWire(w) {
|
|
|
309
374
|
...w.modified_at !== void 0 && { modifiedAt: w.modified_at }
|
|
310
375
|
};
|
|
311
376
|
}
|
|
377
|
+
|
|
378
|
+
// src/fork.ts
|
|
379
|
+
function buildForkRequest(opts) {
|
|
380
|
+
const wait = opts.wait ?? "agent";
|
|
381
|
+
if (wait !== "agent" && wait !== "none") {
|
|
382
|
+
throw new Error(`invalid fork wait mode ${JSON.stringify(wait)}`);
|
|
383
|
+
}
|
|
384
|
+
const query = new URLSearchParams({ wait });
|
|
385
|
+
if (opts.waitTimeoutSec !== void 0 && opts.waitTimeoutSec > 0) {
|
|
386
|
+
query.set("timeout", `${opts.waitTimeoutSec}s`);
|
|
387
|
+
}
|
|
388
|
+
const body = {};
|
|
389
|
+
if (opts.network !== void 0) body.network = opts.network;
|
|
390
|
+
if (opts.tags !== void 0) body.tags = opts.tags;
|
|
391
|
+
if (opts.tagMode !== void 0) body.tag_mode = opts.tagMode;
|
|
392
|
+
if (opts.secrets !== void 0) body.secrets = opts.secrets;
|
|
393
|
+
if (opts.persistent !== void 0) body.persistent = opts.persistent;
|
|
394
|
+
if (opts.fixups !== void 0) body.fixups = opts.fixups;
|
|
395
|
+
if (opts.vcpu !== void 0) body.vcpu = opts.vcpu;
|
|
396
|
+
if (opts.ramBytes !== void 0) body.ram_bytes = opts.ramBytes;
|
|
397
|
+
return {
|
|
398
|
+
query: query.toString(),
|
|
399
|
+
...Object.keys(body).length > 0 && { body }
|
|
400
|
+
};
|
|
401
|
+
}
|
|
312
402
|
function looksLikeUnixSocketPath(s) {
|
|
313
403
|
if (!s) return false;
|
|
314
404
|
return s.startsWith("/") || s.startsWith("./") || s.startsWith("../") || s.includes("/");
|
|
@@ -739,6 +829,48 @@ var VMFileSystem = class {
|
|
|
739
829
|
);
|
|
740
830
|
}
|
|
741
831
|
};
|
|
832
|
+
var CommittedVM = class {
|
|
833
|
+
constructor(transport, hostGroup, response) {
|
|
834
|
+
this.transport = transport;
|
|
835
|
+
this.hostname = response.hostname;
|
|
836
|
+
this.hostGroup = hostGroup;
|
|
837
|
+
this.commitId = response.commitId;
|
|
838
|
+
this.status = response.status;
|
|
839
|
+
if (response.parentStatus !== void 0) this.parentStatus = response.parentStatus;
|
|
840
|
+
this.mode = response.mode;
|
|
841
|
+
if (response.tags !== void 0) this.tags = response.tags;
|
|
842
|
+
if (response.labels !== void 0) this.labels = response.labels;
|
|
843
|
+
if (response.cacheKey !== void 0) this.cacheKey = response.cacheKey;
|
|
844
|
+
}
|
|
845
|
+
transport;
|
|
846
|
+
hostname;
|
|
847
|
+
hostGroup;
|
|
848
|
+
commitId;
|
|
849
|
+
status;
|
|
850
|
+
parentStatus;
|
|
851
|
+
mode;
|
|
852
|
+
tags;
|
|
853
|
+
labels;
|
|
854
|
+
cacheKey;
|
|
855
|
+
async fork(opts = {}) {
|
|
856
|
+
const res = await forkPath(
|
|
857
|
+
this.transport,
|
|
858
|
+
this.commitId,
|
|
859
|
+
opts
|
|
860
|
+
);
|
|
861
|
+
return new VM(this.transport, {
|
|
862
|
+
hostname: res.hostname,
|
|
863
|
+
hostGroup: this.hostGroup
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
async forkRaw(opts = {}) {
|
|
867
|
+
return forkPath(
|
|
868
|
+
this.transport,
|
|
869
|
+
this.commitId,
|
|
870
|
+
opts
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
};
|
|
742
874
|
var VM = class {
|
|
743
875
|
hostname;
|
|
744
876
|
hostGroup;
|
|
@@ -778,6 +910,14 @@ var VM = class {
|
|
|
778
910
|
async logs() {
|
|
779
911
|
return this.transport.request("GET", `/vm/${encodeURIComponent(this.hostname)}/logs`);
|
|
780
912
|
}
|
|
913
|
+
/** Return configuration, fork lineage, and effective network policy. */
|
|
914
|
+
async describe() {
|
|
915
|
+
const wire = await this.transport.request(
|
|
916
|
+
"GET",
|
|
917
|
+
`/vm/${encodeURIComponent(this.hostname)}`
|
|
918
|
+
);
|
|
919
|
+
return vmDescriptionFromWire(wire);
|
|
920
|
+
}
|
|
781
921
|
async waitForAgent(opts = {}) {
|
|
782
922
|
const timeoutMs = opts.timeoutMs ?? 12e4;
|
|
783
923
|
const intervalMs = opts.intervalMs ?? 500;
|
|
@@ -804,10 +944,15 @@ var VM = class {
|
|
|
804
944
|
while (Date.now() < deadline) {
|
|
805
945
|
try {
|
|
806
946
|
last = await this.health();
|
|
807
|
-
if (last.userdataRan) return last;
|
|
808
947
|
} catch (err) {
|
|
809
948
|
lastErr = err;
|
|
810
949
|
}
|
|
950
|
+
if (last?.userdataRan) {
|
|
951
|
+
if (last.userdataExitCode !== void 0 && last.userdataExitCode !== 0) {
|
|
952
|
+
throw new Error(`userdata failed with exit code ${last.userdataExitCode}`);
|
|
953
|
+
}
|
|
954
|
+
return last;
|
|
955
|
+
}
|
|
811
956
|
await sleep(intervalMs);
|
|
812
957
|
}
|
|
813
958
|
throw new Error(
|
|
@@ -839,6 +984,25 @@ var VM = class {
|
|
|
839
984
|
async restore() {
|
|
840
985
|
await this.transport.request("POST", `/vm/${encodeURIComponent(this.hostname)}/restore`);
|
|
841
986
|
}
|
|
987
|
+
/**
|
|
988
|
+
* Commit a stopped persistent VM disk into an immutable parent.
|
|
989
|
+
*/
|
|
990
|
+
async commit(opts = {}) {
|
|
991
|
+
const cacheKey = opts.cacheKey?.trim();
|
|
992
|
+
const tags = opts.tags && opts.tags.length > 0 ? opts.tags : void 0;
|
|
993
|
+
const labels = opts.labels && Object.keys(opts.labels).length > 0 ? opts.labels : void 0;
|
|
994
|
+
const body = tags !== void 0 || labels !== void 0 || cacheKey ? {
|
|
995
|
+
...tags !== void 0 && { tags },
|
|
996
|
+
...labels !== void 0 && { labels },
|
|
997
|
+
...cacheKey && { cache_key: cacheKey }
|
|
998
|
+
} : void 0;
|
|
999
|
+
const wire = await this.transport.request(
|
|
1000
|
+
"POST",
|
|
1001
|
+
`/vm/${encodeURIComponent(this.hostname)}/commit`,
|
|
1002
|
+
body
|
|
1003
|
+
);
|
|
1004
|
+
return new CommittedVM(this.transport, this.hostGroup, vmCommitFromWire(wire));
|
|
1005
|
+
}
|
|
842
1006
|
// --- port forwarding ---------------------------------------------------
|
|
843
1007
|
/**
|
|
844
1008
|
* Open one or more port forwards from the host to this VM. Each spec follows
|
|
@@ -958,6 +1122,23 @@ function sleep(ms) {
|
|
|
958
1122
|
function errMsg(e) {
|
|
959
1123
|
return e instanceof Error ? e.message : String(e);
|
|
960
1124
|
}
|
|
1125
|
+
async function forkPath(transport, commitId, opts) {
|
|
1126
|
+
const id = validateCommitId(commitId);
|
|
1127
|
+
const request = buildForkRequest(opts);
|
|
1128
|
+
const wire = await transport.request(
|
|
1129
|
+
"POST",
|
|
1130
|
+
`/vm/commits/${encodeURIComponent(id)}/fork?${request.query}`,
|
|
1131
|
+
request.body
|
|
1132
|
+
);
|
|
1133
|
+
return vmForkFromWire(wire);
|
|
1134
|
+
}
|
|
1135
|
+
function validateCommitId(commitId) {
|
|
1136
|
+
const value = commitId.trim();
|
|
1137
|
+
if (!value || value === "." || value === ".." || value.includes("..") || value.includes("/") || value.includes("\\") || value.includes("\0")) {
|
|
1138
|
+
throw new Error(`invalid commit ID ${JSON.stringify(value)}`);
|
|
1139
|
+
}
|
|
1140
|
+
return value;
|
|
1141
|
+
}
|
|
961
1142
|
var VMBg = class {
|
|
962
1143
|
constructor(transport, hostname) {
|
|
963
1144
|
this.transport = transport;
|
|
@@ -1216,6 +1397,47 @@ var VMsAPI = class {
|
|
|
1216
1397
|
return createVMResFromWire(wire);
|
|
1217
1398
|
}
|
|
1218
1399
|
};
|
|
1400
|
+
var CommitsAPI = class {
|
|
1401
|
+
constructor(transport) {
|
|
1402
|
+
this.transport = transport;
|
|
1403
|
+
}
|
|
1404
|
+
transport;
|
|
1405
|
+
async list(opts = {}) {
|
|
1406
|
+
const qs = new URLSearchParams();
|
|
1407
|
+
for (const tag of opts.tags ?? []) {
|
|
1408
|
+
const value = tag.trim();
|
|
1409
|
+
if (value) qs.append("tag", value);
|
|
1410
|
+
}
|
|
1411
|
+
const cacheKey = opts.cacheKey?.trim();
|
|
1412
|
+
const source = opts.source?.trim();
|
|
1413
|
+
const mode = opts.mode?.trim();
|
|
1414
|
+
if (cacheKey) qs.set("cache_key", cacheKey);
|
|
1415
|
+
if (source) qs.set("source", source);
|
|
1416
|
+
if (mode) qs.set("mode", mode);
|
|
1417
|
+
const encoded = qs.toString();
|
|
1418
|
+
const query = encoded ? `?${encoded}` : "";
|
|
1419
|
+
const wire = await this.transport.request("GET", `/vm/commits${query}`);
|
|
1420
|
+
return (wire ?? []).map(vmCommitInfoFromWire);
|
|
1421
|
+
}
|
|
1422
|
+
async delete(commitId) {
|
|
1423
|
+
const id = validateCommitId2(commitId);
|
|
1424
|
+
const wire = await this.transport.request(
|
|
1425
|
+
"DELETE",
|
|
1426
|
+
`/vm/commits/${encodeURIComponent(id)}`
|
|
1427
|
+
);
|
|
1428
|
+
return vmCommitDeleteFromWire(wire);
|
|
1429
|
+
}
|
|
1430
|
+
async fork(commitId, opts = {}) {
|
|
1431
|
+
const id = validateCommitId2(commitId);
|
|
1432
|
+
const request = buildForkRequest(opts);
|
|
1433
|
+
const wire = await this.transport.request(
|
|
1434
|
+
"POST",
|
|
1435
|
+
`/vm/commits/${encodeURIComponent(id)}/fork?${request.query}`,
|
|
1436
|
+
request.body
|
|
1437
|
+
);
|
|
1438
|
+
return vmForkFromWire(wire);
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1219
1441
|
var SecretsAPI = class {
|
|
1220
1442
|
constructor(transport) {
|
|
1221
1443
|
this.transport = transport;
|
|
@@ -1262,10 +1484,18 @@ function buildListQuery(opts) {
|
|
|
1262
1484
|
const s = qs.toString();
|
|
1263
1485
|
return s ? `?${s}` : "";
|
|
1264
1486
|
}
|
|
1487
|
+
function validateCommitId2(commitId) {
|
|
1488
|
+
const value = commitId.trim();
|
|
1489
|
+
if (!value || value === "." || value === ".." || value.includes("..") || value.includes("/") || value.includes("\\") || value.includes("\0")) {
|
|
1490
|
+
throw new Error(`invalid commit ID ${JSON.stringify(value)}`);
|
|
1491
|
+
}
|
|
1492
|
+
return value;
|
|
1493
|
+
}
|
|
1265
1494
|
|
|
1266
1495
|
// src/proxy.ts
|
|
1267
1496
|
var ProxySecretBearer = "bearer";
|
|
1268
1497
|
var ProxySecretBasic = "basic";
|
|
1498
|
+
var ProxySecretOAuthClientCredentials = "oauth-client-creds";
|
|
1269
1499
|
function clientFromWire(w) {
|
|
1270
1500
|
return { name: w.name, createdAt: w.created_at };
|
|
1271
1501
|
}
|
|
@@ -1422,12 +1652,14 @@ var SlicerClient = class _SlicerClient {
|
|
|
1422
1652
|
transport;
|
|
1423
1653
|
hostGroups;
|
|
1424
1654
|
vms;
|
|
1655
|
+
commits;
|
|
1425
1656
|
secrets;
|
|
1426
1657
|
proxy;
|
|
1427
1658
|
constructor(opts) {
|
|
1428
1659
|
this.transport = new TransportClient(opts);
|
|
1429
1660
|
this.hostGroups = new HostGroupsAPI(this.transport);
|
|
1430
1661
|
this.vms = new VMsAPI(this.transport);
|
|
1662
|
+
this.commits = new CommitsAPI(this.transport);
|
|
1431
1663
|
this.secrets = new SecretsAPI(this.transport);
|
|
1432
1664
|
this.proxy = new ProxyAPI(this.transport);
|
|
1433
1665
|
}
|
|
@@ -1586,6 +1818,8 @@ var SlicerShellSession = class {
|
|
|
1586
1818
|
}
|
|
1587
1819
|
};
|
|
1588
1820
|
|
|
1821
|
+
exports.CommitsAPI = CommitsAPI;
|
|
1822
|
+
exports.CommittedVM = CommittedVM;
|
|
1589
1823
|
exports.ExecStdioBase64 = ExecStdioBase64;
|
|
1590
1824
|
exports.ExecStdioText = ExecStdioText;
|
|
1591
1825
|
exports.FRAME_TYPE_DATA = FRAME_TYPE_DATA;
|
|
@@ -1603,6 +1837,7 @@ exports.ProxyAllowsAPI = ProxyAllowsAPI;
|
|
|
1603
1837
|
exports.ProxyClientsAPI = ProxyClientsAPI;
|
|
1604
1838
|
exports.ProxySecretBasic = ProxySecretBasic;
|
|
1605
1839
|
exports.ProxySecretBearer = ProxySecretBearer;
|
|
1840
|
+
exports.ProxySecretOAuthClientCredentials = ProxySecretOAuthClientCredentials;
|
|
1606
1841
|
exports.ProxySecretsAPI = ProxySecretsAPI;
|
|
1607
1842
|
exports.SecretExistsError = SecretExistsError;
|
|
1608
1843
|
exports.SecretsAPI = SecretsAPI;
|