@agentdeck/bridge 1.0.21 → 1.0.23
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/apme/activity-history.d.ts +59 -0
- package/dist/apme/activity-history.d.ts.map +1 -0
- package/dist/apme/activity-history.js +265 -0
- package/dist/apme/activity-history.js.map +1 -0
- package/dist/apme/dashboard-html.d.ts.map +1 -1
- package/dist/apme/dashboard-html.js +40 -6
- package/dist/apme/dashboard-html.js.map +1 -1
- package/dist/apme/http.d.ts +1 -0
- package/dist/apme/http.d.ts.map +1 -1
- package/dist/apme/http.js +9 -0
- package/dist/apme/http.js.map +1 -1
- package/dist/apme/runner.d.ts +20 -0
- package/dist/apme/runner.d.ts.map +1 -1
- package/dist/apme/runner.js +57 -0
- package/dist/apme/runner.js.map +1 -1
- package/dist/apme/settings.d.ts +21 -4
- package/dist/apme/settings.d.ts.map +1 -1
- package/dist/apme/settings.js +29 -5
- package/dist/apme/settings.js.map +1 -1
- package/dist/apme/store.d.ts +7 -0
- package/dist/apme/store.d.ts.map +1 -1
- package/dist/apme/store.js +35 -0
- package/dist/apme/store.js.map +1 -1
- package/dist/ble-sync-spawn.d.ts +27 -0
- package/dist/ble-sync-spawn.d.ts.map +1 -1
- package/dist/ble-sync-spawn.js +32 -0
- package/dist/ble-sync-spawn.js.map +1 -1
- package/dist/bridge-core.d.ts +21 -4
- package/dist/bridge-core.d.ts.map +1 -1
- package/dist/bridge-core.js +52 -23
- package/dist/bridge-core.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +261 -13
- package/dist/cli.js.map +1 -1
- package/dist/codex-rate-limits-live.d.ts +58 -9
- package/dist/codex-rate-limits-live.d.ts.map +1 -1
- package/dist/codex-rate-limits-live.js +64 -15
- package/dist/codex-rate-limits-live.js.map +1 -1
- package/dist/codex-rate-limits.d.ts +2 -2
- package/dist/codex-rate-limits.d.ts.map +1 -1
- package/dist/codex-rate-limits.js +34 -16
- package/dist/codex-rate-limits.js.map +1 -1
- package/dist/daemon-server.d.ts +46 -2
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +165 -44
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon-takeover.d.ts +3 -0
- package/dist/daemon-takeover.d.ts.map +1 -1
- package/dist/daemon-takeover.js +5 -0
- package/dist/daemon-takeover.js.map +1 -1
- package/dist/daemon.js +1 -1
- package/dist/esp32-flash-lease.d.ts +47 -0
- package/dist/esp32-flash-lease.d.ts.map +1 -0
- package/dist/esp32-flash-lease.js +84 -0
- package/dist/esp32-flash-lease.js.map +1 -0
- package/dist/esp32-flash-transport.d.ts +90 -0
- package/dist/esp32-flash-transport.d.ts.map +1 -0
- package/dist/esp32-flash-transport.js +157 -0
- package/dist/esp32-flash-transport.js.map +1 -0
- package/dist/esp32-flash.d.ts +171 -0
- package/dist/esp32-flash.d.ts.map +1 -0
- package/dist/esp32-flash.js +523 -0
- package/dist/esp32-flash.js.map +1 -0
- package/dist/esp32-serial.d.ts +9 -1
- package/dist/esp32-serial.d.ts.map +1 -1
- package/dist/esp32-serial.js +74 -1
- package/dist/esp32-serial.js.map +1 -1
- package/dist/esptool-js-node.d.ts +17 -0
- package/dist/esptool-js-node.d.ts.map +1 -0
- package/dist/esptool-js-node.js +17 -0
- package/dist/esptool-js-node.js.map +1 -0
- package/dist/hook-server.d.ts.map +1 -1
- package/dist/hook-server.js +17 -0
- package/dist/hook-server.js.map +1 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.js +13 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/python-ble-runtime.d.ts +2 -0
- package/dist/python-ble-runtime.d.ts.map +1 -1
- package/dist/python-ble-runtime.js +101 -7
- package/dist/python-ble-runtime.js.map +1 -1
- package/dist/review-runner.d.ts.map +1 -1
- package/dist/review-runner.js +27 -9
- package/dist/review-runner.js.map +1 -1
- package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -1
- package/dist/timebox/timebox-daemon-sync.js +11 -1
- package/dist/timebox/timebox-daemon-sync.js.map +1 -1
- package/dist/timeline-summarizer.d.ts +7 -2
- package/dist/timeline-summarizer.d.ts.map +1 -1
- package/dist/timeline-summarizer.js +13 -69
- package/dist/timeline-summarizer.js.map +1 -1
- package/dist/usage-api.d.ts +28 -1
- package/dist/usage-api.d.ts.map +1 -1
- package/dist/usage-api.js +74 -33
- package/dist/usage-api.js.map +1 -1
- package/dist/usage-event.d.ts.map +1 -1
- package/dist/usage-event.js +2 -17
- package/dist/usage-event.js.map +1 -1
- package/package.json +6 -4
|
@@ -43,6 +43,9 @@ export interface TakeoverDeps {
|
|
|
43
43
|
shutdown: (port: number) => Promise<void>;
|
|
44
44
|
waitForExit: (port: number, timeoutMs: number) => Promise<boolean>;
|
|
45
45
|
waitForBindable: (port: number, timeoutMs: number) => Promise<boolean>;
|
|
46
|
+
/** Best-effort derived APME projection capture before the Swift listener
|
|
47
|
+
* disappears. Never blocks takeover on failure. */
|
|
48
|
+
captureActivity?: (port: number, token: unknown) => Promise<boolean>;
|
|
46
49
|
}
|
|
47
50
|
export type TakeoverOutcome =
|
|
48
51
|
/** Nothing in the way (or it yielded) — go bind. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon-takeover.d.ts","sourceRoot":"","sources":["../src/daemon-takeover.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAGxE,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,kBAAkB,CAAC;IACnE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,2DAA2D;IAC3D,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IACxC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"daemon-takeover.d.ts","sourceRoot":"","sources":["../src/daemon-takeover.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAGxE,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,kBAAkB,CAAC;IACnE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,2DAA2D;IAC3D,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IACxC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvE;wDACoD;IACpD,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACtE;AAED,MAAM,MAAM,eAAe;AACzB,oDAAoD;AAClD,SAAS;AACX,2DAA2D;GACzD,iBAAiB;AACnB,0EAA0E;GACxE,QAAQ,CAAC;AAEb;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EAC3C,SAAS,GAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,kBAAuC,GACrF,OAAO,CAET;AASD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAAC,eAAe,EAAE,OAAO,CAAA;CAAE,EACnF,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,eAAe,CAAC,CAwG1B"}
|
package/dist/daemon-takeover.js
CHANGED
|
@@ -81,6 +81,11 @@ export async function negotiateIncumbentDaemon(args, deps) {
|
|
|
81
81
|
log(`Daemon already running on port ${port}. Use 'agentdeck daemon stop' first.`);
|
|
82
82
|
return 'already-running';
|
|
83
83
|
}
|
|
84
|
+
if (deps.captureActivity) {
|
|
85
|
+
const captured = await deps.captureActivity(port, incumbent.pairingToken);
|
|
86
|
+
if (captured)
|
|
87
|
+
log(`Merged the app daemon's activity history into the CLI dashboard cache.`);
|
|
88
|
+
}
|
|
84
89
|
// Reverse two-tier upgrade path: the macOS app may already own the canonical
|
|
85
90
|
// port with its in-process Swift daemon (Tier 1 — limited: no ADB devices,
|
|
86
91
|
// no subscription usage, …). A plain "already running → exit" would strand
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon-takeover.js","sourceRoot":"","sources":["../src/daemon-takeover.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,kBAAkB,EAA2B,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,GAAG,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"daemon-takeover.js","sourceRoot":"","sources":["../src/daemon-takeover.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon.js
CHANGED
|
@@ -17,7 +17,7 @@ const program = new Command();
|
|
|
17
17
|
program
|
|
18
18
|
.name('agentdeck')
|
|
19
19
|
.description('AgentDeck daemon (legacy entry point — use `agentdeck daemon` instead)')
|
|
20
|
-
.version('1.0.
|
|
20
|
+
.version('1.0.23');
|
|
21
21
|
program
|
|
22
22
|
.command('start', { isDefault: true })
|
|
23
23
|
.description('Start monitoring daemon')
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The serial-suspend lease: "nobody but the flasher may open a serial port
|
|
3
|
+
* until <when>".
|
|
4
|
+
*
|
|
5
|
+
* WHY IT IS A FILE AND NOT A VARIABLE. The recorded failure this exists to fix
|
|
6
|
+
* was not the daemon we asked to stand down — it was the daemon that came back.
|
|
7
|
+
* An in-process pause dies with the process; a respawn (LaunchAgent, a user
|
|
8
|
+
* re-running `daemon start`, the app relaunching) opens every port again and
|
|
9
|
+
* DTR-resets the board mid-write. A lease on disk survives that, because the
|
|
10
|
+
* respawned daemon reads it before its first poll.
|
|
11
|
+
*
|
|
12
|
+
* WHY EXPIRY IS ENFORCED ON READ, NEVER BY A TIMER. Same rule the pairing
|
|
13
|
+
* window follows. A timer on a laptop that sleeps fires late, and a late timer
|
|
14
|
+
* extends the suspension past what was promised — the daemon stays deaf to
|
|
15
|
+
* every board for as long as the machine was asleep. Reading the clock at the
|
|
16
|
+
* point of use cannot do that, and it also self-heals when the CLI is killed
|
|
17
|
+
* mid-flash: nothing has to run to end the lease, it simply stops being true.
|
|
18
|
+
*
|
|
19
|
+
* HONEST GAP: the sandboxed Swift daemon cannot read `~/.agentdeck`, so a lease
|
|
20
|
+
* written here does not stop it. That is why the CLI also runs an `lsof`
|
|
21
|
+
* pre-check and refuses to start when a non-AgentDeck process holds the port,
|
|
22
|
+
* and why it verifies after writing rather than trusting the suspension.
|
|
23
|
+
*/
|
|
24
|
+
export interface FlashLease {
|
|
25
|
+
/** epoch ms after which the lease means nothing */
|
|
26
|
+
until: number;
|
|
27
|
+
/** who asked, for a human reading the file or the log */
|
|
28
|
+
reason: string;
|
|
29
|
+
/** pid of the requester, informational only — never a liveness test */
|
|
30
|
+
pid?: number;
|
|
31
|
+
board?: string;
|
|
32
|
+
}
|
|
33
|
+
/** Clamp, so a caller cannot suspend the daemon's device layer indefinitely. */
|
|
34
|
+
export declare const LEASE_MIN_SECONDS = 1;
|
|
35
|
+
export declare const LEASE_MAX_SECONDS = 900;
|
|
36
|
+
export declare function leaseFile(): string;
|
|
37
|
+
export declare function clampLeaseSeconds(seconds: unknown): number;
|
|
38
|
+
/**
|
|
39
|
+
* The lease, or null. Expiry is applied HERE — the only place that reads it —
|
|
40
|
+
* so no caller can observe an expired lease as active.
|
|
41
|
+
*/
|
|
42
|
+
export declare function readLease(now?: number, file?: string): FlashLease | null;
|
|
43
|
+
export declare function serialSuspended(now?: number): boolean;
|
|
44
|
+
export declare function writeLease(lease: FlashLease, file?: string): FlashLease;
|
|
45
|
+
/** Idempotent: resuming when nothing is suspended is a success, not an error. */
|
|
46
|
+
export declare function clearLease(file?: string): void;
|
|
47
|
+
//# sourceMappingURL=esp32-flash-lease.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esp32-flash-lease.d.ts","sourceRoot":"","sources":["../src/esp32-flash-lease.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAOH,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,gFAAgF;AAChF,eAAO,MAAM,iBAAiB,IAAI,CAAC;AACnC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAI1D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,SAAa,EAAE,IAAI,SAAc,GAAG,UAAU,GAAG,IAAI,CAajF;AAED,wBAAgB,eAAe,CAAC,GAAG,SAAa,GAAG,OAAO,CAEzD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,SAAc,GAAG,UAAU,CAS5E;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,SAAc,GAAG,IAAI,CAMnD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The serial-suspend lease: "nobody but the flasher may open a serial port
|
|
3
|
+
* until <when>".
|
|
4
|
+
*
|
|
5
|
+
* WHY IT IS A FILE AND NOT A VARIABLE. The recorded failure this exists to fix
|
|
6
|
+
* was not the daemon we asked to stand down — it was the daemon that came back.
|
|
7
|
+
* An in-process pause dies with the process; a respawn (LaunchAgent, a user
|
|
8
|
+
* re-running `daemon start`, the app relaunching) opens every port again and
|
|
9
|
+
* DTR-resets the board mid-write. A lease on disk survives that, because the
|
|
10
|
+
* respawned daemon reads it before its first poll.
|
|
11
|
+
*
|
|
12
|
+
* WHY EXPIRY IS ENFORCED ON READ, NEVER BY A TIMER. Same rule the pairing
|
|
13
|
+
* window follows. A timer on a laptop that sleeps fires late, and a late timer
|
|
14
|
+
* extends the suspension past what was promised — the daemon stays deaf to
|
|
15
|
+
* every board for as long as the machine was asleep. Reading the clock at the
|
|
16
|
+
* point of use cannot do that, and it also self-heals when the CLI is killed
|
|
17
|
+
* mid-flash: nothing has to run to end the lease, it simply stops being true.
|
|
18
|
+
*
|
|
19
|
+
* HONEST GAP: the sandboxed Swift daemon cannot read `~/.agentdeck`, so a lease
|
|
20
|
+
* written here does not stop it. That is why the CLI also runs an `lsof`
|
|
21
|
+
* pre-check and refuses to start when a non-AgentDeck process holds the port,
|
|
22
|
+
* and why it verifies after writing rather than trusting the suspension.
|
|
23
|
+
*/
|
|
24
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'fs';
|
|
25
|
+
import { dirname, join } from 'path';
|
|
26
|
+
import { getDataDir } from './session-registry.js';
|
|
27
|
+
import { debug } from './logger.js';
|
|
28
|
+
/** Clamp, so a caller cannot suspend the daemon's device layer indefinitely. */
|
|
29
|
+
export const LEASE_MIN_SECONDS = 1;
|
|
30
|
+
export const LEASE_MAX_SECONDS = 900;
|
|
31
|
+
export function leaseFile() {
|
|
32
|
+
return join(getDataDir(), 'esp32-flash-lease.json');
|
|
33
|
+
}
|
|
34
|
+
export function clampLeaseSeconds(seconds) {
|
|
35
|
+
const n = Number(seconds);
|
|
36
|
+
if (!Number.isFinite(n))
|
|
37
|
+
return LEASE_MIN_SECONDS;
|
|
38
|
+
return Math.max(LEASE_MIN_SECONDS, Math.min(LEASE_MAX_SECONDS, Math.floor(n)));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The lease, or null. Expiry is applied HERE — the only place that reads it —
|
|
42
|
+
* so no caller can observe an expired lease as active.
|
|
43
|
+
*/
|
|
44
|
+
export function readLease(now = Date.now(), file = leaseFile()) {
|
|
45
|
+
try {
|
|
46
|
+
if (!existsSync(file))
|
|
47
|
+
return null;
|
|
48
|
+
const raw = JSON.parse(readFileSync(file, 'utf8'));
|
|
49
|
+
const until = Number(raw.until);
|
|
50
|
+
if (!Number.isFinite(until) || until <= now)
|
|
51
|
+
return null;
|
|
52
|
+
return { until, reason: String(raw.reason ?? 'unknown'), pid: raw.pid, board: raw.board };
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// A corrupt lease means "no lease". Failing open is correct: the cost is a
|
|
56
|
+
// reset board during a flash that is probably not running; failing closed
|
|
57
|
+
// would leave every device permanently unreachable behind a bad file.
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function serialSuspended(now = Date.now()) {
|
|
62
|
+
return readLease(now) !== null;
|
|
63
|
+
}
|
|
64
|
+
export function writeLease(lease, file = leaseFile()) {
|
|
65
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
66
|
+
// tmp+rename, like every other file this daemon owns: a torn lease read as
|
|
67
|
+
// corrupt is a lease that silently is not protecting the write in flight.
|
|
68
|
+
const tmp = `${file}.tmp`;
|
|
69
|
+
writeFileSync(tmp, `${JSON.stringify(lease, null, 2)}\n`);
|
|
70
|
+
renameSync(tmp, file);
|
|
71
|
+
debug('ESP32', `flash lease until ${new Date(lease.until).toISOString()} (${lease.reason})`);
|
|
72
|
+
return lease;
|
|
73
|
+
}
|
|
74
|
+
/** Idempotent: resuming when nothing is suspended is a success, not an error. */
|
|
75
|
+
export function clearLease(file = leaseFile()) {
|
|
76
|
+
try {
|
|
77
|
+
if (existsSync(file))
|
|
78
|
+
unlinkSync(file);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Best effort. An unremovable lease still expires on its own clock.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=esp32-flash-lease.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esp32-flash-lease.js","sourceRoot":"","sources":["../src/esp32-flash-lease.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Web Serial-shaped port over `serialport`, so the CLI can drive esptool-js.
|
|
3
|
+
*
|
|
4
|
+
* WHY A SHIM AND NOT AN EXTENSION OF `esp32-serial.ts`. That module opens a raw
|
|
5
|
+
* fd and configures the line with `stty`, which can set baud and framing but
|
|
6
|
+
* **cannot touch DTR or RTS** — those need `ioctl(TIOCMSET)`. DTR/RTS *are* the
|
|
7
|
+
* ROM download-mode entry sequence, so there is no path from the existing
|
|
8
|
+
* serial code to a flasher. `serialport`'s `set({dtr, rts})` is the missing
|
|
9
|
+
* primitive, and once you have it, esptool-js needs only six members of a Web
|
|
10
|
+
* Serial port — open/close/readable/writable/setSignals/getInfo — which is what
|
|
11
|
+
* this class is.
|
|
12
|
+
*
|
|
13
|
+
* This exact adapter was what made the Phase 0 hardware spike possible: the
|
|
14
|
+
* browser's `requestPort()` opens a native chooser no automation can drive, so
|
|
15
|
+
* seven boards were measured headlessly through this code path before a byte
|
|
16
|
+
* was ever written. It is not scaffolding that got reused; it is the thing that
|
|
17
|
+
* was validated.
|
|
18
|
+
*
|
|
19
|
+
* `serialport` is an OPTIONAL dependency (native module, same class as
|
|
20
|
+
* `node-pty` / `better-sqlite3` / `@resvg/resvg-js`), so every entry point here
|
|
21
|
+
* has to survive its absence with an actionable message rather than a
|
|
22
|
+
* MODULE_NOT_FOUND stack.
|
|
23
|
+
*/
|
|
24
|
+
/** Loaded lazily so importing this module never fails on a machine without it. */
|
|
25
|
+
type SerialPortCtor = typeof import('serialport').SerialPort;
|
|
26
|
+
export declare function loadSerialPort(): Promise<SerialPortCtor | null>;
|
|
27
|
+
/** @internal test seam — lets the absent-module path run without uninstalling anything. */
|
|
28
|
+
export declare function __setSerialPortForTests(ctor: SerialPortCtor | null | undefined): void;
|
|
29
|
+
export interface PortCandidate {
|
|
30
|
+
path: string;
|
|
31
|
+
usbVendorId?: number;
|
|
32
|
+
usbProductId?: number;
|
|
33
|
+
manufacturer?: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function listCandidatePorts(): Promise<PortCandidate[]>;
|
|
36
|
+
interface OpenOpts {
|
|
37
|
+
baudRate: number;
|
|
38
|
+
dataBits?: number;
|
|
39
|
+
stopBits?: number;
|
|
40
|
+
parity?: string;
|
|
41
|
+
bufferSize?: number;
|
|
42
|
+
flowControl?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The six members esptool-js touches, and nothing else. Structurally compatible
|
|
46
|
+
* with `SerialPort` from the Web Serial types; cast at the call site.
|
|
47
|
+
*/
|
|
48
|
+
export declare class NodeWebSerialPort {
|
|
49
|
+
private readonly path;
|
|
50
|
+
private readonly info;
|
|
51
|
+
private readonly ctor?;
|
|
52
|
+
private port?;
|
|
53
|
+
private webReadable?;
|
|
54
|
+
private webWritable?;
|
|
55
|
+
constructor(path: string, info?: {
|
|
56
|
+
usbVendorId?: number;
|
|
57
|
+
usbProductId?: number;
|
|
58
|
+
}, ctor?: SerialPortCtor | undefined);
|
|
59
|
+
getInfo(): {
|
|
60
|
+
usbVendorId?: number;
|
|
61
|
+
usbProductId?: number;
|
|
62
|
+
};
|
|
63
|
+
get readable(): ReadableStream<Uint8Array> | null;
|
|
64
|
+
get writable(): WritableStream<Uint8Array> | null;
|
|
65
|
+
open(opts: OpenOpts): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* ALWAYS WRITE BOTH LINES. This is the single most expensive thing the spike
|
|
68
|
+
* learned.
|
|
69
|
+
*
|
|
70
|
+
* Web Serial lets a caller move one signal and leave the other alone;
|
|
71
|
+
* `serialport`'s `set()` applies its OWN defaults to whatever you omit, so a
|
|
72
|
+
* single-signal call silently moves the other line too. esptool-js's
|
|
73
|
+
* `Transport.setRTS()` re-sends DTR immediately after RTS (a usbser.sys
|
|
74
|
+
* work-around), so the clobber lands on every reset strobe and the board
|
|
75
|
+
* never enters the ROM bootloader. The symptom is only ever "failed to
|
|
76
|
+
* connect" — nothing points at the adapter. Every strategy failed on the
|
|
77
|
+
* first probe run for exactly this reason.
|
|
78
|
+
*
|
|
79
|
+
* So the two lines are held here and both are written every time.
|
|
80
|
+
*/
|
|
81
|
+
private dtr;
|
|
82
|
+
private rts;
|
|
83
|
+
setSignals(signals: {
|
|
84
|
+
dataTerminalReady?: boolean;
|
|
85
|
+
requestToSend?: boolean;
|
|
86
|
+
}): Promise<void>;
|
|
87
|
+
close(): Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
export {};
|
|
90
|
+
//# sourceMappingURL=esp32-flash-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esp32-flash-transport.d.ts","sourceRoot":"","sources":["../src/esp32-flash-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,kFAAkF;AAClF,KAAK,cAAc,GAAG,cAAc,YAAY,EAAE,UAAU,CAAC;AAI7D,wBAAsB,cAAc,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CASrE;AAED,2FAA2F;AAC3F,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAErF;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAYD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAYnE;AAED,UAAU,QAAQ;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,iBAAiB;IAM1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;IAPxB,OAAO,CAAC,IAAI,CAAC,CAA+B;IAC5C,OAAO,CAAC,WAAW,CAAC,CAA6B;IACjD,OAAO,CAAC,WAAW,CAAC,CAA6B;gBAG9B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAO,EAC1D,IAAI,CAAC,EAAE,cAAc,YAAA;IAGxC,OAAO,IAAI;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE;IAI1D,IAAI,QAAQ,IAAI,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAEhD;IAED,IAAI,QAAQ,IAAI,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAEhD;IAEK,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BzC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,GAAG,CAAS;IAEd,UAAU,CAAC,OAAO,EAAE;QAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAW7B"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Web Serial-shaped port over `serialport`, so the CLI can drive esptool-js.
|
|
3
|
+
*
|
|
4
|
+
* WHY A SHIM AND NOT AN EXTENSION OF `esp32-serial.ts`. That module opens a raw
|
|
5
|
+
* fd and configures the line with `stty`, which can set baud and framing but
|
|
6
|
+
* **cannot touch DTR or RTS** — those need `ioctl(TIOCMSET)`. DTR/RTS *are* the
|
|
7
|
+
* ROM download-mode entry sequence, so there is no path from the existing
|
|
8
|
+
* serial code to a flasher. `serialport`'s `set({dtr, rts})` is the missing
|
|
9
|
+
* primitive, and once you have it, esptool-js needs only six members of a Web
|
|
10
|
+
* Serial port — open/close/readable/writable/setSignals/getInfo — which is what
|
|
11
|
+
* this class is.
|
|
12
|
+
*
|
|
13
|
+
* This exact adapter was what made the Phase 0 hardware spike possible: the
|
|
14
|
+
* browser's `requestPort()` opens a native chooser no automation can drive, so
|
|
15
|
+
* seven boards were measured headlessly through this code path before a byte
|
|
16
|
+
* was ever written. It is not scaffolding that got reused; it is the thing that
|
|
17
|
+
* was validated.
|
|
18
|
+
*
|
|
19
|
+
* `serialport` is an OPTIONAL dependency (native module, same class as
|
|
20
|
+
* `node-pty` / `better-sqlite3` / `@resvg/resvg-js`), so every entry point here
|
|
21
|
+
* has to survive its absence with an actionable message rather than a
|
|
22
|
+
* MODULE_NOT_FOUND stack.
|
|
23
|
+
*/
|
|
24
|
+
import { Readable, Writable } from 'node:stream';
|
|
25
|
+
let cached;
|
|
26
|
+
export async function loadSerialPort() {
|
|
27
|
+
if (cached !== undefined)
|
|
28
|
+
return cached;
|
|
29
|
+
try {
|
|
30
|
+
const mod = await import('serialport');
|
|
31
|
+
cached = mod.SerialPort;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
cached = null;
|
|
35
|
+
}
|
|
36
|
+
return cached;
|
|
37
|
+
}
|
|
38
|
+
/** @internal test seam — lets the absent-module path run without uninstalling anything. */
|
|
39
|
+
export function __setSerialPortForTests(ctor) {
|
|
40
|
+
cached = ctor;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* macOS exposes each USB serial device twice: `/dev/tty.*` blocks on open until
|
|
44
|
+
* DCD asserts, `/dev/cu.*` does not. Only the callout node is usable here.
|
|
45
|
+
*/
|
|
46
|
+
const toCallout = (p) => p.replace('/dev/tty.', '/dev/cu.');
|
|
47
|
+
/** Same predicate the serial bridge uses, so both agree on what is a candidate. */
|
|
48
|
+
const CANDIDATE = /usbserial|wchusbserial|usbmodem|ttyUSB|ttyACM/;
|
|
49
|
+
const EXCLUDE = /Bluetooth|WLAN|debug/i;
|
|
50
|
+
export async function listCandidatePorts() {
|
|
51
|
+
const SerialPort = await loadSerialPort();
|
|
52
|
+
if (!SerialPort)
|
|
53
|
+
return [];
|
|
54
|
+
const list = await SerialPort.list();
|
|
55
|
+
return list
|
|
56
|
+
.filter((p) => CANDIDATE.test(p.path) && !EXCLUDE.test(p.path))
|
|
57
|
+
.map((p) => ({
|
|
58
|
+
path: toCallout(p.path),
|
|
59
|
+
usbVendorId: p.vendorId ? Number.parseInt(p.vendorId, 16) : undefined,
|
|
60
|
+
usbProductId: p.productId ? Number.parseInt(p.productId, 16) : undefined,
|
|
61
|
+
manufacturer: p.manufacturer,
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The six members esptool-js touches, and nothing else. Structurally compatible
|
|
66
|
+
* with `SerialPort` from the Web Serial types; cast at the call site.
|
|
67
|
+
*/
|
|
68
|
+
export class NodeWebSerialPort {
|
|
69
|
+
path;
|
|
70
|
+
info;
|
|
71
|
+
ctor;
|
|
72
|
+
port;
|
|
73
|
+
webReadable;
|
|
74
|
+
webWritable;
|
|
75
|
+
constructor(path, info = {}, ctor) {
|
|
76
|
+
this.path = path;
|
|
77
|
+
this.info = info;
|
|
78
|
+
this.ctor = ctor;
|
|
79
|
+
}
|
|
80
|
+
getInfo() {
|
|
81
|
+
return this.info;
|
|
82
|
+
}
|
|
83
|
+
get readable() {
|
|
84
|
+
return this.webReadable ?? null;
|
|
85
|
+
}
|
|
86
|
+
get writable() {
|
|
87
|
+
return this.webWritable ?? null;
|
|
88
|
+
}
|
|
89
|
+
async open(opts) {
|
|
90
|
+
const SerialPort = this.ctor ?? (await loadSerialPort());
|
|
91
|
+
if (!SerialPort)
|
|
92
|
+
throw new Error('serialport is not installed');
|
|
93
|
+
// A fresh handle per open: Transport.connect() re-opens at a new baud, and
|
|
94
|
+
// reusing the streams across a close would hand back a locked reader.
|
|
95
|
+
await this.close();
|
|
96
|
+
await new Promise((resolve, reject) => {
|
|
97
|
+
this.port = new SerialPort({
|
|
98
|
+
path: this.path,
|
|
99
|
+
baudRate: opts.baudRate,
|
|
100
|
+
dataBits: (opts.dataBits ?? 8),
|
|
101
|
+
stopBits: (opts.stopBits ?? 1),
|
|
102
|
+
parity: (opts.parity ?? 'none'),
|
|
103
|
+
autoOpen: false,
|
|
104
|
+
// Do NOT let the driver assert DTR/RTS on open. esptool drives the
|
|
105
|
+
// reset sequence itself, and an implicit strobe here resets the board
|
|
106
|
+
// at a moment the chosen strategy is not expecting.
|
|
107
|
+
hupcl: false,
|
|
108
|
+
}, (err) => { if (err)
|
|
109
|
+
reject(err); });
|
|
110
|
+
this.port.open((err) => (err ? reject(err) : resolve()));
|
|
111
|
+
});
|
|
112
|
+
this.webReadable = Readable.toWeb(this.port);
|
|
113
|
+
this.webWritable = Writable.toWeb(this.port);
|
|
114
|
+
}
|
|
115
|
+
/* --------------------------------------------------------------- signals */
|
|
116
|
+
/**
|
|
117
|
+
* ALWAYS WRITE BOTH LINES. This is the single most expensive thing the spike
|
|
118
|
+
* learned.
|
|
119
|
+
*
|
|
120
|
+
* Web Serial lets a caller move one signal and leave the other alone;
|
|
121
|
+
* `serialport`'s `set()` applies its OWN defaults to whatever you omit, so a
|
|
122
|
+
* single-signal call silently moves the other line too. esptool-js's
|
|
123
|
+
* `Transport.setRTS()` re-sends DTR immediately after RTS (a usbser.sys
|
|
124
|
+
* work-around), so the clobber lands on every reset strobe and the board
|
|
125
|
+
* never enters the ROM bootloader. The symptom is only ever "failed to
|
|
126
|
+
* connect" — nothing points at the adapter. Every strategy failed on the
|
|
127
|
+
* first probe run for exactly this reason.
|
|
128
|
+
*
|
|
129
|
+
* So the two lines are held here and both are written every time.
|
|
130
|
+
*/
|
|
131
|
+
dtr = false;
|
|
132
|
+
rts = false;
|
|
133
|
+
async setSignals(signals) {
|
|
134
|
+
const p = this.port;
|
|
135
|
+
if (!p?.isOpen)
|
|
136
|
+
return;
|
|
137
|
+
if (signals.dataTerminalReady !== undefined)
|
|
138
|
+
this.dtr = signals.dataTerminalReady;
|
|
139
|
+
if (signals.requestToSend !== undefined)
|
|
140
|
+
this.rts = signals.requestToSend;
|
|
141
|
+
await new Promise((resolve, reject) => p.set({ dtr: this.dtr, rts: this.rts }, (e) => (e ? reject(e) : resolve())));
|
|
142
|
+
}
|
|
143
|
+
async close() {
|
|
144
|
+
const p = this.port;
|
|
145
|
+
this.webReadable = undefined;
|
|
146
|
+
this.webWritable = undefined;
|
|
147
|
+
this.port = undefined;
|
|
148
|
+
if (!p)
|
|
149
|
+
return;
|
|
150
|
+
await new Promise((resolve) => {
|
|
151
|
+
if (!p.isOpen)
|
|
152
|
+
return resolve();
|
|
153
|
+
p.close(() => resolve());
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=esp32-flash-transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esp32-flash-transport.js","sourceRoot":"","sources":["../src/esp32-flash-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agentdeck esp32 flash <board>` — the reliable half of the firmware install
|
|
3
|
+
* story. (The browser flasher at /flash/ is the reachable half.)
|
|
4
|
+
*
|
|
5
|
+
* WHAT IT WRITES. One `agentdeck-<board>-merged.bin` at offset 0x0, on every
|
|
6
|
+
* chip. `esptool merge-bin --target-offset 0x0` has already put each board's
|
|
7
|
+
* bootloader where its ROM looks for it — classic ESP32 at 0x1000 inside the
|
|
8
|
+
* image, P4 at 0x2000, S3/C6 at 0x0 — so nothing downstream branches on chip
|
|
9
|
+
* family. The old release notes told users to write the app alone at 0x10000,
|
|
10
|
+
* which cannot bring a board up: `boot_app0.bin` was never published, so a
|
|
11
|
+
* stale otadata boots the previous slot.
|
|
12
|
+
*
|
|
13
|
+
* WHAT IT WILL NOT WRITE. The preflight in `@agentdeck/shared` is shared
|
|
14
|
+
* verbatim with the browser, and there is no `--force`. Writing an S3 image to
|
|
15
|
+
* a classic ESP32, or a 16MB-header image to an 8MB part, is how these boards
|
|
16
|
+
* get bricked — and the recovery tool for a bricked board is this one.
|
|
17
|
+
*/
|
|
18
|
+
import { type Esp32BoardSpec, type Esp32PreflightVerdict } from '@agentdeck/shared';
|
|
19
|
+
export declare const GITHUB_REPO = "puritysb/AgentDeck";
|
|
20
|
+
export declare function resolveFlashBoard(target: string): Esp32BoardSpec;
|
|
21
|
+
export interface ManifestArtifact {
|
|
22
|
+
file: string;
|
|
23
|
+
size: number;
|
|
24
|
+
sha256: string;
|
|
25
|
+
offset: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ManifestBoardEntry {
|
|
28
|
+
id: string;
|
|
29
|
+
chipFamily: string;
|
|
30
|
+
flashSize: string;
|
|
31
|
+
flashMode: string;
|
|
32
|
+
flashFreq: string;
|
|
33
|
+
uploadBaud: number;
|
|
34
|
+
merged?: ManifestArtifact;
|
|
35
|
+
}
|
|
36
|
+
export interface ReleaseManifest {
|
|
37
|
+
schema: number;
|
|
38
|
+
release: string;
|
|
39
|
+
firmwareVersion: string;
|
|
40
|
+
boards: ManifestBoardEntry[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Every published `esp32-v*` tag, newest first.
|
|
44
|
+
*
|
|
45
|
+
* PAGED, because a single page is a silent cap. `per_page` maxes at 100 and
|
|
46
|
+
* this repo cuts ~6 tags a round across its channels, so once ~100 releases
|
|
47
|
+
* accumulate after the newest esp32 cut, one page stops containing it — and the
|
|
48
|
+
* damage is not "no answer" but a WRONG one: the existence check would report a
|
|
49
|
+
* genuinely published version as unpublished and quietly fall back to an older
|
|
50
|
+
* release. Stops at the first page that yields no esp32 tag once some are
|
|
51
|
+
* already known, so the common case is still one request.
|
|
52
|
+
*/
|
|
53
|
+
export declare function listFirmwareTags(maxPages?: number): Promise<string[]>;
|
|
54
|
+
/** Newest `esp32-v*` tag, or undefined when the repo has none. */
|
|
55
|
+
export declare function latestFirmwareTag(): Promise<string | undefined>;
|
|
56
|
+
/**
|
|
57
|
+
* Which release a bare `agentdeck esp32 flash <board>` takes firmware from.
|
|
58
|
+
*
|
|
59
|
+
* TWO RULES, IN THIS ORDER, AND NEITHER WORKS ALONE. The checkout's
|
|
60
|
+
* `FIRMWARE_VERSION` names the release this source tree belongs to — but it is
|
|
61
|
+
* bumped BEFORE the tag is pushed, so during that window it names a release
|
|
62
|
+
* that does not exist yet. Taking it unconditionally is how the first command
|
|
63
|
+
* in the docs came to fail from every checkout the moment the version was
|
|
64
|
+
* bumped (and the 404 it produced was indistinguishable from "this release has
|
|
65
|
+
* no manifest", so it also misdiagnosed itself).
|
|
66
|
+
*
|
|
67
|
+
* Deliberately the same shape as `resolveTag` in
|
|
68
|
+
* `scripts/fetch-flash-firmware.mjs`. That one runs in CI with `gh`; this one
|
|
69
|
+
* runs on a user's machine with `fetch`. Same rules, same reported `source`.
|
|
70
|
+
*/
|
|
71
|
+
export declare function pickFirmwareTag(configVersion: string | undefined, published: readonly string[]): {
|
|
72
|
+
tag: string;
|
|
73
|
+
source: 'config' | 'latest';
|
|
74
|
+
};
|
|
75
|
+
export declare function firmwareVersionFromCheckout(configH: string): string | undefined;
|
|
76
|
+
export declare function firmwareCacheDir(tag: string): string;
|
|
77
|
+
export interface ResolveFirmwareOptions {
|
|
78
|
+
tag?: string;
|
|
79
|
+
/** an explicit .bin — skips the download entirely, mirroring esp32-ota's -f */
|
|
80
|
+
firmwarePath?: string;
|
|
81
|
+
/** fail instead of reaching the network */
|
|
82
|
+
offline?: boolean;
|
|
83
|
+
/** the repo checkout's esp32/src/config.h, when running from a checkout */
|
|
84
|
+
checkoutConfigH?: string;
|
|
85
|
+
log?: (msg: string) => void;
|
|
86
|
+
}
|
|
87
|
+
export interface ResolvedFirmware {
|
|
88
|
+
tag: string;
|
|
89
|
+
image: Buffer;
|
|
90
|
+
/** the manifest entry, when one was used; absent for --firmware */
|
|
91
|
+
entry?: ManifestBoardEntry;
|
|
92
|
+
source: 'file' | 'cache' | 'download';
|
|
93
|
+
path?: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get the merged image for a board, verifying its sha256 EVERY time — on a
|
|
97
|
+
* fresh download and on every cache hit. The cache key proves which release a
|
|
98
|
+
* file came from, never that its bits are still intact, and a bit-rotted cached
|
|
99
|
+
* image is a bricked board.
|
|
100
|
+
*/
|
|
101
|
+
export declare function resolveFirmware(board: Esp32BoardSpec, opts?: ResolveFirmwareOptions): Promise<ResolvedFirmware>;
|
|
102
|
+
export interface PortHolder {
|
|
103
|
+
command: string;
|
|
104
|
+
pid: number;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Three answers, not two — because this is the ONLY guard against the holder
|
|
108
|
+
* the lease provably cannot reach (the sandboxed Swift daemon cannot read
|
|
109
|
+
* `~/.agentdeck`).
|
|
110
|
+
*
|
|
111
|
+
* `lsof` exits non-zero when nothing holds the file, so a failed run really is
|
|
112
|
+
* the common "free" case. But it ALSO exits non-zero when it is not installed
|
|
113
|
+
* (a minimal Linux container), when the 5s timeout fires, and on EPERM — and
|
|
114
|
+
* collapsing those into `[]` launders "I could not look" into "nobody is
|
|
115
|
+
* holding it", which is the exact trap `esp32FlashIdIsUsable` exists to avoid
|
|
116
|
+
* on the flash-size axis, inverted to the permissive direction.
|
|
117
|
+
*/
|
|
118
|
+
export type PortHolderScan = {
|
|
119
|
+
known: true;
|
|
120
|
+
holders: PortHolder[];
|
|
121
|
+
} | {
|
|
122
|
+
known: false;
|
|
123
|
+
reason: string;
|
|
124
|
+
};
|
|
125
|
+
export declare function parseLsofHolders(stdout: string): PortHolder[];
|
|
126
|
+
export declare function scanPortHolders(port: string): Promise<PortHolderScan>;
|
|
127
|
+
/** Back-compat convenience: holders, or none when they could not be observed. */
|
|
128
|
+
export declare function whoHoldsPort(port: string): Promise<PortHolder[]>;
|
|
129
|
+
export declare function classifyHolders(holders: PortHolder[]): {
|
|
130
|
+
ours: PortHolder[];
|
|
131
|
+
foreign: PortHolder[];
|
|
132
|
+
};
|
|
133
|
+
export interface FlashProgress {
|
|
134
|
+
onPhase?: (phase: string) => void;
|
|
135
|
+
onProgress?: (written: number, total: number) => void;
|
|
136
|
+
log?: (msg: string) => void;
|
|
137
|
+
}
|
|
138
|
+
export interface FlashOutcome {
|
|
139
|
+
chip: string;
|
|
140
|
+
mac: string;
|
|
141
|
+
flashSize?: string;
|
|
142
|
+
verdict: Esp32PreflightVerdict;
|
|
143
|
+
bytes: number;
|
|
144
|
+
elapsedMs: number;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Connect, identify, refuse-or-write, verify by MD5. Mirrors the browser's
|
|
148
|
+
* `flash.ts` step for step; the two share `esp32PreflightVerdict` so they can
|
|
149
|
+
* never disagree about which board may be written.
|
|
150
|
+
*/
|
|
151
|
+
export declare function flashBoard(board: Esp32BoardSpec, portPath: string, image: Buffer, geometry: Pick<ManifestBoardEntry, 'flashMode' | 'flashFreq' | 'flashSize'> & Partial<Pick<ManifestBoardEntry, 'chipFamily'>>, opts?: {
|
|
152
|
+
eraseAll?: boolean;
|
|
153
|
+
baud?: number;
|
|
154
|
+
imageIsFromManifest?: boolean;
|
|
155
|
+
}, cb?: FlashProgress): Promise<FlashOutcome>;
|
|
156
|
+
export interface DeviceIdentity {
|
|
157
|
+
board?: string;
|
|
158
|
+
version?: string;
|
|
159
|
+
buildHash?: string;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Reopen the port at 115200 and wait for the firmware's `device_info`.
|
|
163
|
+
*
|
|
164
|
+
* `ulanzi_tc001` MUST SKIP THIS. Its CH340 TX is broken in hardware, so it
|
|
165
|
+
* never answers any serial probe — running this on a TC001 reports a perfectly
|
|
166
|
+
* good flash as a failure. The exception is named in the SSOT's notes, in
|
|
167
|
+
* docs/esp32.md, and in the browser flasher's done-state copy.
|
|
168
|
+
*/
|
|
169
|
+
export declare const SERIAL_PROBE_UNAVAILABLE: Set<string>;
|
|
170
|
+
export declare function readDeviceIdentity(portPath: string, timeoutMs?: number): Promise<DeviceIdentity | null>;
|
|
171
|
+
//# sourceMappingURL=esp32-flash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esp32-flash.d.ts","sourceRoot":"","sources":["../src/esp32-flash.ts"],"names":[],"mappings":"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"}
|