endia 0.0.0-stage → 0.0.2
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 +202 -0
- package/README.md +34 -2
- package/dist/archives/archive-file.d.ts +36 -0
- package/dist/archives/archive-file.js +71 -0
- package/dist/archives/build-context.d.ts +63 -0
- package/dist/archives/build-context.js +132 -0
- package/dist/archives/docker-ignore.d.ts +57 -0
- package/dist/archives/docker-ignore.js +250 -0
- package/dist/archives/fleet-archive.d.ts +33 -0
- package/dist/archives/fleet-archive.js +134 -0
- package/dist/compose-model.d.ts +32 -0
- package/dist/compose-model.js +39 -0
- package/dist/contract/archives.d.ts +14 -0
- package/dist/contract/archives.js +1 -0
- package/dist/contract/bases.d.ts +48 -0
- package/dist/contract/bases.js +1 -0
- package/dist/contract/clients.d.ts +29 -0
- package/dist/contract/clients.js +1 -0
- package/dist/contract/common.d.ts +30 -0
- package/dist/contract/common.js +1 -0
- package/dist/contract/compose.d.ts +27 -0
- package/dist/contract/compose.js +1 -0
- package/dist/contract/desktop.d.ts +269 -0
- package/dist/contract/desktop.js +1 -0
- package/dist/contract/execs.d.ts +61 -0
- package/dist/contract/execs.js +1 -0
- package/dist/contract/fleets.d.ts +136 -0
- package/dist/contract/fleets.js +1 -0
- package/dist/contract/images.d.ts +130 -0
- package/dist/contract/images.js +1 -0
- package/dist/contract/machines.d.ts +199 -0
- package/dist/contract/machines.js +1 -0
- package/dist/contract/operations.d.ts +28 -0
- package/dist/contract/operations.js +1 -0
- package/dist/contract/processes.d.ts +75 -0
- package/dist/contract/processes.js +1 -0
- package/dist/contract/purposes.d.ts +62 -0
- package/dist/contract/purposes.js +1 -0
- package/dist/contract/space.d.ts +114 -0
- package/dist/contract/space.js +1 -0
- package/dist/contract/stats.d.ts +58 -0
- package/dist/contract/stats.js +1 -0
- package/dist/contract/status.d.ts +151 -0
- package/dist/contract/status.js +1 -0
- package/dist/contract/volumes.d.ts +22 -0
- package/dist/contract/volumes.js +1 -0
- package/dist/eastindia-client.d.ts +118 -0
- package/dist/eastindia-client.js +142 -0
- package/dist/environment.d.ts +52 -0
- package/dist/environment.js +133 -0
- package/dist/errors.d.ts +81 -0
- package/dist/errors.js +108 -0
- package/dist/exec/exec-frame.d.ts +38 -0
- package/dist/exec/exec-frame.js +50 -0
- package/dist/exec/exec-session.d.ts +64 -0
- package/dist/exec/exec-session.js +117 -0
- package/dist/groups/archives.d.ts +78 -0
- package/dist/groups/archives.js +117 -0
- package/dist/groups/bases.d.ts +28 -0
- package/dist/groups/bases.js +39 -0
- package/dist/groups/certificates.d.ts +32 -0
- package/dist/groups/certificates.js +43 -0
- package/dist/groups/desktop.d.ts +143 -0
- package/dist/groups/desktop.js +182 -0
- package/dist/groups/execs.d.ts +110 -0
- package/dist/groups/execs.js +119 -0
- package/dist/groups/fleets.d.ts +206 -0
- package/dist/groups/fleets.js +255 -0
- package/dist/groups/images.d.ts +130 -0
- package/dist/groups/images.js +109 -0
- package/dist/groups/machines.d.ts +124 -0
- package/dist/groups/machines.js +127 -0
- package/dist/groups/operations.d.ts +127 -0
- package/dist/groups/operations.js +174 -0
- package/dist/groups/processes.d.ts +77 -0
- package/dist/groups/processes.js +75 -0
- package/dist/groups/stats.d.ts +29 -0
- package/dist/groups/stats.js +33 -0
- package/dist/groups/system.d.ts +33 -0
- package/dist/groups/system.js +40 -0
- package/dist/groups/volumes.d.ts +36 -0
- package/dist/groups/volumes.js +40 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +20 -0
- package/dist/purposes.d.ts +17 -0
- package/dist/purposes.js +21 -0
- package/dist/routes.d.ts +71 -0
- package/dist/routes.js +73 -0
- package/dist/transport/api-connection.d.ts +185 -0
- package/dist/transport/api-connection.js +396 -0
- package/dist/transport/connection-failures.d.ts +14 -0
- package/dist/transport/connection-failures.js +57 -0
- package/dist/transport/json-lines.d.ts +10 -0
- package/dist/transport/json-lines.js +23 -0
- package/dist/transport/query-text.d.ts +49 -0
- package/dist/transport/query-text.js +62 -0
- package/dist/transport/service-address.d.ts +30 -0
- package/dist/transport/service-address.js +37 -0
- package/dist/transport/tls-files.d.ts +9 -0
- package/dist/transport/tls-files.js +1 -0
- package/package.json +44 -5
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
2
|
+
import { EastIndiaApiError } from "../errors.js";
|
|
3
|
+
import { Routes } from "../routes.js";
|
|
4
|
+
import { QueryText } from "../transport/query-text.js";
|
|
5
|
+
/** The states after which an operation never changes again. */
|
|
6
|
+
const FinalStates = new Set(["succeeded", "failed", "cancelled", "interrupted"]);
|
|
7
|
+
/**
|
|
8
|
+
* The pause before a followed log is read again when it ended while the operation had not yet finished, as when the connection
|
|
9
|
+
* broke. It keeps a service that keeps closing the stream from being asked again at once, over and over.
|
|
10
|
+
*/
|
|
11
|
+
const FollowAgainDelayMilliseconds = 1000;
|
|
12
|
+
/**
|
|
13
|
+
* Says whether an operation has finished: succeeded, failed, been cancelled or been interrupted.
|
|
14
|
+
*
|
|
15
|
+
* @param operation - The operation as last read.
|
|
16
|
+
* @returns True when its state never changes again.
|
|
17
|
+
*/
|
|
18
|
+
export function isFinished(operation) {
|
|
19
|
+
return FinalStates.has(operation.state);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The operation routes: list, inspect, read the log, cancel; and handles that follow one operation to its end. Every route that
|
|
23
|
+
* starts work returns an `OperationHandle` built here.
|
|
24
|
+
*/
|
|
25
|
+
export class OperationsClient {
|
|
26
|
+
connection;
|
|
27
|
+
/** @internal */
|
|
28
|
+
constructor(connection) {
|
|
29
|
+
this.connection = connection;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Lists every kept operation, newest first: `endia operation ls`.
|
|
33
|
+
*
|
|
34
|
+
* @param options - `filters` such as `{ state: ["running"], kind: ["build"] }`.
|
|
35
|
+
* @returns The operations.
|
|
36
|
+
*/
|
|
37
|
+
list(options = {}) {
|
|
38
|
+
return this.connection.read(Routes.operations + new QueryText().addJson("filters", options.filters).toString(), options.signal);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Reads one operation: `endia operation inspect`.
|
|
42
|
+
*
|
|
43
|
+
* @param id - The operation ID or a unique prefix of it.
|
|
44
|
+
* @param signal - Cancels the request.
|
|
45
|
+
* @returns The operation.
|
|
46
|
+
*/
|
|
47
|
+
inspect(id, signal) {
|
|
48
|
+
return this.connection.read(Routes.operation(id), signal);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Opens an operation's log, the lines the command prints as the CLI prints them: `endia operation logs`.
|
|
52
|
+
*
|
|
53
|
+
* @param id - The operation ID or a unique prefix of it.
|
|
54
|
+
* @param options - With `follow`, the stream stays open until the operation finishes.
|
|
55
|
+
* @returns The log as UTF-8 text.
|
|
56
|
+
*/
|
|
57
|
+
async logs(id, options = {}) {
|
|
58
|
+
const query = new QueryText().add("follow", options.follow ?? false).add("tail", options.tail);
|
|
59
|
+
const stream = await this.connection.openStream({ method: "GET", path: Routes.operationLog(id) + query.toString(), signal: options.signal });
|
|
60
|
+
return stream.setEncoding("utf8");
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Asks the service to cancel an operation: `endia operation cancel`. A build or import cleans up as on failure; a fleet or machine
|
|
64
|
+
* operation stops before its next machine step; a prune stops before its next deletion.
|
|
65
|
+
*
|
|
66
|
+
* @param id - The operation ID or a unique prefix of it.
|
|
67
|
+
* @param signal - Cancels the request.
|
|
68
|
+
* @returns The operation in `cancelling`, or unchanged when it had already finished.
|
|
69
|
+
*/
|
|
70
|
+
cancel(id, signal) {
|
|
71
|
+
return this.connection.change({ method: "POST", path: Routes.operationCancel(id), signal });
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Wraps an operation that was read elsewhere, such as one another program started, in a handle that follows it and waits for its
|
|
75
|
+
* end.
|
|
76
|
+
*
|
|
77
|
+
* @param operation - The operation as last read.
|
|
78
|
+
* @returns The handle.
|
|
79
|
+
*/
|
|
80
|
+
handle(operation) {
|
|
81
|
+
return new OperationHandle(this, operation);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Work the service has accepted and carries out in the background: a build, an import, a fleet or machine change, or a prune. The
|
|
86
|
+
* handle follows that work: it reads the operation's state, follows its log, cancels it, and waits for it to finish.
|
|
87
|
+
*
|
|
88
|
+
* @typeParam TResult - The result the operation gives when it succeeds, such as `BuildResult`.
|
|
89
|
+
*/
|
|
90
|
+
export class OperationHandle {
|
|
91
|
+
operations;
|
|
92
|
+
/** The operation as the service answered when it accepted the work. */
|
|
93
|
+
accepted;
|
|
94
|
+
/** @internal */
|
|
95
|
+
constructor(operations, accepted) {
|
|
96
|
+
this.operations = operations;
|
|
97
|
+
this.accepted = accepted;
|
|
98
|
+
}
|
|
99
|
+
/** The operation ID: 32 lower-case hexadecimal digits. */
|
|
100
|
+
get id() {
|
|
101
|
+
return this.accepted.id;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Reads the operation as it stands now.
|
|
105
|
+
*
|
|
106
|
+
* @param signal - Cancels the request.
|
|
107
|
+
* @returns The operation.
|
|
108
|
+
*/
|
|
109
|
+
inspect(signal) {
|
|
110
|
+
return this.operations.inspect(this.id, signal);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Opens the operation's log.
|
|
114
|
+
*
|
|
115
|
+
* @param options - With `follow`, the stream stays open until the operation finishes.
|
|
116
|
+
* @returns The log as UTF-8 text.
|
|
117
|
+
*/
|
|
118
|
+
logs(options = {}) {
|
|
119
|
+
return this.operations.logs(this.id, options);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Asks the service to cancel the operation. The operation then ends `cancelled`, which `wait` reports.
|
|
123
|
+
*
|
|
124
|
+
* @param signal - Cancels the request.
|
|
125
|
+
* @returns The operation in `cancelling`, or unchanged when it had already finished.
|
|
126
|
+
*/
|
|
127
|
+
cancel(signal) {
|
|
128
|
+
return this.operations.cancel(this.id, signal);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Waits until the operation finishes, whatever its outcome, by following its log to the end as the CLI does.
|
|
132
|
+
*
|
|
133
|
+
* When the followed log ends before the operation has finished, as when the connection breaks, the log is opened again after a
|
|
134
|
+
* pause. The log only grows, so the text `onLog` has already received is skipped and each piece reaches it once.
|
|
135
|
+
*
|
|
136
|
+
* @param options - `onLog` receives the log as it is written.
|
|
137
|
+
* @returns The finished operation, in `succeeded`, `failed`, `cancelled` or `interrupted`.
|
|
138
|
+
*/
|
|
139
|
+
async wait(options = {}) {
|
|
140
|
+
let delivered = 0;
|
|
141
|
+
for (;;) {
|
|
142
|
+
const log = await this.logs({ follow: true, signal: options.signal });
|
|
143
|
+
let read = 0;
|
|
144
|
+
for await (const text of log) {
|
|
145
|
+
const unseen = text.slice(Math.max(0, delivered - read));
|
|
146
|
+
read += text.length;
|
|
147
|
+
delivered += unseen.length;
|
|
148
|
+
if (unseen.length > 0) {
|
|
149
|
+
options.onLog?.(unseen);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const operation = await this.inspect(options.signal);
|
|
153
|
+
if (isFinished(operation)) {
|
|
154
|
+
return operation;
|
|
155
|
+
}
|
|
156
|
+
await delay(FollowAgainDelayMilliseconds, undefined, { signal: options.signal });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Waits until the operation finishes and gives its result.
|
|
161
|
+
*
|
|
162
|
+
* @param options - `onLog` receives the log as it is written.
|
|
163
|
+
* @returns The result of the succeeded operation.
|
|
164
|
+
* @throws EastIndiaApiError when the operation failed, was cancelled or was interrupted; its `code` is the operation's error code,
|
|
165
|
+
* such as `BUILD_FAILED`, and its `operation` is the finished operation.
|
|
166
|
+
*/
|
|
167
|
+
async result(options = {}) {
|
|
168
|
+
const operation = await this.wait(options);
|
|
169
|
+
if (operation.state !== "succeeded") {
|
|
170
|
+
throw EastIndiaApiError.fromOperation(operation);
|
|
171
|
+
}
|
|
172
|
+
return operation.result;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Timestamp } from "../contract/common.js";
|
|
2
|
+
import type { ProcessDetail, ProcessLogLine, ProcessStopResult, ProcessSummary, ProcessWaitResult } from "../contract/processes.js";
|
|
3
|
+
/** Which process a request means, beyond its ID. */
|
|
4
|
+
export interface ProcessIdentity {
|
|
5
|
+
/**
|
|
6
|
+
* The time the process must have started, copied from the answer that named the process, such as the `started` member of a
|
|
7
|
+
* detached exec's answer. Windows reuses process IDs, so the time tells the process apart from a later one with the same ID.
|
|
8
|
+
* When the process with the ID started at another time, the service refuses the request with `NOT_FOUND`. When `started` is
|
|
9
|
+
* undefined, the request means whichever process has the ID.
|
|
10
|
+
*/
|
|
11
|
+
readonly started?: Timestamp;
|
|
12
|
+
readonly signal?: AbortSignal;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The process routes of a running machine: list, inspect, wait, stop and read the output of a program started detached. A request
|
|
16
|
+
* names a process by its Windows process ID, which a detached exec and a desktop launch return.
|
|
17
|
+
*/
|
|
18
|
+
export declare class ProcessesClient {
|
|
19
|
+
private readonly connection;
|
|
20
|
+
/**
|
|
21
|
+
* Lists the machine's processes: `endia process ls`.
|
|
22
|
+
*
|
|
23
|
+
* @param machine - The machine name or a unique prefix of its ID.
|
|
24
|
+
* @param signal - Cancels the request.
|
|
25
|
+
* @returns The processes.
|
|
26
|
+
*/
|
|
27
|
+
list(machine: string, signal?: AbortSignal): Promise<ProcessSummary[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Reads one process: `endia process inspect`.
|
|
30
|
+
*
|
|
31
|
+
* @param machine - The machine name or a unique prefix of its ID.
|
|
32
|
+
* @param processId - The Windows process ID.
|
|
33
|
+
* @param options - The process's start time.
|
|
34
|
+
* @returns The process, with its exit code once it has ended.
|
|
35
|
+
*/
|
|
36
|
+
inspect(machine: string, processId: number, options?: ProcessIdentity): Promise<ProcessDetail>;
|
|
37
|
+
/**
|
|
38
|
+
* Waits until a process ends: `endia process wait`. The service answers only when the process has ended or the time-out has
|
|
39
|
+
* passed, so the client's own time limit does not apply.
|
|
40
|
+
*
|
|
41
|
+
* @param machine - The machine name or a unique prefix of its ID.
|
|
42
|
+
* @param processId - The Windows process ID.
|
|
43
|
+
* @param options - `timeout` is the longest wait in seconds; without it the wait has no limit. When the time-out passes first, the
|
|
44
|
+
* service refuses the request with `TIMEOUT`, and the process keeps running.
|
|
45
|
+
* @returns The exit code in full, such as 3010; null when EastIndia did not start the process on the machine's current boot, because
|
|
46
|
+
* only then does it know the exit code.
|
|
47
|
+
*/
|
|
48
|
+
wait(machine: string, processId: number, options?: ProcessIdentity & {
|
|
49
|
+
readonly timeout?: number;
|
|
50
|
+
}): Promise<ProcessWaitResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Ends a process as `docker stop` ends a container: `endia process stop`. The service first asks the process to close, by
|
|
53
|
+
* sending a close message to each of its top-level windows, or a `CTRL_BREAK` event to a console process without windows. When the
|
|
54
|
+
* process has not ended within the time-out, the service ends it with exit code 1.
|
|
55
|
+
*
|
|
56
|
+
* @param machine - The machine name or a unique prefix of its ID.
|
|
57
|
+
* @param processId - The Windows process ID.
|
|
58
|
+
* @param options - `timeout` is the time in seconds the process has to close, 10 when undefined; 0 ends it at once.
|
|
59
|
+
* @returns How the process ended, and its exit code when known.
|
|
60
|
+
*/
|
|
61
|
+
stop(machine: string, processId: number, options?: ProcessIdentity & {
|
|
62
|
+
readonly timeout?: number;
|
|
63
|
+
}): Promise<ProcessStopResult>;
|
|
64
|
+
/**
|
|
65
|
+
* Reads the output a detached program wrote: `endia process logs`. Only a program started detached on the machine's current
|
|
66
|
+
* boot has its output kept; for any other process the service refuses the request with `NOT_FOUND`.
|
|
67
|
+
*
|
|
68
|
+
* @param machine - The machine name or a unique prefix of its ID.
|
|
69
|
+
* @param processId - The Windows process ID.
|
|
70
|
+
* @param options - `follow` keeps reading new lines until the process ends.
|
|
71
|
+
* @returns The lines, each naming its stream, as they arrive.
|
|
72
|
+
*/
|
|
73
|
+
logs(machine: string, processId: number, options?: {
|
|
74
|
+
readonly follow?: boolean;
|
|
75
|
+
readonly signal?: AbortSignal;
|
|
76
|
+
}): AsyncGenerator<ProcessLogLine, void, undefined>;
|
|
77
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Routes } from "../routes.js";
|
|
2
|
+
import { QueryText } from "../transport/query-text.js";
|
|
3
|
+
/**
|
|
4
|
+
* The process routes of a running machine: list, inspect, wait, stop and read the output of a program started detached. A request
|
|
5
|
+
* names a process by its Windows process ID, which a detached exec and a desktop launch return.
|
|
6
|
+
*/
|
|
7
|
+
export class ProcessesClient {
|
|
8
|
+
connection;
|
|
9
|
+
/** @internal */
|
|
10
|
+
constructor(connection) {
|
|
11
|
+
this.connection = connection;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Lists the machine's processes: `endia process ls`.
|
|
15
|
+
*
|
|
16
|
+
* @param machine - The machine name or a unique prefix of its ID.
|
|
17
|
+
* @param signal - Cancels the request.
|
|
18
|
+
* @returns The processes.
|
|
19
|
+
*/
|
|
20
|
+
list(machine, signal) {
|
|
21
|
+
return this.connection.read(Routes.processes(machine), signal);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Reads one process: `endia process inspect`.
|
|
25
|
+
*
|
|
26
|
+
* @param machine - The machine name or a unique prefix of its ID.
|
|
27
|
+
* @param processId - The Windows process ID.
|
|
28
|
+
* @param options - The process's start time.
|
|
29
|
+
* @returns The process, with its exit code once it has ended.
|
|
30
|
+
*/
|
|
31
|
+
inspect(machine, processId, options = {}) {
|
|
32
|
+
return this.connection.read(Routes.process(machine, processId) + new QueryText().add("started", options.started).toString(), options.signal);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Waits until a process ends: `endia process wait`. The service answers only when the process has ended or the time-out has
|
|
36
|
+
* passed, so the client's own time limit does not apply.
|
|
37
|
+
*
|
|
38
|
+
* @param machine - The machine name or a unique prefix of its ID.
|
|
39
|
+
* @param processId - The Windows process ID.
|
|
40
|
+
* @param options - `timeout` is the longest wait in seconds; without it the wait has no limit. When the time-out passes first, the
|
|
41
|
+
* service refuses the request with `TIMEOUT`, and the process keeps running.
|
|
42
|
+
* @returns The exit code in full, such as 3010; null when EastIndia did not start the process on the machine's current boot, because
|
|
43
|
+
* only then does it know the exit code.
|
|
44
|
+
*/
|
|
45
|
+
wait(machine, processId, options = {}) {
|
|
46
|
+
const query = new QueryText().add("timeout", options.timeout).add("started", options.started);
|
|
47
|
+
return this.connection.change({ method: "POST", path: Routes.processWait(machine, processId) + query.toString(), signal: options.signal }, false);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Ends a process as `docker stop` ends a container: `endia process stop`. The service first asks the process to close, by
|
|
51
|
+
* sending a close message to each of its top-level windows, or a `CTRL_BREAK` event to a console process without windows. When the
|
|
52
|
+
* process has not ended within the time-out, the service ends it with exit code 1.
|
|
53
|
+
*
|
|
54
|
+
* @param machine - The machine name or a unique prefix of its ID.
|
|
55
|
+
* @param processId - The Windows process ID.
|
|
56
|
+
* @param options - `timeout` is the time in seconds the process has to close, 10 when undefined; 0 ends it at once.
|
|
57
|
+
* @returns How the process ended, and its exit code when known.
|
|
58
|
+
*/
|
|
59
|
+
stop(machine, processId, options = {}) {
|
|
60
|
+
const query = new QueryText().add("t", options.timeout).add("started", options.started);
|
|
61
|
+
return this.connection.change({ method: "POST", path: Routes.processStop(machine, processId) + query.toString(), signal: options.signal }, false);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Reads the output a detached program wrote: `endia process logs`. Only a program started detached on the machine's current
|
|
65
|
+
* boot has its output kept; for any other process the service refuses the request with `NOT_FOUND`.
|
|
66
|
+
*
|
|
67
|
+
* @param machine - The machine name or a unique prefix of its ID.
|
|
68
|
+
* @param processId - The Windows process ID.
|
|
69
|
+
* @param options - `follow` keeps reading new lines until the process ends.
|
|
70
|
+
* @returns The lines, each naming its stream, as they arrive.
|
|
71
|
+
*/
|
|
72
|
+
logs(machine, processId, options = {}) {
|
|
73
|
+
return this.connection.readLines(Routes.processLogs(machine, processId) + new QueryText().addFlag("follow", options.follow).toString(), options.signal);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { HostStats, MachineStats } from "../contract/stats.js";
|
|
2
|
+
/** Whether a readings route keeps sending readings or sends one. */
|
|
3
|
+
export interface StatsOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Whether the service keeps sending a new reading at its configured interval until the caller stops iterating. When false or
|
|
6
|
+
* undefined, the service sends one reading and ends the stream.
|
|
7
|
+
*/
|
|
8
|
+
readonly stream?: boolean;
|
|
9
|
+
readonly signal?: AbortSignal;
|
|
10
|
+
}
|
|
11
|
+
/** The readings routes: a machine's processor, memory, network and disk use, and the host's: `endia stats`. */
|
|
12
|
+
export declare class StatsClient {
|
|
13
|
+
private readonly connection;
|
|
14
|
+
/**
|
|
15
|
+
* Reads a machine's readings: `endia stats MACHINE`.
|
|
16
|
+
*
|
|
17
|
+
* @param machine - The machine name or a unique prefix of its ID.
|
|
18
|
+
* @param options - `stream` keeps reading; one reading by default.
|
|
19
|
+
* @returns The readings, as they arrive.
|
|
20
|
+
*/
|
|
21
|
+
machine(machine: string, options?: StatsOptions): AsyncGenerator<MachineStats, void, undefined>;
|
|
22
|
+
/**
|
|
23
|
+
* Reads the host's readings: `endia stats --host`.
|
|
24
|
+
*
|
|
25
|
+
* @param options - `stream` keeps reading; one reading by default.
|
|
26
|
+
* @returns The readings, as they arrive.
|
|
27
|
+
*/
|
|
28
|
+
host(options?: StatsOptions): AsyncGenerator<HostStats, void, undefined>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Routes } from "../routes.js";
|
|
2
|
+
import { QueryText } from "../transport/query-text.js";
|
|
3
|
+
/** The readings routes: a machine's processor, memory, network and disk use, and the host's: `endia stats`. */
|
|
4
|
+
export class StatsClient {
|
|
5
|
+
connection;
|
|
6
|
+
/** @internal */
|
|
7
|
+
constructor(connection) {
|
|
8
|
+
this.connection = connection;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Reads a machine's readings: `endia stats MACHINE`.
|
|
12
|
+
*
|
|
13
|
+
* @param machine - The machine name or a unique prefix of its ID.
|
|
14
|
+
* @param options - `stream` keeps reading; one reading by default.
|
|
15
|
+
* @returns The readings, as they arrive.
|
|
16
|
+
*/
|
|
17
|
+
machine(machine, options = {}) {
|
|
18
|
+
return this.connection.readLines(Routes.machineStats(machine) + streamQuery(options), options.signal);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Reads the host's readings: `endia stats --host`.
|
|
22
|
+
*
|
|
23
|
+
* @param options - `stream` keeps reading; one reading by default.
|
|
24
|
+
* @returns The readings, as they arrive.
|
|
25
|
+
*/
|
|
26
|
+
host(options = {}) {
|
|
27
|
+
return this.connection.readLines(Routes.hostStats + streamQuery(options), options.signal);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// A readings route without the stream parameter streams, as Docker's does, so the client always sends the parameter, false included.
|
|
31
|
+
function streamQuery(options) {
|
|
32
|
+
return new QueryText().add("stream", options.stream ?? false).toString();
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { DiskUsage, PruneResult } from "../contract/space.js";
|
|
2
|
+
import type { OperationHandle } from "./operations.js";
|
|
3
|
+
/** The disk space routes: what images, machines and volumes take, and the prune that deletes what nothing uses. */
|
|
4
|
+
export declare class SystemClient {
|
|
5
|
+
private readonly connection;
|
|
6
|
+
private readonly operations;
|
|
7
|
+
/**
|
|
8
|
+
* Reads what images, machines, volumes and the build cache take on disk, and the free space: `endia system df`.
|
|
9
|
+
*
|
|
10
|
+
* @param signal - Cancels the request.
|
|
11
|
+
* @returns The disk use.
|
|
12
|
+
*/
|
|
13
|
+
diskUsage(signal?: AbortSignal): Promise<DiskUsage>;
|
|
14
|
+
/**
|
|
15
|
+
* Deletes what nothing uses: `endia system prune`. It deletes, in this order:
|
|
16
|
+
* 1. every machine in state `exited` or `created`;
|
|
17
|
+
* 2. every image with no tag, no children and no machine;
|
|
18
|
+
* 3. the unused build cache, the entries that `images.pruneBuildCache` deletes;
|
|
19
|
+
* 4. the finished operations older than the service's retention;
|
|
20
|
+
* 5. the host objects whose owner no longer exists.
|
|
21
|
+
*
|
|
22
|
+
* It deletes what every caller of the service made, not only this caller's.
|
|
23
|
+
*
|
|
24
|
+
* @param options - `all` deletes every image that no machine uses and no image is built on, tagged or not. `volumes` is Docker's
|
|
25
|
+
* option for anonymous volumes; EastIndia has none, so it deletes nothing more.
|
|
26
|
+
* @returns The prune's operation; its result names what was deleted.
|
|
27
|
+
*/
|
|
28
|
+
prune(options?: {
|
|
29
|
+
readonly all?: boolean;
|
|
30
|
+
readonly volumes?: boolean;
|
|
31
|
+
readonly signal?: AbortSignal;
|
|
32
|
+
}): Promise<OperationHandle<PruneResult>>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Routes } from "../routes.js";
|
|
2
|
+
import { QueryText } from "../transport/query-text.js";
|
|
3
|
+
/** The disk space routes: what images, machines and volumes take, and the prune that deletes what nothing uses. */
|
|
4
|
+
export class SystemClient {
|
|
5
|
+
connection;
|
|
6
|
+
operations;
|
|
7
|
+
/** @internal */
|
|
8
|
+
constructor(connection, operations) {
|
|
9
|
+
this.connection = connection;
|
|
10
|
+
this.operations = operations;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Reads what images, machines, volumes and the build cache take on disk, and the free space: `endia system df`.
|
|
14
|
+
*
|
|
15
|
+
* @param signal - Cancels the request.
|
|
16
|
+
* @returns The disk use.
|
|
17
|
+
*/
|
|
18
|
+
diskUsage(signal) {
|
|
19
|
+
return this.connection.read(Routes.systemDf, signal);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Deletes what nothing uses: `endia system prune`. It deletes, in this order:
|
|
23
|
+
* 1. every machine in state `exited` or `created`;
|
|
24
|
+
* 2. every image with no tag, no children and no machine;
|
|
25
|
+
* 3. the unused build cache, the entries that `images.pruneBuildCache` deletes;
|
|
26
|
+
* 4. the finished operations older than the service's retention;
|
|
27
|
+
* 5. the host objects whose owner no longer exists.
|
|
28
|
+
*
|
|
29
|
+
* It deletes what every caller of the service made, not only this caller's.
|
|
30
|
+
*
|
|
31
|
+
* @param options - `all` deletes every image that no machine uses and no image is built on, tagged or not. `volumes` is Docker's
|
|
32
|
+
* option for anonymous volumes; EastIndia has none, so it deletes nothing more.
|
|
33
|
+
* @returns The prune's operation; its result names what was deleted.
|
|
34
|
+
*/
|
|
35
|
+
async prune(options = {}) {
|
|
36
|
+
const query = new QueryText().addFlag("all", options.all).addFlag("volumes", options.volumes);
|
|
37
|
+
const accepted = await this.connection.change({ method: "POST", path: Routes.systemPrune + query.toString(), signal: options.signal });
|
|
38
|
+
return this.operations.handle(accepted);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Filters } from "../contract/common.js";
|
|
2
|
+
import type { VolumeDetail } from "../contract/volumes.js";
|
|
3
|
+
/** The named volume routes: list, inspect and remove. A fleet creates its named volumes when it comes up. */
|
|
4
|
+
export declare class VolumesClient {
|
|
5
|
+
private readonly connection;
|
|
6
|
+
/**
|
|
7
|
+
* Lists named volumes: `endia volume ls`.
|
|
8
|
+
*
|
|
9
|
+
* @param options - `filters` such as `{ label: ["team=qa"] }`.
|
|
10
|
+
* @returns The volumes.
|
|
11
|
+
*/
|
|
12
|
+
list(options?: {
|
|
13
|
+
readonly filters?: Filters;
|
|
14
|
+
readonly signal?: AbortSignal;
|
|
15
|
+
}): Promise<VolumeDetail[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Reads one named volume: `endia volume inspect`.
|
|
18
|
+
*
|
|
19
|
+
* @param name - The volume's name.
|
|
20
|
+
* @param signal - Cancels the request.
|
|
21
|
+
* @returns The volume.
|
|
22
|
+
*/
|
|
23
|
+
inspect(name: string, signal?: AbortSignal): Promise<VolumeDetail>;
|
|
24
|
+
/**
|
|
25
|
+
* Deletes a named volume and its disk: `endia volume rm`.
|
|
26
|
+
*
|
|
27
|
+
* @param name - The volume's name.
|
|
28
|
+
* @param options - `force` is sent as `endia volume rm -f` sends it.
|
|
29
|
+
* @returns When the service has deleted the volume. A volume attached to a machine is refused with `RESOURCE_IN_USE`, with or
|
|
30
|
+
* without `force`.
|
|
31
|
+
*/
|
|
32
|
+
remove(name: string, options?: {
|
|
33
|
+
readonly force?: boolean;
|
|
34
|
+
readonly signal?: AbortSignal;
|
|
35
|
+
}): Promise<void>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Routes } from "../routes.js";
|
|
2
|
+
import { QueryText } from "../transport/query-text.js";
|
|
3
|
+
/** The named volume routes: list, inspect and remove. A fleet creates its named volumes when it comes up. */
|
|
4
|
+
export class VolumesClient {
|
|
5
|
+
connection;
|
|
6
|
+
/** @internal */
|
|
7
|
+
constructor(connection) {
|
|
8
|
+
this.connection = connection;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Lists named volumes: `endia volume ls`.
|
|
12
|
+
*
|
|
13
|
+
* @param options - `filters` such as `{ label: ["team=qa"] }`.
|
|
14
|
+
* @returns The volumes.
|
|
15
|
+
*/
|
|
16
|
+
list(options = {}) {
|
|
17
|
+
return this.connection.read(Routes.volumes + new QueryText().addJson("filters", options.filters).toString(), options.signal);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Reads one named volume: `endia volume inspect`.
|
|
21
|
+
*
|
|
22
|
+
* @param name - The volume's name.
|
|
23
|
+
* @param signal - Cancels the request.
|
|
24
|
+
* @returns The volume.
|
|
25
|
+
*/
|
|
26
|
+
inspect(name, signal) {
|
|
27
|
+
return this.connection.read(Routes.volume(name), signal);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Deletes a named volume and its disk: `endia volume rm`.
|
|
31
|
+
*
|
|
32
|
+
* @param name - The volume's name.
|
|
33
|
+
* @param options - `force` is sent as `endia volume rm -f` sends it.
|
|
34
|
+
* @returns When the service has deleted the volume. A volume attached to a machine is refused with `RESOURCE_IN_USE`, with or
|
|
35
|
+
* without `force`.
|
|
36
|
+
*/
|
|
37
|
+
remove(name, options = {}) {
|
|
38
|
+
return this.connection.changeWithoutAnswer({ method: "DELETE", path: Routes.volume(name) + new QueryText().addFlag("force", options.force).toString(), signal: options.signal });
|
|
39
|
+
}
|
|
40
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export { EastIndiaClient, type ClientOptions } from "./eastindia-client.js";
|
|
2
|
+
export { EastIndiaApiError, EastIndiaConnectionError, MachineFailedError, type ConnectionFailure } from "./errors.js";
|
|
3
|
+
export { readTlsFolder } from "./environment.js";
|
|
4
|
+
export { resolveComposeModel, type ComposeProjectOptions, type ResolvedComposeModel } from "./compose-model.js";
|
|
5
|
+
export type { TlsFiles } from "./transport/tls-files.js";
|
|
6
|
+
export { ArchivesClient, type MachineArchive } from "./groups/archives.js";
|
|
7
|
+
export { BasesClient } from "./groups/bases.js";
|
|
8
|
+
export { CertificatesClient } from "./groups/certificates.js";
|
|
9
|
+
export { DesktopClient, type DesktopScreenshot } from "./groups/desktop.js";
|
|
10
|
+
export { ExecsClient, type DetachedExec, type ExecResult, type RunOptions } from "./groups/execs.js";
|
|
11
|
+
export { FleetsClient, type FleetArchive, type FleetServicesOptions, type FleetUpOptions, type FleetWaitOptions } from "./groups/fleets.js";
|
|
12
|
+
export { ImagesClient, type ArchiveContext, type BuildOptions, type FolderContext } from "./groups/images.js";
|
|
13
|
+
export { MachinesClient, type LogOptions } from "./groups/machines.js";
|
|
14
|
+
export { isFinished, OperationHandle, OperationsClient, type OperationLogOptions, type OperationWaitOptions } from "./groups/operations.js";
|
|
15
|
+
export { ProcessesClient, type ProcessIdentity } from "./groups/processes.js";
|
|
16
|
+
export { StatsClient, type StatsOptions } from "./groups/stats.js";
|
|
17
|
+
export { SystemClient } from "./groups/system.js";
|
|
18
|
+
export { VolumesClient } from "./groups/volumes.js";
|
|
19
|
+
export { MaxPurposeDetailBytes } from "./purposes.js";
|
|
20
|
+
export { ExecSession } from "./exec/exec-session.js";
|
|
21
|
+
export type { ComposeModel, ComposeService } from "./contract/compose.js";
|
|
22
|
+
export type * from "./contract/archives.js";
|
|
23
|
+
export type * from "./contract/bases.js";
|
|
24
|
+
export type * from "./contract/clients.js";
|
|
25
|
+
export type * from "./contract/common.js";
|
|
26
|
+
export type * from "./contract/desktop.js";
|
|
27
|
+
export type * from "./contract/execs.js";
|
|
28
|
+
export type * from "./contract/fleets.js";
|
|
29
|
+
export type * from "./contract/images.js";
|
|
30
|
+
export type * from "./contract/machines.js";
|
|
31
|
+
export type * from "./contract/operations.js";
|
|
32
|
+
export type * from "./contract/processes.js";
|
|
33
|
+
export type * from "./contract/purposes.js";
|
|
34
|
+
export type * from "./contract/space.js";
|
|
35
|
+
export type * from "./contract/stats.js";
|
|
36
|
+
export type * from "./contract/status.js";
|
|
37
|
+
export type * from "./contract/volumes.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// The public surface of the endia package: the client, its route groups and options, the errors, and the API's records.
|
|
2
|
+
export { EastIndiaClient } from "./eastindia-client.js";
|
|
3
|
+
export { EastIndiaApiError, EastIndiaConnectionError, MachineFailedError } from "./errors.js";
|
|
4
|
+
export { readTlsFolder } from "./environment.js";
|
|
5
|
+
export { resolveComposeModel } from "./compose-model.js";
|
|
6
|
+
export { ArchivesClient } from "./groups/archives.js";
|
|
7
|
+
export { BasesClient } from "./groups/bases.js";
|
|
8
|
+
export { CertificatesClient } from "./groups/certificates.js";
|
|
9
|
+
export { DesktopClient } from "./groups/desktop.js";
|
|
10
|
+
export { ExecsClient } from "./groups/execs.js";
|
|
11
|
+
export { FleetsClient } from "./groups/fleets.js";
|
|
12
|
+
export { ImagesClient } from "./groups/images.js";
|
|
13
|
+
export { MachinesClient } from "./groups/machines.js";
|
|
14
|
+
export { isFinished, OperationHandle, OperationsClient } from "./groups/operations.js";
|
|
15
|
+
export { ProcessesClient } from "./groups/processes.js";
|
|
16
|
+
export { StatsClient } from "./groups/stats.js";
|
|
17
|
+
export { SystemClient } from "./groups/system.js";
|
|
18
|
+
export { VolumesClient } from "./groups/volumes.js";
|
|
19
|
+
export { MaxPurposeDetailBytes } from "./purposes.js";
|
|
20
|
+
export { ExecSession } from "./exec/exec-session.js";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** The most bytes a purpose's detail may take in UTF-8: 64 KiB. */
|
|
2
|
+
export declare const MaxPurposeDetailBytes = 65536;
|
|
3
|
+
/** The JSON of a purpose a caller sets: the body of `PUT .../purpose` and the content of `purpose.json` in the archive of `compose up`. */
|
|
4
|
+
export interface PurposeBody {
|
|
5
|
+
readonly title: string;
|
|
6
|
+
/** Left out when the purpose has no detail. */
|
|
7
|
+
readonly detail?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Checks a purpose a caller sets and gives its JSON. The service checks the other limits: a title of 1 to 200 characters.
|
|
11
|
+
*
|
|
12
|
+
* @param title - What the fleet or machine is being used for, in one line.
|
|
13
|
+
* @param detail - Markdown notes, or undefined for none.
|
|
14
|
+
* @returns The purpose's JSON; an undefined detail is left out of it.
|
|
15
|
+
* @throws Error when the title has a line break or the detail takes more than 65536 bytes in UTF-8. Nothing is sent then.
|
|
16
|
+
*/
|
|
17
|
+
export declare function purposeBody(title: string, detail: string | undefined): PurposeBody;
|
package/dist/purposes.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** The most bytes a purpose's detail may take in UTF-8: 64 KiB. */
|
|
2
|
+
export const MaxPurposeDetailBytes = 65536;
|
|
3
|
+
/** Carriage return, line feed, next line, line separator and paragraph separator: each ends a line in some reader. */
|
|
4
|
+
const LineBreak = /[\r\n\u0085\u2028\u2029]/u;
|
|
5
|
+
/**
|
|
6
|
+
* Checks a purpose a caller sets and gives its JSON. The service checks the other limits: a title of 1 to 200 characters.
|
|
7
|
+
*
|
|
8
|
+
* @param title - What the fleet or machine is being used for, in one line.
|
|
9
|
+
* @param detail - Markdown notes, or undefined for none.
|
|
10
|
+
* @returns The purpose's JSON; an undefined detail is left out of it.
|
|
11
|
+
* @throws Error when the title has a line break or the detail takes more than 65536 bytes in UTF-8. Nothing is sent then.
|
|
12
|
+
*/
|
|
13
|
+
export function purposeBody(title, detail) {
|
|
14
|
+
if (LineBreak.test(title)) {
|
|
15
|
+
throw new Error("A purpose's title is one line; the title given has a line break.");
|
|
16
|
+
}
|
|
17
|
+
if (detail !== undefined && Buffer.byteLength(detail, "utf8") > MaxPurposeDetailBytes) {
|
|
18
|
+
throw new Error(`A purpose's detail takes at most ${String(MaxPurposeDetailBytes)} bytes in UTF-8.`);
|
|
19
|
+
}
|
|
20
|
+
return detail === undefined ? { title } : { title, detail };
|
|
21
|
+
}
|