@gjermundgaraba/clankerbox-sdk 0.15.3 → 0.15.5

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.
@@ -129,7 +129,7 @@ const formatId = (host, name) => {
129
129
  };
130
130
  //#endregion
131
131
  //#region package.json
132
- var version$1 = "0.15.3";
132
+ var version$1 = "0.15.5";
133
133
  //#endregion
134
134
  //#region src/resources.ts
135
135
  /** The resources a host reports: machines, checkpoints and the host itself. */
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { A as Invalid, C as ClankerboxError, D as HostError, E as ErrorTag, M as Precondition, N as Unavailable, O as HostErrorTag, P as hostErrors, S as Capacity, T as ConflictKind, _ as Setup, b as release, c as ActionRecord, d as Host, f as Limit, g as Runtime, h as Profile, i as Interface, j as NotFound, k as Internal, l as Checkpoint, m as MachineSpec, n as CreateOptions, o as client_d_exports, p as Machine, s as ActionName, u as CreateRequest, v as SshEndpoint, w as Conflict, x as version, y as SshLogin } from "./client-Cn1bj4It.mjs";
2
2
  import * as Action from "@gjermundgaraba/effect-actions/Action";
3
3
  import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
4
- import { Duration, Effect, FileSystem, Path, PlatformError, Schema } from "effect";
4
+ import { Duration, Effect, FileSystem, Path, PlatformError, Schema, Scope } from "effect";
5
5
  //#region src/api.d.ts
6
6
  /**
7
7
  * How long a client waits for a read's reply from one host, unless it sets its own. A host
@@ -381,6 +381,28 @@ export declare const packRecipe: (directory: string) => Effect.Effect<string, In
381
381
  /** Reads a setup: a script file's text, or a recipe directory packed into one script. */
382
382
  export declare const readSetup: (path: string) => Effect.Effect<string, Invalid, FileSystem.FileSystem>;
383
383
  //#endregion
384
+ //#region src/ssh.d.ts
385
+ /** What ssh needs of a machine: where it is reached, as whom, and the host key to pin. */
386
+ export interface SshTarget {
387
+ readonly id: string;
388
+ readonly login: SshLogin;
389
+ readonly hostKey: string;
390
+ }
391
+ /**
392
+ * The machine's SSH target, or what it lacks: an endpoint, which only a running machine has, or
393
+ * a host key, which preparation records.
394
+ */
395
+ export declare const sshTarget: (machine: Machine) => SshTarget | {
396
+ readonly lacks: "SSH endpoint" | "SSH host key on record";
397
+ };
398
+ /**
399
+ * Writes a one-line known-hosts file pinning the target's host key, and returns the arguments
400
+ * that run ssh against it with strict host key checking, as `user`. The file lasts as long as
401
+ * the scope, which must outlast the ssh it is for. Ports change on fork and restore, and on
402
+ * boat at every start, so a target is read anew for each ssh.
403
+ */
404
+ export declare const pinnedSsh: (target: SshTarget, user: string, extra: ReadonlyArray<string>) => Effect.Effect<Array<string>, PlatformError.PlatformError, FileSystem.FileSystem | Path.Path | Scope.Scope>;
405
+ //#endregion
384
406
  //#region src/text.d.ts
385
407
  /**
386
408
  * A RAM size, exactly, as a size flag takes one: GiB when it is whole or half GiB, `32G` or
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { A as parseName, B as Unavailable, C as release, D as Name, E as Id, F as HostErrorTag, I as Internal, L as Invalid, M as Conflict, N as ConflictKind, O as formatId, P as ErrorTag, R as NotFound, S as SshLogin, T as HostId, V as hostErrors, _ as MachineSpec, a as describeIssues, b as Setup, c as readTimeout, d as ActionRecord, f as Checkpoint, g as Machine, h as Limit, i as describeFileError, j as Capacity, k as parseId, l as versionHeader, m as Host, n as client_exports, o as formatMib, p as CreateRequest, s as Http, u as ActionName, v as Profile, w as version, x as SshEndpoint, y as Runtime, z as Precondition } from "./client-CtI-rSJQ.mjs";
2
+ import { A as parseName, B as Unavailable, C as release, D as Name, E as Id, F as HostErrorTag, I as Internal, L as Invalid, M as Conflict, N as ConflictKind, O as formatId, P as ErrorTag, R as NotFound, S as SshLogin, T as HostId, V as hostErrors, _ as MachineSpec, a as describeIssues, b as Setup, c as readTimeout, d as ActionRecord, f as Checkpoint, g as Machine, h as Limit, i as describeFileError, j as Capacity, k as parseId, l as versionHeader, m as Host, n as client_exports, o as formatMib, p as CreateRequest, s as Http, u as ActionName, v as Profile, w as version, x as SshEndpoint, y as Runtime, z as Precondition } from "./client-C2Z7F3Tu.mjs";
3
3
  import { Effect, FileSystem, Option, Path, Predicate, Schema } from "effect";
4
4
  //#region src/prebuild.ts
5
5
  /**
@@ -218,4 +218,71 @@ const readSetup = (path) => Effect.gen(function* () {
218
218
  return yield* fs.readFileString(path);
219
219
  }).pipe(Effect.catchTag("PlatformError", (error) => Effect.fail(new Invalid({ message: `couldn't read setup ${path}: ${describeFileError(error)}` }))));
220
220
  //#endregion
221
- export { ActionName, ActionRecord, Capacity, Checkpoint, client_exports as Client, Conflict, ConflictKind, CreateRequest, ErrorTag, Host, HostErrorTag, HostId, Http, Id, Internal, Invalid, Limit, Machine, MachineSpec, Name, NotFound, prebuild_exports as Prebuild, Precondition, Profile, Runtime, Setup, SshEndpoint, SshLogin, Unavailable, describeFileError, describeIssues, formatId, formatMib, hostErrors, loadProfile, packRecipe, parseId, parseName, readSetup, readTimeout, release, version, versionHeader };
221
+ //#region src/ssh.ts
222
+ /**
223
+ * The system `ssh`'s arguments for a machine, pinned to its host key: what `clankerbox ssh` runs,
224
+ * and what anything else that reaches a guest through port 22 does, such as a port forward.
225
+ */
226
+ /**
227
+ * The machine's SSH target, or what it lacks: an endpoint, which only a running machine has, or
228
+ * a host key, which preparation records.
229
+ */
230
+ const sshTarget = (machine) => {
231
+ const { id, ssh: login, hostKey } = machine;
232
+ if (login === void 0) return { lacks: "SSH endpoint" };
233
+ return hostKey === void 0 ? { lacks: "SSH host key on record" } : {
234
+ id,
235
+ login,
236
+ hostKey
237
+ };
238
+ };
239
+ /**
240
+ * The known-hosts line pinning the machine's host key under its ID: ssh looks the key up by
241
+ * `HostKeyAlias`, so the line doesn't depend on the endpoint, which changes.
242
+ */
243
+ const knownHostsLine = (id, hostKey) => {
244
+ const [type = "", key = ""] = hostKey.trim().split(/\s+/u);
245
+ return `${id} ${type} ${key}\n`;
246
+ };
247
+ /**
248
+ * ssh's arguments. OpenSSH keeps the first user it is given, so the leading `-l` decides it:
249
+ * one in the caller's arguments or in an ssh config doesn't apply. The destination is the
250
+ * machine's ID, so a `Host <pattern>` block in the user's ssh config can set keys and options
251
+ * per machine; the endpoint and the pinned host key come from command-line options, which no
252
+ * config overrides. The caller's arguments go after the destination, where OpenSSH still reads
253
+ * options (`-L …`) before a remote command.
254
+ */
255
+ const sshArguments = (target, user, knownHosts, extra) => [
256
+ "-l",
257
+ user,
258
+ "-o",
259
+ `HostName=${target.login.host}`,
260
+ "-o",
261
+ `Port=${target.login.port}`,
262
+ "-o",
263
+ `HostKeyAlias=${target.id}`,
264
+ "-o",
265
+ `UserKnownHostsFile=${knownHosts}`,
266
+ "-o",
267
+ "GlobalKnownHostsFile=/dev/null",
268
+ "-o",
269
+ "StrictHostKeyChecking=yes",
270
+ target.id,
271
+ ...extra
272
+ ];
273
+ /**
274
+ * Writes a one-line known-hosts file pinning the target's host key, and returns the arguments
275
+ * that run ssh against it with strict host key checking, as `user`. The file lasts as long as
276
+ * the scope, which must outlast the ssh it is for. Ports change on fork and restore, and on
277
+ * boat at every start, so a target is read anew for each ssh.
278
+ */
279
+ const pinnedSsh = (target, user, extra) => Effect.gen(function* () {
280
+ const fs = yield* FileSystem.FileSystem;
281
+ const path = yield* Path.Path;
282
+ const directory = yield* fs.makeTempDirectoryScoped({ prefix: "clankerbox-ssh-" });
283
+ const knownHosts = path.join(directory, "known_hosts");
284
+ yield* fs.writeFileString(knownHosts, knownHostsLine(target.id, target.hostKey));
285
+ return sshArguments(target, user, knownHosts, extra);
286
+ });
287
+ //#endregion
288
+ export { ActionName, ActionRecord, Capacity, Checkpoint, client_exports as Client, Conflict, ConflictKind, CreateRequest, ErrorTag, Host, HostErrorTag, HostId, Http, Id, Internal, Invalid, Limit, Machine, MachineSpec, Name, NotFound, prebuild_exports as Prebuild, Precondition, Profile, Runtime, Setup, SshEndpoint, SshLogin, Unavailable, describeFileError, describeIssues, formatId, formatMib, hostErrors, loadProfile, packRecipe, parseId, parseName, pinnedSsh, readSetup, readTimeout, release, sshTarget, version, versionHeader };
package/dist/node.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { r as make, t as Client } from "./client-CtI-rSJQ.mjs";
1
+ import { r as make, t as Client } from "./client-C2Z7F3Tu.mjs";
2
2
  import { Layer } from "effect";
3
3
  import * as NodeHttpClient from "@effect/platform-node/NodeHttpClient";
4
4
  //#region src/node.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjermundgaraba/clankerbox-sdk",
3
- "version": "0.15.3",
3
+ "version": "0.15.5",
4
4
  "description": "clankerbox's API contract and client library, for Effect",
5
5
  "license": "MIT",
6
6
  "repository": {