@slicervm/sdk 0.1.6 → 0.1.8

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/dist/index.js CHANGED
@@ -255,6 +255,7 @@ function createVMReqToWire(r) {
255
255
  if (r.ip !== void 0) o.ip = r.ip;
256
256
  if (r.tags !== void 0) o.tags = r.tags;
257
257
  if (r.secrets !== void 0) o.secrets = r.secrets;
258
+ if (r.network !== void 0) o.network = r.network;
258
259
  return o;
259
260
  }
260
261
  function createVMResFromWire(w) {
@@ -272,7 +273,71 @@ function agentHealthFromWire(w) {
272
273
  ...w.agent_uptime !== void 0 && { agentUptime: w.agent_uptime },
273
274
  ...w.agent_version !== void 0 && { agentVersion: w.agent_version },
274
275
  ...w.system_uptime !== void 0 && { systemUptime: w.system_uptime },
275
- ...w.userdata_ran !== void 0 && { userdataRan: w.userdata_ran }
276
+ ...w.userdata_ran !== void 0 && { userdataRan: w.userdata_ran },
277
+ ...w.userdata_exit_code !== void 0 && { userdataExitCode: w.userdata_exit_code }
278
+ };
279
+ }
280
+ function vmCommitFromWire(w) {
281
+ return {
282
+ hostname: w.hostname,
283
+ commitId: w.commit_id,
284
+ status: w.status,
285
+ ...w.parent_status !== void 0 && { parentStatus: w.parent_status },
286
+ mode: w.mode ?? "",
287
+ ...w.tags !== void 0 && { tags: w.tags },
288
+ ...w.labels !== void 0 && { labels: w.labels },
289
+ ...w.cache_key !== void 0 && { cacheKey: w.cache_key }
290
+ };
291
+ }
292
+ function vmCommitInfoFromWire(w) {
293
+ return {
294
+ commitId: w.commit_id,
295
+ sourceHostname: w.source_hostname,
296
+ sourceHostGroup: w.source_host_group,
297
+ createdAt: w.created_at,
298
+ mode: w.mode,
299
+ ...w.tags !== void 0 && { tags: w.tags },
300
+ ...w.labels !== void 0 && { labels: w.labels },
301
+ ...w.cache_key !== void 0 && { cacheKey: w.cache_key }
302
+ };
303
+ }
304
+ function vmCommitDeleteFromWire(w) {
305
+ return { commitId: w.commit_id, status: w.status };
306
+ }
307
+ function vmForkFromWire(w) {
308
+ return {
309
+ hostname: w.hostname,
310
+ sourceHostname: w.source_hostname,
311
+ ...w.commit_id !== void 0 && { commitId: w.commit_id },
312
+ status: w.status,
313
+ ...w.parent_status !== void 0 && { parentStatus: w.parent_status },
314
+ ...w.child_status !== void 0 && { childStatus: w.child_status },
315
+ mode: w.mode ?? ""
316
+ };
317
+ }
318
+ function vmDescriptionFromWire(w) {
319
+ return {
320
+ hostname: w.hostname,
321
+ ...w.hostgroup !== void 0 && { hostGroup: w.hostgroup },
322
+ ...w.ip !== void 0 && { ip: w.ip },
323
+ ...w.ram_bytes !== void 0 && { ramBytes: w.ram_bytes },
324
+ ...w.cpus !== void 0 && { cpus: w.cpus },
325
+ ...w.created_at !== void 0 && { createdAt: w.created_at },
326
+ ...w.arch !== void 0 && { arch: w.arch },
327
+ ...w.tags !== void 0 && { tags: w.tags },
328
+ ...w.status !== void 0 && { status: w.status },
329
+ ...w.persistent !== void 0 && { persistent: w.persistent },
330
+ ...w.storage !== void 0 && { storage: w.storage },
331
+ ...w.image !== void 0 && { image: w.image },
332
+ ...w.commit_id !== void 0 && { commitId: w.commit_id },
333
+ ...w.parent_commit_id !== void 0 && { parentCommitId: w.parent_commit_id },
334
+ network: {
335
+ ...w.network.mode !== void 0 && { mode: w.network.mode },
336
+ ...w.network.policy_source !== void 0 && { policySource: w.network.policy_source },
337
+ hostGroup: w.network.host_group,
338
+ ...w.network.override !== void 0 && { override: w.network.override },
339
+ effective: w.network.effective
340
+ }
276
341
  };
277
342
  }
278
343
  function fsEntryFromWire(w) {
@@ -727,6 +792,48 @@ var VMFileSystem = class {
727
792
  );
728
793
  }
729
794
  };
795
+ var CommittedVM = class {
796
+ constructor(transport, hostGroup, response) {
797
+ this.transport = transport;
798
+ this.hostname = response.hostname;
799
+ this.hostGroup = hostGroup;
800
+ this.commitId = response.commitId;
801
+ this.status = response.status;
802
+ if (response.parentStatus !== void 0) this.parentStatus = response.parentStatus;
803
+ this.mode = response.mode;
804
+ if (response.tags !== void 0) this.tags = response.tags;
805
+ if (response.labels !== void 0) this.labels = response.labels;
806
+ if (response.cacheKey !== void 0) this.cacheKey = response.cacheKey;
807
+ }
808
+ transport;
809
+ hostname;
810
+ hostGroup;
811
+ commitId;
812
+ status;
813
+ parentStatus;
814
+ mode;
815
+ tags;
816
+ labels;
817
+ cacheKey;
818
+ async fork(opts = {}) {
819
+ const res = await forkPath(
820
+ this.transport,
821
+ this.commitId,
822
+ opts
823
+ );
824
+ return new VM(this.transport, {
825
+ hostname: res.hostname,
826
+ hostGroup: this.hostGroup
827
+ });
828
+ }
829
+ async forkRaw(opts = {}) {
830
+ return forkPath(
831
+ this.transport,
832
+ this.commitId,
833
+ opts
834
+ );
835
+ }
836
+ };
730
837
  var VM = class {
731
838
  hostname;
732
839
  hostGroup;
@@ -766,6 +873,14 @@ var VM = class {
766
873
  async logs() {
767
874
  return this.transport.request("GET", `/vm/${encodeURIComponent(this.hostname)}/logs`);
768
875
  }
876
+ /** Return configuration, fork lineage, and effective network policy. */
877
+ async describe() {
878
+ const wire = await this.transport.request(
879
+ "GET",
880
+ `/vm/${encodeURIComponent(this.hostname)}`
881
+ );
882
+ return vmDescriptionFromWire(wire);
883
+ }
769
884
  async waitForAgent(opts = {}) {
770
885
  const timeoutMs = opts.timeoutMs ?? 12e4;
771
886
  const intervalMs = opts.intervalMs ?? 500;
@@ -792,10 +907,15 @@ var VM = class {
792
907
  while (Date.now() < deadline) {
793
908
  try {
794
909
  last = await this.health();
795
- if (last.userdataRan) return last;
796
910
  } catch (err) {
797
911
  lastErr = err;
798
912
  }
913
+ if (last?.userdataRan) {
914
+ if (last.userdataExitCode !== void 0 && last.userdataExitCode !== 0) {
915
+ throw new Error(`userdata failed with exit code ${last.userdataExitCode}`);
916
+ }
917
+ return last;
918
+ }
799
919
  await sleep(intervalMs);
800
920
  }
801
921
  throw new Error(
@@ -827,6 +947,25 @@ var VM = class {
827
947
  async restore() {
828
948
  await this.transport.request("POST", `/vm/${encodeURIComponent(this.hostname)}/restore`);
829
949
  }
950
+ /**
951
+ * Commit a stopped persistent VM disk into an immutable parent.
952
+ */
953
+ async commit(opts = {}) {
954
+ const cacheKey = opts.cacheKey?.trim();
955
+ const tags = opts.tags && opts.tags.length > 0 ? opts.tags : void 0;
956
+ const labels = opts.labels && Object.keys(opts.labels).length > 0 ? opts.labels : void 0;
957
+ const body = tags !== void 0 || labels !== void 0 || cacheKey ? {
958
+ ...tags !== void 0 && { tags },
959
+ ...labels !== void 0 && { labels },
960
+ ...cacheKey && { cache_key: cacheKey }
961
+ } : void 0;
962
+ const wire = await this.transport.request(
963
+ "POST",
964
+ `/vm/${encodeURIComponent(this.hostname)}/commit`,
965
+ body
966
+ );
967
+ return new CommittedVM(this.transport, this.hostGroup, vmCommitFromWire(wire));
968
+ }
830
969
  // --- port forwarding ---------------------------------------------------
831
970
  /**
832
971
  * Open one or more port forwards from the host to this VM. Each spec follows
@@ -946,6 +1085,32 @@ function sleep(ms) {
946
1085
  function errMsg(e) {
947
1086
  return e instanceof Error ? e.message : String(e);
948
1087
  }
1088
+ async function forkPath(transport, commitId, opts) {
1089
+ const id = validateCommitId(commitId);
1090
+ const q = new URLSearchParams();
1091
+ q.set("wait", "agent");
1092
+ if (opts.waitTimeoutSec !== void 0 && opts.waitTimeoutSec > 0) {
1093
+ q.set("timeout", `${opts.waitTimeoutSec}s`);
1094
+ }
1095
+ const query = q.toString() ? `?${q.toString()}` : "";
1096
+ const body = opts.network !== void 0 || (opts.tags?.length ?? 0) > 0 ? {
1097
+ ...opts.network !== void 0 && { network: opts.network },
1098
+ ...(opts.tags?.length ?? 0) > 0 && { tags: opts.tags }
1099
+ } : void 0;
1100
+ const wire = await transport.request(
1101
+ "POST",
1102
+ `/vm/commits/${encodeURIComponent(id)}/fork${query}`,
1103
+ body
1104
+ );
1105
+ return vmForkFromWire(wire);
1106
+ }
1107
+ function validateCommitId(commitId) {
1108
+ const value = commitId.trim();
1109
+ if (!value || value === "." || value === ".." || value.includes("..") || value.includes("/") || value.includes("\\") || value.includes("\0")) {
1110
+ throw new Error(`invalid commit ID ${JSON.stringify(value)}`);
1111
+ }
1112
+ return value;
1113
+ }
949
1114
  var VMBg = class {
950
1115
  constructor(transport, hostname) {
951
1116
  this.transport = transport;
@@ -1204,6 +1369,54 @@ var VMsAPI = class {
1204
1369
  return createVMResFromWire(wire);
1205
1370
  }
1206
1371
  };
1372
+ var CommitsAPI = class {
1373
+ constructor(transport) {
1374
+ this.transport = transport;
1375
+ }
1376
+ transport;
1377
+ async list(opts = {}) {
1378
+ const qs = new URLSearchParams();
1379
+ for (const tag of opts.tags ?? []) {
1380
+ const value = tag.trim();
1381
+ if (value) qs.append("tag", value);
1382
+ }
1383
+ const cacheKey = opts.cacheKey?.trim();
1384
+ const source = opts.source?.trim();
1385
+ const mode = opts.mode?.trim();
1386
+ if (cacheKey) qs.set("cache_key", cacheKey);
1387
+ if (source) qs.set("source", source);
1388
+ if (mode) qs.set("mode", mode);
1389
+ const encoded = qs.toString();
1390
+ const query = encoded ? `?${encoded}` : "";
1391
+ const wire = await this.transport.request("GET", `/vm/commits${query}`);
1392
+ return (wire ?? []).map(vmCommitInfoFromWire);
1393
+ }
1394
+ async delete(commitId) {
1395
+ const id = validateCommitId2(commitId);
1396
+ const wire = await this.transport.request(
1397
+ "DELETE",
1398
+ `/vm/commits/${encodeURIComponent(id)}`
1399
+ );
1400
+ return vmCommitDeleteFromWire(wire);
1401
+ }
1402
+ async fork(commitId, opts = {}) {
1403
+ const id = validateCommitId2(commitId);
1404
+ const qs = new URLSearchParams({ wait: "agent" });
1405
+ if (opts.waitTimeoutSec !== void 0 && opts.waitTimeoutSec > 0) {
1406
+ qs.set("timeout", `${opts.waitTimeoutSec}s`);
1407
+ }
1408
+ const body = opts.network !== void 0 || (opts.tags?.length ?? 0) > 0 ? {
1409
+ ...opts.network !== void 0 && { network: opts.network },
1410
+ ...(opts.tags?.length ?? 0) > 0 && { tags: opts.tags }
1411
+ } : void 0;
1412
+ const wire = await this.transport.request(
1413
+ "POST",
1414
+ `/vm/commits/${encodeURIComponent(id)}/fork?${qs.toString()}`,
1415
+ body
1416
+ );
1417
+ return vmForkFromWire(wire);
1418
+ }
1419
+ };
1207
1420
  var SecretsAPI = class {
1208
1421
  constructor(transport) {
1209
1422
  this.transport = transport;
@@ -1250,18 +1463,184 @@ function buildListQuery(opts) {
1250
1463
  const s = qs.toString();
1251
1464
  return s ? `?${s}` : "";
1252
1465
  }
1466
+ function validateCommitId2(commitId) {
1467
+ const value = commitId.trim();
1468
+ if (!value || value === "." || value === ".." || value.includes("..") || value.includes("/") || value.includes("\\") || value.includes("\0")) {
1469
+ throw new Error(`invalid commit ID ${JSON.stringify(value)}`);
1470
+ }
1471
+ return value;
1472
+ }
1473
+
1474
+ // src/proxy.ts
1475
+ var ProxySecretBearer = "bearer";
1476
+ var ProxySecretBasic = "basic";
1477
+ var ProxySecretOAuthClientCredentials = "oauth-client-creds";
1478
+ function clientFromWire(w) {
1479
+ return { name: w.name, createdAt: w.created_at };
1480
+ }
1481
+ function clientCreatedFromWire(w) {
1482
+ return { name: w.name, token: w.token, createdAt: w.created_at };
1483
+ }
1484
+ function secretFromWire2(w) {
1485
+ const out = { name: w.name, host: w.host, createdAt: w.created_at };
1486
+ if (w.type) out.type = w.type;
1487
+ return out;
1488
+ }
1489
+ function ruleFromWire(w) {
1490
+ const out = { host: w.host };
1491
+ if (w.secret) out.secret = w.secret;
1492
+ if (w.methods && w.methods.length > 0) out.methods = w.methods;
1493
+ if (w.paths && w.paths.length > 0) out.paths = w.paths;
1494
+ if (w.expires && w.expires !== "0001-01-01T00:00:00Z") out.expires = w.expires;
1495
+ if (w.passthrough) out.passthrough = w.passthrough;
1496
+ return out;
1497
+ }
1498
+ var ProxyAPI = class {
1499
+ constructor(transport) {
1500
+ this.transport = transport;
1501
+ this.clients = new ProxyClientsAPI(transport);
1502
+ this.secrets = new ProxySecretsAPI(transport);
1503
+ this.allows = new ProxyAllowsAPI(transport);
1504
+ }
1505
+ transport;
1506
+ clients;
1507
+ secrets;
1508
+ allows;
1509
+ };
1510
+ var ProxyClientsAPI = class {
1511
+ constructor(transport) {
1512
+ this.transport = transport;
1513
+ }
1514
+ transport;
1515
+ /** Mint a new proxy client. The returned token is shown once. */
1516
+ async create(name, opts = {}) {
1517
+ const body = { name };
1518
+ if (opts.token) body.token = opts.token;
1519
+ const wire = await this.transport.request(
1520
+ "POST",
1521
+ "/proxy/v1/clients",
1522
+ body
1523
+ );
1524
+ return clientCreatedFromWire(wire);
1525
+ }
1526
+ async list() {
1527
+ const wire = await this.transport.request("GET", "/proxy/v1/clients");
1528
+ return (wire ?? []).map(clientFromWire);
1529
+ }
1530
+ /**
1531
+ * Revoke the token, drop every allow rule the client owned, and
1532
+ * remove the client.
1533
+ */
1534
+ async delete(name) {
1535
+ await this.transport.request("DELETE", `/proxy/v1/clients/${encodeURIComponent(name)}`);
1536
+ }
1537
+ /** List a client's allow rules in declaration order (first-match-wins). */
1538
+ async rules(name) {
1539
+ const wire = await this.transport.request(
1540
+ "GET",
1541
+ `/proxy/v1/clients/${encodeURIComponent(name)}`
1542
+ );
1543
+ return (wire ?? []).map(ruleFromWire);
1544
+ }
1545
+ };
1546
+ var ProxySecretsAPI = class {
1547
+ constructor(transport) {
1548
+ this.transport = transport;
1549
+ }
1550
+ transport;
1551
+ async create(req) {
1552
+ const body = {
1553
+ name: req.name,
1554
+ host: req.host,
1555
+ value: req.value
1556
+ };
1557
+ if (req.type) body.type = req.type;
1558
+ await this.transport.request("POST", "/proxy/v1/secrets", body);
1559
+ }
1560
+ async list() {
1561
+ const wire = await this.transport.request("GET", "/proxy/v1/secrets");
1562
+ return (wire ?? []).map(secretFromWire2);
1563
+ }
1564
+ /**
1565
+ * Remove a secret. Allow rules that reference it stop matching until
1566
+ * the secret is recreated or the rule is rewritten.
1567
+ */
1568
+ async delete(name) {
1569
+ await this.transport.request("DELETE", `/proxy/v1/secrets/${encodeURIComponent(name)}`);
1570
+ }
1571
+ };
1572
+ var ProxyAllowsAPI = class {
1573
+ constructor(transport) {
1574
+ this.transport = transport;
1575
+ }
1576
+ transport;
1577
+ /** Add an allow rule. Returns the resolved rule with absolute `expires`. */
1578
+ async add(req) {
1579
+ const body = {
1580
+ client: req.client,
1581
+ host: req.host
1582
+ };
1583
+ if (req.secret) body.secret = req.secret;
1584
+ if (req.methods && req.methods.length > 0) body.methods = req.methods;
1585
+ if (req.paths && req.paths.length > 0) body.paths = req.paths;
1586
+ if (req.ttlSeconds && req.ttlSeconds > 0) body.ttl_seconds = req.ttlSeconds;
1587
+ if (req.passthrough) body.passthrough = true;
1588
+ const wire = await this.transport.request(
1589
+ "POST",
1590
+ "/proxy/v1/allows",
1591
+ body
1592
+ );
1593
+ return ruleFromWire(wire);
1594
+ }
1595
+ /**
1596
+ * Host-bulk revoke: removes **every** rule on the client whose host
1597
+ * matches. For surgical removal of one rule among siblings on the
1598
+ * same host (e.g. several path-scoped rules on `github.com`), use
1599
+ * `removeByTuple` instead.
1600
+ */
1601
+ async remove(client, host) {
1602
+ await this.transport.request(
1603
+ "DELETE",
1604
+ `/proxy/v1/allows/${encodeURIComponent(client)}/${encodeURIComponent(host)}`
1605
+ );
1606
+ }
1607
+ /**
1608
+ * Surgical revoke: removes the single rule whose
1609
+ * (host, methods, paths, passthrough) tuple matches the request.
1610
+ * Pass exactly the same fields you used at create time. Method and
1611
+ * host casing are normalised server-side, so `"GET"` / `"get"` and
1612
+ * `"github.com"` / `"GITHUB.COM"` all match the same stored rule.
1613
+ *
1614
+ * Returns 404 (surfaced as a SlicerAPIError) when no rule matches.
1615
+ */
1616
+ async removeByTuple(req) {
1617
+ const body = {
1618
+ client: req.client,
1619
+ host: req.host
1620
+ };
1621
+ if (req.secret) body.secret = req.secret;
1622
+ if (req.methods && req.methods.length > 0) body.methods = req.methods;
1623
+ if (req.paths && req.paths.length > 0) body.paths = req.paths;
1624
+ if (req.passthrough) body.passthrough = true;
1625
+ await this.transport.request("POST", "/proxy/v1/allows/revoke", body);
1626
+ }
1627
+ };
1253
1628
 
1254
1629
  // src/client.ts
1255
1630
  var SlicerClient = class _SlicerClient {
1256
1631
  transport;
1257
1632
  hostGroups;
1258
1633
  vms;
1634
+ commits;
1259
1635
  secrets;
1636
+ proxy;
1260
1637
  constructor(opts) {
1261
1638
  this.transport = new TransportClient(opts);
1262
1639
  this.hostGroups = new HostGroupsAPI(this.transport);
1263
1640
  this.vms = new VMsAPI(this.transport);
1641
+ this.commits = new CommitsAPI(this.transport);
1264
1642
  this.secrets = new SecretsAPI(this.transport);
1643
+ this.proxy = new ProxyAPI(this.transport);
1265
1644
  }
1266
1645
  static fromEnv(overrides = {}) {
1267
1646
  const baseURL = overrides.baseURL ?? process.env.SLICER_URL;
@@ -1418,6 +1797,6 @@ var SlicerShellSession = class {
1418
1797
  }
1419
1798
  };
1420
1799
 
1421
- export { ExecStdioBase64, ExecStdioText, FRAME_TYPE_DATA, FRAME_TYPE_HEARTBEAT, FRAME_TYPE_SESSION_CLOSE, FRAME_TYPE_SHUTDOWN, FRAME_TYPE_WINDOW_SIZE, Forwarder, GiB, HostGroupsAPI, MiB, NonRootUser, SecretExistsError, SecretsAPI, SlicerAPIError, SlicerClient, SlicerShellSession, VM, VMBg, VMFileSystem, VMsAPI, encodeFrame, parseAddressMapping, parseFrame, resolveTransport };
1800
+ export { CommitsAPI, CommittedVM, ExecStdioBase64, ExecStdioText, FRAME_TYPE_DATA, FRAME_TYPE_HEARTBEAT, FRAME_TYPE_SESSION_CLOSE, FRAME_TYPE_SHUTDOWN, FRAME_TYPE_WINDOW_SIZE, Forwarder, GiB, HostGroupsAPI, MiB, NonRootUser, ProxyAPI, ProxyAllowsAPI, ProxyClientsAPI, ProxySecretBasic, ProxySecretBearer, ProxySecretOAuthClientCredentials, ProxySecretsAPI, SecretExistsError, SecretsAPI, SlicerAPIError, SlicerClient, SlicerShellSession, VM, VMBg, VMFileSystem, VMsAPI, encodeFrame, parseAddressMapping, parseFrame, resolveTransport };
1422
1801
  //# sourceMappingURL=index.js.map
1423
1802
  //# sourceMappingURL=index.js.map