@agentdeck/bridge 1.0.22 → 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.
Files changed (62) hide show
  1. package/dist/apme/activity-history.d.ts +59 -0
  2. package/dist/apme/activity-history.d.ts.map +1 -0
  3. package/dist/apme/activity-history.js +265 -0
  4. package/dist/apme/activity-history.js.map +1 -0
  5. package/dist/apme/dashboard-html.d.ts.map +1 -1
  6. package/dist/apme/dashboard-html.js +40 -6
  7. package/dist/apme/dashboard-html.js.map +1 -1
  8. package/dist/apme/http.d.ts +1 -0
  9. package/dist/apme/http.d.ts.map +1 -1
  10. package/dist/apme/http.js +9 -0
  11. package/dist/apme/http.js.map +1 -1
  12. package/dist/apme/runner.d.ts +20 -0
  13. package/dist/apme/runner.d.ts.map +1 -1
  14. package/dist/apme/runner.js +57 -0
  15. package/dist/apme/runner.js.map +1 -1
  16. package/dist/apme/settings.d.ts +21 -4
  17. package/dist/apme/settings.d.ts.map +1 -1
  18. package/dist/apme/settings.js +29 -5
  19. package/dist/apme/settings.js.map +1 -1
  20. package/dist/apme/store.d.ts +7 -0
  21. package/dist/apme/store.d.ts.map +1 -1
  22. package/dist/apme/store.js +35 -0
  23. package/dist/apme/store.js.map +1 -1
  24. package/dist/cli.d.ts.map +1 -1
  25. package/dist/cli.js +248 -13
  26. package/dist/cli.js.map +1 -1
  27. package/dist/daemon-server.d.ts.map +1 -1
  28. package/dist/daemon-server.js +100 -4
  29. package/dist/daemon-server.js.map +1 -1
  30. package/dist/daemon-takeover.d.ts +3 -0
  31. package/dist/daemon-takeover.d.ts.map +1 -1
  32. package/dist/daemon-takeover.js +5 -0
  33. package/dist/daemon-takeover.js.map +1 -1
  34. package/dist/daemon.js +1 -1
  35. package/dist/esp32-flash-lease.d.ts +47 -0
  36. package/dist/esp32-flash-lease.d.ts.map +1 -0
  37. package/dist/esp32-flash-lease.js +84 -0
  38. package/dist/esp32-flash-lease.js.map +1 -0
  39. package/dist/esp32-flash-transport.d.ts +90 -0
  40. package/dist/esp32-flash-transport.d.ts.map +1 -0
  41. package/dist/esp32-flash-transport.js +157 -0
  42. package/dist/esp32-flash-transport.js.map +1 -0
  43. package/dist/esp32-flash.d.ts +171 -0
  44. package/dist/esp32-flash.d.ts.map +1 -0
  45. package/dist/esp32-flash.js +523 -0
  46. package/dist/esp32-flash.js.map +1 -0
  47. package/dist/esp32-serial.d.ts +9 -1
  48. package/dist/esp32-serial.d.ts.map +1 -1
  49. package/dist/esp32-serial.js +74 -1
  50. package/dist/esp32-serial.js.map +1 -1
  51. package/dist/esptool-js-node.d.ts +17 -0
  52. package/dist/esptool-js-node.d.ts.map +1 -0
  53. package/dist/esptool-js-node.js +17 -0
  54. package/dist/esptool-js-node.js.map +1 -0
  55. package/dist/review-runner.d.ts.map +1 -1
  56. package/dist/review-runner.js +27 -9
  57. package/dist/review-runner.js.map +1 -1
  58. package/dist/timeline-summarizer.d.ts +7 -2
  59. package/dist/timeline-summarizer.d.ts.map +1 -1
  60. package/dist/timeline-summarizer.js +13 -69
  61. package/dist/timeline-summarizer.js.map +1 -1
  62. package/package.json +6 -4
@@ -102,7 +102,8 @@ import { codexTurnOutcomeFromRollout, lastAgentMessageFromCodexRollout } from '.
102
102
  import { callFoundationModelsHelper } from './foundation-models-helper.js';
103
103
  import { initModules, stopModules, createDefaultModules, } from './modules/index.js';
104
104
  import { bindHostFor, daemonModuleConfigs, describeDaemonPosture, resolveDaemonPosture, } from './network-posture.js';
105
- import { esp32ConnectionCount, getESP32DeviceInfo, onESP32Message, sendAuthProvisionToAll, sendWifiProvision, sendWifiProvisionToAll, handleESP32Wake, getESP32Ports, getSerialConnectionStatus, getSerialLastError, getSerialReachableBoards } from './esp32-serial.js';
105
+ import { esp32ConnectionCount, getESP32DeviceInfo, onESP32Message, sendAuthProvisionToAll, sendWifiProvision, sendWifiProvisionToAll, handleESP32Wake, getESP32Ports, getSerialConnectionStatus, getSerialLastError, getSerialReachableBoards, releaseESP32SerialPorts } from './esp32-serial.js';
106
+ import { clampLeaseSeconds, clearLease, readLease, writeLease } from './esp32-flash-lease.js';
106
107
  import { loadWifiConfig } from './wifi-config.js';
107
108
  import { getConnectedAdbDevices, hasAdb, getAdbDeviceCount } from './adb-reverse.js';
108
109
  import { getPixooDeviceDetails, pixooDeviceCount } from './pixoo/pixoo-bridge.js';
@@ -1134,6 +1135,10 @@ export async function startDaemon(opts) {
1134
1135
  /** Produces live timeline rows for observed Kiro sessions — see the class
1135
1136
  * doc for why Kiro needs a producer when hook agents do not. */
1136
1137
  const kiroTimelineFeed = new KiroTimelineFeed();
1138
+ /** Kiro has no CLI hooks, so transcript rows also feed APME. These maps are
1139
+ * bounded by the observer's current live set and discarded on disappearance. */
1140
+ const kiroApmeSessions = new Set();
1141
+ const kiroApmeResponseBySession = new Map();
1137
1142
  /** Observed Claude sessions whose AskUserQuestion PreToolUse is held open for
1138
1143
  * a device answer, keyed by the bare Claude session UUID. Its presence is
1139
1144
  * what makes the session's options pressable (`liveAnswerable`) and what
@@ -1647,6 +1652,62 @@ export async function startDaemon(opts) {
1647
1652
  }
1648
1653
  return;
1649
1654
  }
1655
+ // ===== Serial suspend / resume (USB flashing) =====
1656
+ // Both sit BEHIND the normal auth gate — `isAuthorizedHttpRequest()` treats
1657
+ // same-machine as authenticated, which is exactly the audience here, so
1658
+ // this adds no standing unauthenticated route. The one always-on pre-auth
1659
+ // route is still `GET /health`.
1660
+ //
1661
+ // `agentdeck esp32 flash` calls suspend, flashes, and calls resume in a
1662
+ // `finally`. Neither call is load-bearing on its own: expiry is enforced
1663
+ // when the lease is READ, so a CLI that is killed mid-flash recovers with
1664
+ // nothing running.
1665
+ if (req.method === 'POST' && pathname === '/esp32/serial/suspend') {
1666
+ (async () => {
1667
+ const body = await readJsonBody(req);
1668
+ const seconds = clampLeaseSeconds(body.seconds ?? 120);
1669
+ const lease = writeLease({
1670
+ until: Date.now() + seconds * 1000,
1671
+ reason: typeof body.reason === 'string' ? body.reason : 'usb flash',
1672
+ pid: typeof body.pid === 'number' ? body.pid : undefined,
1673
+ board: typeof body.board === 'string' ? body.board : undefined,
1674
+ });
1675
+ // Refusing to OPEN is only half of it — a port this daemon already
1676
+ // holds still blocks the flasher, and two readers on one TTY steal
1677
+ // each other's bytes instead of failing cleanly.
1678
+ const released = releaseESP32SerialPorts(`flash lease (${lease.board ?? 'usb flash'})`);
1679
+ return { ok: true, until: lease.until, seconds, released };
1680
+ })().then((result) => {
1681
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1682
+ res.end(JSON.stringify(result));
1683
+ }).catch((err) => {
1684
+ res.writeHead(500, { 'Content-Type': 'application/json' });
1685
+ res.end(JSON.stringify({ ok: false, error: err instanceof Error ? err.message : String(err) }));
1686
+ });
1687
+ return;
1688
+ }
1689
+ if (req.method === 'POST' && pathname === '/esp32/serial/resume') {
1690
+ // Idempotent by contract: resuming when nothing is suspended is a
1691
+ // success. The CLI calls this from a `finally`, where it may well run
1692
+ // after the lease has already expired on its own.
1693
+ const was = readLease();
1694
+ clearLease();
1695
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1696
+ res.end(JSON.stringify({ ok: true, wasSuspended: was !== null }));
1697
+ return;
1698
+ }
1699
+ if (req.method === 'GET' && pathname === '/esp32/serial/status') {
1700
+ const lease = readLease();
1701
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1702
+ res.end(JSON.stringify({
1703
+ ok: true,
1704
+ suspended: lease !== null,
1705
+ until: lease?.until,
1706
+ board: lease?.board,
1707
+ ports: getESP32Ports(),
1708
+ }));
1709
+ return;
1710
+ }
1650
1711
  if (req.method === 'POST' && pathname === '/esp32/ota') {
1651
1712
  (async () => {
1652
1713
  const body = await readJsonBody(req);
@@ -3512,11 +3573,46 @@ export async function startDaemon(opts) {
3512
3573
  // beside it stays empty. #218 taught the per-session QUERY to read Kiro's
3513
3574
  // own transcript; the main timeline is a STREAM, and this is what feeds it.
3514
3575
  try {
3515
- const kiroIds = passiveSessionObserver.collect([])
3516
- .filter((s) => typeof s.agentType === 'string' && s.agentType.startsWith('kiro'))
3517
- .map((s) => s.id);
3576
+ const observedKiro = passiveSessionObserver.collect([])
3577
+ .filter((s) => typeof s.agentType === 'string' && s.agentType.startsWith('kiro'));
3578
+ const kiroIds = observedKiro.map((s) => s.id);
3579
+ const projectBySession = new Map(observedKiro.map((s) => [rawSessionId(s.id), s.projectName]));
3580
+ const currentSessions = new Set(kiroIds.map(rawSessionId));
3581
+ for (const sessionId of kiroApmeSessions) {
3582
+ if (currentSessions.has(sessionId))
3583
+ continue;
3584
+ apme?.collector.closeRun(sessionId);
3585
+ kiroApmeSessions.delete(sessionId);
3586
+ kiroApmeResponseBySession.delete(sessionId);
3587
+ }
3518
3588
  for (const entry of kiroTimelineFeed.pump(kiroIds)) {
3519
3589
  core.bridgeTimeline.addEntry(entry);
3590
+ if (!apme || !entry.sessionId)
3591
+ continue;
3592
+ const sessionId = rawSessionId(entry.sessionId);
3593
+ if (entry.type === 'chat_start') {
3594
+ if (!apme.collector.getRunId(sessionId)) {
3595
+ apme.collector.openRun({
3596
+ sessionId,
3597
+ agentType: 'kiro-cli',
3598
+ projectName: projectBySession.get(sessionId) ?? undefined,
3599
+ });
3600
+ }
3601
+ kiroApmeSessions.add(sessionId);
3602
+ kiroApmeResponseBySession.delete(sessionId);
3603
+ apme.collector.ingestHook(sessionId, 'user_prompt_submit', {
3604
+ prompt: entry.detail || entry.raw,
3605
+ agent_type: 'kiro-cli',
3606
+ project_name: projectBySession.get(sessionId),
3607
+ });
3608
+ }
3609
+ else if (entry.type === 'chat_response' && apme.collector.getRunId(sessionId)) {
3610
+ const piece = entry.detail || entry.raw;
3611
+ const response = [kiroApmeResponseBySession.get(sessionId), piece].filter(Boolean).join('\n');
3612
+ kiroApmeResponseBySession.set(sessionId, response);
3613
+ apme.collector.setTurnResponse(sessionId, response);
3614
+ apme.collector.noteTurnStop(sessionId);
3615
+ }
3520
3616
  }
3521
3617
  }
3522
3618
  catch (err) {