@gjermundgaraba/clankerbox-sdk 0.15.6 → 0.16.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/dist/{client-BkZhmYJx.mjs → client-CLemHDCl.mjs} +202 -217
- package/dist/client-DgDb-TR7.d.mts +958 -0
- package/dist/index.d.mts +80 -357
- package/dist/index.mjs +129 -116
- package/dist/node.d.mts +2 -2
- package/dist/node.mjs +5 -5
- package/package.json +2 -1
- package/dist/client-Cn1bj4It.d.mts +0 -328
|
@@ -1,21 +1,33 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
2
|
import * as Action from "@gjermundgaraba/effect-actions/Action";
|
|
3
3
|
import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
|
|
4
|
-
import { Context, Duration, Effect, Fiber, Match, Predicate, Result, Schema, SchemaIssue } from "effect";
|
|
4
|
+
import { Context, Duration, Effect, Fiber, Layer, Match, Predicate, Result, Schema, SchemaIssue } from "effect";
|
|
5
5
|
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
6
6
|
//#region src/errors.ts
|
|
7
|
+
/** A host sends none of these; the client library sets them. */
|
|
8
|
+
const located = {
|
|
9
|
+
host: Schema.optionalKey(Schema.String),
|
|
10
|
+
action: Schema.optionalKey(Schema.String),
|
|
11
|
+
id: Schema.optionalKey(Schema.String)
|
|
12
|
+
};
|
|
7
13
|
/**
|
|
8
|
-
* The
|
|
9
|
-
*
|
|
14
|
+
* The request is malformed, or the host list is one a client can't route by, or a config,
|
|
15
|
+
* profile or setup file couldn't be read or doesn't hold what it should: then `file` names it.
|
|
10
16
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
var Invalid = class extends Schema.TaggedError()("Invalid", {
|
|
18
|
+
message: Schema.String,
|
|
19
|
+
...located,
|
|
20
|
+
file: Schema.optionalKey(Schema.String)
|
|
21
|
+
}, { httpApiStatus: 400 }) {
|
|
13
22
|
get retryable() {
|
|
14
23
|
return false;
|
|
15
24
|
}
|
|
16
25
|
};
|
|
17
26
|
/** The machine or checkpoint doesn't exist. A `delete` that meets it is done. */
|
|
18
|
-
var NotFound = class extends Schema.TaggedError()("NotFound", {
|
|
27
|
+
var NotFound = class extends Schema.TaggedError()("NotFound", {
|
|
28
|
+
message: Schema.String,
|
|
29
|
+
...located
|
|
30
|
+
}, { httpApiStatus: 404 }) {
|
|
19
31
|
get retryable() {
|
|
20
32
|
return false;
|
|
21
33
|
}
|
|
@@ -24,43 +36,53 @@ const ConflictKind = Schema.Literals(["exists", "busy"]);
|
|
|
24
36
|
/** The name is taken (`exists`), or another action holds the resource (`busy`). */
|
|
25
37
|
var Conflict = class extends Schema.TaggedError()("Conflict", {
|
|
26
38
|
message: Schema.String,
|
|
27
|
-
kind: ConflictKind
|
|
39
|
+
kind: ConflictKind,
|
|
40
|
+
...located
|
|
28
41
|
}, { httpApiStatus: 409 }) {
|
|
29
42
|
get retryable() {
|
|
30
43
|
return this.kind === "busy";
|
|
31
44
|
}
|
|
32
45
|
};
|
|
33
46
|
/** The resource's state doesn't allow the action, or no host offers the requested base. */
|
|
34
|
-
var Precondition = class extends Schema.TaggedError()("Precondition", {
|
|
47
|
+
var Precondition = class extends Schema.TaggedError()("Precondition", {
|
|
48
|
+
message: Schema.String,
|
|
49
|
+
...located
|
|
50
|
+
}, { httpApiStatus: 412 }) {
|
|
35
51
|
get retryable() {
|
|
36
52
|
return false;
|
|
37
53
|
}
|
|
38
54
|
};
|
|
39
55
|
/** The host has no room now; room can free up, so a later call may succeed. */
|
|
40
|
-
var Capacity = class extends Schema.TaggedError()("Capacity", {
|
|
56
|
+
var Capacity = class extends Schema.TaggedError()("Capacity", {
|
|
57
|
+
message: Schema.String,
|
|
58
|
+
...located
|
|
59
|
+
}, { httpApiStatus: 503 }) {
|
|
41
60
|
get retryable() {
|
|
42
61
|
return true;
|
|
43
62
|
}
|
|
44
63
|
};
|
|
45
64
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* the host and run, so it is never retryable.
|
|
65
|
+
* No reply: only the client library makes it. A mutation's request may have reached the host
|
|
66
|
+
* and run, so it is retryable only when `readOnly`.
|
|
49
67
|
*/
|
|
50
68
|
var Unavailable = class extends Schema.TaggedError()("Unavailable", {
|
|
51
69
|
message: Schema.String,
|
|
52
|
-
readOnly: Schema.Boolean
|
|
70
|
+
readOnly: Schema.Boolean,
|
|
71
|
+
...located
|
|
53
72
|
}) {
|
|
54
73
|
get retryable() {
|
|
55
74
|
return this.readOnly;
|
|
56
75
|
}
|
|
57
76
|
};
|
|
58
|
-
var Internal = class extends Schema.TaggedError()("Internal", {
|
|
77
|
+
var Internal = class extends Schema.TaggedError()("Internal", {
|
|
78
|
+
message: Schema.String,
|
|
79
|
+
...located
|
|
80
|
+
}, { httpApiStatus: 500 }) {
|
|
59
81
|
get retryable() {
|
|
60
82
|
return false;
|
|
61
83
|
}
|
|
62
84
|
};
|
|
63
|
-
/** What a host
|
|
85
|
+
/** What a host may answer with; each action declares its own share. */
|
|
64
86
|
const hostErrors = [
|
|
65
87
|
Invalid,
|
|
66
88
|
NotFound,
|
|
@@ -69,20 +91,13 @@ const hostErrors = [
|
|
|
69
91
|
Capacity,
|
|
70
92
|
Internal
|
|
71
93
|
];
|
|
72
|
-
|
|
94
|
+
const ClankerboxError = Schema.Union([...hostErrors, Unavailable]);
|
|
73
95
|
const HostErrorTag = Schema.Literals(hostErrors.map((error) => error.fields._tag.schema.literal));
|
|
74
|
-
/** The tag of any error a call through the client library can fail with. */
|
|
75
|
-
const ErrorTag = Schema.Literals([...HostErrorTag.literals, Unavailable.fields._tag.schema.literal]);
|
|
76
96
|
//#endregion
|
|
77
97
|
//#region src/ids.ts
|
|
78
|
-
/**
|
|
79
|
-
* Machine and checkpoint IDs, `<host>_<name>`: the one place that parses and formats them.
|
|
80
|
-
* Neither part contains `_`, so an ID has exactly one. The whole ID matches
|
|
81
|
-
* `^[A-Za-z0-9_-]{1,62}$` because it is written to `/var/lib/clankerbox/machine-id`, and
|
|
82
|
-
* clankercreds accepts only that pattern.
|
|
83
|
-
*/
|
|
84
98
|
const separator = "_";
|
|
85
99
|
const maxIdLength = 62;
|
|
100
|
+
/** The ID is written to `/var/lib/clankerbox/machine-id`, and clankercreds accepts only this. */
|
|
86
101
|
const idPattern = /^[A-Za-z0-9_-]{1,62}$/u;
|
|
87
102
|
/**
|
|
88
103
|
* boat's named snapshots are `cbx-<host>-<inst>`, and boat allows `^[a-z0-9][a-z0-9-]{0,62}$`:
|
|
@@ -96,9 +111,8 @@ const hostIdRule = "a host ID is 1 to 32 lowercase letters, digits and '-', star
|
|
|
96
111
|
*/
|
|
97
112
|
const namePattern = /^[A-Za-z][A-Za-z0-9]*(?:-[A-Za-z0-9]+)*$/u;
|
|
98
113
|
const nameRule = "a name starts with a letter, then letters, digits and '-', with no '--' and no '-' at the end";
|
|
99
|
-
/** A host's part of every ID it holds. */
|
|
100
114
|
const HostId = Schema.String.check(Schema.isPattern(hostIdPattern, { message: hostIdRule }));
|
|
101
|
-
/**
|
|
115
|
+
/** Unique per host and per resource type. */
|
|
102
116
|
const Name = Schema.String.check(Schema.isPattern(namePattern, { message: nameRule }));
|
|
103
117
|
const describeId = (id) => {
|
|
104
118
|
if (!idPattern.test(id)) return `ID ${JSON.stringify(id)} must be 1 to ${maxIdLength} letters, digits, '_' or '-'`;
|
|
@@ -107,9 +121,8 @@ const describeId = (id) => {
|
|
|
107
121
|
if (!hostIdPattern.test(id.slice(0, at))) return `ID ${JSON.stringify(id)} has an invalid host: ${hostIdRule}`;
|
|
108
122
|
if (!namePattern.test(id.slice(at + 1))) return `ID ${JSON.stringify(id)} has an invalid name: ${nameRule}`;
|
|
109
123
|
};
|
|
110
|
-
/**
|
|
124
|
+
/** `<host>_<name>`: neither part contains `_`, so an ID has exactly one. */
|
|
111
125
|
const Id = Schema.String.check(Schema.makeFilter((id) => describeId(id) ?? true));
|
|
112
|
-
/** Splits an ID at its `_` into host and name. */
|
|
113
126
|
const parseId = (id) => {
|
|
114
127
|
const problem = describeId(id);
|
|
115
128
|
if (problem !== void 0) return Effect.fail(new Invalid({ message: problem }));
|
|
@@ -119,20 +132,15 @@ const parseId = (id) => {
|
|
|
119
132
|
name: id.slice(at + 1)
|
|
120
133
|
});
|
|
121
134
|
};
|
|
122
|
-
/** Checks a name on its own, before it is joined to a host. */
|
|
123
135
|
const parseName = (name) => namePattern.test(name) ? Effect.succeed(name) : Effect.fail(new Invalid({ message: `name ${JSON.stringify(name)}: ${nameRule}` }));
|
|
124
|
-
/**
|
|
136
|
+
/** Checks both parts and the whole. */
|
|
125
137
|
const formatId = (host, name) => {
|
|
126
138
|
if (!hostIdPattern.test(host)) return Effect.fail(new Invalid({ message: `host ID ${JSON.stringify(host)}: ${hostIdRule}` }));
|
|
127
139
|
const id = `${host}${separator}${name}`;
|
|
128
140
|
return parseName(name).pipe(Effect.andThen(parseId(id)), Effect.as(id));
|
|
129
141
|
};
|
|
130
142
|
//#endregion
|
|
131
|
-
//#region package.json
|
|
132
|
-
var version$1 = "0.15.6";
|
|
133
|
-
//#endregion
|
|
134
143
|
//#region src/resources.ts
|
|
135
|
-
/** The resources a host reports: machines, checkpoints and the host itself. */
|
|
136
144
|
const Runtime = Schema.Literals([
|
|
137
145
|
"smolvm",
|
|
138
146
|
"tart",
|
|
@@ -149,23 +157,27 @@ const ActionName = Schema.Literals([
|
|
|
149
157
|
"restore",
|
|
150
158
|
"capture"
|
|
151
159
|
]);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
error: Schema.
|
|
160
|
+
const ActionStatus = Schema.Literals([
|
|
161
|
+
"running",
|
|
162
|
+
"failed",
|
|
163
|
+
"done"
|
|
164
|
+
]);
|
|
165
|
+
const actionRecord = (name) => Schema.Union([Schema.Struct({
|
|
166
|
+
name,
|
|
167
|
+
status: Schema.Literals(["running", "done"])
|
|
168
|
+
}), Schema.Struct({
|
|
169
|
+
name,
|
|
170
|
+
status: Schema.Literal("failed"),
|
|
171
|
+
error: Schema.Struct({
|
|
164
172
|
tag: HostErrorTag,
|
|
165
173
|
message: Schema.String
|
|
166
|
-
})
|
|
167
|
-
});
|
|
168
|
-
/**
|
|
174
|
+
})
|
|
175
|
+
})]);
|
|
176
|
+
/** A resource's last action; the next action replaces it. */
|
|
177
|
+
const ActionRecord = actionRecord(ActionName);
|
|
178
|
+
/** A checkpoint is only ever captured or deleted. */
|
|
179
|
+
const CheckpointActionName = Schema.Literals(["capture", "delete"]);
|
|
180
|
+
const CheckpointActionRecord = actionRecord(CheckpointActionName);
|
|
169
181
|
const SshEndpoint = Schema.Struct({
|
|
170
182
|
host: Schema.String,
|
|
171
183
|
port: Schema.Int.check(Schema.isBetween({
|
|
@@ -173,7 +185,6 @@ const SshEndpoint = Schema.Struct({
|
|
|
173
185
|
maximum: 65535
|
|
174
186
|
}))
|
|
175
187
|
});
|
|
176
|
-
/** Where a running machine's SSH is reached, and the guest user its runtime's logins are for. */
|
|
177
188
|
const SshLogin = Schema.Struct({
|
|
178
189
|
user: Schema.String,
|
|
179
190
|
...SshEndpoint.fields
|
|
@@ -183,63 +194,51 @@ const Machine = Schema.Struct({
|
|
|
183
194
|
runtime: Runtime,
|
|
184
195
|
createdAt: Schema.DateTimeUtcFromString,
|
|
185
196
|
base: Schema.String,
|
|
186
|
-
/** The label the client passed at create, normally its profile's name. */
|
|
187
197
|
profile: Schema.optionalKey(Schema.String),
|
|
188
198
|
cpu: Size,
|
|
189
199
|
ramMib: Size,
|
|
190
200
|
diskGib: Size,
|
|
191
|
-
/**
|
|
192
|
-
* Read from the runtime, never stored: `missing` when the runtime doesn't have the machine,
|
|
193
|
-
* `unknown` when it couldn't be read.
|
|
194
|
-
*/
|
|
201
|
+
/** `missing`: the runtime doesn't have it; `unknown`: it couldn't be read. */
|
|
195
202
|
state: Schema.Literals([
|
|
196
203
|
"running",
|
|
197
204
|
"stopped",
|
|
198
205
|
"missing",
|
|
199
206
|
"unknown"
|
|
200
207
|
]),
|
|
201
|
-
/**
|
|
202
|
-
* Whether its create, fork or restore has made it. One that isn't made, because that action
|
|
203
|
-
* still runs or failed, can only be read, stopped or deleted.
|
|
204
|
-
*/
|
|
208
|
+
/** Until its create, fork or restore has made it, it can only be read, stopped or deleted. */
|
|
205
209
|
made: Schema.Boolean,
|
|
206
210
|
action: ActionRecord,
|
|
207
211
|
/** Only on a running machine whose runtime gives it an endpoint. */
|
|
208
212
|
ssh: Schema.optionalKey(SshLogin),
|
|
209
|
-
/**
|
|
213
|
+
/** `<type> <base64>`, once preparation has recorded it. */
|
|
210
214
|
hostKey: Schema.optionalKey(Schema.String)
|
|
211
215
|
}).annotate({ identifier: "Machine" });
|
|
212
216
|
const Checkpoint = Schema.Struct({
|
|
213
217
|
id: Id,
|
|
214
218
|
createdAt: Schema.DateTimeUtcFromString,
|
|
215
|
-
/** The ID of the machine it was captured from. */
|
|
216
219
|
machine: Id,
|
|
217
|
-
/**
|
|
220
|
+
/** `ram` (RAM and disks) on smolvm, `disk` on Tart and boat. */
|
|
218
221
|
kind: Schema.Literals(["ram", "disk"]),
|
|
219
222
|
base: Schema.String,
|
|
220
223
|
profile: Schema.optionalKey(Schema.String),
|
|
221
224
|
cpu: Size,
|
|
222
225
|
ramMib: Size,
|
|
223
226
|
diskGib: Size,
|
|
224
|
-
action:
|
|
227
|
+
action: CheckpointActionRecord
|
|
225
228
|
}).annotate({ identifier: "Checkpoint" });
|
|
226
|
-
/** The SDK's version, which is also the version of the clankerbox binaries it ships with. */
|
|
227
|
-
const version = version$1;
|
|
228
|
-
/** A version's major.minor: releases that share it speak the same API. */
|
|
229
|
-
const release = (of) => of.split(".").slice(0, 2).join(".");
|
|
230
229
|
const Host = Schema.Struct({
|
|
231
230
|
id: HostId,
|
|
232
231
|
runtime: Runtime,
|
|
233
|
-
/** The clankerbox version the host runs. */
|
|
234
232
|
version: Schema.String,
|
|
235
|
-
/**
|
|
233
|
+
/** For boat, its API version. */
|
|
236
234
|
runtimeVersion: Schema.String,
|
|
235
|
+
/** The state a machine must be in to be captured: Tart's is `stopped`. */
|
|
236
|
+
captureFrom: Schema.Literals(["running", "stopped"]),
|
|
237
237
|
bases: Schema.Array(Schema.String)
|
|
238
238
|
}).annotate({ identifier: "Host" });
|
|
239
239
|
/**
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
* never stored.
|
|
240
|
+
* `used` is counted as a `Capacity` refusal counts it, so it needn't match the host's own
|
|
241
|
+
* machines.
|
|
243
242
|
*/
|
|
244
243
|
const Limit = Schema.Struct({
|
|
245
244
|
host: HostId,
|
|
@@ -258,10 +257,7 @@ const Limit = Schema.Struct({
|
|
|
258
257
|
limit: Schema.Int,
|
|
259
258
|
used: Schema.Int
|
|
260
259
|
}).annotate({ identifier: "Limit" });
|
|
261
|
-
/**
|
|
262
|
-
* A setup script and how long it may run, the host's default when it doesn't say. Nothing logs
|
|
263
|
-
* the script: it can carry secrets.
|
|
264
|
-
*/
|
|
260
|
+
/** Nothing logs the script: it can carry secrets. */
|
|
265
261
|
const Setup = Schema.Struct({
|
|
266
262
|
script: Schema.String,
|
|
267
263
|
timeoutSeconds: Schema.optionalKey(Size)
|
|
@@ -274,117 +270,97 @@ const specFields = {
|
|
|
274
270
|
setup: Schema.optionalKey(Setup),
|
|
275
271
|
profile: Schema.optionalKey(Schema.String)
|
|
276
272
|
};
|
|
277
|
-
/**
|
|
278
|
-
* What a new machine is made from: a base, its sizes, an optional setup script with its
|
|
279
|
-
* timeout, and an optional `profile` label. A profile file fills these in; the host never
|
|
280
|
-
* sees the profile itself.
|
|
281
|
-
*/
|
|
273
|
+
/** A profile file fills these in; the host sees only the `profile` label. */
|
|
282
274
|
const MachineSpec = Schema.Struct(specFields);
|
|
283
|
-
/**
|
|
284
|
-
* `machine.create`'s input: the spec and the new machine's full ID. The host checks that the ID
|
|
285
|
-
* names it, so a create never lands under another host's ID.
|
|
286
|
-
*/
|
|
275
|
+
/** The host checks that `id` names it, so a create never lands under another host's ID. */
|
|
287
276
|
const CreateRequest = Schema.Struct({
|
|
288
277
|
id: Id,
|
|
289
278
|
...specFields
|
|
290
279
|
});
|
|
291
|
-
/**
|
|
292
|
-
* A profile file: a client-side file that fills in a create request. `setup.path` is a script
|
|
293
|
-
* file or a recipe directory, relative to the profile file; without a `timeoutSeconds` the host's
|
|
294
|
-
* default applies. `host` places the create on that host instead of by base.
|
|
295
|
-
*/
|
|
296
|
-
const Profile = Schema.Struct({
|
|
297
|
-
base: specFields.base,
|
|
298
|
-
cpu: specFields.cpu,
|
|
299
|
-
ramMib: specFields.ramMib,
|
|
300
|
-
diskGib: specFields.diskGib,
|
|
301
|
-
setup: Schema.optionalKey(Schema.Struct({
|
|
302
|
-
path: Schema.String,
|
|
303
|
-
timeoutSeconds: Schema.optionalKey(Size)
|
|
304
|
-
})),
|
|
305
|
-
host: Schema.optionalKey(HostId)
|
|
306
|
-
});
|
|
307
280
|
//#endregion
|
|
308
281
|
//#region src/api.ts
|
|
309
|
-
/**
|
|
310
|
-
* The host API: one effect-actions binding, each action a JSON POST route at `/api/<action>`.
|
|
311
|
-
* A mutation replies when its action has finished. Every action is public: a host listens
|
|
312
|
-
* only on the tailnet or loopback.
|
|
313
|
-
*/
|
|
314
|
-
/**
|
|
315
|
-
* How long a client waits for a read's reply from one host, unless it sets its own. A host
|
|
316
|
-
* bounds its own reads of runtime state under it, so a list answers in time.
|
|
317
|
-
*/
|
|
282
|
+
/** A host bounds its own reads of runtime state under it, so a list answers in time. */
|
|
318
283
|
const readTimeout = Duration.seconds(10);
|
|
319
|
-
/**
|
|
320
|
-
* The header every client request carries the SDK's `version` in. A host refuses a request
|
|
321
|
-
* whose release (major.minor) differs from its own, or that carries none, before it runs
|
|
322
|
-
* anything.
|
|
323
|
-
*/
|
|
324
|
-
const versionHeader = "clankerbox-version";
|
|
325
284
|
const ById = Schema.Struct({ id: Id });
|
|
326
|
-
/** Input of the actions that make a new resource from a machine: its ID and the new name. */
|
|
327
285
|
const FromMachine = Schema.Struct({
|
|
328
286
|
machine: Id,
|
|
329
287
|
name: Name
|
|
330
288
|
});
|
|
331
|
-
/**
|
|
332
|
-
* What every action shares, by whether it reads or changes the host. Every action declares
|
|
333
|
-
* `hostErrors`, so a client decodes the `Invalid` the host's version check answers with.
|
|
334
|
-
*/
|
|
289
|
+
/** Every action is public, as a host listens only on the tailnet or loopback. */
|
|
335
290
|
const read = {
|
|
336
291
|
caller: Action.Anyone,
|
|
337
|
-
error: hostErrors,
|
|
338
292
|
readOnly: true
|
|
339
293
|
};
|
|
340
294
|
const write = {
|
|
341
295
|
caller: Action.Anyone,
|
|
342
|
-
error: hostErrors,
|
|
343
296
|
readOnly: false
|
|
344
297
|
};
|
|
298
|
+
/** What each action fails with; each has `Invalid`, which the host's version check answers. */
|
|
299
|
+
const listing = [Invalid, Internal];
|
|
300
|
+
const reading = [
|
|
301
|
+
Invalid,
|
|
302
|
+
NotFound,
|
|
303
|
+
Internal
|
|
304
|
+
];
|
|
305
|
+
const changing = [
|
|
306
|
+
Invalid,
|
|
307
|
+
NotFound,
|
|
308
|
+
Conflict,
|
|
309
|
+
Precondition,
|
|
310
|
+
Internal
|
|
311
|
+
];
|
|
312
|
+
const takingRoom = [...changing, Capacity];
|
|
345
313
|
const Http = ActionHttp.make([
|
|
346
314
|
Action.make("listMachines", {
|
|
347
315
|
...read,
|
|
316
|
+
error: listing,
|
|
348
317
|
description: "List the host's machines.",
|
|
349
318
|
success: Schema.Array(Machine)
|
|
350
319
|
}),
|
|
351
320
|
Action.make("getMachine", {
|
|
352
321
|
...read,
|
|
322
|
+
error: reading,
|
|
353
323
|
description: "Read one machine.",
|
|
354
324
|
input: ById,
|
|
355
325
|
success: Machine
|
|
356
326
|
}),
|
|
357
327
|
Action.make("createMachine", {
|
|
358
328
|
...write,
|
|
329
|
+
error: takingRoom,
|
|
359
330
|
description: "Create a machine from a base, run its setup once, and prepare it.",
|
|
360
331
|
input: CreateRequest,
|
|
361
332
|
success: Machine
|
|
362
333
|
}),
|
|
363
334
|
Action.make("startMachine", {
|
|
364
335
|
...write,
|
|
336
|
+
error: takingRoom,
|
|
365
337
|
description: "Start a machine. On a running machine, run preparation again.",
|
|
366
338
|
input: ById,
|
|
367
339
|
success: Machine
|
|
368
340
|
}),
|
|
369
341
|
Action.make("stopMachine", {
|
|
370
342
|
...write,
|
|
371
|
-
|
|
343
|
+
error: changing,
|
|
344
|
+
description: "Stop a machine: a power-off that keeps its disk and ends its processes. A stopped machine stays stopped, and the stop is still its last action.",
|
|
372
345
|
input: ById,
|
|
373
346
|
success: Machine
|
|
374
347
|
}),
|
|
375
348
|
Action.make("deleteMachine", {
|
|
376
349
|
...write,
|
|
350
|
+
error: changing,
|
|
377
351
|
description: "Delete a machine and everything native it owns.",
|
|
378
352
|
input: ById
|
|
379
353
|
}),
|
|
380
354
|
Action.make("forkMachine", {
|
|
381
355
|
...write,
|
|
356
|
+
error: takingRoom,
|
|
382
357
|
description: "Copy a machine to a new name on the same host.",
|
|
383
358
|
input: FromMachine,
|
|
384
359
|
success: Machine
|
|
385
360
|
}),
|
|
386
361
|
Action.make("restoreMachine", {
|
|
387
362
|
...write,
|
|
363
|
+
error: takingRoom,
|
|
388
364
|
description: "Create a machine on the checkpoint's host from that checkpoint.",
|
|
389
365
|
input: Schema.Struct({
|
|
390
366
|
checkpoint: Id,
|
|
@@ -394,66 +370,74 @@ const Http = ActionHttp.make([
|
|
|
394
370
|
}),
|
|
395
371
|
Action.make("listCheckpoints", {
|
|
396
372
|
...read,
|
|
373
|
+
error: listing,
|
|
397
374
|
description: "List the host's checkpoints.",
|
|
398
375
|
success: Schema.Array(Checkpoint)
|
|
399
376
|
}),
|
|
400
377
|
Action.make("getCheckpoint", {
|
|
401
378
|
...read,
|
|
379
|
+
error: reading,
|
|
402
380
|
description: "Read one checkpoint.",
|
|
403
381
|
input: ById,
|
|
404
382
|
success: Checkpoint
|
|
405
383
|
}),
|
|
406
384
|
Action.make("captureCheckpoint", {
|
|
407
385
|
...write,
|
|
386
|
+
error: takingRoom,
|
|
408
387
|
description: "Capture a checkpoint of a machine. Its kind follows the runtime: ram on smolvm, which captures only a running machine, and disk on Tart and boat.",
|
|
409
388
|
input: FromMachine,
|
|
410
389
|
success: Checkpoint
|
|
411
390
|
}),
|
|
412
391
|
Action.make("deleteCheckpoint", {
|
|
413
392
|
...write,
|
|
393
|
+
error: changing,
|
|
414
394
|
description: "Delete a checkpoint.",
|
|
415
395
|
input: ById
|
|
416
396
|
}),
|
|
417
397
|
Action.make("getHost", {
|
|
418
398
|
...read,
|
|
399
|
+
error: [Invalid],
|
|
419
400
|
description: "Read the host: its runtime, versions and bases.",
|
|
420
401
|
success: Host
|
|
421
402
|
}),
|
|
422
403
|
Action.make("getCapacity", {
|
|
423
404
|
...read,
|
|
405
|
+
error: [
|
|
406
|
+
Invalid,
|
|
407
|
+
Precondition,
|
|
408
|
+
Internal
|
|
409
|
+
],
|
|
424
410
|
description: "Read the host's limits and how much of each is in use. It fails when the runtime can't be read.",
|
|
425
411
|
success: Schema.Array(Limit)
|
|
426
412
|
})
|
|
427
413
|
], { prefix: "/api" });
|
|
414
|
+
const actionsByName = Action.byName(Http.actions);
|
|
415
|
+
//#endregion
|
|
416
|
+
//#region src/release.ts
|
|
417
|
+
/** Also the version of the clankerbox binaries it ships with. */
|
|
418
|
+
const version = "0.16.0";
|
|
419
|
+
/** major.minor: releases that share it speak the same API. */
|
|
420
|
+
const release = (of) => of.split(".").slice(0, 2).join(".");
|
|
421
|
+
/** A host refuses a request whose `release` differs from its own, or that has none. */
|
|
422
|
+
const versionHeader = "clankerbox-version";
|
|
428
423
|
//#endregion
|
|
429
424
|
//#region src/text.ts
|
|
430
|
-
/** How sizes, decode failures and file failures read in a message or a table cell. */
|
|
431
425
|
const mibPerGib = 1024;
|
|
432
|
-
/**
|
|
433
|
-
|
|
434
|
-
* `1.5G`, and MiB when it isn't, `512M` or `1500M`.
|
|
435
|
-
*/
|
|
436
|
-
const formatMib = (mib) => mib >= mibPerGib && mib % (mibPerGib / 2) === 0 ? `${mib / mibPerGib}G` : `${mib}M`;
|
|
437
|
-
/**
|
|
438
|
-
* Where a decode failed, as a JSON path: `hosts[0].id`. `names` gives a top-level field the
|
|
439
|
-
* name its caller knows it by.
|
|
440
|
-
*/
|
|
426
|
+
/** Exactly, as a size flag takes it: `32G`, `1.5G`, `512M`, `1500M`. */
|
|
427
|
+
const formatMib = (mib) => mib >= 1024 && mib % 512 === 0 ? `${mib / mibPerGib}G` : `${mib}M`;
|
|
441
428
|
const jsonPath = (path, names) => path.map((key, index) => {
|
|
442
429
|
if (Number.isInteger(key)) return `[${String(key)}]`;
|
|
443
430
|
return index === 0 ? names?.get(key) ?? String(key) : `.${String(key)}`;
|
|
444
431
|
}).join("");
|
|
445
432
|
const flatten = SchemaIssue.makeFormatterStandardSchemaV1({ leafHook: (issue) => Match.value(issue).pipe(Match.tag("MissingKey", () => "missing"), Match.tag("UnexpectedKey", () => "not a known field"), Match.orElse(SchemaIssue.defaultLeafHook)) });
|
|
446
|
-
/**
|
|
447
|
-
* Everything a decode refused, on one line, each under its JSON path: `cpu: missing; ram: not a
|
|
448
|
-
* known field`. `names` renames top-level fields, as when a flag set each: `--cpu: missing`.
|
|
449
|
-
*/
|
|
433
|
+
/** On one line: `cpu: missing; ram: not a known field`. `names` renames top-level fields. */
|
|
450
434
|
const describeIssues = (error, names) => flatten(error.issue).issues.map(({ path = [], message }) => {
|
|
451
435
|
const keys = path.map((segment) => Predicate.isPropertyKey(segment) ? segment : segment.key);
|
|
452
436
|
return keys.length === 0 ? message : `${jsonPath(keys, names)}: ${message}`;
|
|
453
437
|
}).join("; ");
|
|
454
438
|
/**
|
|
455
|
-
*
|
|
456
|
-
*
|
|
439
|
+
* Node describes a failure only in its cause, so what has no wording here reads as the system's
|
|
440
|
+
* own.
|
|
457
441
|
*/
|
|
458
442
|
const describeFileError = ({ reason }) => {
|
|
459
443
|
if (Predicate.isTagged(reason, "NotFound")) return "no such file or directory";
|
|
@@ -462,47 +446,38 @@ const describeFileError = ({ reason }) => {
|
|
|
462
446
|
};
|
|
463
447
|
//#endregion
|
|
464
448
|
//#region src/client.ts
|
|
465
|
-
/**
|
|
466
|
-
* The client library: a host list in placement order, routing by ID, list fan-out and
|
|
467
|
-
* create placement. Nothing here retries: a mutation whose reply is lost, or that outlasts
|
|
468
|
-
* the client's mutation timeout, is `Unavailable`, and the caller reads the resource to see
|
|
469
|
-
* what happened.
|
|
470
|
-
*/
|
|
471
449
|
var client_exports = /* @__PURE__ */ __exportAll({
|
|
472
450
|
Client: () => Client,
|
|
473
451
|
HostEntry: () => HostEntry,
|
|
452
|
+
layer: () => layer,
|
|
474
453
|
make: () => make
|
|
475
454
|
});
|
|
476
|
-
/**
|
|
455
|
+
/** `id` is the host part of every ID the host holds. */
|
|
477
456
|
const HostEntry = Schema.Struct({
|
|
478
457
|
id: HostId,
|
|
479
458
|
url: Schema.String.check(Schema.makeFilter((url) => URL.canParse(url) || "a URL, such as http://linux:8484"))
|
|
480
459
|
});
|
|
460
|
+
/**
|
|
461
|
+
* Nothing retries: a mutation whose reply is lost or times out is `Unavailable`, and the caller
|
|
462
|
+
* reads the resource to see what happened.
|
|
463
|
+
*/
|
|
481
464
|
var Client = class extends Context.Service()("@gjermundgaraba/clankerbox-sdk/Client") {};
|
|
482
465
|
/**
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
* sent, so a `SchemaError` that reaches `settle` is a reply that didn't decode.
|
|
466
|
+
* Input that doesn't encode fails as `InvalidInput` before anything is sent, so a `SchemaError`
|
|
467
|
+
* that reaches `settle` is a reply that didn't decode.
|
|
486
468
|
*/
|
|
487
|
-
const
|
|
469
|
+
const hostApi = (entry) => ActionHttp.client(Http, {
|
|
488
470
|
baseUrl: entry.url,
|
|
489
471
|
transformClient: HttpClient.mapRequest(HttpClientRequest.setHeader(versionHeader, version))
|
|
490
472
|
});
|
|
491
|
-
/** Every action's contract, by name: a call's timeout and retry follow its `readOnly`. */
|
|
492
|
-
const actions = Action.byName(Http.actions);
|
|
493
473
|
const causeDetail = (error) => error.cause instanceof Error ? error.cause.message : error.message;
|
|
494
|
-
/**
|
|
495
|
-
* No reply came: the connection failed or dropped, or the timeout passed. A mutation's
|
|
496
|
-
* request may or may not have reached its host, so it may have run.
|
|
497
|
-
*/
|
|
498
474
|
const unanswered = (context, detail) => context.readOnly ? new Unavailable({
|
|
499
|
-
message: `host ${context.host.id} (${context.host.url}) didn't answer ${context.
|
|
500
|
-
readOnly:
|
|
475
|
+
message: `host ${context.host.id} (${context.host.url}) didn't answer ${context.what}: ${detail}`,
|
|
476
|
+
readOnly: true
|
|
501
477
|
}) : new Unavailable({
|
|
502
|
-
message: `no reply from host ${context.host.id} to ${context.
|
|
503
|
-
readOnly:
|
|
478
|
+
message: `no reply from host ${context.host.id} to ${context.what} (${detail}); the action may have run: read ${context.target ?? "the resource"} to see`,
|
|
479
|
+
readOnly: false
|
|
504
480
|
});
|
|
505
|
-
/** Bounds the wait for the reply; the clock starts when the request is sent. */
|
|
506
481
|
const bounded = (call, context) => {
|
|
507
482
|
const { timeout } = context;
|
|
508
483
|
return timeout === void 0 ? call : Effect.timeoutOrElse(call, {
|
|
@@ -510,32 +485,36 @@ const bounded = (call, context) => {
|
|
|
510
485
|
orElse: () => Effect.fail(unanswered(context, `timed out after ${Duration.format(timeout)}`))
|
|
511
486
|
});
|
|
512
487
|
};
|
|
513
|
-
|
|
514
|
-
const unexpected = (context, error) => new Internal({ message: `host ${context.host.id} answered ${context.action} unexpectedly: ${error.message}` });
|
|
488
|
+
const unexpected = (context, error) => new Internal({ message: `host ${context.host.id} answered ${context.what} unexpectedly: ${error.message}` });
|
|
515
489
|
const settle = (call, context) => bounded(call, context).pipe(Effect.catchTags({
|
|
516
490
|
HttpClientError: (error) => Effect.fail(Match.value(error.reason).pipe(Match.tag("TransportError", () => unanswered(context, causeDetail(error))), Match.when({
|
|
517
491
|
_tag: "DecodeError",
|
|
518
492
|
cause: (cause) => cause !== void 0 && !(cause instanceof SyntaxError)
|
|
519
493
|
}, () => unanswered(context, causeDetail(error))), Match.orElse(() => unexpected(context, error)))),
|
|
520
|
-
InvalidInput: (error) => Effect.fail(new Invalid({ message: `${context.
|
|
494
|
+
InvalidInput: (error) => Effect.fail(new Invalid({ message: `${context.what}: the request doesn't match the action's input: ${error.message}` })),
|
|
521
495
|
SchemaError: (error) => Effect.fail(new Internal({ message: `host ${context.host.id}'s reply didn't decode: ${error.message}` })),
|
|
522
496
|
Unauthenticated: (error) => Effect.fail(unexpected(context, error)),
|
|
523
497
|
Forbidden: (error) => Effect.fail(unexpected(context, error))
|
|
524
498
|
}));
|
|
525
|
-
|
|
499
|
+
const encodeError = Schema.encodeSync(ClankerboxError);
|
|
500
|
+
const decodeError = Schema.decodeSync(ClankerboxError);
|
|
501
|
+
/** The error with the call's location set, and every other field it had. */
|
|
502
|
+
const locate = (where) => (error) => decodeError({
|
|
503
|
+
...encodeError(error),
|
|
504
|
+
...where
|
|
505
|
+
});
|
|
526
506
|
const checkHost = (entry, host) => host.id === entry.id ? Effect.succeed(host) : Effect.fail(new Invalid({ message: `host ${entry.id} (${entry.url}) calls itself ${host.id}; its entry in the host list must use that ID` }));
|
|
527
|
-
/** Every entry is checked here, so a call never meets a malformed URL. */
|
|
528
507
|
const decodeHosts = Schema.decodeUnknownEffect(Schema.Array(HostEntry));
|
|
529
508
|
/**
|
|
530
|
-
*
|
|
531
|
-
*
|
|
509
|
+
* A client over `hosts`, in placement order. It sends nothing until called. An empty list, a
|
|
510
|
+
* malformed entry or a host ID that appears twice is Invalid.
|
|
532
511
|
*/
|
|
533
512
|
const make = (hosts, options) => Effect.gen(function* () {
|
|
534
513
|
if (hosts.length === 0) return yield* new Invalid({ message: "the host list is empty: a client needs a host" });
|
|
535
514
|
yield* decodeHosts(hosts, { errors: "all" }).pipe(Effect.mapError((error) => new Invalid({ message: `host list: ${describeIssues(error)}` })));
|
|
536
515
|
const twice = hosts.find(({ id }, index) => hosts.findIndex((other) => other.id === id) < index);
|
|
537
516
|
if (twice !== void 0) return yield* new Invalid({ message: `host ${twice.id} appears twice in the host list` });
|
|
538
|
-
const routed = yield* Effect.forEach(hosts, (entry) => Effect.map(
|
|
517
|
+
const routed = yield* Effect.forEach(hosts, (entry) => Effect.map(hostApi(entry), (api) => ({
|
|
539
518
|
entry,
|
|
540
519
|
api
|
|
541
520
|
})));
|
|
@@ -545,25 +524,34 @@ const make = (hosts, options) => Effect.gen(function* () {
|
|
|
545
524
|
return found === void 0 ? Effect.fail(new Invalid({ message: `host ${host} isn't in the host list` })) : Effect.succeed(found);
|
|
546
525
|
};
|
|
547
526
|
/**
|
|
548
|
-
*
|
|
549
|
-
*
|
|
527
|
+
* A call of `name` on one host, about `subject`. Its errors carry the host, the action and
|
|
528
|
+
* `target`, the resource it reads, changes or makes.
|
|
550
529
|
*/
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
530
|
+
const send = ({ entry, api }, name, input, subject, target = subject) => {
|
|
531
|
+
const { readOnly } = actionsByName[name];
|
|
532
|
+
const method = api[name];
|
|
533
|
+
return settle(method(input), {
|
|
534
|
+
host: entry,
|
|
535
|
+
timeout: readOnly ? options?.readTimeout ?? readTimeout : options?.timeout,
|
|
536
|
+
readOnly,
|
|
537
|
+
what: subject === void 0 ? name : `${name} ${subject}`,
|
|
538
|
+
target
|
|
539
|
+
}).pipe(Effect.mapError(locate(target === void 0 ? {
|
|
540
|
+
action: name,
|
|
541
|
+
host: entry.id
|
|
542
|
+
} : {
|
|
543
|
+
action: name,
|
|
544
|
+
host: entry.id,
|
|
545
|
+
id: target
|
|
546
|
+
})));
|
|
547
|
+
};
|
|
558
548
|
/**
|
|
559
|
-
*
|
|
560
|
-
*
|
|
549
|
+
* A call of `name` on the host of `id`. A fork, restore or capture names the one it makes as
|
|
550
|
+
* `made`, which its errors then carry, so a caller can find what a failure left behind.
|
|
561
551
|
*/
|
|
562
|
-
const byId = (
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
});
|
|
566
|
-
/** Asks every host at once; a host that fails is named rather than failing the whole. */
|
|
552
|
+
const byId = (name, id, input, made) => Effect.flatMap(Effect.flatMap(parseId(id), ({ host }) => route(host)), (found) => send(found, name, input, id, made ?? id));
|
|
553
|
+
/** An error raised before the call is sent names its action too. */
|
|
554
|
+
const naming = (name) => (effect) => Effect.mapError(effect, (error) => error.action === void 0 ? locate({ action: name })(error) : error);
|
|
567
555
|
const gather = (call) => Effect.forEach(routed, (route) => Effect.map(Effect.result(call(route)), (result) => ({
|
|
568
556
|
host: route.entry.id,
|
|
569
557
|
result
|
|
@@ -580,30 +568,27 @@ const make = (hosts, options) => Effect.gen(function* () {
|
|
|
580
568
|
unreachable
|
|
581
569
|
};
|
|
582
570
|
}));
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
const readHost = (route) => ask(route, actions.getHost, "get host", (api) => Effect.flatMap(api.getHost(), (host) => checkHost(route.entry, host)));
|
|
571
|
+
const readHost = (route) => Effect.flatMap(send(route, "getHost", {}), (host) => checkHost(route.entry, host)).pipe(Effect.mapError(locate({
|
|
572
|
+
action: "getHost",
|
|
573
|
+
host: route.entry.id
|
|
574
|
+
})));
|
|
588
575
|
const createOn = (host, name, spec) => Effect.gen(function* () {
|
|
589
576
|
const id = yield* formatId(host, name);
|
|
590
|
-
return yield*
|
|
577
|
+
return yield* send(yield* route(host), "createMachine", {
|
|
591
578
|
...spec,
|
|
592
579
|
id
|
|
593
|
-
}
|
|
580
|
+
}, id);
|
|
594
581
|
});
|
|
595
582
|
/**
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
* a host gave no reply; otherwise asking again changes nothing, and it is `Precondition`.
|
|
583
|
+
* The first host in list order that offers `base`; the requests still out are interrupted,
|
|
584
|
+
* so a silent host after it delays nothing. With no winner it is `Unavailable` only if a
|
|
585
|
+
* host gave no reply: otherwise asking again changes nothing, and it is `Precondition`.
|
|
600
586
|
*/
|
|
601
587
|
const offering = (base) => Effect.gen(function* () {
|
|
602
588
|
const asked = yield* Effect.forEach(routed, (route) => Effect.map(Effect.forkScoped(Effect.result(readHost(route))), (fiber) => ({
|
|
603
589
|
host: route.entry.id,
|
|
604
590
|
fiber
|
|
605
591
|
})));
|
|
606
|
-
/** What each host answered, in list order. */
|
|
607
592
|
const misses = [];
|
|
608
593
|
let noReply = false;
|
|
609
594
|
for (const { host, fiber } of asked) {
|
|
@@ -629,34 +614,34 @@ const make = (hosts, options) => Effect.gen(function* () {
|
|
|
629
614
|
return yield* createOn((yield* offering(spec.base)).id, name, spec);
|
|
630
615
|
});
|
|
631
616
|
const place = (base, options) => options?.host === void 0 ? offering(base) : Effect.flatMap(route(options.host), readHost);
|
|
632
|
-
/** The ID a call that makes a resource on `source`'s host gives it. */
|
|
633
617
|
const sibling = (source, name) => Effect.flatMap(parseId(source), ({ host }) => formatId(host, name));
|
|
634
618
|
return {
|
|
635
619
|
hosts: gather((route) => Effect.map(readHost(route), (host) => [host])),
|
|
636
|
-
machines: gather((route) =>
|
|
637
|
-
checkpoints: gather((route) =>
|
|
638
|
-
capacity: gather((route) =>
|
|
639
|
-
machine: (id) => byId(
|
|
640
|
-
checkpoint: (id) => byId(
|
|
641
|
-
place,
|
|
642
|
-
create,
|
|
643
|
-
start: (id) => byId(
|
|
644
|
-
stop: (id) => byId(
|
|
645
|
-
delete: (id) => byId(
|
|
646
|
-
fork: (machine, name) => Effect.flatMap(sibling(machine, name), (made) => byId(
|
|
620
|
+
machines: gather((route) => send(route, "listMachines", {})),
|
|
621
|
+
checkpoints: gather((route) => send(route, "listCheckpoints", {})),
|
|
622
|
+
capacity: gather((route) => send(route, "getCapacity", {})),
|
|
623
|
+
machine: (id) => naming("getMachine")(byId("getMachine", id, { id })),
|
|
624
|
+
checkpoint: (id) => naming("getCheckpoint")(byId("getCheckpoint", id, { id })),
|
|
625
|
+
place: (base, options) => naming("getHost")(place(base, options)),
|
|
626
|
+
create: (name, spec, options) => naming("createMachine")(create(name, spec, options)),
|
|
627
|
+
start: (id) => naming("startMachine")(byId("startMachine", id, { id })),
|
|
628
|
+
stop: (id) => naming("stopMachine")(byId("stopMachine", id, { id })),
|
|
629
|
+
delete: (id) => naming("deleteMachine")(byId("deleteMachine", id, { id })),
|
|
630
|
+
fork: (machine, name) => naming("forkMachine")(Effect.flatMap(sibling(machine, name), (made) => byId("forkMachine", machine, {
|
|
647
631
|
machine,
|
|
648
632
|
name
|
|
649
|
-
}
|
|
650
|
-
restore: (checkpoint, name) => Effect.flatMap(sibling(checkpoint, name), (made) => byId(
|
|
633
|
+
}, made))),
|
|
634
|
+
restore: (checkpoint, name) => naming("restoreMachine")(Effect.flatMap(sibling(checkpoint, name), (made) => byId("restoreMachine", checkpoint, {
|
|
651
635
|
checkpoint,
|
|
652
636
|
name
|
|
653
|
-
}
|
|
654
|
-
capture: (machine, name) => Effect.flatMap(sibling(machine, name), (made) => byId(
|
|
637
|
+
}, made))),
|
|
638
|
+
capture: (machine, name) => naming("captureCheckpoint")(Effect.flatMap(sibling(machine, name), (made) => byId("captureCheckpoint", machine, {
|
|
655
639
|
machine,
|
|
656
640
|
name
|
|
657
|
-
}
|
|
658
|
-
deleteCheckpoint: (id) => byId(
|
|
641
|
+
}, made))),
|
|
642
|
+
deleteCheckpoint: (id) => naming("deleteCheckpoint")(byId("deleteCheckpoint", id, { id }))
|
|
659
643
|
};
|
|
660
644
|
});
|
|
645
|
+
const layer = (hosts, options) => Layer.effect(Client, make(hosts, options));
|
|
661
646
|
//#endregion
|
|
662
|
-
export {
|
|
647
|
+
export { Name as A, Invalid as B, MachineSpec as C, SshLogin as D, SshEndpoint as E, ClankerboxError as F, Precondition as H, Conflict as I, ConflictKind as L, parseId as M, parseName as N, HostId as O, Capacity as P, HostErrorTag as R, Machine as S, Setup as T, Unavailable as U, NotFound as V, hostErrors as W, CheckpointActionName as _, formatMib as a, Host as b, version as c, actionsByName as d, readTimeout as f, Checkpoint as g, ActionStatus as h, describeIssues as i, formatId as j, Id as k, versionHeader as l, ActionRecord as m, layer as n, mibPerGib as o, ActionName as p, describeFileError as r, release as s, client_exports as t, Http as u, CheckpointActionRecord as v, Runtime as w, Limit as x, CreateRequest as y, Internal as z };
|