@bridgething/updater 0.1.4

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.
Files changed (50) hide show
  1. package/README.md +108 -0
  2. package/dist/ack-window.d.ts +38 -0
  3. package/dist/ack-window.d.ts.map +1 -0
  4. package/dist/ack-window.js +89 -0
  5. package/dist/ack-window.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +204 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/driver.d.ts +56 -0
  11. package/dist/driver.d.ts.map +1 -0
  12. package/dist/driver.js +240 -0
  13. package/dist/driver.js.map +1 -0
  14. package/dist/fragments.d.ts +29 -0
  15. package/dist/fragments.d.ts.map +1 -0
  16. package/dist/fragments.js +45 -0
  17. package/dist/fragments.js.map +1 -0
  18. package/dist/gateway-device.d.ts +3 -0
  19. package/dist/gateway-device.d.ts.map +1 -0
  20. package/dist/gateway-device.js +2 -0
  21. package/dist/gateway-device.js.map +1 -0
  22. package/dist/index.d.ts +12 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +6 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/manifest.d.ts +50 -0
  27. package/dist/manifest.d.ts.map +1 -0
  28. package/dist/manifest.js +61 -0
  29. package/dist/manifest.js.map +1 -0
  30. package/dist/node.d.ts +3 -0
  31. package/dist/node.d.ts.map +1 -0
  32. package/dist/node.js +15 -0
  33. package/dist/node.js.map +1 -0
  34. package/dist/range-serve.d.ts +5 -0
  35. package/dist/range-serve.d.ts.map +1 -0
  36. package/dist/range-serve.js +58 -0
  37. package/dist/range-serve.js.map +1 -0
  38. package/dist/source.d.ts +9 -0
  39. package/dist/source.d.ts.map +1 -0
  40. package/dist/source.js +33 -0
  41. package/dist/source.js.map +1 -0
  42. package/dist/web-serial.d.ts +2 -0
  43. package/dist/web-serial.d.ts.map +1 -0
  44. package/dist/web-serial.js +2 -0
  45. package/dist/web-serial.js.map +1 -0
  46. package/dist/websocket.d.ts +2 -0
  47. package/dist/websocket.d.ts.map +1 -0
  48. package/dist/websocket.js +2 -0
  49. package/dist/websocket.js.map +1 -0
  50. package/package.json +67 -0
package/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # @bridgething/updater
2
+
3
+ Transport-agnostic OTA updater for a [bridgething](https://github.com/JoeyEamigh/bridgething)
4
+ Spotify Car Thing: a `bunx`/`npx` CLI that brings a device to the latest release
5
+ on a channel, plus the library the CLI is built from.
6
+
7
+ It updates the **device** (daemon binary and/or full image), not a webapp. To
8
+ install or share a webapp, use [`@bridgething/client`](https://www.npmjs.com/package/@bridgething/client)
9
+ and the `push`/`share` scripts a [`create-bridgething`](https://www.npmjs.com/package/create-bridgething)
10
+ project ships.
11
+
12
+ ## CLI
13
+
14
+ ```sh
15
+ bunx @bridgething/updater
16
+ ```
17
+
18
+ Connects to a Car Thing over the daemon's network gateway (the USB-gadget link
19
+ by default), reads the discover manifest, resolves the target channel's `latest`
20
+ composite version, downloads whatever artifacts are missing, and pushes the
21
+ update. If the image half of the version changed it pushes a full image OTA,
22
+ otherwise it pushes just the daemon binary. A `latest` release that is yanked or
23
+ deprecated is refused.
24
+
25
+ Options:
26
+
27
+ | Flag | Default | Meaning |
28
+ | --- | --- | --- |
29
+ | `--root <url>` | `https://ota.bridgething.com` | Manifest + artifact root. |
30
+ | `--channel <name>` | `stable` | Channel to track. |
31
+ | `--host <ws-url>` | `ws://bridgething.local:8892/` | Daemon network gateway. |
32
+ | `--cache-dir <path>` | OS tmpdir `/bridgething-updater` | Artifact download cache. |
33
+ | `--daemon-only` | off | Skip the image OTA even if the image half changed. |
34
+
35
+ Multiple devices on the network resolve to distinct mDNS names; point `--host` at
36
+ the one you mean:
37
+
38
+ ```sh
39
+ bunx @bridgething/updater --host ws://bridgething-<serial>.local:8892/
40
+ ```
41
+
42
+ ## Library
43
+
44
+ The CLI is a thin driver over `OtaDriver`, which streams an artifact to the
45
+ daemon with sender-side flow control and reports progress to a terminal state.
46
+
47
+ ```ts
48
+ import { BridgethingGateway } from '@bridgething/gateway';
49
+ import { NetworkAdapter } from '@bridgething/adapter-network';
50
+ import { OtaDriver } from '@bridgething/updater';
51
+ import { fileArtifactSource } from '@bridgething/updater/node';
52
+
53
+ const gateway = new BridgethingGateway(new NetworkAdapter());
54
+ await gateway.start();
55
+ // resolve a deviceId from a 'version' announce event, then:
56
+
57
+ const driver = new OtaDriver(gateway, deviceId);
58
+ try {
59
+ const snapshot = await driver.pushDaemon(await fileArtifactSource('./bridgething'), s =>
60
+ console.log(s.phase, 'percent' in s ? s.percent : ''),
61
+ );
62
+ if (snapshot.phase === 'failed') console.error(snapshot.reason);
63
+ } finally {
64
+ driver.close();
65
+ }
66
+ ```
67
+
68
+ `OtaDriver` covers every OTA kind the daemon accepts:
69
+
70
+ - `pushImage({ source, zcks, updateUrlBase })` - full image OTA. When the daemon
71
+ requests delta ranges of the compressed asset, `zcks` (a map of asset name to
72
+ source) serves them back over the same link; `serveAssetRanges` wires that up.
73
+ - `pushDaemon(source)` - stage and activate a new daemon binary.
74
+ - `pushBuiltinWebapp(source)` - stage and activate a built-in webapp.
75
+ - `pushBandaidBatch(artifacts)` - stage several bandaid artifacts, then commit
76
+ them together.
77
+ - `installWebapp(source)` - install a third-party webapp bundle.
78
+
79
+ ### Artifact sources
80
+
81
+ An `ArtifactSource` is a random-access, sized byte source. The driver reads it in
82
+ 64 KiB fragments and never buffers the whole file. Construct one from wherever
83
+ the bytes live:
84
+
85
+ - `fileArtifactSource(path)` from `@bridgething/updater/node` - a filesystem file.
86
+ - `bytesArtifactSource(bytes)` - an in-memory `Uint8Array`.
87
+ - `blobArtifactSource(blob)` - a browser `Blob` / `File`.
88
+
89
+ ### Manifest helpers
90
+
91
+ `@bridgething/updater/manifest` exposes the discover-manifest types and helpers
92
+ the CLI uses: `fetchManifest`, `parseCompositeVersion`, `otaArtifactUrls`, and
93
+ `builtinWebappUrl`.
94
+
95
+ ### Adapters
96
+
97
+ Re-exported for convenience so a consumer needs one import:
98
+
99
+ - `@bridgething/updater/websocket` - `NetworkAdapter` (network gateway, any host
100
+ that can open a WebSocket).
101
+ - `@bridgething/updater/web-serial` - `WebSerialAdapter` (in-browser Bluetooth
102
+ Classic over Web Serial).
103
+
104
+ ## Posture
105
+
106
+ The network gateway has no auth, matching the project posture. Treat the update
107
+ path like a debug interface: run it over the USB-CDC-ECM gadget link or a trusted
108
+ LAN, not an exposed one.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Sender-side flow control, ported from `host-gateway`'s `AckWindow`/`AckRegistry`
3
+ * (`crates/host-gateway/src/transfer.rs`): the daemon acks the byte offset it has durably
4
+ * consumed, and the streamer holds no more than `OTA_WINDOW_BYTES` past that. Acks are absolute
5
+ * file offsets, so the baseline seeds to the resume point.
6
+ *
7
+ * The Rust version uses an atomic + `Notify` with `notify_one` specifically to avoid a lost
8
+ * wakeup between a waiter's recheck and its `await`. That race doesn't exist here: JS is
9
+ * single-threaded, so a synchronous "check the ack, then register a listener" pair can't be
10
+ * interleaved by another callback - there's no gap for a `note()` to land in.
11
+ */
12
+ export type AckWindowOptions = {
13
+ windowBytes?: number;
14
+ ackTimeoutMs?: number;
15
+ };
16
+ export declare class AckWindow {
17
+ private acked;
18
+ private waiters;
19
+ private readonly windowBytes;
20
+ private readonly ackTimeoutMs;
21
+ constructor(baseline: number, options?: AckWindowOptions);
22
+ note(received: number): void;
23
+ get ackedBytes(): number;
24
+ waitForRoom(offset: number): Promise<boolean>;
25
+ private waitForProgress;
26
+ }
27
+ export declare class AckRegistry {
28
+ private readonly windowOptions;
29
+ private readonly windows;
30
+ constructor(windowOptions?: AckWindowOptions);
31
+ register(transferId: string, baseline: number): AckWindow;
32
+ deregister(transferId: string): void;
33
+ note(transferId: string, received: number): void;
34
+ }
35
+ export declare class TransferStalledError extends Error {
36
+ constructor(offset: number, totalSize: number);
37
+ }
38
+ //# sourceMappingURL=ack-window.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ack-window.d.ts","sourceRoot":"","sources":["../src/ack-window.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAM5D,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQ5B,IAAI,UAAU,IAAI,MAAM,CAEvB;IAEK,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASnD,OAAO,CAAC,eAAe;CAgBxB;AAED,qBAAa,WAAW;IAGV,OAAO,CAAC,QAAQ,CAAC,aAAa;IAF1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;gBAE3B,aAAa,GAAE,gBAAqB;IAEjE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS;IAMzD,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIpC,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;CAGjD;AAED,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAI9C"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Sender-side flow control, ported from `host-gateway`'s `AckWindow`/`AckRegistry`
3
+ * (`crates/host-gateway/src/transfer.rs`): the daemon acks the byte offset it has durably
4
+ * consumed, and the streamer holds no more than `OTA_WINDOW_BYTES` past that. Acks are absolute
5
+ * file offsets, so the baseline seeds to the resume point.
6
+ *
7
+ * The Rust version uses an atomic + `Notify` with `notify_one` specifically to avoid a lost
8
+ * wakeup between a waiter's recheck and its `await`. That race doesn't exist here: JS is
9
+ * single-threaded, so a synchronous "check the ack, then register a listener" pair can't be
10
+ * interleaved by another callback - there's no gap for a `note()` to land in.
11
+ */
12
+ const OTA_WINDOW_BYTES = 512 * 1024;
13
+ const OTA_ACK_TIMEOUT_MS = 30_000;
14
+ export class AckWindow {
15
+ acked;
16
+ waiters = [];
17
+ windowBytes;
18
+ ackTimeoutMs;
19
+ constructor(baseline, options = {}) {
20
+ this.acked = baseline;
21
+ this.windowBytes = options.windowBytes ?? OTA_WINDOW_BYTES;
22
+ this.ackTimeoutMs = options.ackTimeoutMs ?? OTA_ACK_TIMEOUT_MS;
23
+ }
24
+ note(received) {
25
+ if (received <= this.acked)
26
+ return;
27
+ this.acked = received;
28
+ const waiters = this.waiters;
29
+ this.waiters = [];
30
+ for (const waiter of waiters)
31
+ waiter();
32
+ }
33
+ get ackedBytes() {
34
+ return this.acked;
35
+ }
36
+ async waitForRoom(offset) {
37
+ for (;;) {
38
+ if (offset < this.acked + this.windowBytes)
39
+ return true;
40
+ const priorAcked = this.acked;
41
+ const progressed = await this.waitForProgress();
42
+ if (!progressed && this.acked <= priorAcked)
43
+ return false;
44
+ }
45
+ }
46
+ waitForProgress() {
47
+ return new Promise(resolve => {
48
+ const timer = setTimeout(() => settle(false), this.ackTimeoutMs);
49
+ const onProgress = () => settle(true);
50
+ let settled = false;
51
+ const settle = (result) => {
52
+ if (settled)
53
+ return;
54
+ settled = true;
55
+ clearTimeout(timer);
56
+ const idx = this.waiters.indexOf(onProgress);
57
+ if (idx >= 0)
58
+ this.waiters.splice(idx, 1);
59
+ resolve(result);
60
+ };
61
+ this.waiters.push(onProgress);
62
+ });
63
+ }
64
+ }
65
+ export class AckRegistry {
66
+ windowOptions;
67
+ windows = new Map();
68
+ constructor(windowOptions = {}) {
69
+ this.windowOptions = windowOptions;
70
+ }
71
+ register(transferId, baseline) {
72
+ const window = new AckWindow(baseline, this.windowOptions);
73
+ this.windows.set(transferId, window);
74
+ return window;
75
+ }
76
+ deregister(transferId) {
77
+ this.windows.delete(transferId);
78
+ }
79
+ note(transferId, received) {
80
+ this.windows.get(transferId)?.note(received);
81
+ }
82
+ }
83
+ export class TransferStalledError extends Error {
84
+ constructor(offset, totalSize) {
85
+ super(`transfer stalled: no drain-ack progress at offset ${offset}/${totalSize}`);
86
+ this.name = 'TransferStalledError';
87
+ }
88
+ }
89
+ //# sourceMappingURL=ack-window.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ack-window.js","sourceRoot":"","sources":["../src/ack-window.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env node
2
+ //! `bridgething-updater` - manifest-driven CLI. Connects to a Car Thing over the daemon's
3
+ //! network gateway, resolves the target channel's `latest` composite version from the discover
4
+ //! manifest, downloads whatever artifacts are missing, and pushes daemon and/or image OTA.
5
+ import { mkdir, stat, writeFile } from 'node:fs/promises';
6
+ import { tmpdir } from 'node:os';
7
+ import { join } from 'node:path';
8
+ import { BridgethingGateway } from '@bridgething/gateway';
9
+ import { OtaDriver } from './driver';
10
+ import { fetchManifest, otaArtifactUrls, parseCompositeVersion } from './manifest';
11
+ import { fileArtifactSource } from './node';
12
+ import { NetworkAdapter } from './websocket';
13
+ const DEFAULT_ROOT = 'https://ota.bridgething.com';
14
+ const DEFAULT_HOST = 'ws://bridgething.local:8892/';
15
+ const CONNECT_TIMEOUT_MS = 15_000;
16
+ function parseArgs(argv) {
17
+ let root = DEFAULT_ROOT;
18
+ let channel = 'stable';
19
+ let host = DEFAULT_HOST;
20
+ let cacheDir = join(tmpdir(), 'bridgething-updater');
21
+ let daemonOnly = false;
22
+ for (let i = 0; i < argv.length; i++) {
23
+ const arg = argv[i];
24
+ if (arg === '--root')
25
+ root = requireValue(argv, ++i, arg);
26
+ else if (arg === '--channel')
27
+ channel = requireValue(argv, ++i, arg);
28
+ else if (arg === '--host')
29
+ host = requireValue(argv, ++i, arg);
30
+ else if (arg === '--cache-dir')
31
+ cacheDir = requireValue(argv, ++i, arg);
32
+ else if (arg === '--daemon-only')
33
+ daemonOnly = true;
34
+ else if (arg === '--help' || arg === '-h') {
35
+ printHelp();
36
+ process.exit(0);
37
+ }
38
+ else {
39
+ console.error(`unknown argument: ${arg}`);
40
+ printHelp();
41
+ process.exit(1);
42
+ }
43
+ }
44
+ return { root: root.replace(/\/$/, ''), channel, host, cacheDir, daemonOnly };
45
+ }
46
+ function requireValue(argv, idx, flag) {
47
+ const value = argv[idx];
48
+ if (value === undefined) {
49
+ console.error(`${flag} requires a value`);
50
+ process.exit(1);
51
+ }
52
+ return value;
53
+ }
54
+ function printHelp() {
55
+ console.log(`Usage: bridgething-updater [options]
56
+
57
+ Updates a Car Thing over its network gateway (USB-gadget by default) to the latest
58
+ release on a channel, per the discover manifest.
59
+
60
+ Options:
61
+ --root <url> Manifest root URL. Default ${DEFAULT_ROOT}.
62
+ --channel <name> Channel to track. Default 'stable'.
63
+ --host <ws-url> Daemon network gateway URL. Default ${DEFAULT_HOST}.
64
+ --cache-dir <path> Artifact download cache. Default a bridgething-updater dir under the OS tmpdir.
65
+ --daemon-only Skip the image OTA even if the manifest's image half changed.
66
+ `);
67
+ }
68
+ async function main() {
69
+ const args = parseArgs(process.argv.slice(2));
70
+ await mkdir(args.cacheDir, { recursive: true });
71
+ console.log(`connecting to ${args.host} ...`);
72
+ const adapter = new NetworkAdapter({ discovery: args.host });
73
+ const gateway = new BridgethingGateway(adapter);
74
+ await gateway.start();
75
+ const { deviceId, meta } = await waitForDevice(gateway);
76
+ console.log(`connected: ${meta.modelName} (${deviceId}) - daemon ${meta.appVersion}, image ${meta.imageVersion} (${meta.imageVariant}/${meta.channel})`);
77
+ console.log(`fetching manifest from ${args.root} ...`);
78
+ const manifest = await fetchManifest(args.root);
79
+ const channel = manifest.channels[args.channel];
80
+ if (!channel) {
81
+ fail(`channel '${args.channel}' not present in manifest`);
82
+ }
83
+ const composite = parseCompositeVersion(channel.latest);
84
+ if (!composite) {
85
+ fail(`channel.latest '${channel.latest}' is not a composite version`);
86
+ }
87
+ const release = manifest.releases[channel.latest];
88
+ if (release && (release.yanked !== null || release.deprecated)) {
89
+ fail(`latest release ${channel.latest} is ${release.yanked !== null ? 'yanked' : 'deprecated'}; refusing to install`);
90
+ }
91
+ const urls = otaArtifactUrls({
92
+ rootURL: args.root,
93
+ channel: args.channel,
94
+ daemonVersion: composite.daemon,
95
+ imageVersion: composite.image,
96
+ imageVariant: meta.imageVariant,
97
+ });
98
+ const driver = new OtaDriver(gateway, deviceId);
99
+ let ok = true;
100
+ try {
101
+ if (!args.daemonOnly && meta.imageVersion !== composite.image) {
102
+ ok = await runImagePush(driver, args, urls);
103
+ }
104
+ else if (meta.appVersion !== composite.daemon) {
105
+ ok = await runDaemonPush(driver, args, urls.daemonBinary, composite.daemon);
106
+ }
107
+ else {
108
+ console.log('already up to date.');
109
+ }
110
+ }
111
+ finally {
112
+ driver.close();
113
+ }
114
+ await gateway.stop();
115
+ process.exit(ok ? 0 : 1);
116
+ }
117
+ async function runImagePush(driver, args, urls) {
118
+ console.log('downloading image artifacts ...');
119
+ const swuPath = await downloadIfNeeded(urls.imageSwu, args.cacheDir, 'image.swu');
120
+ const zckPath = await downloadIfNeeded(urls.imageZck, args.cacheDir, 'image.zck');
121
+ const bootZckPath = await downloadIfNeeded(urls.imageBootZck, args.cacheDir, 'image-boot.zck');
122
+ const source = await fileArtifactSource(swuPath);
123
+ const zcks = new Map([
124
+ ['system.img.zck', await fileArtifactSource(zckPath)],
125
+ ['boot.vfat.zck', await fileArtifactSource(bootZckPath)],
126
+ ]);
127
+ console.log('pushing image OTA ...');
128
+ const snapshot = await driver.pushImage({ source, zcks, updateUrlBase: args.root, onProgress: logProgress });
129
+ return reportOutcome(snapshot);
130
+ }
131
+ async function runDaemonPush(driver, args, url, version) {
132
+ console.log(`downloading daemon ${version} ...`);
133
+ const path = await downloadIfNeeded(url, args.cacheDir, `daemon-${version}`);
134
+ const source = await fileArtifactSource(path);
135
+ console.log('pushing daemon OTA ...');
136
+ const snapshot = await driver.pushDaemon(source, logProgress);
137
+ return reportOutcome(snapshot);
138
+ }
139
+ function logProgress(snapshot) {
140
+ switch (snapshot.phase) {
141
+ case 'streaming':
142
+ console.log(` streaming: ${snapshot.percent}%`);
143
+ break;
144
+ case 'applying':
145
+ console.log(` ${snapshot.otaPhase}: ${snapshot.percent}%`);
146
+ break;
147
+ case 'staged':
148
+ console.log(' staged, activating ...');
149
+ break;
150
+ case 'completed':
151
+ console.log(' completed.');
152
+ break;
153
+ case 'failed':
154
+ console.error(` failed: ${snapshot.reason}`);
155
+ break;
156
+ }
157
+ }
158
+ function reportOutcome(snapshot) {
159
+ if (snapshot.phase === 'failed') {
160
+ console.error(`update failed: ${snapshot.reason}`);
161
+ return false;
162
+ }
163
+ console.log('update finished.');
164
+ return true;
165
+ }
166
+ async function waitForDevice(gateway) {
167
+ return new Promise((resolve, reject) => {
168
+ const timeout = setTimeout(() => {
169
+ unsubscribe();
170
+ reject(new Error(`no device announced itself within ${CONNECT_TIMEOUT_MS / 1000}s`));
171
+ }, CONNECT_TIMEOUT_MS);
172
+ const unsubscribe = gateway.on(event => {
173
+ if (event.type !== 'message')
174
+ return;
175
+ if (event.message.data.type !== 'version')
176
+ return;
177
+ clearTimeout(timeout);
178
+ unsubscribe();
179
+ resolve({ deviceId: event.deviceId, meta: event.message.data.data });
180
+ });
181
+ });
182
+ }
183
+ async function downloadIfNeeded(url, cacheDir, filename) {
184
+ const target = join(cacheDir, filename);
185
+ const existing = await stat(target).catch(() => null);
186
+ if (existing && existing.size > 0)
187
+ return target;
188
+ const response = await fetch(url);
189
+ if (!response.ok || !response.body) {
190
+ throw new Error(`download failed: HTTP ${response.status} for ${url}`);
191
+ }
192
+ const bytes = new Uint8Array(await response.arrayBuffer());
193
+ await writeFile(target, bytes);
194
+ return target;
195
+ }
196
+ function fail(message) {
197
+ console.error(message);
198
+ process.exit(1);
199
+ }
200
+ main().catch(err => {
201
+ console.error(err instanceof Error ? (err.stack ?? err.message) : err);
202
+ process.exit(1);
203
+ });
204
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import type { BridgethingGateway, OtaKind, OtaPhase, WebappInfo } from '@bridgething/gateway';
2
+ import type { AckWindowOptions } from './ack-window';
3
+ import type { ArtifactSource } from './source';
4
+ export type OtaProgressSnapshot = {
5
+ phase: 'streaming';
6
+ percent: number;
7
+ } | {
8
+ phase: 'applying';
9
+ otaPhase: OtaPhase;
10
+ percent: number;
11
+ } | {
12
+ phase: 'staged';
13
+ } | {
14
+ phase: 'completed';
15
+ } | {
16
+ phase: 'failed';
17
+ reason: string;
18
+ };
19
+ export type ProgressListener = (snapshot: OtaProgressSnapshot) => void;
20
+ export type WebappInstallResult = {
21
+ ok: true;
22
+ info: WebappInfo;
23
+ } | {
24
+ ok: false;
25
+ reason: string;
26
+ };
27
+ type BandaidArtifact = {
28
+ kind: OtaKind;
29
+ source: ArtifactSource;
30
+ };
31
+ export declare class OtaDriver {
32
+ private readonly device;
33
+ private readonly registry;
34
+ private readonly unsubscribeAck;
35
+ private unsubscribeRangeServer;
36
+ constructor(gateway: BridgethingGateway, deviceId: string, ackWindowOptions?: AckWindowOptions);
37
+ serveAssetRanges(zcks: Map<string, ArtifactSource>): void;
38
+ stopServingAssetRanges(): void;
39
+ close(): void;
40
+ pushImage(opts: {
41
+ source: ArtifactSource;
42
+ zcks?: Map<string, ArtifactSource>;
43
+ updateUrlBase?: string;
44
+ onProgress?: ProgressListener;
45
+ }): Promise<OtaProgressSnapshot>;
46
+ pushDaemon(source: ArtifactSource, onProgress?: ProgressListener): Promise<OtaProgressSnapshot>;
47
+ pushBuiltinWebapp(source: ArtifactSource, onProgress?: ProgressListener): Promise<OtaProgressSnapshot>;
48
+ pushBandaidBatch(artifacts: BandaidArtifact[], onProgress?: ProgressListener): Promise<OtaProgressSnapshot>;
49
+ installWebapp(source: ArtifactSource): Promise<WebappInstallResult>;
50
+ private applyBandaidBatch;
51
+ private commitBandaid;
52
+ private driveOta;
53
+ private awaitTerminal;
54
+ }
55
+ export {};
56
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAG9F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAM/C,MAAM,MAAM,mBAAmB,GAC3B;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,KAAK,EAAE,QAAQ,CAAA;CAAE,GACnB;IAAE,KAAK,EAAE,WAAW,CAAA;CAAE,GACtB;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAEvE,MAAM,MAAM,mBAAmB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjG,KAAK,eAAe,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,CAAC;AAEjE,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAa;IAC5C,OAAO,CAAC,sBAAsB,CAA6B;gBAE/C,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,GAAE,gBAAqB;IAMlG,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI;IAKzD,sBAAsB,IAAI,IAAI;IAK9B,KAAK,IAAI,IAAI;IAKP,SAAS,CAAC,IAAI,EAAE;QACpB,MAAM,EAAE,cAAc,CAAC;QACvB,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QACnC,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,UAAU,CAAC,EAAE,gBAAgB,CAAC;KAC/B,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAY1B,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAI/F,iBAAiB,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAItG,gBAAgB,CAAC,SAAS,EAAE,eAAe,EAAE,EAAE,UAAU,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAI3G,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,mBAAmB,CAAC;YAqD3D,iBAAiB;YAmBjB,aAAa;YAWb,QAAQ;IA+DtB,OAAO,CAAC,aAAa;CAiDtB"}