signalk-container-helper 0.6.1 → 0.8.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 +197 -18
- package/dist/endpoint.d.ts +93 -0
- package/dist/endpoint.d.ts.map +1 -0
- package/dist/endpoint.js +109 -0
- package/dist/endpoint.js.map +1 -0
- package/dist/host-device.d.ts +41 -0
- package/dist/host-device.d.ts.map +1 -0
- package/dist/host-device.js +50 -0
- package/dist/host-device.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +47 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +71 -0
- package/dist/schema.js.map +1 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/components.d.ts +30 -0
- package/dist/ui/components.d.ts.map +1 -1
- package/dist/ui/components.js +35 -0
- package/dist/ui/components.js.map +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/ui/index.js.map +1 -1
- package/dist/url.d.ts +39 -0
- package/dist/url.d.ts.map +1 -0
- package/dist/url.js +138 -0
- package/dist/url.js.map +1 -0
- package/dist/util.d.ts +1 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js.map +1 -1
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -157,6 +157,28 @@ field from _its own_ app object — so you get
|
|
|
157
157
|
| A private writable area in the SK data tree | `signalkDataMount` (signalk-container's) |
|
|
158
158
|
| The whole SK config root | `signalkConfigRootMount` (incl. `security.json`) |
|
|
159
159
|
| **Your own plugin's data dir** | **`resolveMount()`** + a `volumes` entry |
|
|
160
|
+
| **Is there a GPU / device on the host?** | **`probeHostDevice()`** |
|
|
161
|
+
|
|
162
|
+
`probeHostDevice(manager, path)` answers whether the **host** has a device,
|
|
163
|
+
which a plugin cannot determine itself — `stat("/dev/dri")` describes the
|
|
164
|
+
plugin's own filesystem, and that is the Signal K container whenever Signal K
|
|
165
|
+
is containerized:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { probeHostDevice } from "signalk-container-helper";
|
|
169
|
+
|
|
170
|
+
const gpu = await probeHostDevice(manager, "/dev/dri");
|
|
171
|
+
if (gpu?.exists) {
|
|
172
|
+
config.devices = ["/dev/dri"];
|
|
173
|
+
config.groupAdd = gpu.groups; // names, resolved on the host
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`null` means **unknown** (no runtime, or the host could not be read) and is
|
|
178
|
+
deliberately distinct from `{ exists: false }` — assume no device, but do not
|
|
179
|
+
report it as absent. Throws `ContainerHelperError('unsupported-manager')` on
|
|
180
|
+
signalk-container older than 1.30.0, so catch it if you would rather degrade
|
|
181
|
+
than fail. Works on both Docker and rootless podman.
|
|
160
182
|
|
|
161
183
|
`resolveMount` translates any absolute host path into a mount the container
|
|
162
184
|
runtime can actually bind, on bare-metal and containerized Signal K alike:
|
|
@@ -272,6 +294,158 @@ Why not `manager.getState()` for health? signalk-container namespace-prefixes th
|
|
|
272
294
|
containers it manages (`sk-<name>`); externally-managed peers don't carry the prefix,
|
|
273
295
|
so the manager can't see them — and "running" isn't "healthy" anyway.
|
|
274
296
|
|
|
297
|
+
## Managed or self-hosted
|
|
298
|
+
|
|
299
|
+
Some operators would rather run the service themselves — on a NAS, another Pi, or
|
|
300
|
+
a box that is already doing the job — and point the plugin at it. That is a
|
|
301
|
+
config switch (`managedContainer`) plus an address, and every plugin that offers
|
|
302
|
+
it had written its own.
|
|
303
|
+
|
|
304
|
+
**Scope, precisely:** this means the _service_ runs elsewhere. It does **not**
|
|
305
|
+
mean a container _engine_ elsewhere — signalk-container talks to local unix
|
|
306
|
+
sockets only and rejects a `tcp://` endpoint, so there is no remote-engine mode
|
|
307
|
+
to opt into.
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
import {
|
|
311
|
+
resolveEndpoint,
|
|
312
|
+
waitForEndpointReady,
|
|
313
|
+
isManagedMode,
|
|
314
|
+
} from "signalk-container-helper";
|
|
315
|
+
|
|
316
|
+
const endpoint = await resolveEndpoint({
|
|
317
|
+
managed: settings.managedContainer, // pass the RAW config value
|
|
318
|
+
externalUrl: settings.externalUrl,
|
|
319
|
+
container, // a started ManagedContainer; managed mode only
|
|
320
|
+
port: 3020,
|
|
321
|
+
productName: "signalk-tailscale-server",
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
await waitForEndpointReady(endpoint, { path: "/api/health" });
|
|
325
|
+
const client = new ShimClient(endpoint.baseUrl);
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
`endpoint.baseUrl` is scheme-ful with no trailing slash in both modes, so the
|
|
329
|
+
transport half of your plugin stops caring which mode it is in. `endpoint.mode`
|
|
330
|
+
is there for the parts that legitimately differ.
|
|
331
|
+
|
|
332
|
+
### Pass the raw config value
|
|
333
|
+
|
|
334
|
+
`managed` is `boolean | undefined`, and **`undefined` means managed**. Signal K
|
|
335
|
+
calls `plugin.start()` with `{}` when a plugin is enabled without its form ever
|
|
336
|
+
being saved, so `Boolean(config.managedContainer)` would flip exactly those
|
|
337
|
+
installs into self-hosted mode with an empty URL — a plugin that used to work
|
|
338
|
+
now errors on enable. Use `isManagedMode(...)` anywhere you need the mode
|
|
339
|
+
question on its own (route guards, status fields).
|
|
340
|
+
|
|
341
|
+
### Readiness retries are opt-in
|
|
342
|
+
|
|
343
|
+
`waitForEndpointReady` makes **one** attempt unless you pass `retry`. The
|
|
344
|
+
reference plugins genuinely differ here — one retries forever because an
|
|
345
|
+
unattended boat has to heal itself, another reports once and waits for the
|
|
346
|
+
operator — so neither behaviour is imposed:
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
await waitForEndpointReady(endpoint, {
|
|
350
|
+
path: "/api/health",
|
|
351
|
+
signal,
|
|
352
|
+
retry: {
|
|
353
|
+
minDelayMs: 15_000,
|
|
354
|
+
onAttemptFailed: (err, next) =>
|
|
355
|
+
app.setPluginError(
|
|
356
|
+
`${endpoint.baseUrl} unreachable: ${errMsg(err)} — retrying in ${Math.round(next / 1000)}s`,
|
|
357
|
+
),
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### The config fields
|
|
363
|
+
|
|
364
|
+
`managedModeSchema` emits the two form fields as **plain JSON Schema**. It has to
|
|
365
|
+
be plain: consumers are split between `typebox` 1.x and `@sinclair/typebox` 0.34,
|
|
366
|
+
which are mutually incompatible, and this library depends on neither.
|
|
367
|
+
|
|
368
|
+
Splice each fragment with `Type.Unsafe`. The call is written identically in both
|
|
369
|
+
packages:
|
|
370
|
+
|
|
371
|
+
```ts
|
|
372
|
+
import { Type, type Static } from "@sinclair/typebox"; // or "typebox"
|
|
373
|
+
import { managedModeSchema } from "signalk-container-helper/schema";
|
|
374
|
+
|
|
375
|
+
const MODE = managedModeSchema({
|
|
376
|
+
productName: "signalk-tailscale-server",
|
|
377
|
+
image: "ghcr.io/dirkwa/signalk-tailscale-server",
|
|
378
|
+
exampleUrl: "http://192.168.1.50:3020",
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
export const ConfigSchema = Type.Object({
|
|
382
|
+
managedContainer: Type.Unsafe<boolean>(MODE.managedContainer),
|
|
383
|
+
externalUrl: Type.Unsafe<string>(MODE.externalUrl),
|
|
384
|
+
// …your own fields
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
export const SCHEMA_DEFAULTS: Config = {
|
|
388
|
+
...MODE.defaults, // { managedContainer: true, externalUrl: "" }
|
|
389
|
+
// …your own defaults
|
|
390
|
+
};
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
`Type.Unsafe` is not optional. A bare fragment spread straight into
|
|
394
|
+
`Type.Object({...})` compiles under `typebox` 1.x and **fails** under
|
|
395
|
+
`@sinclair/typebox` 0.34 (`missing the following properties from type
|
|
396
|
+
'TSchema': params, static, [Kind]`).
|
|
397
|
+
|
|
398
|
+
Two things to know about it. It **asserts** the static type rather than deriving
|
|
399
|
+
it, so keep the type argument and the fragment's `type` in step — nothing checks
|
|
400
|
+
that for you. And `Type.Unsafe` emits the same keys and values as
|
|
401
|
+
`Type.Boolean`/`Type.String` but in a different **order** (`type` first rather
|
|
402
|
+
than last); that is meaningless to JSON Schema and to the Admin UI's form
|
|
403
|
+
renderer, so compare with sorted keys if you assert on the emitted schema while
|
|
404
|
+
migrating.
|
|
405
|
+
|
|
406
|
+
#### Why not just move every plugin to `typebox` 1.x?
|
|
407
|
+
|
|
408
|
+
Because it would not remove the split. `@signalk/server-api` depends on
|
|
409
|
+
`@sinclair/typebox` 0.34 itself, so the scoped package sits in every consumer's
|
|
410
|
+
dependency tree regardless of which one the plugin picks for its own schema —
|
|
411
|
+
`signalk-questdb` already resolves **both** today. Migrating a plugin adds a
|
|
412
|
+
second TypeBox beside the first rather than replacing it.
|
|
413
|
+
|
|
414
|
+
Migrating is still reasonable on its own merits: `@sinclair/typebox` is frozen
|
|
415
|
+
at 0.34.52 (TypeBox 1.x was republished under the unscoped `typebox` name
|
|
416
|
+
instead of taking a major bump), so plugins on the scoped package are on a line
|
|
417
|
+
that receives no further fixes. For the constructs these plugins actually use —
|
|
418
|
+
`Object`, `String`, `Boolean`, `Number`, `Literal`, `Union`, `Array` — the two
|
|
419
|
+
versions emit semantically identical JSON Schema (verified; only key order
|
|
420
|
+
differs), and `Type`/`Static` import the same way, so it is a per-plugin import
|
|
421
|
+
swap rather than a port.
|
|
422
|
+
|
|
423
|
+
What it is not is a prerequisite for this API. Emitting plain JSON Schema keeps
|
|
424
|
+
this module out of the question entirely, and works before, during and after any
|
|
425
|
+
such migration.
|
|
426
|
+
|
|
427
|
+
Spreading `MODE.defaults` matters because Signal K uses a schema `default` only
|
|
428
|
+
to seed the _form_, never the config object a plugin receives — which is why
|
|
429
|
+
every consumer hand-writes a `SCHEMA_DEFAULTS` beside its schema, and why those
|
|
430
|
+
two drift.
|
|
431
|
+
|
|
432
|
+
### `managedContainer` means something else in two plugins
|
|
433
|
+
|
|
434
|
+
`signalk-doctor` and `signalk-updater` also have a `managedContainer` option, but
|
|
435
|
+
it defaults to **false** and means _"a systemd Quadlet owns this local
|
|
436
|
+
container"_ — not _"the service is on another host"_. It is a name collision,
|
|
437
|
+
not a variant of this feature: those plugins have no remote endpoint, and
|
|
438
|
+
`AdoptedContainer` is what already models them. Do not wire them to
|
|
439
|
+
`resolveEndpoint`.
|
|
440
|
+
|
|
441
|
+
### A self-hosted upstream has to be resolved, not assumed
|
|
442
|
+
|
|
443
|
+
A consumer that hardcodes a container DNS name (`sk-<name>:<port>`) to reach a
|
|
444
|
+
peer will break when that peer is self-hosted — and, in fact, already breaks on
|
|
445
|
+
a bare-metal or shared-netns host, since signalk-container picks between
|
|
446
|
+
loopback and container DNS depending on the topology. Resolve peer addresses
|
|
447
|
+
rather than assuming one.
|
|
448
|
+
|
|
275
449
|
## Config-panel UI (`signalk-container-helper/ui`)
|
|
276
450
|
|
|
277
451
|
The reference plugins also share a hand-copied React config panel: a container
|
|
@@ -498,24 +672,29 @@ Adopt these even where you don't use the components:
|
|
|
498
672
|
|
|
499
673
|
## API overview
|
|
500
674
|
|
|
501
|
-
| Export
|
|
502
|
-
|
|
|
503
|
-
| `ManagedContainer`
|
|
504
|
-
| `AdoptedContainer`
|
|
505
|
-
| `getContainerManager()`
|
|
506
|
-
| `waitForContainerManager(opts)`
|
|
507
|
-
| `resolveMount(manager, opts)`
|
|
508
|
-
| `
|
|
509
|
-
| `
|
|
510
|
-
| `
|
|
511
|
-
| `
|
|
512
|
-
| `
|
|
513
|
-
| `
|
|
514
|
-
| `
|
|
515
|
-
| `
|
|
516
|
-
| `
|
|
517
|
-
| `
|
|
518
|
-
|
|
|
675
|
+
| Export | Purpose |
|
|
676
|
+
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
677
|
+
| `ManagedContainer` | Full lifecycle: `start`, `stop`, `applyUpdate`, `checkForUpdate`, `getState`, `getInfo`, `resolveAddress`, `getLogs`, `registerUpdateRoutes` |
|
|
678
|
+
| `AdoptedContainer` | Update registration + checks for externally-managed containers |
|
|
679
|
+
| `getContainerManager()` | Read the `globalThis.__signalk_containerManager` global |
|
|
680
|
+
| `waitForContainerManager(opts)` | Two-phase wait (manager present → runtime settled); returns `{ manager, runtime }` so the two failure modes get distinct messages |
|
|
681
|
+
| `resolveMount(manager, opts)` | Translate an absolute host path into a mountable `{ source, containerPath, subPath }` — how you mount **your own** plugin's data dir (see [Mounting your plugin's own data directory](#mounting-your-plugins-own-data-directory)) |
|
|
682
|
+
| `resolveEndpoint(opts)` | Resolve the base URL for a managed container or a self-hosted service — pass the RAW `managedContainer` value (`undefined` means managed) |
|
|
683
|
+
| `waitForEndpointReady(ep, opts)` | Wait until a resolved endpoint answers 2xx. One attempt unless `retry` is given |
|
|
684
|
+
| `isManagedMode(value)` | The `!== false` rule, named — for route guards and status fields |
|
|
685
|
+
| `normalizeExternalUrl(raw)` | Normalise operator-typed input into a base URL (scheme defaulted, trailing slash stripped, path discarded); `null` when unusable |
|
|
686
|
+
| `managedModeSchema(opts)` | The two config fields as plain JSON Schema + matching defaults (`signalk-container-helper/schema`) |
|
|
687
|
+
| `waitForHttpReady(url, opts)` | Poll until 2xx or deadline (throws) |
|
|
688
|
+
| `retryForever(fn, opts)` | Retry until success — 15s doubling to a 120s ceiling, no attempt cap. `ManagedContainer` takes it as `readinessRetry`; exported standalone for work this library does not manage |
|
|
689
|
+
| `anySignal(signals)` | Compose several `AbortSignal`s into one that aborts when any does (`undefined` when none are given) |
|
|
690
|
+
| `probeHttpHealth(url, opts)` | Retrying liveness probe with slow-response detection (never throws) |
|
|
691
|
+
| `fetchWithTimeout(url, opts)` | `fetch` with an `AbortController` timeout |
|
|
692
|
+
| `throwIfAborted(signal)` | Throw `ContainerHelperError` `cancelled` if the signal has fired — the check the lifecycle methods run between steps |
|
|
693
|
+
| `startSafely(app, fn)` | Sync wrapper for async plugin startup — Signal K does not await `start()` |
|
|
694
|
+
| `isValidImageTag(tag)` | Tag guard (`IMAGE_TAG_PATTERN`) |
|
|
695
|
+
| `errMsg(err)` | Normalize unknown errors to strings |
|
|
696
|
+
| `ContainerHelperError` | Typed error with `code` and `reported` |
|
|
697
|
+
| Types | Local mirror of signalk-container's public API — `ContainerManagerApi`, `ContainerConfig`, `EnsureRunningOptions`, `UpdateServiceApi`, … — verified at build time against `signalk-container/types` (≥ 1.23.2) so it never silently drifts. Feature-detected members stay optional here. |
|
|
519
698
|
|
|
520
699
|
### Error codes
|
|
521
700
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { ManagedContainer } from "./managed-container.js";
|
|
2
|
+
import type { FetchLike } from "./http.js";
|
|
3
|
+
import { type RetryForeverOptions } from "./retry.js";
|
|
4
|
+
export type EndpointMode = "managed" | "external";
|
|
5
|
+
export interface ResolvedEndpoint {
|
|
6
|
+
mode: EndpointMode;
|
|
7
|
+
/** Scheme-ful base with no trailing slash — append absolute paths to it. */
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
/**
|
|
10
|
+
* `host:port` — as the container manager reports it in managed mode, or as
|
|
11
|
+
* the operator's URL resolved to in self-hosted mode. A bare `host` when no
|
|
12
|
+
* port was given and none was defaulted. Never null in practice today; the
|
|
13
|
+
* type keeps the door open for a manager that cannot answer.
|
|
14
|
+
*/
|
|
15
|
+
address: string | null;
|
|
16
|
+
/** The container, in managed mode only — null when self-hosted. */
|
|
17
|
+
container: ManagedContainer | null;
|
|
18
|
+
}
|
|
19
|
+
export interface ResolveEndpointOptions {
|
|
20
|
+
/**
|
|
21
|
+
* The raw config value. `undefined` MUST mean managed: Signal K calls
|
|
22
|
+
* `start()` with `{}` when a plugin is enabled without ever saving its form,
|
|
23
|
+
* and treating that as self-hosted would break every such install.
|
|
24
|
+
*/
|
|
25
|
+
managed: boolean | undefined;
|
|
26
|
+
/** Raw operator input; normalised internally. Self-hosted mode only. */
|
|
27
|
+
externalUrl?: string;
|
|
28
|
+
/** Required in managed mode. Pass one that has already been started. */
|
|
29
|
+
container?: ManagedContainer;
|
|
30
|
+
/** Container port to resolve in managed mode. */
|
|
31
|
+
port?: number;
|
|
32
|
+
/** Product noun for error copy, e.g. "backup-server". */
|
|
33
|
+
productName: string;
|
|
34
|
+
/** Config key named in the empty-URL error. Default "externalUrl". */
|
|
35
|
+
urlFieldName?: string;
|
|
36
|
+
/** Port assumed when the external URL carries none. */
|
|
37
|
+
defaultPort?: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Whether the plugin owns its container's lifecycle.
|
|
41
|
+
*
|
|
42
|
+
* `undefined` means managed — Signal K hands `plugin.start()` an empty object
|
|
43
|
+
* when a plugin is enabled without its form ever being saved, so a plain
|
|
44
|
+
* `Boolean(managed)` would silently flip those installs into self-hosted mode
|
|
45
|
+
* with an empty URL: a plugin that used to just work now errors on enable.
|
|
46
|
+
*
|
|
47
|
+
* Exported because most call sites are route handlers and status fields that
|
|
48
|
+
* only need the mode question, not a resolved endpoint. Naming the rule makes
|
|
49
|
+
* it greppable and keeps the UI and the runtime from drifting apart.
|
|
50
|
+
*/
|
|
51
|
+
export declare function isManagedMode(managed: boolean | undefined): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the base URL for whichever mode the config selects.
|
|
54
|
+
*
|
|
55
|
+
* Throws `ContainerHelperError` rather than returning a failure union: every
|
|
56
|
+
* consumer's existing branch is `setPluginError(...); return`, and throwing
|
|
57
|
+
* lets `startSafely` do that uniformly. `reported` is left false so the
|
|
58
|
+
* message — which matches the copy the plugins already show — is surfaced.
|
|
59
|
+
*
|
|
60
|
+
* Readiness is NOT waited for here; that is `waitForEndpointReady`, so the
|
|
61
|
+
* retry policy stays a caller decision.
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolveEndpoint(options: ResolveEndpointOptions): Promise<ResolvedEndpoint>;
|
|
64
|
+
export interface EndpointReadyOptions {
|
|
65
|
+
/** Probe path appended to the base URL. Default "/". */
|
|
66
|
+
path?: string;
|
|
67
|
+
/** Deadline for one readiness attempt. Default 15_000. */
|
|
68
|
+
maxMs?: number;
|
|
69
|
+
/** Delay between polls within an attempt. */
|
|
70
|
+
intervalMs?: number;
|
|
71
|
+
/** Per-request timeout. */
|
|
72
|
+
requestTimeoutMs?: number;
|
|
73
|
+
signal?: AbortSignal;
|
|
74
|
+
fetchImpl?: FetchLike;
|
|
75
|
+
/**
|
|
76
|
+
* When present, wrap the readiness wait in `retryForever` with these
|
|
77
|
+
* options. ABSENT MEANS ONE ATTEMPT, then throw.
|
|
78
|
+
*
|
|
79
|
+
* Not defaulted on, and that is deliberate: the reference plugins genuinely
|
|
80
|
+
* differ here — one retries forever because an unattended boat must heal
|
|
81
|
+
* itself, another reports the error once and waits for the operator. Baking
|
|
82
|
+
* either in would change the other's behaviour under cover of a refactor.
|
|
83
|
+
*/
|
|
84
|
+
retry?: RetryForeverOptions;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Wait until the endpoint answers 2xx. Same call in both modes — the mode
|
|
88
|
+
* difference is already spent by the time there is a base URL.
|
|
89
|
+
*/
|
|
90
|
+
export declare function waitForEndpointReady(endpoint: ResolvedEndpoint, options?: EndpointReadyOptions): Promise<void>;
|
|
91
|
+
/** Convenience for status fields and route guards. */
|
|
92
|
+
export declare function describeEndpoint(endpoint: ResolvedEndpoint): string;
|
|
93
|
+
//# sourceMappingURL=endpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint.d.ts","sourceRoot":"","sources":["../src/endpoint.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAIpE,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,UAAU,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,mEAAmE;IACnE,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAEnE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAmE3B;AAED,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2BAA2B;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,mBAAmB,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAInE"}
|
package/dist/endpoint.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// The managed/self-hosted seam.
|
|
2
|
+
//
|
|
3
|
+
// A container-backed plugin reaches its service in one of two ways: through a
|
|
4
|
+
// container signalk-container manages for it, or over the network at an address
|
|
5
|
+
// the operator supplied ("self-hosted"). The transport half of those two is
|
|
6
|
+
// identical — a base URL in, an HTTP client out — but every consumer currently
|
|
7
|
+
// re-derives the mode branch, the empty-URL guard and the readiness wait.
|
|
8
|
+
//
|
|
9
|
+
// Scope note: "self-hosted" means a SERVICE on another host, never a container
|
|
10
|
+
// ENGINE on another host. signalk-container talks to local unix sockets only
|
|
11
|
+
// and throws on a tcp:// endpoint, so there is no remote-engine mode to model.
|
|
12
|
+
import { waitForHttpReady } from "./http.js";
|
|
13
|
+
import { retryForever } from "./retry.js";
|
|
14
|
+
import { ContainerHelperError } from "./util.js";
|
|
15
|
+
import { normalizeExternalUrl } from "./url.js";
|
|
16
|
+
/**
|
|
17
|
+
* Whether the plugin owns its container's lifecycle.
|
|
18
|
+
*
|
|
19
|
+
* `undefined` means managed — Signal K hands `plugin.start()` an empty object
|
|
20
|
+
* when a plugin is enabled without its form ever being saved, so a plain
|
|
21
|
+
* `Boolean(managed)` would silently flip those installs into self-hosted mode
|
|
22
|
+
* with an empty URL: a plugin that used to just work now errors on enable.
|
|
23
|
+
*
|
|
24
|
+
* Exported because most call sites are route handlers and status fields that
|
|
25
|
+
* only need the mode question, not a resolved endpoint. Naming the rule makes
|
|
26
|
+
* it greppable and keeps the UI and the runtime from drifting apart.
|
|
27
|
+
*/
|
|
28
|
+
export function isManagedMode(managed) {
|
|
29
|
+
return managed !== false;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the base URL for whichever mode the config selects.
|
|
33
|
+
*
|
|
34
|
+
* Throws `ContainerHelperError` rather than returning a failure union: every
|
|
35
|
+
* consumer's existing branch is `setPluginError(...); return`, and throwing
|
|
36
|
+
* lets `startSafely` do that uniformly. `reported` is left false so the
|
|
37
|
+
* message — which matches the copy the plugins already show — is surfaced.
|
|
38
|
+
*
|
|
39
|
+
* Readiness is NOT waited for here; that is `waitForEndpointReady`, so the
|
|
40
|
+
* retry policy stays a caller decision.
|
|
41
|
+
*/
|
|
42
|
+
export async function resolveEndpoint(options) {
|
|
43
|
+
const { managed, externalUrl, container, port, productName, urlFieldName = "externalUrl", defaultPort, } = options;
|
|
44
|
+
if (!isManagedMode(managed)) {
|
|
45
|
+
const normalized = normalizeExternalUrl(externalUrl, { defaultPort });
|
|
46
|
+
if (!normalized) {
|
|
47
|
+
// The empty and malformed cases get different copy: one is "you haven't
|
|
48
|
+
// finished configuring", the other "what you typed cannot work".
|
|
49
|
+
const typed = typeof externalUrl === "string" ? externalUrl.trim() : "";
|
|
50
|
+
throw typed
|
|
51
|
+
? new ContainerHelperError("external-url-invalid", `${productName}: ${urlFieldName} is not a valid http(s) URL: "${typed}". ` +
|
|
52
|
+
`Expected something like http://192.168.1.50:3010.`)
|
|
53
|
+
: new ContainerHelperError("external-url-missing", `managedContainer is disabled but ${urlFieldName} is empty. ` +
|
|
54
|
+
`Set ${urlFieldName} in plugin config.`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
mode: "external",
|
|
58
|
+
baseUrl: normalized.baseUrl,
|
|
59
|
+
address: normalized.address,
|
|
60
|
+
container: null,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (!container) {
|
|
64
|
+
throw new ContainerHelperError("invalid-option", `${productName}: resolveEndpoint requires a ManagedContainer in managed mode.`);
|
|
65
|
+
}
|
|
66
|
+
if (typeof port !== "number") {
|
|
67
|
+
throw new ContainerHelperError("invalid-option", `${productName}: resolveEndpoint requires a port in managed mode.`);
|
|
68
|
+
}
|
|
69
|
+
// resolveAddress never throws — it already absorbs the case where
|
|
70
|
+
// resolveContainerAddress rejects because ensureRunning has not run yet.
|
|
71
|
+
const address = await container.resolveAddress(port);
|
|
72
|
+
if (!address) {
|
|
73
|
+
throw new ContainerHelperError("address-unresolved", `Could not resolve address for ${productName} port ${port}. ` +
|
|
74
|
+
`Declare the port in signalkAccessiblePorts.`);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
mode: "managed",
|
|
78
|
+
baseUrl: `http://${address}`,
|
|
79
|
+
address,
|
|
80
|
+
container,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Wait until the endpoint answers 2xx. Same call in both modes — the mode
|
|
85
|
+
* difference is already spent by the time there is a base URL.
|
|
86
|
+
*/
|
|
87
|
+
export async function waitForEndpointReady(endpoint, options = {}) {
|
|
88
|
+
const { path = "/", retry, ...wait } = options;
|
|
89
|
+
const url = `${endpoint.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
90
|
+
const attempt = () => waitForHttpReady(url, {
|
|
91
|
+
maxMs: wait.maxMs ?? 15_000,
|
|
92
|
+
intervalMs: wait.intervalMs,
|
|
93
|
+
requestTimeoutMs: wait.requestTimeoutMs,
|
|
94
|
+
fetchImpl: wait.fetchImpl,
|
|
95
|
+
signal: wait.signal,
|
|
96
|
+
});
|
|
97
|
+
if (!retry) {
|
|
98
|
+
await attempt();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
await retryForever(attempt, retry);
|
|
102
|
+
}
|
|
103
|
+
/** Convenience for status fields and route guards. */
|
|
104
|
+
export function describeEndpoint(endpoint) {
|
|
105
|
+
return endpoint.mode === "managed"
|
|
106
|
+
? `managed container at ${endpoint.baseUrl}`
|
|
107
|
+
: `external service at ${endpoint.baseUrl}`;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=endpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint.js","sourceRoot":"","sources":["../src/endpoint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asking whether the HOST has a device.
|
|
3
|
+
*
|
|
4
|
+
* A plugin cannot answer this itself. `stat("/dev/dri")` reports on the
|
|
5
|
+
* plugin's own filesystem, which is the Signal K container whenever Signal K
|
|
6
|
+
* is containerized — the common deployment — and that container has no
|
|
7
|
+
* `/dev/dri` even on a machine with a GPU. The result is a silent false
|
|
8
|
+
* negative: the plugin runs its workload under software rendering and nothing
|
|
9
|
+
* says why.
|
|
10
|
+
*
|
|
11
|
+
* signalk-container can see the host, so it answers instead.
|
|
12
|
+
*/
|
|
13
|
+
import type { ContainerManagerApi, HostDeviceProbeResult } from "./types.js";
|
|
14
|
+
/**
|
|
15
|
+
* Probe a host path for device nodes and the groups that own them.
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* const gpu = await probeHostDevice(manager, "/dev/dri");
|
|
19
|
+
* if (gpu?.exists) {
|
|
20
|
+
* config.devices = ["/dev/dri"];
|
|
21
|
+
* config.groupAdd = gpu.groups; // names, resolved on the host
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* `groups` are NAMES rather than gids on purpose: `groupAdd` resolves names
|
|
26
|
+
* against the host's `/etc/group`, and the gid of `render` or `video` differs
|
|
27
|
+
* per distro and per install. Hardcoding a number — as the upstream
|
|
28
|
+
* opencpn-kiosk compose does with `group_add: "993"` — silently loses access
|
|
29
|
+
* on any host that numbers them differently.
|
|
30
|
+
*
|
|
31
|
+
* @returns the probe result, or **null meaning "unknown"** — no runtime, or
|
|
32
|
+
* the host could not be inspected. That is deliberately distinct from
|
|
33
|
+
* `{ exists: false }`, which means "definitely not there". Treat null as
|
|
34
|
+
* "assume no device, but do not report it as absent".
|
|
35
|
+
*
|
|
36
|
+
* @throws ContainerHelperError `unsupported-manager` on signalk-container
|
|
37
|
+
* older than 1.30.0, which has no probe at all. Catch it if the caller would
|
|
38
|
+
* rather degrade than fail.
|
|
39
|
+
*/
|
|
40
|
+
export declare function probeHostDevice(manager: ContainerManagerApi, path: string): Promise<HostDeviceProbeResult | null>;
|
|
41
|
+
//# sourceMappingURL=host-device.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-device.d.ts","sourceRoot":"","sources":["../src/host-device.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAevC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asking whether the HOST has a device.
|
|
3
|
+
*
|
|
4
|
+
* A plugin cannot answer this itself. `stat("/dev/dri")` reports on the
|
|
5
|
+
* plugin's own filesystem, which is the Signal K container whenever Signal K
|
|
6
|
+
* is containerized — the common deployment — and that container has no
|
|
7
|
+
* `/dev/dri` even on a machine with a GPU. The result is a silent false
|
|
8
|
+
* negative: the plugin runs its workload under software rendering and nothing
|
|
9
|
+
* says why.
|
|
10
|
+
*
|
|
11
|
+
* signalk-container can see the host, so it answers instead.
|
|
12
|
+
*/
|
|
13
|
+
import { ContainerHelperError } from "./util.js";
|
|
14
|
+
/**
|
|
15
|
+
* Probe a host path for device nodes and the groups that own them.
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* const gpu = await probeHostDevice(manager, "/dev/dri");
|
|
19
|
+
* if (gpu?.exists) {
|
|
20
|
+
* config.devices = ["/dev/dri"];
|
|
21
|
+
* config.groupAdd = gpu.groups; // names, resolved on the host
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* `groups` are NAMES rather than gids on purpose: `groupAdd` resolves names
|
|
26
|
+
* against the host's `/etc/group`, and the gid of `render` or `video` differs
|
|
27
|
+
* per distro and per install. Hardcoding a number — as the upstream
|
|
28
|
+
* opencpn-kiosk compose does with `group_add: "993"` — silently loses access
|
|
29
|
+
* on any host that numbers them differently.
|
|
30
|
+
*
|
|
31
|
+
* @returns the probe result, or **null meaning "unknown"** — no runtime, or
|
|
32
|
+
* the host could not be inspected. That is deliberately distinct from
|
|
33
|
+
* `{ exists: false }`, which means "definitely not there". Treat null as
|
|
34
|
+
* "assume no device, but do not report it as absent".
|
|
35
|
+
*
|
|
36
|
+
* @throws ContainerHelperError `unsupported-manager` on signalk-container
|
|
37
|
+
* older than 1.30.0, which has no probe at all. Catch it if the caller would
|
|
38
|
+
* rather degrade than fail.
|
|
39
|
+
*/
|
|
40
|
+
export async function probeHostDevice(manager, path) {
|
|
41
|
+
if (!path.startsWith("/")) {
|
|
42
|
+
throw new ContainerHelperError("invalid-option", `probeHostDevice: path must be absolute, got '${path}'`);
|
|
43
|
+
}
|
|
44
|
+
if (typeof manager.probeHostDevice !== "function") {
|
|
45
|
+
throw new ContainerHelperError("unsupported-manager", "probeHostDevice requires signalk-container 1.30.0+; " +
|
|
46
|
+
"upgrade it to detect host devices from a containerized Signal K");
|
|
47
|
+
}
|
|
48
|
+
return manager.probeHostDevice(path);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=host-device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-device.js","sourceRoot":"","sources":["../src/host-device.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGjD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAA4B,EAC5B,IAAY;IAEZ,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,oBAAoB,CAC5B,gBAAgB,EAChB,gDAAgD,IAAI,GAAG,CACxD,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,OAAO,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;QAClD,MAAM,IAAI,oBAAoB,CAC5B,qBAAqB,EACrB,sDAAsD;YACpD,iEAAiE,CACpE,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,10 +8,17 @@ export type { AdoptedContainerOptions } from "./adopted-container.js";
|
|
|
8
8
|
export { buildVersionSource } from "./version-source.js";
|
|
9
9
|
export type { VersionSourceSpec } from "./version-source.js";
|
|
10
10
|
export { resolveMount } from "./host-path.js";
|
|
11
|
+
export { probeHostDevice } from "./host-device.js";
|
|
11
12
|
export type { HostMount, ResolveMountOptions } from "./host-path.js";
|
|
12
13
|
export { retryForever, anySignal } from "./retry.js";
|
|
13
14
|
export type { RetryForeverOptions } from "./retry.js";
|
|
14
15
|
export { fetchWithTimeout, waitForHttpReady, probeHttpHealth } from "./http.js";
|
|
16
|
+
export { resolveEndpoint, waitForEndpointReady, isManagedMode, describeEndpoint, } from "./endpoint.js";
|
|
17
|
+
export type { EndpointMode, ResolvedEndpoint, ResolveEndpointOptions, EndpointReadyOptions, } from "./endpoint.js";
|
|
18
|
+
export { normalizeExternalUrl } from "./url.js";
|
|
19
|
+
export type { NormalizeUrlOptions, NormalizedUrl } from "./url.js";
|
|
20
|
+
export { managedModeSchema } from "./schema.js";
|
|
21
|
+
export type { JsonSchemaFragment, ManagedModeSchema, ManagedModeSchemaOptions, } from "./schema.js";
|
|
15
22
|
export type { FetchLike, FetchWithTimeoutOptions, WaitForHttpReadyOptions, ProbeHttpHealthOptions, HealthProbeResult, } from "./http.js";
|
|
16
23
|
export { startSafely, throwIfAborted, errMsg, isValidImageTag, IMAGE_TAG_PATTERN, ContainerHelperError, } from "./util.js";
|
|
17
24
|
export type { ContainerHelperErrorCode } from "./util.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EACV,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACrD,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAChF,YAAY,EACV,SAAS,EACT,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,WAAW,EACX,cAAc,EACd,MAAM,EACN,eAAe,EACf,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EACV,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,YAAY,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACrD,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAChF,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAChD,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,SAAS,EACT,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,WAAW,EACX,cAAc,EACd,MAAM,EACN,eAAe,EACf,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,11 @@ export { ManagedContainer } from "./managed-container.js";
|
|
|
4
4
|
export { AdoptedContainer } from "./adopted-container.js";
|
|
5
5
|
export { buildVersionSource } from "./version-source.js";
|
|
6
6
|
export { resolveMount } from "./host-path.js";
|
|
7
|
+
export { probeHostDevice } from "./host-device.js";
|
|
7
8
|
export { retryForever, anySignal } from "./retry.js";
|
|
8
9
|
export { fetchWithTimeout, waitForHttpReady, probeHttpHealth } from "./http.js";
|
|
10
|
+
export { resolveEndpoint, waitForEndpointReady, isManagedMode, describeEndpoint, } from "./endpoint.js";
|
|
11
|
+
export { normalizeExternalUrl } from "./url.js";
|
|
12
|
+
export { managedModeSchema } from "./schema.js";
|
|
9
13
|
export { startSafely, throwIfAborted, errMsg, isValidImageTag, IMAGE_TAG_PATTERN, ContainerHelperError, } from "./util.js";
|
|
10
14
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAE5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAW1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAE5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAW1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAChF,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,gBAAgB,GACjB,MAAM,eAAe,CAAC;AAOvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAahD,OAAO,EACL,WAAW,EACX,cAAc,EACd,MAAM,EACN,eAAe,EACf,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,WAAW,CAAC"}
|