@zgeoff/imp-client 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/index.d.ts +204 -0
- package/dist/index.js +23 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# @zgeoff/imp-client
|
|
2
2
|
|
|
3
3
|
A typed client for [impd](https://github.com/zgeoff/imp), the imp host daemon. It runs in Bun, in
|
|
4
|
-
Node 22 or later, and in browsers with
|
|
5
|
-
17.4 or later.
|
|
4
|
+
binaries that `bun build --compile` makes, in Node 22.6 or later, and in browsers with
|
|
5
|
+
`Promise.withResolvers`: Chrome 119, Firefox 121 and Safari 17.4 or later. CI checks each release
|
|
6
|
+
under Node, Bun and a compiled Bun binary.
|
|
6
7
|
|
|
7
8
|
```sh
|
|
8
9
|
npm install @zgeoff/imp-client
|
|
@@ -123,7 +124,8 @@ the command did not run to its exit, `exit` rejects with an `ExecError` whose `c
|
|
|
123
124
|
in the table below, plus `EXEC_FAILED` when the command cannot start) or one of `UNAUTHORIZED` (also
|
|
124
125
|
for an exec ticket that expired or was used), `UNREACHABLE`, `RESTARTING`, `CONNECTION_CLOSED`,
|
|
125
126
|
`DETACHED`, `BAD_MESSAGE`, `CLOSED`, `OUTPUT_OVERFLOW` and `LOCAL_ERROR`. Its `data` is impd's error
|
|
126
|
-
data.
|
|
127
|
+
data. An abort before the command starts, during the ticket call or the connect, rejects with the
|
|
128
|
+
abort's reason instead, an `AbortError` by default; after the start it ends the session as `CLOSED`.
|
|
127
129
|
|
|
128
130
|
## Errors
|
|
129
131
|
|
package/dist/index.d.ts
CHANGED
|
@@ -255,6 +255,14 @@ declare const impContract: {
|
|
|
255
255
|
memoryMib: z.ZodOptional<z.ZodInt>;
|
|
256
256
|
diskMib: z.ZodOptional<z.ZodInt>;
|
|
257
257
|
httpPort: z.ZodOptional<z.ZodInt>;
|
|
258
|
+
policy: z.ZodOptional<z.ZodObject<{
|
|
259
|
+
mode: z.ZodEnum<{
|
|
260
|
+
box: "box";
|
|
261
|
+
none: "none";
|
|
262
|
+
open: "open";
|
|
263
|
+
}>;
|
|
264
|
+
allow: z.ZodDefault<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
|
|
265
|
+
}, z.core.$strip>>;
|
|
258
266
|
}, z.core.$strip>, z.ZodObject<{
|
|
259
267
|
id: z.ZodString;
|
|
260
268
|
name: z.ZodString;
|
|
@@ -1584,6 +1592,202 @@ declare const impContract: {
|
|
|
1584
1592
|
readonly status: 409;
|
|
1585
1593
|
};
|
|
1586
1594
|
}>, Record<never, never>>;
|
|
1595
|
+
policy: import("@orpc/contract").ContractProcedureBuilderWithInputOutput<z.ZodObject<{
|
|
1596
|
+
name: z.ZodString;
|
|
1597
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1598
|
+
mode: z.ZodEnum<{
|
|
1599
|
+
box: "box";
|
|
1600
|
+
none: "none";
|
|
1601
|
+
open: "open";
|
|
1602
|
+
}>;
|
|
1603
|
+
allow: z.ZodDefault<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
|
|
1604
|
+
}, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, {
|
|
1605
|
+
NOT_FOUND: {
|
|
1606
|
+
readonly message: "Not found";
|
|
1607
|
+
readonly data: z.ZodObject<{
|
|
1608
|
+
kind: z.ZodEnum<{
|
|
1609
|
+
backup: "backup";
|
|
1610
|
+
checkpoint: "checkpoint";
|
|
1611
|
+
grant: "grant";
|
|
1612
|
+
image: "image";
|
|
1613
|
+
imp: "imp";
|
|
1614
|
+
secret: "secret";
|
|
1615
|
+
session: "session";
|
|
1616
|
+
token: "token";
|
|
1617
|
+
}>;
|
|
1618
|
+
name: z.ZodString;
|
|
1619
|
+
}, z.core.$strip>;
|
|
1620
|
+
};
|
|
1621
|
+
CONFLICT: {
|
|
1622
|
+
readonly message: "Already exists";
|
|
1623
|
+
readonly data: z.ZodObject<{
|
|
1624
|
+
kind: z.ZodEnum<{
|
|
1625
|
+
backup: "backup";
|
|
1626
|
+
checkpoint: "checkpoint";
|
|
1627
|
+
grant: "grant";
|
|
1628
|
+
image: "image";
|
|
1629
|
+
imp: "imp";
|
|
1630
|
+
secret: "secret";
|
|
1631
|
+
session: "session";
|
|
1632
|
+
token: "token";
|
|
1633
|
+
}>;
|
|
1634
|
+
name: z.ZodString;
|
|
1635
|
+
}, z.core.$strip>;
|
|
1636
|
+
};
|
|
1637
|
+
FORBIDDEN: {
|
|
1638
|
+
readonly message: "Not allowed";
|
|
1639
|
+
};
|
|
1640
|
+
PRECONDITION_FAILED: {
|
|
1641
|
+
readonly message: "Not possible on this host";
|
|
1642
|
+
};
|
|
1643
|
+
RAM_BUDGET_EXCEEDED: {
|
|
1644
|
+
readonly message: "Not enough RAM budget, even after sleeping idle imps";
|
|
1645
|
+
readonly status: 503;
|
|
1646
|
+
readonly data: z.ZodObject<{
|
|
1647
|
+
budgetMib: z.ZodInt;
|
|
1648
|
+
usedMib: z.ZodInt;
|
|
1649
|
+
requestedMib: z.ZodInt;
|
|
1650
|
+
}, z.core.$strip>;
|
|
1651
|
+
};
|
|
1652
|
+
SERVICE_UNAVAILABLE: {
|
|
1653
|
+
readonly message: "impd is stopping";
|
|
1654
|
+
};
|
|
1655
|
+
INVALID_STATE: {
|
|
1656
|
+
readonly message: "The imp is not in a state that allows this";
|
|
1657
|
+
readonly status: 409;
|
|
1658
|
+
readonly data: z.ZodObject<{
|
|
1659
|
+
state: z.ZodEnum<{
|
|
1660
|
+
creating: "creating";
|
|
1661
|
+
error: "error";
|
|
1662
|
+
running: "running";
|
|
1663
|
+
sleeping: "sleeping";
|
|
1664
|
+
stopped: "stopped";
|
|
1665
|
+
}>;
|
|
1666
|
+
allowed: z.ZodArray<z.ZodEnum<{
|
|
1667
|
+
creating: "creating";
|
|
1668
|
+
error: "error";
|
|
1669
|
+
running: "running";
|
|
1670
|
+
sleeping: "sleeping";
|
|
1671
|
+
stopped: "stopped";
|
|
1672
|
+
}>>;
|
|
1673
|
+
}, z.core.$strip>;
|
|
1674
|
+
};
|
|
1675
|
+
DISK_FULL: {
|
|
1676
|
+
readonly message: "Not enough free disk on the host";
|
|
1677
|
+
readonly status: 507;
|
|
1678
|
+
readonly data: z.ZodObject<{
|
|
1679
|
+
availableBytes: z.ZodInt;
|
|
1680
|
+
reserveBytes: z.ZodInt;
|
|
1681
|
+
requestedBytes: z.ZodInt;
|
|
1682
|
+
}, z.core.$strip>;
|
|
1683
|
+
};
|
|
1684
|
+
AGENT_OUTDATED: {
|
|
1685
|
+
readonly message: "The imp's agent is too old for this";
|
|
1686
|
+
readonly status: 409;
|
|
1687
|
+
};
|
|
1688
|
+
}>, Record<never, never>>;
|
|
1689
|
+
setPolicy: import("@orpc/contract").ContractProcedureBuilderWithInputOutput<z.ZodObject<{
|
|
1690
|
+
name: z.ZodString;
|
|
1691
|
+
policy: z.ZodObject<{
|
|
1692
|
+
mode: z.ZodEnum<{
|
|
1693
|
+
box: "box";
|
|
1694
|
+
none: "none";
|
|
1695
|
+
open: "open";
|
|
1696
|
+
}>;
|
|
1697
|
+
allow: z.ZodDefault<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
|
|
1698
|
+
}, z.core.$strip>;
|
|
1699
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1700
|
+
mode: z.ZodEnum<{
|
|
1701
|
+
box: "box";
|
|
1702
|
+
none: "none";
|
|
1703
|
+
open: "open";
|
|
1704
|
+
}>;
|
|
1705
|
+
allow: z.ZodDefault<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
|
|
1706
|
+
}, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, {
|
|
1707
|
+
NOT_FOUND: {
|
|
1708
|
+
readonly message: "Not found";
|
|
1709
|
+
readonly data: z.ZodObject<{
|
|
1710
|
+
kind: z.ZodEnum<{
|
|
1711
|
+
backup: "backup";
|
|
1712
|
+
checkpoint: "checkpoint";
|
|
1713
|
+
grant: "grant";
|
|
1714
|
+
image: "image";
|
|
1715
|
+
imp: "imp";
|
|
1716
|
+
secret: "secret";
|
|
1717
|
+
session: "session";
|
|
1718
|
+
token: "token";
|
|
1719
|
+
}>;
|
|
1720
|
+
name: z.ZodString;
|
|
1721
|
+
}, z.core.$strip>;
|
|
1722
|
+
};
|
|
1723
|
+
CONFLICT: {
|
|
1724
|
+
readonly message: "Already exists";
|
|
1725
|
+
readonly data: z.ZodObject<{
|
|
1726
|
+
kind: z.ZodEnum<{
|
|
1727
|
+
backup: "backup";
|
|
1728
|
+
checkpoint: "checkpoint";
|
|
1729
|
+
grant: "grant";
|
|
1730
|
+
image: "image";
|
|
1731
|
+
imp: "imp";
|
|
1732
|
+
secret: "secret";
|
|
1733
|
+
session: "session";
|
|
1734
|
+
token: "token";
|
|
1735
|
+
}>;
|
|
1736
|
+
name: z.ZodString;
|
|
1737
|
+
}, z.core.$strip>;
|
|
1738
|
+
};
|
|
1739
|
+
FORBIDDEN: {
|
|
1740
|
+
readonly message: "Not allowed";
|
|
1741
|
+
};
|
|
1742
|
+
PRECONDITION_FAILED: {
|
|
1743
|
+
readonly message: "Not possible on this host";
|
|
1744
|
+
};
|
|
1745
|
+
RAM_BUDGET_EXCEEDED: {
|
|
1746
|
+
readonly message: "Not enough RAM budget, even after sleeping idle imps";
|
|
1747
|
+
readonly status: 503;
|
|
1748
|
+
readonly data: z.ZodObject<{
|
|
1749
|
+
budgetMib: z.ZodInt;
|
|
1750
|
+
usedMib: z.ZodInt;
|
|
1751
|
+
requestedMib: z.ZodInt;
|
|
1752
|
+
}, z.core.$strip>;
|
|
1753
|
+
};
|
|
1754
|
+
SERVICE_UNAVAILABLE: {
|
|
1755
|
+
readonly message: "impd is stopping";
|
|
1756
|
+
};
|
|
1757
|
+
INVALID_STATE: {
|
|
1758
|
+
readonly message: "The imp is not in a state that allows this";
|
|
1759
|
+
readonly status: 409;
|
|
1760
|
+
readonly data: z.ZodObject<{
|
|
1761
|
+
state: z.ZodEnum<{
|
|
1762
|
+
creating: "creating";
|
|
1763
|
+
error: "error";
|
|
1764
|
+
running: "running";
|
|
1765
|
+
sleeping: "sleeping";
|
|
1766
|
+
stopped: "stopped";
|
|
1767
|
+
}>;
|
|
1768
|
+
allowed: z.ZodArray<z.ZodEnum<{
|
|
1769
|
+
creating: "creating";
|
|
1770
|
+
error: "error";
|
|
1771
|
+
running: "running";
|
|
1772
|
+
sleeping: "sleeping";
|
|
1773
|
+
stopped: "stopped";
|
|
1774
|
+
}>>;
|
|
1775
|
+
}, z.core.$strip>;
|
|
1776
|
+
};
|
|
1777
|
+
DISK_FULL: {
|
|
1778
|
+
readonly message: "Not enough free disk on the host";
|
|
1779
|
+
readonly status: 507;
|
|
1780
|
+
readonly data: z.ZodObject<{
|
|
1781
|
+
availableBytes: z.ZodInt;
|
|
1782
|
+
reserveBytes: z.ZodInt;
|
|
1783
|
+
requestedBytes: z.ZodInt;
|
|
1784
|
+
}, z.core.$strip>;
|
|
1785
|
+
};
|
|
1786
|
+
AGENT_OUTDATED: {
|
|
1787
|
+
readonly message: "The imp's agent is too old for this";
|
|
1788
|
+
readonly status: 409;
|
|
1789
|
+
};
|
|
1790
|
+
}>, Record<never, never>>;
|
|
1587
1791
|
fork: import("@orpc/contract").ContractProcedureBuilderWithInputOutput<z.ZodObject<{
|
|
1588
1792
|
source: z.ZodString;
|
|
1589
1793
|
name: z.ZodString;
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { RPCLink } from "@orpc/client/fetch";
|
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
//#endregion
|
|
5
5
|
//#region src/check-server.ts
|
|
6
|
-
const CLIENT_VERSION = "0.
|
|
6
|
+
const CLIENT_VERSION = "0.7.0";
|
|
7
7
|
async function checkServer(rpc) {
|
|
8
8
|
const info = await rpc.system.info();
|
|
9
9
|
return {
|
|
@@ -222,7 +222,8 @@ function openExecSession(options) {
|
|
|
222
222
|
const state = {
|
|
223
223
|
opened: false,
|
|
224
224
|
started: false,
|
|
225
|
-
finished: false
|
|
225
|
+
finished: false,
|
|
226
|
+
refused: false
|
|
226
227
|
};
|
|
227
228
|
ws.binaryType = "arraybuffer";
|
|
228
229
|
const stopSocket = () => {
|
|
@@ -311,6 +312,8 @@ function openExecSession(options) {
|
|
|
311
312
|
}
|
|
312
313
|
});
|
|
313
314
|
const resolveRefusal = async (reason) => {
|
|
315
|
+
if (state.refused) return;
|
|
316
|
+
state.refused = true;
|
|
314
317
|
const access = await checkImpdAccess(options.baseUrl, options.token, options.fetch);
|
|
315
318
|
if (access === "unauthorized") resolveOutcome({ kind: "unauthorized" });
|
|
316
319
|
else if (access === "reachable" && options.ticket !== void 0) resolveOutcome({
|
|
@@ -326,6 +329,9 @@ function openExecSession(options) {
|
|
|
326
329
|
reason
|
|
327
330
|
});
|
|
328
331
|
};
|
|
332
|
+
ws.addEventListener("error", () => {
|
|
333
|
+
if (!state.opened && !state.finished) resolveRefusal("the connection failed");
|
|
334
|
+
});
|
|
329
335
|
ws.addEventListener("close", (event) => {
|
|
330
336
|
if (state.finished) return;
|
|
331
337
|
const reason = event.reason === "" ? `code ${String(event.code)}` : event.reason;
|
|
@@ -461,13 +467,21 @@ async function openHandle(deps, options, tty, start) {
|
|
|
461
467
|
state.ended = true;
|
|
462
468
|
stdout.end();
|
|
463
469
|
stderr.end();
|
|
464
|
-
abort?.removeEventListener("abort",
|
|
470
|
+
abort?.removeEventListener("abort", stopForAbort);
|
|
465
471
|
if ("error" in result) started.resolve({ error: result.error });
|
|
466
472
|
ended.resolve(result);
|
|
467
473
|
};
|
|
468
474
|
const stopSession = () => {
|
|
469
475
|
stopWith("CLOSED", "the exec session was closed");
|
|
470
476
|
};
|
|
477
|
+
const stopForAbort = () => {
|
|
478
|
+
if (session.isStarted()) {
|
|
479
|
+
stopSession();
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
session.stop();
|
|
483
|
+
resolveHandle({ error: readAbortReason(abort) });
|
|
484
|
+
};
|
|
471
485
|
const waitForOutcome = async () => {
|
|
472
486
|
const outcome = await session.outcome;
|
|
473
487
|
const result = outcome.kind === "exit" ? { value: {
|
|
@@ -476,8 +490,8 @@ async function openHandle(deps, options, tty, start) {
|
|
|
476
490
|
} } : { error: toExecError(outcome) };
|
|
477
491
|
resolveHandle(result);
|
|
478
492
|
};
|
|
479
|
-
if (abort?.aborted === true)
|
|
480
|
-
else abort?.addEventListener("abort",
|
|
493
|
+
if (abort?.aborted === true) stopForAbort();
|
|
494
|
+
else abort?.addEventListener("abort", stopForAbort, { once: true });
|
|
481
495
|
waitForOutcome();
|
|
482
496
|
return buildHandle(session, {
|
|
483
497
|
started: started.promise,
|
|
@@ -488,6 +502,10 @@ async function openHandle(deps, options, tty, start) {
|
|
|
488
502
|
streams: [stdout.stream, stderr.stream]
|
|
489
503
|
});
|
|
490
504
|
}
|
|
505
|
+
function readAbortReason(abort) {
|
|
506
|
+
const reason = abort?.reason;
|
|
507
|
+
return reason instanceof Error ? reason : new DOMException("the exec was aborted", "AbortError");
|
|
508
|
+
}
|
|
491
509
|
function openConsole(deps, name, options = {}) {
|
|
492
510
|
return openExec(deps, name, [
|
|
493
511
|
"/bin/sh",
|