@elizaos/plugin-browser 2.0.0-beta.1 → 2.0.3-beta.3

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 (196) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -64
  3. package/dist/actions/browser-autofill-login.d.ts.map +1 -1
  4. package/dist/actions/browser-autofill-login.js.map +1 -1
  5. package/dist/actions/browser.d.ts +5 -6
  6. package/dist/actions/browser.d.ts.map +1 -1
  7. package/dist/actions/browser.js +312 -60
  8. package/dist/actions/browser.js.map +1 -1
  9. package/dist/actions/manage-browser-bridge.d.ts.map +1 -1
  10. package/dist/actions/manage-browser-bridge.js +10 -14
  11. package/dist/actions/manage-browser-bridge.js.map +1 -1
  12. package/dist/actions/wait-for-url-predicate.d.ts +34 -0
  13. package/dist/actions/wait-for-url-predicate.d.ts.map +1 -0
  14. package/dist/actions/wait-for-url-predicate.js +33 -0
  15. package/dist/actions/wait-for-url-predicate.js.map +1 -0
  16. package/dist/actions/wait-for-url.d.ts +64 -0
  17. package/dist/actions/wait-for-url.d.ts.map +1 -0
  18. package/dist/actions/wait-for-url.js +89 -0
  19. package/dist/actions/wait-for-url.js.map +1 -0
  20. package/dist/bridge-policy.d.ts +10 -0
  21. package/dist/bridge-policy.d.ts.map +1 -0
  22. package/dist/bridge-policy.js +37 -0
  23. package/dist/bridge-policy.js.map +1 -0
  24. package/dist/bridge-readiness.d.ts +16 -0
  25. package/dist/bridge-readiness.d.ts.map +1 -0
  26. package/dist/bridge-readiness.js +82 -0
  27. package/dist/bridge-readiness.js.map +1 -0
  28. package/dist/bridge-records.d.ts +9 -0
  29. package/dist/bridge-records.d.ts.map +1 -0
  30. package/dist/bridge-records.js +37 -0
  31. package/dist/bridge-records.js.map +1 -0
  32. package/dist/browser-capture-hooks.d.ts +9 -0
  33. package/dist/browser-capture-hooks.d.ts.map +1 -0
  34. package/dist/browser-capture-hooks.js +15 -0
  35. package/dist/browser-capture-hooks.js.map +1 -0
  36. package/dist/browser-service.d.ts +22 -4
  37. package/dist/browser-service.d.ts.map +1 -1
  38. package/dist/browser-service.js +63 -15
  39. package/dist/browser-service.js.map +1 -1
  40. package/dist/browser-workspace-hooks.d.ts +14 -0
  41. package/dist/browser-workspace-hooks.d.ts.map +1 -0
  42. package/dist/browser-workspace-hooks.js +15 -0
  43. package/dist/browser-workspace-hooks.js.map +1 -0
  44. package/dist/companion-auth.d.ts +34 -0
  45. package/dist/companion-auth.d.ts.map +1 -0
  46. package/dist/companion-auth.js +98 -0
  47. package/dist/companion-auth.js.map +1 -0
  48. package/dist/index.d.ts +12 -3
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +66 -12
  51. package/dist/index.js.map +1 -1
  52. package/dist/message-adapter.d.ts +9 -0
  53. package/dist/message-adapter.d.ts.map +1 -0
  54. package/dist/message-adapter.js +104 -0
  55. package/dist/message-adapter.js.map +1 -0
  56. package/dist/packaging.d.ts.map +1 -1
  57. package/dist/packaging.js +2 -0
  58. package/dist/packaging.js.map +1 -1
  59. package/dist/parity/browser-matrix.d.ts +45 -0
  60. package/dist/parity/browser-matrix.d.ts.map +1 -0
  61. package/dist/parity/browser-matrix.js +361 -0
  62. package/dist/parity/browser-matrix.js.map +1 -0
  63. package/dist/parity/index.d.ts +5 -0
  64. package/dist/parity/index.d.ts.map +1 -0
  65. package/dist/parity/index.js +13 -0
  66. package/dist/parity/index.js.map +1 -0
  67. package/dist/password-manager-bridge.d.ts +50 -0
  68. package/dist/password-manager-bridge.d.ts.map +1 -0
  69. package/dist/password-manager-bridge.js +437 -0
  70. package/dist/password-manager-bridge.js.map +1 -0
  71. package/dist/plugin.d.ts.map +1 -1
  72. package/dist/plugin.js +8 -4
  73. package/dist/plugin.js.map +1 -1
  74. package/dist/providers/workspace.d.ts +1 -1
  75. package/dist/providers/workspace.js.map +1 -1
  76. package/dist/routes/bridge.d.ts.map +1 -1
  77. package/dist/routes/bridge.js +63 -14
  78. package/dist/routes/bridge.js.map +1 -1
  79. package/dist/routes/workspace-setup.d.ts.map +1 -1
  80. package/dist/routes/workspace-setup.js +1 -1
  81. package/dist/routes/workspace-setup.js.map +1 -1
  82. package/dist/routes/workspace.d.ts +1 -2
  83. package/dist/routes/workspace.d.ts.map +1 -1
  84. package/dist/routes/workspace.js +104 -4
  85. package/dist/routes/workspace.js.map +1 -1
  86. package/dist/schema.d.ts +2 -2
  87. package/dist/schema.js.map +1 -1
  88. package/dist/service.d.ts +1 -1
  89. package/dist/service.d.ts.map +1 -1
  90. package/dist/service.js.map +1 -1
  91. package/dist/targets/bridge-target.d.ts +1 -1
  92. package/dist/targets/bridge-target.d.ts.map +1 -1
  93. package/dist/targets/bridge-target.js.map +1 -1
  94. package/dist/targets/stagehand-target.d.ts +3 -0
  95. package/dist/targets/stagehand-target.d.ts.map +1 -0
  96. package/dist/targets/stagehand-target.js +187 -0
  97. package/dist/targets/stagehand-target.js.map +1 -0
  98. package/dist/workspace/browser-capture.d.ts +1 -1
  99. package/dist/workspace/browser-capture.d.ts.map +1 -1
  100. package/dist/workspace/browser-capture.js +33 -1
  101. package/dist/workspace/browser-capture.js.map +1 -1
  102. package/dist/workspace/browser-workspace-desktop.d.ts +1 -1
  103. package/dist/workspace/browser-workspace-desktop.d.ts.map +1 -1
  104. package/dist/workspace/browser-workspace-desktop.js +66 -30
  105. package/dist/workspace/browser-workspace-desktop.js.map +1 -1
  106. package/dist/workspace/browser-workspace-errors.d.ts +62 -0
  107. package/dist/workspace/browser-workspace-errors.d.ts.map +1 -0
  108. package/dist/workspace/browser-workspace-errors.js +69 -0
  109. package/dist/workspace/browser-workspace-errors.js.map +1 -0
  110. package/dist/workspace/browser-workspace-forms.d.ts.map +1 -1
  111. package/dist/workspace/browser-workspace-forms.js +1 -1
  112. package/dist/workspace/browser-workspace-forms.js.map +1 -1
  113. package/dist/workspace/browser-workspace-helpers.d.ts +7 -0
  114. package/dist/workspace/browser-workspace-helpers.d.ts.map +1 -1
  115. package/dist/workspace/browser-workspace-helpers.js +64 -6
  116. package/dist/workspace/browser-workspace-helpers.js.map +1 -1
  117. package/dist/workspace/browser-workspace-network.d.ts +1 -1
  118. package/dist/workspace/browser-workspace-network.d.ts.map +1 -1
  119. package/dist/workspace/browser-workspace-types.d.ts +15 -0
  120. package/dist/workspace/browser-workspace-types.d.ts.map +1 -1
  121. package/dist/workspace/browser-workspace-types.js.map +1 -1
  122. package/dist/workspace/browser-workspace-web.d.ts.map +1 -1
  123. package/dist/workspace/browser-workspace-web.js +34 -93
  124. package/dist/workspace/browser-workspace-web.js.map +1 -1
  125. package/dist/workspace/browser-workspace.d.ts +1 -1
  126. package/dist/workspace/browser-workspace.d.ts.map +1 -1
  127. package/dist/workspace/browser-workspace.js +9 -4
  128. package/dist/workspace/browser-workspace.js.map +1 -1
  129. package/dist/workspace/index.d.ts +1 -0
  130. package/dist/workspace/index.d.ts.map +1 -1
  131. package/dist/workspace/index.js +1 -0
  132. package/dist/workspace/index.js.map +1 -1
  133. package/package.json +29 -7
  134. package/registry-entry.json +75 -0
  135. package/dist/actions/browser-autofill-login.d.js +0 -1
  136. package/dist/actions/browser-autofill-login.d.js.map +0 -1
  137. package/dist/actions/browser.d.js +0 -1
  138. package/dist/actions/browser.d.js.map +0 -1
  139. package/dist/actions/manage-browser-bridge.d.js +0 -1
  140. package/dist/actions/manage-browser-bridge.d.js.map +0 -1
  141. package/dist/ambient-jsdom.d.js +0 -1
  142. package/dist/ambient-jsdom.d.js.map +0 -1
  143. package/dist/browser-service.d.js +0 -1
  144. package/dist/browser-service.d.js.map +0 -1
  145. package/dist/contracts.d.js +0 -1
  146. package/dist/contracts.d.js.map +0 -1
  147. package/dist/index.d.js +0 -21
  148. package/dist/index.d.js.map +0 -1
  149. package/dist/lifeops-session-contracts.d.js +0 -1
  150. package/dist/lifeops-session-contracts.d.js.map +0 -1
  151. package/dist/packaging.d.js +0 -1
  152. package/dist/packaging.d.js.map +0 -1
  153. package/dist/plugin.d.js +0 -1
  154. package/dist/plugin.d.js.map +0 -1
  155. package/dist/providers/workspace.d.js +0 -1
  156. package/dist/providers/workspace.d.js.map +0 -1
  157. package/dist/routes/bridge.d.js +0 -1
  158. package/dist/routes/bridge.d.js.map +0 -1
  159. package/dist/routes/workspace-account-gate.d.js +0 -1
  160. package/dist/routes/workspace-account-gate.d.js.map +0 -1
  161. package/dist/routes/workspace-setup.d.js +0 -1
  162. package/dist/routes/workspace-setup.d.js.map +0 -1
  163. package/dist/routes/workspace.d.js +0 -1
  164. package/dist/routes/workspace.d.js.map +0 -1
  165. package/dist/schema.d.js +0 -1
  166. package/dist/schema.d.js.map +0 -1
  167. package/dist/service.d.js +0 -1
  168. package/dist/service.d.js.map +0 -1
  169. package/dist/targets/bridge-target.d.js +0 -1
  170. package/dist/targets/bridge-target.d.js.map +0 -1
  171. package/dist/workspace/browser-capture.d.js +0 -1
  172. package/dist/workspace/browser-capture.d.js.map +0 -1
  173. package/dist/workspace/browser-workspace-desktop.d.js +0 -1
  174. package/dist/workspace/browser-workspace-desktop.d.js.map +0 -1
  175. package/dist/workspace/browser-workspace-elements.d.js +0 -1
  176. package/dist/workspace/browser-workspace-elements.d.js.map +0 -1
  177. package/dist/workspace/browser-workspace-forms.d.js +0 -1
  178. package/dist/workspace/browser-workspace-forms.d.js.map +0 -1
  179. package/dist/workspace/browser-workspace-helpers.d.js +0 -1
  180. package/dist/workspace/browser-workspace-helpers.d.js.map +0 -1
  181. package/dist/workspace/browser-workspace-jsdom.d.js +0 -1
  182. package/dist/workspace/browser-workspace-jsdom.d.js.map +0 -1
  183. package/dist/workspace/browser-workspace-network.d.js +0 -1
  184. package/dist/workspace/browser-workspace-network.d.js.map +0 -1
  185. package/dist/workspace/browser-workspace-snapshots.d.js +0 -1
  186. package/dist/workspace/browser-workspace-snapshots.d.js.map +0 -1
  187. package/dist/workspace/browser-workspace-state.d.js +0 -1
  188. package/dist/workspace/browser-workspace-state.d.js.map +0 -1
  189. package/dist/workspace/browser-workspace-types.d.js +0 -1
  190. package/dist/workspace/browser-workspace-types.d.js.map +0 -1
  191. package/dist/workspace/browser-workspace-web.d.js +0 -1
  192. package/dist/workspace/browser-workspace-web.d.js.map +0 -1
  193. package/dist/workspace/browser-workspace.d.js +0 -11
  194. package/dist/workspace/browser-workspace.d.js.map +0 -1
  195. package/dist/workspace/index.d.js +0 -3
  196. package/dist/workspace/index.d.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/actions/wait-for-url.ts"],"sourcesContent":["/**\n * BROWSER `wait_for_url` subaction core.\n *\n * Opens (or reuses) a tab, then polls the current tab URL until it matches a\n * caller-supplied pattern (substring or regex — see\n * {@link buildWaitForUrlPredicate}) or a deadline passes. Each poll iteration\n * emits a streaming status update through the action's `HandlerCallback` so a\n * Telegram/chat user sees progress; the loop never throws on timeout — it\n * returns a typed {@link WaitForUrlOutcome}.\n *\n * The poll loop takes its URL source, clock, and sleep as injected\n * dependencies so it is fully deterministic under test (fake URL source + fake\n * timer) with no real browser.\n */\n\nimport { buildWaitForUrlPredicate } from \"./wait-for-url-predicate.js\";\n\n/** Default deadline: 5 minutes. */\nexport const WAIT_FOR_URL_DEFAULT_TIMEOUT_MS = 300_000;\n/** Default poll cadence: ~2 seconds. */\nexport const WAIT_FOR_URL_DEFAULT_POLL_INTERVAL_MS = 2_000;\n/** Lower bound for the poll interval (avoids a hot loop). */\nconst WAIT_FOR_URL_MIN_POLL_INTERVAL_MS = 50;\n/** Lower bound for the timeout (always allow at least one poll). */\nconst WAIT_FOR_URL_MIN_TIMEOUT_MS = 1;\n\nexport type WaitForUrlStatus = \"matched\" | \"timeout\";\n\nexport interface WaitForUrlOutcome {\n status: WaitForUrlStatus;\n /** True iff the URL matched before the deadline. */\n matched: boolean;\n /** The pattern the caller supplied. */\n pattern: string;\n /** The last URL observed from the tab (null if never readable). */\n lastUrl: string | null;\n /** Number of poll iterations performed. */\n polls: number;\n /** Wall-clock elapsed time, in ms, measured from the injected clock. */\n elapsedMs: number;\n /** Human-readable summary suitable for a chat reply. */\n message: string;\n}\n\nexport interface WaitForUrlOptions {\n /** Substring or regex to match against the current tab URL. */\n pattern: string;\n /** Deadline in ms. Defaults to {@link WAIT_FOR_URL_DEFAULT_TIMEOUT_MS}. */\n timeoutMs?: number;\n /**\n * Poll cadence in ms. Defaults to\n * {@link WAIT_FOR_URL_DEFAULT_POLL_INTERVAL_MS}.\n */\n pollIntervalMs?: number;\n}\n\nexport interface WaitForUrlDeps {\n /**\n * Reads the current tab URL. Returns null when the URL is not yet readable\n * (e.g. tab still loading); the loop keeps polling until the deadline.\n */\n getCurrentUrl: () => Promise<string | null> | string | null;\n /** Emits a status update to the user. Optional (may be a no-op). */\n emitStatus?: (text: string) => Promise<void> | void;\n /** Monotonic clock in ms. Defaults to `Date.now`. */\n now?: () => number;\n /** Async sleep. Defaults to a real `setTimeout` promise. */\n sleep?: (ms: number) => Promise<void>;\n}\n\nfunction realSleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nfunction clampPollInterval(value: number | undefined): number {\n const candidate = value ?? WAIT_FOR_URL_DEFAULT_POLL_INTERVAL_MS;\n if (!Number.isFinite(candidate) || candidate <= 0) {\n return WAIT_FOR_URL_DEFAULT_POLL_INTERVAL_MS;\n }\n return Math.max(WAIT_FOR_URL_MIN_POLL_INTERVAL_MS, Math.floor(candidate));\n}\n\nfunction clampTimeout(value: number | undefined): number {\n const candidate = value ?? WAIT_FOR_URL_DEFAULT_TIMEOUT_MS;\n if (!Number.isFinite(candidate) || candidate <= 0) {\n return WAIT_FOR_URL_DEFAULT_TIMEOUT_MS;\n }\n return Math.max(WAIT_FOR_URL_MIN_TIMEOUT_MS, Math.floor(candidate));\n}\n\nfunction describeCurrentUrl(url: string | null): string {\n return url ? `current: ${url}` : \"current: unknown\";\n}\n\n/**\n * Poll the current tab URL until it matches `pattern` or the deadline passes.\n * Never throws on timeout — returns a typed {@link WaitForUrlOutcome}.\n */\nexport async function waitForUrl(\n options: WaitForUrlOptions,\n deps: WaitForUrlDeps,\n): Promise<WaitForUrlOutcome> {\n const predicate = buildWaitForUrlPredicate(options.pattern);\n const timeoutMs = clampTimeout(options.timeoutMs);\n const pollIntervalMs = clampPollInterval(options.pollIntervalMs);\n const now = deps.now ?? Date.now;\n const sleep = deps.sleep ?? realSleep;\n\n const startedAt = now();\n const deadline = startedAt + timeoutMs;\n\n let polls = 0;\n let lastUrl: string | null = null;\n\n while (true) {\n polls += 1;\n\n let currentUrl: string | null = null;\n try {\n currentUrl = (await deps.getCurrentUrl()) ?? null;\n } catch {\n // Treat an unreadable URL like an empty poll; keep waiting.\n currentUrl = null;\n }\n if (currentUrl !== null) {\n lastUrl = currentUrl;\n }\n\n if (currentUrl !== null && predicate.test(currentUrl)) {\n const elapsedMs = now() - startedAt;\n const message = `Done — tab reached ${currentUrl} (matched ${predicate.kind} \"${predicate.pattern}\" after ${polls} check${polls === 1 ? \"\" : \"s\"}).`;\n await deps.emitStatus?.(`✅ ${message}`);\n return {\n status: \"matched\",\n matched: true,\n pattern: predicate.pattern,\n lastUrl,\n polls,\n elapsedMs,\n message,\n };\n }\n\n const elapsedMs = now() - startedAt;\n const remainingMs = deadline - now();\n if (remainingMs <= 0) {\n const message = `Timed out after ${Math.round(elapsedMs / 1000)}s waiting for \"${predicate.pattern}\" (${describeCurrentUrl(lastUrl)}).`;\n await deps.emitStatus?.(`⌛ ${message}`);\n return {\n status: \"timeout\",\n matched: false,\n pattern: predicate.pattern,\n lastUrl,\n polls,\n elapsedMs,\n message,\n };\n }\n\n await deps.emitStatus?.(\n `⏳ still waiting for \"${predicate.pattern}\"… (${describeCurrentUrl(currentUrl)})`,\n );\n\n await sleep(Math.min(pollIntervalMs, remainingMs));\n }\n}\n"],"mappings":"AAeA,SAAS,gCAAgC;AAGlC,MAAM,kCAAkC;AAExC,MAAM,wCAAwC;AAErD,MAAM,oCAAoC;AAE1C,MAAM,8BAA8B;AA8CpC,SAAS,UAAU,IAA2B;AAC5C,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,eAAW,SAAS,EAAE;AAAA,EACxB,CAAC;AACH;AAEA,SAAS,kBAAkB,OAAmC;AAC5D,QAAM,YAAY,SAAS;AAC3B,MAAI,CAAC,OAAO,SAAS,SAAS,KAAK,aAAa,GAAG;AACjD,WAAO;AAAA,EACT;AACA,SAAO,KAAK,IAAI,mCAAmC,KAAK,MAAM,SAAS,CAAC;AAC1E;AAEA,SAAS,aAAa,OAAmC;AACvD,QAAM,YAAY,SAAS;AAC3B,MAAI,CAAC,OAAO,SAAS,SAAS,KAAK,aAAa,GAAG;AACjD,WAAO;AAAA,EACT;AACA,SAAO,KAAK,IAAI,6BAA6B,KAAK,MAAM,SAAS,CAAC;AACpE;AAEA,SAAS,mBAAmB,KAA4B;AACtD,SAAO,MAAM,YAAY,GAAG,KAAK;AACnC;AAMA,eAAsB,WACpB,SACA,MAC4B;AAC5B,QAAM,YAAY,yBAAyB,QAAQ,OAAO;AAC1D,QAAM,YAAY,aAAa,QAAQ,SAAS;AAChD,QAAM,iBAAiB,kBAAkB,QAAQ,cAAc;AAC/D,QAAM,MAAM,KAAK,OAAO,KAAK;AAC7B,QAAM,QAAQ,KAAK,SAAS;AAE5B,QAAM,YAAY,IAAI;AACtB,QAAM,WAAW,YAAY;AAE7B,MAAI,QAAQ;AACZ,MAAI,UAAyB;AAE7B,SAAO,MAAM;AACX,aAAS;AAET,QAAI,aAA4B;AAChC,QAAI;AACF,mBAAc,MAAM,KAAK,cAAc,KAAM;AAAA,IAC/C,QAAQ;AAEN,mBAAa;AAAA,IACf;AACA,QAAI,eAAe,MAAM;AACvB,gBAAU;AAAA,IACZ;AAEA,QAAI,eAAe,QAAQ,UAAU,KAAK,UAAU,GAAG;AACrD,YAAMA,aAAY,IAAI,IAAI;AAC1B,YAAM,UAAU,2BAAsB,UAAU,aAAa,UAAU,IAAI,KAAK,UAAU,OAAO,WAAW,KAAK,SAAS,UAAU,IAAI,KAAK,GAAG;AAChJ,YAAM,KAAK,aAAa,UAAK,OAAO,EAAE;AACtC,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,SAAS,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA,WAAAA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,YAAY,IAAI,IAAI;AAC1B,UAAM,cAAc,WAAW,IAAI;AACnC,QAAI,eAAe,GAAG;AACpB,YAAM,UAAU,mBAAmB,KAAK,MAAM,YAAY,GAAI,CAAC,kBAAkB,UAAU,OAAO,MAAM,mBAAmB,OAAO,CAAC;AACnI,YAAM,KAAK,aAAa,UAAK,OAAO,EAAE;AACtC,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,SAAS,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,KAAK;AAAA,MACT,6BAAwB,UAAU,OAAO,YAAO,mBAAmB,UAAU,CAAC;AAAA,IAChF;AAEA,UAAM,MAAM,KAAK,IAAI,gBAAgB,WAAW,CAAC;AAAA,EACnD;AACF;","names":["elapsedMs"]}
@@ -0,0 +1,10 @@
1
+ export declare const MAX_BROWSER_FOCUS_WINDOW_MS: number;
2
+ export declare const DEFAULT_BROWSER_COMPANION_PAIRING_TOKEN_TTL_MS: number;
3
+ type BrowserBridgeCompanionPairingTokenEnv = {
4
+ readonly [key: string]: string | undefined;
5
+ };
6
+ export declare function resolveBrowserBridgeCompanionPairingTokenTtlMs(env?: BrowserBridgeCompanionPairingTokenEnv): number;
7
+ export declare function resolveBrowserBridgeCompanionPairingTokenExpiresAt(nowMs?: number, env?: Parameters<typeof resolveBrowserBridgeCompanionPairingTokenTtlMs>[0]): string;
8
+ export declare function browserBridgeDomainFromUrl(url: string): string | null;
9
+ export {};
10
+ //# sourceMappingURL=bridge-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-policy.d.ts","sourceRoot":"","sources":["../src/bridge-policy.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,2BAA2B,QAAgB,CAAC;AACzD,eAAO,MAAM,8CAA8C,QACjC,CAAC;AAE3B,KAAK,qCAAqC,GAAG;IAC3C,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC5C,CAAC;AAEF,wBAAgB,8CAA8C,CAC5D,GAAG,GAAE,qCAAmD,GACvD,MAAM,CAWR;AAED,wBAAgB,kDAAkD,CAChE,KAAK,SAAa,EAClB,GAAG,CAAC,EAAE,UAAU,CAAC,OAAO,8CAA8C,CAAC,CAAC,CAAC,CAAC,GACzE,MAAM,CAIR;AAED,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWrE"}
@@ -0,0 +1,37 @@
1
+ const MAX_BROWSER_FOCUS_WINDOW_MS = 2 * 60 * 1e3;
2
+ const DEFAULT_BROWSER_COMPANION_PAIRING_TOKEN_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
3
+ function resolveBrowserBridgeCompanionPairingTokenTtlMs(env = process.env) {
4
+ const raw = env.BROWSER_BRIDGE_COMPANION_TOKEN_TTL_MS ?? env.ELIZA_BROWSER_BRIDGE_COMPANION_TOKEN_TTL_MS;
5
+ if (typeof raw === "string" && raw.trim().length > 0) {
6
+ const parsed = Number(raw);
7
+ if (Number.isFinite(parsed) && parsed > 0) {
8
+ return Math.trunc(parsed);
9
+ }
10
+ }
11
+ return DEFAULT_BROWSER_COMPANION_PAIRING_TOKEN_TTL_MS;
12
+ }
13
+ function resolveBrowserBridgeCompanionPairingTokenExpiresAt(nowMs = Date.now(), env) {
14
+ return new Date(
15
+ nowMs + resolveBrowserBridgeCompanionPairingTokenTtlMs(env)
16
+ ).toISOString();
17
+ }
18
+ function browserBridgeDomainFromUrl(url) {
19
+ try {
20
+ const parsed = new URL(url);
21
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
22
+ return null;
23
+ }
24
+ const hostname = parsed.hostname.trim().toLowerCase().replace(/\.+$/, "");
25
+ return hostname.length > 0 ? hostname : null;
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+ export {
31
+ DEFAULT_BROWSER_COMPANION_PAIRING_TOKEN_TTL_MS,
32
+ MAX_BROWSER_FOCUS_WINDOW_MS,
33
+ browserBridgeDomainFromUrl,
34
+ resolveBrowserBridgeCompanionPairingTokenExpiresAt,
35
+ resolveBrowserBridgeCompanionPairingTokenTtlMs
36
+ };
37
+ //# sourceMappingURL=bridge-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridge-policy.ts"],"sourcesContent":["export const MAX_BROWSER_FOCUS_WINDOW_MS = 2 * 60 * 1000;\nexport const DEFAULT_BROWSER_COMPANION_PAIRING_TOKEN_TTL_MS =\n 30 * 24 * 60 * 60 * 1000;\n\ntype BrowserBridgeCompanionPairingTokenEnv = {\n readonly [key: string]: string | undefined;\n};\n\nexport function resolveBrowserBridgeCompanionPairingTokenTtlMs(\n env: BrowserBridgeCompanionPairingTokenEnv = process.env,\n): number {\n const raw =\n env.BROWSER_BRIDGE_COMPANION_TOKEN_TTL_MS ??\n env.ELIZA_BROWSER_BRIDGE_COMPANION_TOKEN_TTL_MS;\n if (typeof raw === \"string\" && raw.trim().length > 0) {\n const parsed = Number(raw);\n if (Number.isFinite(parsed) && parsed > 0) {\n return Math.trunc(parsed);\n }\n }\n return DEFAULT_BROWSER_COMPANION_PAIRING_TOKEN_TTL_MS;\n}\n\nexport function resolveBrowserBridgeCompanionPairingTokenExpiresAt(\n nowMs = Date.now(),\n env?: Parameters<typeof resolveBrowserBridgeCompanionPairingTokenTtlMs>[0],\n): string {\n return new Date(\n nowMs + resolveBrowserBridgeCompanionPairingTokenTtlMs(env),\n ).toISOString();\n}\n\nexport function browserBridgeDomainFromUrl(url: string): string | null {\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return null;\n }\n const hostname = parsed.hostname.trim().toLowerCase().replace(/\\.+$/, \"\");\n return hostname.length > 0 ? hostname : null;\n } catch {\n return null;\n }\n}\n"],"mappings":"AAAO,MAAM,8BAA8B,IAAI,KAAK;AAC7C,MAAM,iDACX,KAAK,KAAK,KAAK,KAAK;AAMf,SAAS,+CACd,MAA6C,QAAQ,KAC7C;AACR,QAAM,MACJ,IAAI,yCACJ,IAAI;AACN,MAAI,OAAO,QAAQ,YAAY,IAAI,KAAK,EAAE,SAAS,GAAG;AACpD,UAAM,SAAS,OAAO,GAAG;AACzB,QAAI,OAAO,SAAS,MAAM,KAAK,SAAS,GAAG;AACzC,aAAO,KAAK,MAAM,MAAM;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,mDACd,QAAQ,KAAK,IAAI,GACjB,KACQ;AACR,SAAO,IAAI;AAAA,IACT,QAAQ,+CAA+C,GAAG;AAAA,EAC5D,EAAE,YAAY;AAChB;AAEO,SAAS,2BAA2B,KAA4B;AACrE,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,QAAI,OAAO,aAAa,WAAW,OAAO,aAAa,UAAU;AAC/D,aAAO;AAAA,IACT;AACA,UAAM,WAAW,OAAO,SAAS,KAAK,EAAE,YAAY,EAAE,QAAQ,QAAQ,EAAE;AACxE,WAAO,SAAS,SAAS,IAAI,WAAW;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1,16 @@
1
+ import type { BrowserBridgeCompanionStatus, BrowserBridgePermissionState, BrowserBridgeSettings } from "./contracts.js";
2
+ export declare const BROWSER_BRIDGE_RECENT_CONTACT_WINDOW_MS: number;
3
+ export type BrowserBridgeReadinessState = "ready" | "disabled" | "tracking_off" | "paused" | "control_disabled" | "no_companion" | "stale" | "permission_blocked";
4
+ export interface BrowserBridgeReadiness {
5
+ state: BrowserBridgeReadinessState;
6
+ ready: boolean;
7
+ connectedCompanions: BrowserBridgeCompanionStatus[];
8
+ recentConnectedCompanions: BrowserBridgeCompanionStatus[];
9
+ primaryCompanion: BrowserBridgeCompanionStatus | null;
10
+ }
11
+ export declare function isBrowserBridgePaused(settings: Pick<BrowserBridgeSettings, "pauseUntil">, nowMs?: number): boolean;
12
+ export declare function browserBridgeCompanionIsRecent(companion: Pick<BrowserBridgeCompanionStatus, "lastSeenAt">, nowMs?: number, recentWindowMs?: number): boolean;
13
+ export declare function browserBridgeSiteAccessReady(settings: Pick<BrowserBridgeSettings, "siteAccessMode" | "grantedOrigins">, permissions: BrowserBridgePermissionState): boolean;
14
+ export declare function browserBridgePermissionsReady(settings: Pick<BrowserBridgeSettings, "siteAccessMode" | "grantedOrigins">, permissions: BrowserBridgePermissionState): boolean;
15
+ export declare function resolveBrowserBridgeReadiness(settings: BrowserBridgeSettings, companions: readonly BrowserBridgeCompanionStatus[], nowMs?: number): BrowserBridgeReadiness;
16
+ //# sourceMappingURL=bridge-readiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-readiness.d.ts","sourceRoot":"","sources":["../src/bridge-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,4BAA4B,EAC5B,4BAA4B,EAC5B,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AAExB,eAAO,MAAM,uCAAuC,QAAa,CAAC;AAElE,MAAM,MAAM,2BAA2B,GACnC,OAAO,GACP,UAAU,GACV,cAAc,GACd,QAAQ,GACR,kBAAkB,GAClB,cAAc,GACd,OAAO,GACP,oBAAoB,CAAC;AAEzB,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,2BAA2B,CAAC;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,mBAAmB,EAAE,4BAA4B,EAAE,CAAC;IACpD,yBAAyB,EAAE,4BAA4B,EAAE,CAAC;IAC1D,gBAAgB,EAAE,4BAA4B,GAAG,IAAI,CAAC;CACvD;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,IAAI,CAAC,qBAAqB,EAAE,YAAY,CAAC,EACnD,KAAK,SAAa,GACjB,OAAO,CAMT;AAED,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,IAAI,CAAC,4BAA4B,EAAE,YAAY,CAAC,EAC3D,KAAK,SAAa,EAClB,cAAc,SAA0C,GACvD,OAAO,CAMT;AAED,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,IAAI,CAAC,qBAAqB,EAAE,gBAAgB,GAAG,gBAAgB,CAAC,EAC1E,WAAW,EAAE,4BAA4B,GACxC,OAAO,CAeT;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,IAAI,CAAC,qBAAqB,EAAE,gBAAgB,GAAG,gBAAgB,CAAC,EAC1E,WAAW,EAAE,4BAA4B,GACxC,OAAO,CAOT;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,qBAAqB,EAC/B,UAAU,EAAE,SAAS,4BAA4B,EAAE,EACnD,KAAK,SAAa,GACjB,sBAAsB,CAqDxB"}
@@ -0,0 +1,82 @@
1
+ const BROWSER_BRIDGE_RECENT_CONTACT_WINDOW_MS = 5 * 6e4;
2
+ function isBrowserBridgePaused(settings, nowMs = Date.now()) {
3
+ if (!settings.pauseUntil) {
4
+ return false;
5
+ }
6
+ const pauseUntilMs = Date.parse(settings.pauseUntil);
7
+ return Number.isFinite(pauseUntilMs) && pauseUntilMs > nowMs;
8
+ }
9
+ function browserBridgeCompanionIsRecent(companion, nowMs = Date.now(), recentWindowMs = BROWSER_BRIDGE_RECENT_CONTACT_WINDOW_MS) {
10
+ if (!companion.lastSeenAt) {
11
+ return false;
12
+ }
13
+ const lastSeenMs = Date.parse(companion.lastSeenAt);
14
+ return Number.isFinite(lastSeenMs) && nowMs - lastSeenMs < recentWindowMs;
15
+ }
16
+ function browserBridgeSiteAccessReady(settings, permissions) {
17
+ switch (settings.siteAccessMode) {
18
+ case "all_sites":
19
+ return permissions.allOrigins;
20
+ case "granted_sites":
21
+ return permissions.allOrigins || settings.grantedOrigins.length > 0 && permissions.grantedOrigins.length > 0;
22
+ case "current_site_only":
23
+ return permissions.activeTab;
24
+ default:
25
+ return false;
26
+ }
27
+ }
28
+ function browserBridgePermissionsReady(settings, permissions) {
29
+ return permissions.tabs && permissions.scripting && permissions.activeTab && browserBridgeSiteAccessReady(settings, permissions);
30
+ }
31
+ function resolveBrowserBridgeReadiness(settings, companions, nowMs = Date.now()) {
32
+ const connectedCompanions = companions.filter(
33
+ (companion) => companion.connectionState === "connected"
34
+ );
35
+ const recentConnectedCompanions = connectedCompanions.filter(
36
+ (companion) => browserBridgeCompanionIsRecent(companion, nowMs)
37
+ );
38
+ const primaryCompanion = recentConnectedCompanions[0] ?? connectedCompanions[0] ?? companions[0] ?? null;
39
+ const base = {
40
+ connectedCompanions,
41
+ recentConnectedCompanions,
42
+ primaryCompanion
43
+ };
44
+ if (!settings.enabled) {
45
+ return { ...base, ready: false, state: "disabled" };
46
+ }
47
+ if (settings.trackingMode === "off") {
48
+ return { ...base, ready: false, state: "tracking_off" };
49
+ }
50
+ if (isBrowserBridgePaused(settings, nowMs)) {
51
+ return { ...base, ready: false, state: "paused" };
52
+ }
53
+ if (!settings.allowBrowserControl) {
54
+ return { ...base, ready: false, state: "control_disabled" };
55
+ }
56
+ if (companions.length === 0) {
57
+ return { ...base, ready: false, state: "no_companion" };
58
+ }
59
+ if (connectedCompanions.length === 0 && companions.some(
60
+ (companion) => companion.connectionState === "permission_blocked"
61
+ )) {
62
+ return { ...base, ready: false, state: "permission_blocked" };
63
+ }
64
+ if (recentConnectedCompanions.length === 0) {
65
+ return { ...base, ready: false, state: "stale" };
66
+ }
67
+ if (recentConnectedCompanions.some(
68
+ (companion) => browserBridgePermissionsReady(settings, companion.permissions)
69
+ )) {
70
+ return { ...base, ready: true, state: "ready" };
71
+ }
72
+ return { ...base, ready: false, state: "permission_blocked" };
73
+ }
74
+ export {
75
+ BROWSER_BRIDGE_RECENT_CONTACT_WINDOW_MS,
76
+ browserBridgeCompanionIsRecent,
77
+ browserBridgePermissionsReady,
78
+ browserBridgeSiteAccessReady,
79
+ isBrowserBridgePaused,
80
+ resolveBrowserBridgeReadiness
81
+ };
82
+ //# sourceMappingURL=bridge-readiness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridge-readiness.ts"],"sourcesContent":["import type {\n BrowserBridgeCompanionStatus,\n BrowserBridgePermissionState,\n BrowserBridgeSettings,\n} from \"./contracts.js\";\n\nexport const BROWSER_BRIDGE_RECENT_CONTACT_WINDOW_MS = 5 * 60_000;\n\nexport type BrowserBridgeReadinessState =\n | \"ready\"\n | \"disabled\"\n | \"tracking_off\"\n | \"paused\"\n | \"control_disabled\"\n | \"no_companion\"\n | \"stale\"\n | \"permission_blocked\";\n\nexport interface BrowserBridgeReadiness {\n state: BrowserBridgeReadinessState;\n ready: boolean;\n connectedCompanions: BrowserBridgeCompanionStatus[];\n recentConnectedCompanions: BrowserBridgeCompanionStatus[];\n primaryCompanion: BrowserBridgeCompanionStatus | null;\n}\n\nexport function isBrowserBridgePaused(\n settings: Pick<BrowserBridgeSettings, \"pauseUntil\">,\n nowMs = Date.now(),\n): boolean {\n if (!settings.pauseUntil) {\n return false;\n }\n const pauseUntilMs = Date.parse(settings.pauseUntil);\n return Number.isFinite(pauseUntilMs) && pauseUntilMs > nowMs;\n}\n\nexport function browserBridgeCompanionIsRecent(\n companion: Pick<BrowserBridgeCompanionStatus, \"lastSeenAt\">,\n nowMs = Date.now(),\n recentWindowMs = BROWSER_BRIDGE_RECENT_CONTACT_WINDOW_MS,\n): boolean {\n if (!companion.lastSeenAt) {\n return false;\n }\n const lastSeenMs = Date.parse(companion.lastSeenAt);\n return Number.isFinite(lastSeenMs) && nowMs - lastSeenMs < recentWindowMs;\n}\n\nexport function browserBridgeSiteAccessReady(\n settings: Pick<BrowserBridgeSettings, \"siteAccessMode\" | \"grantedOrigins\">,\n permissions: BrowserBridgePermissionState,\n): boolean {\n switch (settings.siteAccessMode) {\n case \"all_sites\":\n return permissions.allOrigins;\n case \"granted_sites\":\n return (\n permissions.allOrigins ||\n (settings.grantedOrigins.length > 0 &&\n permissions.grantedOrigins.length > 0)\n );\n case \"current_site_only\":\n return permissions.activeTab;\n default:\n return false;\n }\n}\n\nexport function browserBridgePermissionsReady(\n settings: Pick<BrowserBridgeSettings, \"siteAccessMode\" | \"grantedOrigins\">,\n permissions: BrowserBridgePermissionState,\n): boolean {\n return (\n permissions.tabs &&\n permissions.scripting &&\n permissions.activeTab &&\n browserBridgeSiteAccessReady(settings, permissions)\n );\n}\n\nexport function resolveBrowserBridgeReadiness(\n settings: BrowserBridgeSettings,\n companions: readonly BrowserBridgeCompanionStatus[],\n nowMs = Date.now(),\n): BrowserBridgeReadiness {\n const connectedCompanions = companions.filter(\n (companion) => companion.connectionState === \"connected\",\n );\n const recentConnectedCompanions = connectedCompanions.filter((companion) =>\n browserBridgeCompanionIsRecent(companion, nowMs),\n );\n const primaryCompanion =\n recentConnectedCompanions[0] ??\n connectedCompanions[0] ??\n companions[0] ??\n null;\n\n const base = {\n connectedCompanions,\n recentConnectedCompanions,\n primaryCompanion,\n };\n\n if (!settings.enabled) {\n return { ...base, ready: false, state: \"disabled\" };\n }\n if (settings.trackingMode === \"off\") {\n return { ...base, ready: false, state: \"tracking_off\" };\n }\n if (isBrowserBridgePaused(settings, nowMs)) {\n return { ...base, ready: false, state: \"paused\" };\n }\n if (!settings.allowBrowserControl) {\n return { ...base, ready: false, state: \"control_disabled\" };\n }\n if (companions.length === 0) {\n return { ...base, ready: false, state: \"no_companion\" };\n }\n if (\n connectedCompanions.length === 0 &&\n companions.some(\n (companion) => companion.connectionState === \"permission_blocked\",\n )\n ) {\n return { ...base, ready: false, state: \"permission_blocked\" };\n }\n if (recentConnectedCompanions.length === 0) {\n return { ...base, ready: false, state: \"stale\" };\n }\n if (\n recentConnectedCompanions.some((companion) =>\n browserBridgePermissionsReady(settings, companion.permissions),\n )\n ) {\n return { ...base, ready: true, state: \"ready\" };\n }\n return { ...base, ready: false, state: \"permission_blocked\" };\n}\n"],"mappings":"AAMO,MAAM,0CAA0C,IAAI;AAoBpD,SAAS,sBACd,UACA,QAAQ,KAAK,IAAI,GACR;AACT,MAAI,CAAC,SAAS,YAAY;AACxB,WAAO;AAAA,EACT;AACA,QAAM,eAAe,KAAK,MAAM,SAAS,UAAU;AACnD,SAAO,OAAO,SAAS,YAAY,KAAK,eAAe;AACzD;AAEO,SAAS,+BACd,WACA,QAAQ,KAAK,IAAI,GACjB,iBAAiB,yCACR;AACT,MAAI,CAAC,UAAU,YAAY;AACzB,WAAO;AAAA,EACT;AACA,QAAM,aAAa,KAAK,MAAM,UAAU,UAAU;AAClD,SAAO,OAAO,SAAS,UAAU,KAAK,QAAQ,aAAa;AAC7D;AAEO,SAAS,6BACd,UACA,aACS;AACT,UAAQ,SAAS,gBAAgB;AAAA,IAC/B,KAAK;AACH,aAAO,YAAY;AAAA,IACrB,KAAK;AACH,aACE,YAAY,cACX,SAAS,eAAe,SAAS,KAChC,YAAY,eAAe,SAAS;AAAA,IAE1C,KAAK;AACH,aAAO,YAAY;AAAA,IACrB;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,8BACd,UACA,aACS;AACT,SACE,YAAY,QACZ,YAAY,aACZ,YAAY,aACZ,6BAA6B,UAAU,WAAW;AAEtD;AAEO,SAAS,8BACd,UACA,YACA,QAAQ,KAAK,IAAI,GACO;AACxB,QAAM,sBAAsB,WAAW;AAAA,IACrC,CAAC,cAAc,UAAU,oBAAoB;AAAA,EAC/C;AACA,QAAM,4BAA4B,oBAAoB;AAAA,IAAO,CAAC,cAC5D,+BAA+B,WAAW,KAAK;AAAA,EACjD;AACA,QAAM,mBACJ,0BAA0B,CAAC,KAC3B,oBAAoB,CAAC,KACrB,WAAW,CAAC,KACZ;AAEF,QAAM,OAAO;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,SAAS;AACrB,WAAO,EAAE,GAAG,MAAM,OAAO,OAAO,OAAO,WAAW;AAAA,EACpD;AACA,MAAI,SAAS,iBAAiB,OAAO;AACnC,WAAO,EAAE,GAAG,MAAM,OAAO,OAAO,OAAO,eAAe;AAAA,EACxD;AACA,MAAI,sBAAsB,UAAU,KAAK,GAAG;AAC1C,WAAO,EAAE,GAAG,MAAM,OAAO,OAAO,OAAO,SAAS;AAAA,EAClD;AACA,MAAI,CAAC,SAAS,qBAAqB;AACjC,WAAO,EAAE,GAAG,MAAM,OAAO,OAAO,OAAO,mBAAmB;AAAA,EAC5D;AACA,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO,EAAE,GAAG,MAAM,OAAO,OAAO,OAAO,eAAe;AAAA,EACxD;AACA,MACE,oBAAoB,WAAW,KAC/B,WAAW;AAAA,IACT,CAAC,cAAc,UAAU,oBAAoB;AAAA,EAC/C,GACA;AACA,WAAO,EAAE,GAAG,MAAM,OAAO,OAAO,OAAO,qBAAqB;AAAA,EAC9D;AACA,MAAI,0BAA0B,WAAW,GAAG;AAC1C,WAAO,EAAE,GAAG,MAAM,OAAO,OAAO,OAAO,QAAQ;AAAA,EACjD;AACA,MACE,0BAA0B;AAAA,IAAK,CAAC,cAC9B,8BAA8B,UAAU,UAAU,WAAW;AAAA,EAC/D,GACA;AACA,WAAO,EAAE,GAAG,MAAM,OAAO,MAAM,OAAO,QAAQ;AAAA,EAChD;AACA,SAAO,EAAE,GAAG,MAAM,OAAO,OAAO,OAAO,qBAAqB;AAC9D;","names":[]}
@@ -0,0 +1,9 @@
1
+ import type { BrowserBridgeCompanionStatus, BrowserBridgePageContext, BrowserBridgeTabSummary } from "./contracts.js";
2
+ export declare function createBrowserBridgeCompanionStatus(params: Omit<BrowserBridgeCompanionStatus, "id" | "createdAt" | "updatedAt" | "pairedAt" | "pairingTokenExpiresAt" | "pairingTokenRevokedAt"> & {
3
+ pairedAt?: string | null;
4
+ pairingTokenExpiresAt?: string | null;
5
+ pairingTokenRevokedAt?: string | null;
6
+ }): BrowserBridgeCompanionStatus;
7
+ export declare function createBrowserBridgeTabSummary(params: Omit<BrowserBridgeTabSummary, "id" | "createdAt" | "updatedAt">): BrowserBridgeTabSummary;
8
+ export declare function createBrowserBridgePageContext(params: Omit<BrowserBridgePageContext, "id">): BrowserBridgePageContext;
9
+ //# sourceMappingURL=bridge-records.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-records.d.ts","sourceRoot":"","sources":["../src/bridge-records.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,4BAA4B,EAC5B,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,gBAAgB,CAAC;AAMxB,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,IAAI,CACV,4BAA4B,EAC1B,IAAI,GACJ,WAAW,GACX,WAAW,GACX,UAAU,GACV,uBAAuB,GACvB,uBAAuB,CAC1B,GAAG;IACF,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC,GACA,4BAA4B,CAW9B;AAED,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,IAAI,CAAC,uBAAuB,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,CAAC,GACtE,uBAAuB,CAQzB;AAED,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,IAAI,CAAC,wBAAwB,EAAE,IAAI,CAAC,GAC3C,wBAAwB,CAK1B"}
@@ -0,0 +1,37 @@
1
+ import crypto from "node:crypto";
2
+ function isoNow() {
3
+ return (/* @__PURE__ */ new Date()).toISOString();
4
+ }
5
+ function createBrowserBridgeCompanionStatus(params) {
6
+ const timestamp = isoNow();
7
+ return {
8
+ ...params,
9
+ id: crypto.randomUUID(),
10
+ pairedAt: params.pairedAt ?? timestamp,
11
+ pairingTokenExpiresAt: params.pairingTokenExpiresAt ?? null,
12
+ pairingTokenRevokedAt: params.pairingTokenRevokedAt ?? null,
13
+ createdAt: timestamp,
14
+ updatedAt: timestamp
15
+ };
16
+ }
17
+ function createBrowserBridgeTabSummary(params) {
18
+ const timestamp = isoNow();
19
+ return {
20
+ ...params,
21
+ id: crypto.randomUUID(),
22
+ createdAt: timestamp,
23
+ updatedAt: timestamp
24
+ };
25
+ }
26
+ function createBrowserBridgePageContext(params) {
27
+ return {
28
+ ...params,
29
+ id: crypto.randomUUID()
30
+ };
31
+ }
32
+ export {
33
+ createBrowserBridgeCompanionStatus,
34
+ createBrowserBridgePageContext,
35
+ createBrowserBridgeTabSummary
36
+ };
37
+ //# sourceMappingURL=bridge-records.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridge-records.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport type {\n BrowserBridgeCompanionStatus,\n BrowserBridgePageContext,\n BrowserBridgeTabSummary,\n} from \"./contracts.js\";\n\nfunction isoNow(): string {\n return new Date().toISOString();\n}\n\nexport function createBrowserBridgeCompanionStatus(\n params: Omit<\n BrowserBridgeCompanionStatus,\n | \"id\"\n | \"createdAt\"\n | \"updatedAt\"\n | \"pairedAt\"\n | \"pairingTokenExpiresAt\"\n | \"pairingTokenRevokedAt\"\n > & {\n pairedAt?: string | null;\n pairingTokenExpiresAt?: string | null;\n pairingTokenRevokedAt?: string | null;\n },\n): BrowserBridgeCompanionStatus {\n const timestamp = isoNow();\n return {\n ...params,\n id: crypto.randomUUID(),\n pairedAt: params.pairedAt ?? timestamp,\n pairingTokenExpiresAt: params.pairingTokenExpiresAt ?? null,\n pairingTokenRevokedAt: params.pairingTokenRevokedAt ?? null,\n createdAt: timestamp,\n updatedAt: timestamp,\n };\n}\n\nexport function createBrowserBridgeTabSummary(\n params: Omit<BrowserBridgeTabSummary, \"id\" | \"createdAt\" | \"updatedAt\">,\n): BrowserBridgeTabSummary {\n const timestamp = isoNow();\n return {\n ...params,\n id: crypto.randomUUID(),\n createdAt: timestamp,\n updatedAt: timestamp,\n };\n}\n\nexport function createBrowserBridgePageContext(\n params: Omit<BrowserBridgePageContext, \"id\">,\n): BrowserBridgePageContext {\n return {\n ...params,\n id: crypto.randomUUID(),\n };\n}\n"],"mappings":"AAAA,OAAO,YAAY;AAOnB,SAAS,SAAiB;AACxB,UAAO,oBAAI,KAAK,GAAE,YAAY;AAChC;AAEO,SAAS,mCACd,QAa8B;AAC9B,QAAM,YAAY,OAAO;AACzB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,IAAI,OAAO,WAAW;AAAA,IACtB,UAAU,OAAO,YAAY;AAAA,IAC7B,uBAAuB,OAAO,yBAAyB;AAAA,IACvD,uBAAuB,OAAO,yBAAyB;AAAA,IACvD,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACF;AAEO,SAAS,8BACd,QACyB;AACzB,QAAM,YAAY,OAAO;AACzB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,IAAI,OAAO,WAAW;AAAA,IACtB,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACF;AAEO,SAAS,+BACd,QAC0B;AAC1B,SAAO;AAAA,IACL,GAAG;AAAA,IACH,IAAI,OAAO,WAAW;AAAA,EACxB;AACF;","names":[]}
@@ -0,0 +1,9 @@
1
+ import type { BrowserCaptureConfig } from "./workspace/browser-capture.js";
2
+ export interface BrowserCaptureHooks {
3
+ frameFile: string;
4
+ startBrowserCapture(config: BrowserCaptureConfig): Promise<void>;
5
+ stopBrowserCapture(): Promise<void>;
6
+ }
7
+ export declare function registerBrowserCaptureHooks(hooks: BrowserCaptureHooks): void;
8
+ export declare function getBrowserCaptureHooks(): BrowserCaptureHooks | null;
9
+ //# sourceMappingURL=browser-capture-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-capture-hooks.d.ts","sourceRoot":"","sources":["../src/browser-capture-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAYD,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAE5E;AAED,wBAAgB,sBAAsB,IAAI,mBAAmB,GAAG,IAAI,CAEnE"}
@@ -0,0 +1,15 @@
1
+ const BROWSER_CAPTURE_HOOKS = /* @__PURE__ */ Symbol.for("elizaos.browser-capture.hooks");
2
+ function hooksGlobal() {
3
+ return globalThis;
4
+ }
5
+ function registerBrowserCaptureHooks(hooks) {
6
+ hooksGlobal()[BROWSER_CAPTURE_HOOKS] = hooks;
7
+ }
8
+ function getBrowserCaptureHooks() {
9
+ return hooksGlobal()[BROWSER_CAPTURE_HOOKS] ?? null;
10
+ }
11
+ export {
12
+ getBrowserCaptureHooks,
13
+ registerBrowserCaptureHooks
14
+ };
15
+ //# sourceMappingURL=browser-capture-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/browser-capture-hooks.ts"],"sourcesContent":["import type { BrowserCaptureConfig } from \"./workspace/browser-capture.js\";\n\nexport interface BrowserCaptureHooks {\n frameFile: string;\n startBrowserCapture(config: BrowserCaptureConfig): Promise<void>;\n stopBrowserCapture(): Promise<void>;\n}\n\nconst BROWSER_CAPTURE_HOOKS = Symbol.for(\"elizaos.browser-capture.hooks\");\n\ntype BrowserCaptureHooksGlobal = typeof globalThis & {\n [BROWSER_CAPTURE_HOOKS]?: BrowserCaptureHooks;\n};\n\nfunction hooksGlobal(): BrowserCaptureHooksGlobal {\n return globalThis as BrowserCaptureHooksGlobal;\n}\n\nexport function registerBrowserCaptureHooks(hooks: BrowserCaptureHooks): void {\n hooksGlobal()[BROWSER_CAPTURE_HOOKS] = hooks;\n}\n\nexport function getBrowserCaptureHooks(): BrowserCaptureHooks | null {\n return hooksGlobal()[BROWSER_CAPTURE_HOOKS] ?? null;\n}\n"],"mappings":"AAQA,MAAM,wBAAwB,uBAAO,IAAI,+BAA+B;AAMxE,SAAS,cAAyC;AAChD,SAAO;AACT;AAEO,SAAS,4BAA4B,OAAkC;AAC5E,cAAY,EAAE,qBAAqB,IAAI;AACzC;AAEO,SAAS,yBAAqD;AACnE,SAAO,YAAY,EAAE,qBAAqB,KAAK;AACjD;","names":[]}
@@ -20,6 +20,8 @@
20
20
  * - `computeruse` — registered by `@elizaos/plugin-computeruse` on plugin
21
21
  * init when its capabilities indicate the puppeteer-driven Chromium is
22
22
  * ready.
23
+ * - `stagehand` — registered by this plugin when a Stagehand command
24
+ * endpoint is configured; used as a low-priority fallback.
23
25
  *
24
26
  * Anyone can add a new target later by calling `registerTarget` — that's
25
27
  * the whole point of the pattern. The BROWSER action stays one action.
@@ -27,6 +29,12 @@
27
29
  import { type IAgentRuntime, Service } from "@elizaos/core";
28
30
  import type { BrowserWorkspaceCommand, BrowserWorkspaceCommandResult } from "./workspace/browser-workspace-types.js";
29
31
  export declare const BROWSER_SERVICE_TYPE = "browser";
32
+ export type BrowserTargetKind = "app" | "companion" | "stagehand" | "external";
33
+ export interface BrowserTargetResolutionContext {
34
+ command: BrowserWorkspaceCommand;
35
+ env: NodeJS.ProcessEnv;
36
+ mobile: boolean;
37
+ }
30
38
  /**
31
39
  * Pluggable browser backend. Implementations translate the canonical
32
40
  * BrowserWorkspaceCommand surface into whatever native shape they speak
@@ -35,7 +43,7 @@ export declare const BROWSER_SERVICE_TYPE = "browser";
35
43
  *
36
44
  * Targets MAY decline subactions they don't support — throw a clear
37
45
  * `Error` from `execute` and the caller will see the message. Don't
38
- * silently no-op.
46
+ * silently ignore it.
39
47
  */
40
48
  export interface BrowserTarget {
41
49
  /** Stable identifier — `workspace`, `bridge`, `computeruse`, etc. */
@@ -44,6 +52,15 @@ export interface BrowserTarget {
44
52
  readonly name: string;
45
53
  /** One-line description of what this target controls. */
46
54
  readonly description: string;
55
+ /** Broad target class used for automatic routing. */
56
+ readonly kind?: BrowserTargetKind;
57
+ /** Lower scores are fallback choices. */
58
+ readonly priority?: number;
59
+ /**
60
+ * Optional command-aware score. Return `null` to opt out of automatic
61
+ * routing for this command while still allowing explicit `target`.
62
+ */
63
+ score?(context: BrowserTargetResolutionContext): number | null;
47
64
  /**
48
65
  * Cheap availability check. Called when the BROWSER action wants to
49
66
  * route a command and the caller didn't pin a target. Should be fast
@@ -71,11 +88,12 @@ export declare class BrowserService extends Service {
71
88
  listTargets(): BrowserTarget[];
72
89
  /**
73
90
  * Resolve the active target for a command. If `preferredId` is given,
74
- * returns that target only if available; otherwise scans registered
75
- * targets in registration order and returns the first available one.
91
+ * returns that target only if available; otherwise scores registered
92
+ * targets and returns the best available one.
76
93
  * Returns `null` if nothing is available.
77
94
  */
78
- resolveTarget(preferredId?: string): Promise<BrowserTarget | null>;
95
+ resolveTarget(preferredId?: string, command?: BrowserWorkspaceCommand): Promise<BrowserTarget | null>;
96
+ resolveTargets(preferredId?: string, command?: BrowserWorkspaceCommand): Promise<BrowserTarget[]>;
79
97
  /**
80
98
  * Dispatch a command. `targetId` pins the target; otherwise the service
81
99
  * picks the first available one in registration order.
@@ -1 +1 @@
1
- {"version":3,"file":"browser-service.d.ts","sourceRoot":"","sources":["../src/browser-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EACL,KAAK,aAAa,EAElB,OAAO,EACR,MAAM,eAAe,CAAC;AAKvB,OAAO,KAAK,EACV,uBAAuB,EACvB,6BAA6B,EAC9B,MAAM,wCAAwC,CAAC;AAEhD,eAAO,MAAM,oBAAoB,YAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,wDAAwD;IACxD,OAAO,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAAC;CACnF;AAED,qBAAa,cAAe,SAAQ,OAAO;IACzC,gBAAyB,WAAW,aAAwB;IACnD,qBAAqB,SAC8K;IAE5M,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgB;IAEtC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;WAKL,KAAK,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAmB5E;;;;OAIG;IACH,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAU3C,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IASrC,WAAW,IAAI,aAAa,EAAE;IAM9B;;;;;OAKG;IACG,aAAa,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAsBxE;;;OAGG;IACG,OAAO,CACX,OAAO,EAAE,uBAAuB,EAChC,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,6BAA6B,CAAC;CAY1C"}
1
+ {"version":3,"file":"browser-service.d.ts","sourceRoot":"","sources":["../src/browser-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAU,OAAO,EAAE,MAAM,eAAe,CAAC;AAMpE,OAAO,KAAK,EACV,uBAAuB,EACvB,6BAA6B,EAC9B,MAAM,wCAAwC,CAAC;AAEhD,eAAO,MAAM,oBAAoB,YAAY,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAC;AAE/E,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,uBAAuB,CAAC;IACjC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,qDAAqD;IACrD,QAAQ,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAC;IAClC,yCAAyC;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,KAAK,CAAC,CAAC,OAAO,EAAE,8BAA8B,GAAG,MAAM,GAAG,IAAI,CAAC;IAC/D;;;;OAIG;IACH,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,wDAAwD;IACxD,OAAO,CACL,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,6BAA6B,CAAC,CAAC;CAC3C;AAED,qBAAa,cAAe,SAAQ,OAAO;IACzC,gBAAyB,WAAW,aAAwB;IACnD,qBAAqB,SAC8K;IAE5M,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgB;IAEtC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;WAKL,KAAK,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IA2B5E;;;;OAIG;IACH,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAU3C,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IASrC,WAAW,IAAI,aAAa,EAAE;IAM9B;;;;;OAKG;IACG,aAAa,CACjB,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,GAAE,uBAAgD,GACxD,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAK1B,cAAc,CAClB,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,GAAE,uBAAgD,GACxD,OAAO,CAAC,aAAa,EAAE,CAAC;IA+C3B;;;OAGG;IACG,OAAO,CACX,OAAO,EAAE,uBAAuB,EAChC,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,6BAA6B,CAAC;CA6B1C"}
@@ -1,10 +1,8 @@
1
- import {
2
- logger,
3
- Service
4
- } from "@elizaos/core";
1
+ import { logger, Service } from "@elizaos/core";
5
2
  import {
6
3
  BROWSER_BRIDGE_ROUTE_SERVICE_TYPE
7
4
  } from "./service.js";
5
+ import { maybeCreateStagehandTarget } from "./targets/stagehand-target.js";
8
6
  const BROWSER_SERVICE_TYPE = "browser";
9
7
  class BrowserService extends Service {
10
8
  static serviceType = BROWSER_SERVICE_TYPE;
@@ -28,6 +26,15 @@ class BrowserService extends Service {
28
26
  `[BrowserService] bridge target not registered at start: ${message}`
29
27
  );
30
28
  }
29
+ try {
30
+ const stagehandTarget = await maybeCreateStagehandTarget();
31
+ if (stagehandTarget) service.registerTarget(stagehandTarget);
32
+ } catch (err) {
33
+ const message = err instanceof Error ? err.message : String(err);
34
+ logger.debug(
35
+ `[BrowserService] stagehand target not registered at start: ${message}`
36
+ );
37
+ }
31
38
  return service;
32
39
  }
33
40
  /**
@@ -57,43 +64,74 @@ class BrowserService extends Service {
57
64
  }
58
65
  /**
59
66
  * Resolve the active target for a command. If `preferredId` is given,
60
- * returns that target only if available; otherwise scans registered
61
- * targets in registration order and returns the first available one.
67
+ * returns that target only if available; otherwise scores registered
68
+ * targets and returns the best available one.
62
69
  * Returns `null` if nothing is available.
63
70
  */
64
- async resolveTarget(preferredId) {
71
+ async resolveTarget(preferredId, command = { subaction: "state" }) {
72
+ const targets = await this.resolveTargets(preferredId, command);
73
+ return targets[0] ?? null;
74
+ }
75
+ async resolveTargets(preferredId, command = { subaction: "state" }) {
65
76
  if (preferredId) {
66
77
  const target = this.targets.get(preferredId);
67
- if (!target) return null;
78
+ if (!target) return [];
68
79
  try {
69
- return await target.available() ? target : null;
80
+ return await target.available() ? [target] : [];
70
81
  } catch {
71
- return null;
82
+ return [];
72
83
  }
73
84
  }
85
+ const context = {
86
+ command,
87
+ env: process.env,
88
+ mobile: isMobileBrowserRuntime(process.env)
89
+ };
90
+ const available = [];
74
91
  for (const id of this.targetOrder) {
75
92
  const target = this.targets.get(id);
76
93
  if (!target) continue;
77
94
  try {
78
- if (await target.available()) return target;
95
+ const score = target.score ? target.score(context) : target.priority ?? 0;
96
+ if (score === null) continue;
97
+ if (await target.available()) {
98
+ available.push({
99
+ score,
100
+ order: this.targetOrder.indexOf(id),
101
+ target
102
+ });
103
+ }
79
104
  } catch {
80
105
  }
81
106
  }
82
- return null;
107
+ return available.sort((a, b) => b.score - a.score || a.order - b.order).map(({ target }) => target);
83
108
  }
84
109
  /**
85
110
  * Dispatch a command. `targetId` pins the target; otherwise the service
86
111
  * picks the first available one in registration order.
87
112
  */
88
113
  async execute(command, targetId) {
89
- const target = await this.resolveTarget(targetId);
90
- if (!target) {
114
+ const targets = await this.resolveTargets(targetId, command);
115
+ if (targets.length === 0) {
91
116
  const availableIds = this.targetOrder.join(", ") || "(none)";
92
117
  throw new Error(
93
118
  targetId ? `Browser target "${targetId}" is not available. Registered targets: ${availableIds}.` : `No browser target is available. Registered targets: ${availableIds}.`
94
119
  );
95
120
  }
96
- return target.execute(command);
121
+ let lastError = null;
122
+ for (const target of targets) {
123
+ try {
124
+ return await target.execute(command);
125
+ } catch (err) {
126
+ lastError = err;
127
+ if (targetId) break;
128
+ const message = err instanceof Error ? err.message : String(err);
129
+ logger.debug(
130
+ `[BrowserService] target "${target.id}" failed; trying next target: ${message}`
131
+ );
132
+ }
133
+ }
134
+ throw lastError instanceof Error ? lastError : new Error("Browser target execution failed.");
97
135
  }
98
136
  }
99
137
  function createWorkspaceTarget() {
@@ -101,6 +139,9 @@ function createWorkspaceTarget() {
101
139
  id: "workspace",
102
140
  name: "Browser Workspace",
103
141
  description: "Eliza's electrobun-embedded BrowserView (desktop) or JSDOM fallback (web). Always available.",
142
+ kind: "app",
143
+ priority: 100,
144
+ score: ({ mobile }) => mobile ? 120 : 100,
104
145
  available: async () => true,
105
146
  execute: async (command) => {
106
147
  const { executeBrowserWorkspaceCommand } = await import("./workspace/browser-workspace.js");
@@ -117,6 +158,9 @@ async function maybeCreateBridgeTarget(runtime) {
117
158
  id: "bridge",
118
159
  name: "Browser Bridge (Chrome / Safari companion)",
119
160
  description: "Routes commands to the user's real Chrome or Safari via the Agent Browser Bridge companion extension. Subset of subactions supported (open / navigate / close / list / state / show / hide / tab / get).",
161
+ kind: "companion",
162
+ priority: 80,
163
+ score: ({ mobile }) => mobile ? null : 80,
120
164
  available: async () => {
121
165
  try {
122
166
  const companions = await service.listBrowserCompanions();
@@ -131,6 +175,10 @@ async function maybeCreateBridgeTarget(runtime) {
131
175
  }
132
176
  };
133
177
  }
178
+ function isMobileBrowserRuntime(env) {
179
+ const platform = (env.ELIZA_MOBILE_PLATFORM ?? env.ELIZA_PLATFORM ?? env.CAPACITOR_PLATFORM ?? "").toLowerCase();
180
+ return platform === "ios" || platform === "android" || platform === "mobile";
181
+ }
134
182
  export {
135
183
  BROWSER_SERVICE_TYPE,
136
184
  BrowserService
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/browser-service.ts"],"sourcesContent":["/**\n * BrowserService — single browser dispatcher with a pluggable target\n * registry.\n *\n * The agent uses what is available: targets register themselves at plugin\n * init (or later), and the BROWSER action calls into BrowserService which\n * picks the active target. Targets can be queried by id, listed, or\n * resolved by availability.\n *\n * Built-in targets:\n * - `workspace` — Eliza's electrobun-embedded BrowserView (with a JSDOM\n * web-mode fallback when the desktop bridge isn't configured). Always\n * registered by this plugin's `start`. Always available.\n *\n * Optional targets registered by other plugins:\n * - `bridge` — registered by this plugin when a `BrowserBridgeRouteService`\n * is reachable via the runtime; routes commands to the user's real\n * Chrome / Safari via the Agent Browser Bridge companion extension.\n * Available iff at least one companion is paired.\n * - `computeruse` — registered by `@elizaos/plugin-computeruse` on plugin\n * init when its capabilities indicate the puppeteer-driven Chromium is\n * ready.\n *\n * Anyone can add a new target later by calling `registerTarget` — that's\n * the whole point of the pattern. The BROWSER action stays one action.\n */\n\nimport {\n type IAgentRuntime,\n logger,\n Service,\n} from \"@elizaos/core\";\nimport {\n BROWSER_BRIDGE_ROUTE_SERVICE_TYPE,\n type BrowserBridgeRouteService,\n} from \"./service.js\";\nimport type {\n BrowserWorkspaceCommand,\n BrowserWorkspaceCommandResult,\n} from \"./workspace/browser-workspace-types.js\";\n\nexport const BROWSER_SERVICE_TYPE = \"browser\";\n\n/**\n * Pluggable browser backend. Implementations translate the canonical\n * BrowserWorkspaceCommand surface into whatever native shape they speak\n * (electrobun bridge, Chrome companion HTTP, puppeteer CDP, etc.) and\n * return the canonical BrowserWorkspaceCommandResult.\n *\n * Targets MAY decline subactions they don't support — throw a clear\n * `Error` from `execute` and the caller will see the message. Don't\n * silently no-op.\n */\nexport interface BrowserTarget {\n /** Stable identifier — `workspace`, `bridge`, `computeruse`, etc. */\n readonly id: string;\n /** Short human-readable name for diagnostics. */\n readonly name: string;\n /** One-line description of what this target controls. */\n readonly description: string;\n /**\n * Cheap availability check. Called when the BROWSER action wants to\n * route a command and the caller didn't pin a target. Should be fast\n * (no network round-trips) when possible.\n */\n available(): Promise<boolean>;\n /** Run the command. Throw on unsupported subactions. */\n execute(command: BrowserWorkspaceCommand): Promise<BrowserWorkspaceCommandResult>;\n}\n\nexport class BrowserService extends Service {\n static override readonly serviceType = BROWSER_SERVICE_TYPE;\n override capabilityDescription =\n \"Single browser dispatcher with a pluggable target registry. Targets (workspace / bridge / computeruse / …) register themselves; the BROWSER action picks the active target or honors a pinned override.\";\n\n private readonly targets = new Map<string, BrowserTarget>();\n /** Registration order — used as the default preference order. */\n private readonly targetOrder: string[] = [];\n\n async stop(): Promise<void> {\n this.targets.clear();\n this.targetOrder.length = 0;\n }\n\n static override async start(runtime: IAgentRuntime): Promise<BrowserService> {\n const service = new BrowserService(runtime);\n service.registerTarget(createWorkspaceTarget());\n // Bridge target self-registers when its dependencies (BrowserBridgeRouteService\n // implementor) are reachable via the runtime; we attempt registration here\n // and silently skip if unavailable, so the agent can still boot in\n // workspace-only mode.\n try {\n const bridgeTarget = await maybeCreateBridgeTarget(runtime);\n if (bridgeTarget) service.registerTarget(bridgeTarget);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger.debug(\n `[BrowserService] bridge target not registered at start: ${message}`,\n );\n }\n return service;\n }\n\n /**\n * Register a target. Idempotent on `id` — calling twice with the same id\n * replaces the previous registration without affecting registration\n * order. New ids are appended to the order list.\n */\n registerTarget(target: BrowserTarget): void {\n if (!this.targets.has(target.id)) {\n this.targetOrder.push(target.id);\n }\n this.targets.set(target.id, target);\n logger.debug(\n `[BrowserService] registered target \"${target.id}\" (${target.name})`,\n );\n }\n\n unregisterTarget(id: string): boolean {\n const removed = this.targets.delete(id);\n if (removed) {\n const idx = this.targetOrder.indexOf(id);\n if (idx >= 0) this.targetOrder.splice(idx, 1);\n }\n return removed;\n }\n\n listTargets(): BrowserTarget[] {\n return this.targetOrder\n .map((id) => this.targets.get(id))\n .filter((target): target is BrowserTarget => target !== undefined);\n }\n\n /**\n * Resolve the active target for a command. If `preferredId` is given,\n * returns that target only if available; otherwise scans registered\n * targets in registration order and returns the first available one.\n * Returns `null` if nothing is available.\n */\n async resolveTarget(preferredId?: string): Promise<BrowserTarget | null> {\n if (preferredId) {\n const target = this.targets.get(preferredId);\n if (!target) return null;\n try {\n return (await target.available()) ? target : null;\n } catch {\n return null;\n }\n }\n for (const id of this.targetOrder) {\n const target = this.targets.get(id);\n if (!target) continue;\n try {\n if (await target.available()) return target;\n } catch {\n // skip unhealthy targets\n }\n }\n return null;\n }\n\n /**\n * Dispatch a command. `targetId` pins the target; otherwise the service\n * picks the first available one in registration order.\n */\n async execute(\n command: BrowserWorkspaceCommand,\n targetId?: string,\n ): Promise<BrowserWorkspaceCommandResult> {\n const target = await this.resolveTarget(targetId);\n if (!target) {\n const availableIds = this.targetOrder.join(\", \") || \"(none)\";\n throw new Error(\n targetId\n ? `Browser target \"${targetId}\" is not available. Registered targets: ${availableIds}.`\n : `No browser target is available. Registered targets: ${availableIds}.`,\n );\n }\n return target.execute(command);\n }\n}\n\nfunction createWorkspaceTarget(): BrowserTarget {\n return {\n id: \"workspace\",\n name: \"Browser Workspace\",\n description:\n \"Eliza's electrobun-embedded BrowserView (desktop) or JSDOM fallback (web). Always available.\",\n available: async () => true,\n execute: async (command) => {\n const { executeBrowserWorkspaceCommand } = await import(\n \"./workspace/browser-workspace.js\"\n );\n return executeBrowserWorkspaceCommand(command);\n },\n };\n}\n\nasync function maybeCreateBridgeTarget(\n runtime: IAgentRuntime,\n): Promise<BrowserTarget | null> {\n const service = runtime.getService<BrowserBridgeRouteService>(\n BROWSER_BRIDGE_ROUTE_SERVICE_TYPE,\n );\n if (!service) return null;\n return {\n id: \"bridge\",\n name: \"Browser Bridge (Chrome / Safari companion)\",\n description:\n \"Routes commands to the user's real Chrome or Safari via the Agent Browser Bridge companion extension. Subset of subactions supported (open / navigate / close / list / state / show / hide / tab / get).\",\n available: async () => {\n try {\n const companions = await service.listBrowserCompanions();\n return companions.length > 0;\n } catch {\n return false;\n }\n },\n execute: async (command) => {\n const { dispatchBridgeCommand } = await import(\n \"./targets/bridge-target.js\"\n );\n return dispatchBridgeCommand(service, command);\n },\n };\n}\n"],"mappings":"AA2BA;AAAA,EAEE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,OAEK;AAMA,MAAM,uBAAuB;AA6B7B,MAAM,uBAAuB,QAAQ;AAAA,EAC1C,OAAyB,cAAc;AAAA,EAC9B,wBACP;AAAA,EAEe,UAAU,oBAAI,IAA2B;AAAA;AAAA,EAEzC,cAAwB,CAAC;AAAA,EAE1C,MAAM,OAAsB;AAC1B,SAAK,QAAQ,MAAM;AACnB,SAAK,YAAY,SAAS;AAAA,EAC5B;AAAA,EAEA,aAAsB,MAAM,SAAiD;AAC3E,UAAM,UAAU,IAAI,eAAe,OAAO;AAC1C,YAAQ,eAAe,sBAAsB,CAAC;AAK9C,QAAI;AACF,YAAM,eAAe,MAAM,wBAAwB,OAAO;AAC1D,UAAI,aAAc,SAAQ,eAAe,YAAY;AAAA,IACvD,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,2DAA2D,OAAO;AAAA,MACpE;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAe,QAA6B;AAC1C,QAAI,CAAC,KAAK,QAAQ,IAAI,OAAO,EAAE,GAAG;AAChC,WAAK,YAAY,KAAK,OAAO,EAAE;AAAA,IACjC;AACA,SAAK,QAAQ,IAAI,OAAO,IAAI,MAAM;AAClC,WAAO;AAAA,MACL,uCAAuC,OAAO,EAAE,MAAM,OAAO,IAAI;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,iBAAiB,IAAqB;AACpC,UAAM,UAAU,KAAK,QAAQ,OAAO,EAAE;AACtC,QAAI,SAAS;AACX,YAAM,MAAM,KAAK,YAAY,QAAQ,EAAE;AACvC,UAAI,OAAO,EAAG,MAAK,YAAY,OAAO,KAAK,CAAC;AAAA,IAC9C;AACA,WAAO;AAAA,EACT;AAAA,EAEA,cAA+B;AAC7B,WAAO,KAAK,YACT,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,EAAE,CAAC,EAChC,OAAO,CAAC,WAAoC,WAAW,MAAS;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cAAc,aAAqD;AACvE,QAAI,aAAa;AACf,YAAM,SAAS,KAAK,QAAQ,IAAI,WAAW;AAC3C,UAAI,CAAC,OAAQ,QAAO;AACpB,UAAI;AACF,eAAQ,MAAM,OAAO,UAAU,IAAK,SAAS;AAAA,MAC/C,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AACA,eAAW,MAAM,KAAK,aAAa;AACjC,YAAM,SAAS,KAAK,QAAQ,IAAI,EAAE;AAClC,UAAI,CAAC,OAAQ;AACb,UAAI;AACF,YAAI,MAAM,OAAO,UAAU,EAAG,QAAO;AAAA,MACvC,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QACJ,SACA,UACwC;AACxC,UAAM,SAAS,MAAM,KAAK,cAAc,QAAQ;AAChD,QAAI,CAAC,QAAQ;AACX,YAAM,eAAe,KAAK,YAAY,KAAK,IAAI,KAAK;AACpD,YAAM,IAAI;AAAA,QACR,WACI,mBAAmB,QAAQ,2CAA2C,YAAY,MAClF,uDAAuD,YAAY;AAAA,MACzE;AAAA,IACF;AACA,WAAO,OAAO,QAAQ,OAAO;AAAA,EAC/B;AACF;AAEA,SAAS,wBAAuC;AAC9C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aACE;AAAA,IACF,WAAW,YAAY;AAAA,IACvB,SAAS,OAAO,YAAY;AAC1B,YAAM,EAAE,+BAA+B,IAAI,MAAM,OAC/C,kCACF;AACA,aAAO,+BAA+B,OAAO;AAAA,IAC/C;AAAA,EACF;AACF;AAEA,eAAe,wBACb,SAC+B;AAC/B,QAAM,UAAU,QAAQ;AAAA,IACtB;AAAA,EACF;AACA,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aACE;AAAA,IACF,WAAW,YAAY;AACrB,UAAI;AACF,cAAM,aAAa,MAAM,QAAQ,sBAAsB;AACvD,eAAO,WAAW,SAAS;AAAA,MAC7B,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,SAAS,OAAO,YAAY;AAC1B,YAAM,EAAE,sBAAsB,IAAI,MAAM,OACtC,4BACF;AACA,aAAO,sBAAsB,SAAS,OAAO;AAAA,IAC/C;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/browser-service.ts"],"sourcesContent":["/**\n * BrowserService — single browser dispatcher with a pluggable target\n * registry.\n *\n * The agent uses what is available: targets register themselves at plugin\n * init (or later), and the BROWSER action calls into BrowserService which\n * picks the active target. Targets can be queried by id, listed, or\n * resolved by availability.\n *\n * Built-in targets:\n * - `workspace` — Eliza's electrobun-embedded BrowserView (with a JSDOM\n * web-mode fallback when the desktop bridge isn't configured). Always\n * registered by this plugin's `start`. Always available.\n *\n * Optional targets registered by other plugins:\n * - `bridge` — registered by this plugin when a `BrowserBridgeRouteService`\n * is reachable via the runtime; routes commands to the user's real\n * Chrome / Safari via the Agent Browser Bridge companion extension.\n * Available iff at least one companion is paired.\n * - `computeruse` — registered by `@elizaos/plugin-computeruse` on plugin\n * init when its capabilities indicate the puppeteer-driven Chromium is\n * ready.\n * - `stagehand` — registered by this plugin when a Stagehand command\n * endpoint is configured; used as a low-priority fallback.\n *\n * Anyone can add a new target later by calling `registerTarget` — that's\n * the whole point of the pattern. The BROWSER action stays one action.\n */\n\nimport { type IAgentRuntime, logger, Service } from \"@elizaos/core\";\nimport {\n BROWSER_BRIDGE_ROUTE_SERVICE_TYPE,\n type BrowserBridgeRouteService,\n} from \"./service.js\";\nimport { maybeCreateStagehandTarget } from \"./targets/stagehand-target.js\";\nimport type {\n BrowserWorkspaceCommand,\n BrowserWorkspaceCommandResult,\n} from \"./workspace/browser-workspace-types.js\";\n\nexport const BROWSER_SERVICE_TYPE = \"browser\";\n\nexport type BrowserTargetKind = \"app\" | \"companion\" | \"stagehand\" | \"external\";\n\nexport interface BrowserTargetResolutionContext {\n command: BrowserWorkspaceCommand;\n env: NodeJS.ProcessEnv;\n mobile: boolean;\n}\n\n/**\n * Pluggable browser backend. Implementations translate the canonical\n * BrowserWorkspaceCommand surface into whatever native shape they speak\n * (electrobun bridge, Chrome companion HTTP, puppeteer CDP, etc.) and\n * return the canonical BrowserWorkspaceCommandResult.\n *\n * Targets MAY decline subactions they don't support — throw a clear\n * `Error` from `execute` and the caller will see the message. Don't\n * silently ignore it.\n */\nexport interface BrowserTarget {\n /** Stable identifier — `workspace`, `bridge`, `computeruse`, etc. */\n readonly id: string;\n /** Short human-readable name for diagnostics. */\n readonly name: string;\n /** One-line description of what this target controls. */\n readonly description: string;\n /** Broad target class used for automatic routing. */\n readonly kind?: BrowserTargetKind;\n /** Lower scores are fallback choices. */\n readonly priority?: number;\n /**\n * Optional command-aware score. Return `null` to opt out of automatic\n * routing for this command while still allowing explicit `target`.\n */\n score?(context: BrowserTargetResolutionContext): number | null;\n /**\n * Cheap availability check. Called when the BROWSER action wants to\n * route a command and the caller didn't pin a target. Should be fast\n * (no network round-trips) when possible.\n */\n available(): Promise<boolean>;\n /** Run the command. Throw on unsupported subactions. */\n execute(\n command: BrowserWorkspaceCommand,\n ): Promise<BrowserWorkspaceCommandResult>;\n}\n\nexport class BrowserService extends Service {\n static override readonly serviceType = BROWSER_SERVICE_TYPE;\n override capabilityDescription =\n \"Single browser dispatcher with a pluggable target registry. Targets (workspace / bridge / computeruse / …) register themselves; the BROWSER action picks the active target or honors a pinned override.\";\n\n private readonly targets = new Map<string, BrowserTarget>();\n /** Registration order — used as the default preference order. */\n private readonly targetOrder: string[] = [];\n\n async stop(): Promise<void> {\n this.targets.clear();\n this.targetOrder.length = 0;\n }\n\n static override async start(runtime: IAgentRuntime): Promise<BrowserService> {\n const service = new BrowserService(runtime);\n service.registerTarget(createWorkspaceTarget());\n // Bridge target self-registers when its dependencies (BrowserBridgeRouteService\n // implementor) are reachable via the runtime. Missing dependencies keep the\n // agent in workspace-only mode.\n try {\n const bridgeTarget = await maybeCreateBridgeTarget(runtime);\n if (bridgeTarget) service.registerTarget(bridgeTarget);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger.debug(\n `[BrowserService] bridge target not registered at start: ${message}`,\n );\n }\n try {\n const stagehandTarget = await maybeCreateStagehandTarget();\n if (stagehandTarget) service.registerTarget(stagehandTarget);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger.debug(\n `[BrowserService] stagehand target not registered at start: ${message}`,\n );\n }\n return service;\n }\n\n /**\n * Register a target. Idempotent on `id` — calling twice with the same id\n * replaces the previous registration without affecting registration\n * order. New ids are appended to the order list.\n */\n registerTarget(target: BrowserTarget): void {\n if (!this.targets.has(target.id)) {\n this.targetOrder.push(target.id);\n }\n this.targets.set(target.id, target);\n logger.debug(\n `[BrowserService] registered target \"${target.id}\" (${target.name})`,\n );\n }\n\n unregisterTarget(id: string): boolean {\n const removed = this.targets.delete(id);\n if (removed) {\n const idx = this.targetOrder.indexOf(id);\n if (idx >= 0) this.targetOrder.splice(idx, 1);\n }\n return removed;\n }\n\n listTargets(): BrowserTarget[] {\n return this.targetOrder\n .map((id) => this.targets.get(id))\n .filter((target): target is BrowserTarget => target !== undefined);\n }\n\n /**\n * Resolve the active target for a command. If `preferredId` is given,\n * returns that target only if available; otherwise scores registered\n * targets and returns the best available one.\n * Returns `null` if nothing is available.\n */\n async resolveTarget(\n preferredId?: string,\n command: BrowserWorkspaceCommand = { subaction: \"state\" },\n ): Promise<BrowserTarget | null> {\n const targets = await this.resolveTargets(preferredId, command);\n return targets[0] ?? null;\n }\n\n async resolveTargets(\n preferredId?: string,\n command: BrowserWorkspaceCommand = { subaction: \"state\" },\n ): Promise<BrowserTarget[]> {\n if (preferredId) {\n const target = this.targets.get(preferredId);\n if (!target) return [];\n try {\n return (await target.available()) ? [target] : [];\n } catch {\n return [];\n }\n }\n\n const context: BrowserTargetResolutionContext = {\n command,\n env: process.env,\n mobile: isMobileBrowserRuntime(process.env),\n };\n const available: Array<{\n score: number;\n order: number;\n target: BrowserTarget;\n }> = [];\n\n for (const id of this.targetOrder) {\n const target = this.targets.get(id);\n if (!target) continue;\n try {\n const score = target.score\n ? target.score(context)\n : (target.priority ?? 0);\n if (score === null) continue;\n if (await target.available()) {\n available.push({\n score,\n order: this.targetOrder.indexOf(id),\n target,\n });\n }\n } catch {\n // Ignore unhealthy targets during target resolution.\n }\n }\n\n return available\n .sort((a, b) => b.score - a.score || a.order - b.order)\n .map(({ target }) => target);\n }\n\n /**\n * Dispatch a command. `targetId` pins the target; otherwise the service\n * picks the first available one in registration order.\n */\n async execute(\n command: BrowserWorkspaceCommand,\n targetId?: string,\n ): Promise<BrowserWorkspaceCommandResult> {\n const targets = await this.resolveTargets(targetId, command);\n if (targets.length === 0) {\n const availableIds = this.targetOrder.join(\", \") || \"(none)\";\n throw new Error(\n targetId\n ? `Browser target \"${targetId}\" is not available. Registered targets: ${availableIds}.`\n : `No browser target is available. Registered targets: ${availableIds}.`,\n );\n }\n\n let lastError: unknown = null;\n for (const target of targets) {\n try {\n return await target.execute(command);\n } catch (err) {\n lastError = err;\n if (targetId) break;\n const message = err instanceof Error ? err.message : String(err);\n logger.debug(\n `[BrowserService] target \"${target.id}\" failed; trying next target: ${message}`,\n );\n }\n }\n\n throw lastError instanceof Error\n ? lastError\n : new Error(\"Browser target execution failed.\");\n }\n}\n\nfunction createWorkspaceTarget(): BrowserTarget {\n return {\n id: \"workspace\",\n name: \"Browser Workspace\",\n description:\n \"Eliza's electrobun-embedded BrowserView (desktop) or JSDOM fallback (web). Always available.\",\n kind: \"app\",\n priority: 100,\n score: ({ mobile }) => (mobile ? 120 : 100),\n available: async () => true,\n execute: async (command) => {\n const { executeBrowserWorkspaceCommand } = await import(\n \"./workspace/browser-workspace.js\"\n );\n return executeBrowserWorkspaceCommand(command);\n },\n };\n}\n\nasync function maybeCreateBridgeTarget(\n runtime: IAgentRuntime,\n): Promise<BrowserTarget | null> {\n const service = runtime.getService<BrowserBridgeRouteService>(\n BROWSER_BRIDGE_ROUTE_SERVICE_TYPE,\n );\n if (!service) return null;\n return {\n id: \"bridge\",\n name: \"Browser Bridge (Chrome / Safari companion)\",\n description:\n \"Routes commands to the user's real Chrome or Safari via the Agent Browser Bridge companion extension. Subset of subactions supported (open / navigate / close / list / state / show / hide / tab / get).\",\n kind: \"companion\",\n priority: 80,\n score: ({ mobile }) => (mobile ? null : 80),\n available: async () => {\n try {\n const companions = await service.listBrowserCompanions();\n return companions.length > 0;\n } catch {\n return false;\n }\n },\n execute: async (command) => {\n const { dispatchBridgeCommand } = await import(\n \"./targets/bridge-target.js\"\n );\n return dispatchBridgeCommand(service, command);\n },\n };\n}\n\nfunction isMobileBrowserRuntime(env: NodeJS.ProcessEnv): boolean {\n const platform = (\n env.ELIZA_MOBILE_PLATFORM ??\n env.ELIZA_PLATFORM ??\n env.CAPACITOR_PLATFORM ??\n \"\"\n ).toLowerCase();\n return platform === \"ios\" || platform === \"android\" || platform === \"mobile\";\n}\n"],"mappings":"AA6BA,SAA6B,QAAQ,eAAe;AACpD;AAAA,EACE;AAAA,OAEK;AACP,SAAS,kCAAkC;AAMpC,MAAM,uBAAuB;AAgD7B,MAAM,uBAAuB,QAAQ;AAAA,EAC1C,OAAyB,cAAc;AAAA,EAC9B,wBACP;AAAA,EAEe,UAAU,oBAAI,IAA2B;AAAA;AAAA,EAEzC,cAAwB,CAAC;AAAA,EAE1C,MAAM,OAAsB;AAC1B,SAAK,QAAQ,MAAM;AACnB,SAAK,YAAY,SAAS;AAAA,EAC5B;AAAA,EAEA,aAAsB,MAAM,SAAiD;AAC3E,UAAM,UAAU,IAAI,eAAe,OAAO;AAC1C,YAAQ,eAAe,sBAAsB,CAAC;AAI9C,QAAI;AACF,YAAM,eAAe,MAAM,wBAAwB,OAAO;AAC1D,UAAI,aAAc,SAAQ,eAAe,YAAY;AAAA,IACvD,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,2DAA2D,OAAO;AAAA,MACpE;AAAA,IACF;AACA,QAAI;AACF,YAAM,kBAAkB,MAAM,2BAA2B;AACzD,UAAI,gBAAiB,SAAQ,eAAe,eAAe;AAAA,IAC7D,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,8DAA8D,OAAO;AAAA,MACvE;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAe,QAA6B;AAC1C,QAAI,CAAC,KAAK,QAAQ,IAAI,OAAO,EAAE,GAAG;AAChC,WAAK,YAAY,KAAK,OAAO,EAAE;AAAA,IACjC;AACA,SAAK,QAAQ,IAAI,OAAO,IAAI,MAAM;AAClC,WAAO;AAAA,MACL,uCAAuC,OAAO,EAAE,MAAM,OAAO,IAAI;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,iBAAiB,IAAqB;AACpC,UAAM,UAAU,KAAK,QAAQ,OAAO,EAAE;AACtC,QAAI,SAAS;AACX,YAAM,MAAM,KAAK,YAAY,QAAQ,EAAE;AACvC,UAAI,OAAO,EAAG,MAAK,YAAY,OAAO,KAAK,CAAC;AAAA,IAC9C;AACA,WAAO;AAAA,EACT;AAAA,EAEA,cAA+B;AAC7B,WAAO,KAAK,YACT,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,EAAE,CAAC,EAChC,OAAO,CAAC,WAAoC,WAAW,MAAS;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cACJ,aACA,UAAmC,EAAE,WAAW,QAAQ,GACzB;AAC/B,UAAM,UAAU,MAAM,KAAK,eAAe,aAAa,OAAO;AAC9D,WAAO,QAAQ,CAAC,KAAK;AAAA,EACvB;AAAA,EAEA,MAAM,eACJ,aACA,UAAmC,EAAE,WAAW,QAAQ,GAC9B;AAC1B,QAAI,aAAa;AACf,YAAM,SAAS,KAAK,QAAQ,IAAI,WAAW;AAC3C,UAAI,CAAC,OAAQ,QAAO,CAAC;AACrB,UAAI;AACF,eAAQ,MAAM,OAAO,UAAU,IAAK,CAAC,MAAM,IAAI,CAAC;AAAA,MAClD,QAAQ;AACN,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AAEA,UAAM,UAA0C;AAAA,MAC9C;AAAA,MACA,KAAK,QAAQ;AAAA,MACb,QAAQ,uBAAuB,QAAQ,GAAG;AAAA,IAC5C;AACA,UAAM,YAID,CAAC;AAEN,eAAW,MAAM,KAAK,aAAa;AACjC,YAAM,SAAS,KAAK,QAAQ,IAAI,EAAE;AAClC,UAAI,CAAC,OAAQ;AACb,UAAI;AACF,cAAM,QAAQ,OAAO,QACjB,OAAO,MAAM,OAAO,IACnB,OAAO,YAAY;AACxB,YAAI,UAAU,KAAM;AACpB,YAAI,MAAM,OAAO,UAAU,GAAG;AAC5B,oBAAU,KAAK;AAAA,YACb;AAAA,YACA,OAAO,KAAK,YAAY,QAAQ,EAAE;AAAA,YAClC;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,WAAO,UACJ,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EACrD,IAAI,CAAC,EAAE,OAAO,MAAM,MAAM;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QACJ,SACA,UACwC;AACxC,UAAM,UAAU,MAAM,KAAK,eAAe,UAAU,OAAO;AAC3D,QAAI,QAAQ,WAAW,GAAG;AACxB,YAAM,eAAe,KAAK,YAAY,KAAK,IAAI,KAAK;AACpD,YAAM,IAAI;AAAA,QACR,WACI,mBAAmB,QAAQ,2CAA2C,YAAY,MAClF,uDAAuD,YAAY;AAAA,MACzE;AAAA,IACF;AAEA,QAAI,YAAqB;AACzB,eAAW,UAAU,SAAS;AAC5B,UAAI;AACF,eAAO,MAAM,OAAO,QAAQ,OAAO;AAAA,MACrC,SAAS,KAAK;AACZ,oBAAY;AACZ,YAAI,SAAU;AACd,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,eAAO;AAAA,UACL,4BAA4B,OAAO,EAAE,iCAAiC,OAAO;AAAA,QAC/E;AAAA,MACF;AAAA,IACF;AAEA,UAAM,qBAAqB,QACvB,YACA,IAAI,MAAM,kCAAkC;AAAA,EAClD;AACF;AAEA,SAAS,wBAAuC;AAC9C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aACE;AAAA,IACF,MAAM;AAAA,IACN,UAAU;AAAA,IACV,OAAO,CAAC,EAAE,OAAO,MAAO,SAAS,MAAM;AAAA,IACvC,WAAW,YAAY;AAAA,IACvB,SAAS,OAAO,YAAY;AAC1B,YAAM,EAAE,+BAA+B,IAAI,MAAM,OAC/C,kCACF;AACA,aAAO,+BAA+B,OAAO;AAAA,IAC/C;AAAA,EACF;AACF;AAEA,eAAe,wBACb,SAC+B;AAC/B,QAAM,UAAU,QAAQ;AAAA,IACtB;AAAA,EACF;AACA,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aACE;AAAA,IACF,MAAM;AAAA,IACN,UAAU;AAAA,IACV,OAAO,CAAC,EAAE,OAAO,MAAO,SAAS,OAAO;AAAA,IACxC,WAAW,YAAY;AACrB,UAAI;AACF,cAAM,aAAa,MAAM,QAAQ,sBAAsB;AACvD,eAAO,WAAW,SAAS;AAAA,MAC7B,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,SAAS,OAAO,YAAY;AAC1B,YAAM,EAAE,sBAAsB,IAAI,MAAM,OACtC,4BACF;AACA,aAAO,sBAAsB,SAAS,OAAO;AAAA,IAC/C;AAAA,EACF;AACF;AAEA,SAAS,uBAAuB,KAAiC;AAC/D,QAAM,YACJ,IAAI,yBACJ,IAAI,kBACJ,IAAI,sBACJ,IACA,YAAY;AACd,SAAO,aAAa,SAAS,aAAa,aAAa,aAAa;AACtE;","names":[]}
@@ -0,0 +1,14 @@
1
+ import type { BrowserWorkspaceTab, EvaluateBrowserWorkspaceTabRequest, NavigateBrowserWorkspaceTabRequest, OpenBrowserWorkspaceTabRequest } from "./workspace/browser-workspace-types.js";
2
+ export interface BrowserWorkspaceHooks {
3
+ closeBrowserWorkspaceTab(id: string, env?: NodeJS.ProcessEnv): Promise<boolean>;
4
+ evaluateBrowserWorkspaceTab(request: EvaluateBrowserWorkspaceTabRequest, env?: NodeJS.ProcessEnv): Promise<unknown>;
5
+ isBrowserWorkspaceBridgeConfigured(env?: NodeJS.ProcessEnv): boolean;
6
+ listBrowserWorkspaceTabs(env?: NodeJS.ProcessEnv): Promise<BrowserWorkspaceTab[]>;
7
+ navigateBrowserWorkspaceTab(request: NavigateBrowserWorkspaceTabRequest, env?: NodeJS.ProcessEnv): Promise<BrowserWorkspaceTab>;
8
+ openBrowserWorkspaceTab(request: OpenBrowserWorkspaceTabRequest, env?: NodeJS.ProcessEnv): Promise<BrowserWorkspaceTab>;
9
+ resolveBrowserWorkspaceConnectorPartition(provider: string, accountId: string): string;
10
+ showBrowserWorkspaceTab(id: string, env?: NodeJS.ProcessEnv): Promise<BrowserWorkspaceTab>;
11
+ }
12
+ export declare function registerBrowserWorkspaceHooks(hooks: BrowserWorkspaceHooks): void;
13
+ export declare function getBrowserWorkspaceHooks(): BrowserWorkspaceHooks | null;
14
+ //# sourceMappingURL=browser-workspace-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-workspace-hooks.d.ts","sourceRoot":"","sources":["../src/browser-workspace-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,kCAAkC,EAClC,kCAAkC,EAClC,8BAA8B,EAC/B,MAAM,wCAAwC,CAAC;AAEhD,MAAM,WAAW,qBAAqB;IACpC,wBAAwB,CACtB,EAAE,EAAE,MAAM,EACV,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GACtB,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,2BAA2B,CACzB,OAAO,EAAE,kCAAkC,EAC3C,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GACtB,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,kCAAkC,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC;IACrE,wBAAwB,CACtB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GACtB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAClC,2BAA2B,CACzB,OAAO,EAAE,kCAAkC,EAC3C,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAChC,uBAAuB,CACrB,OAAO,EAAE,8BAA8B,EACvC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAChC,yCAAyC,CACvC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC;IACV,uBAAuB,CACrB,EAAE,EAAE,MAAM,EACV,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAAC;CACjC;AAYD,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,qBAAqB,GAC3B,IAAI,CAEN;AAED,wBAAgB,wBAAwB,IAAI,qBAAqB,GAAG,IAAI,CAEvE"}
@@ -0,0 +1,15 @@
1
+ const BROWSER_WORKSPACE_HOOKS = /* @__PURE__ */ Symbol.for("elizaos.browser-workspace.hooks");
2
+ function hooksGlobal() {
3
+ return globalThis;
4
+ }
5
+ function registerBrowserWorkspaceHooks(hooks) {
6
+ hooksGlobal()[BROWSER_WORKSPACE_HOOKS] = hooks;
7
+ }
8
+ function getBrowserWorkspaceHooks() {
9
+ return hooksGlobal()[BROWSER_WORKSPACE_HOOKS] ?? null;
10
+ }
11
+ export {
12
+ getBrowserWorkspaceHooks,
13
+ registerBrowserWorkspaceHooks
14
+ };
15
+ //# sourceMappingURL=browser-workspace-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/browser-workspace-hooks.ts"],"sourcesContent":["import type {\n BrowserWorkspaceTab,\n EvaluateBrowserWorkspaceTabRequest,\n NavigateBrowserWorkspaceTabRequest,\n OpenBrowserWorkspaceTabRequest,\n} from \"./workspace/browser-workspace-types.js\";\n\nexport interface BrowserWorkspaceHooks {\n closeBrowserWorkspaceTab(\n id: string,\n env?: NodeJS.ProcessEnv,\n ): Promise<boolean>;\n evaluateBrowserWorkspaceTab(\n request: EvaluateBrowserWorkspaceTabRequest,\n env?: NodeJS.ProcessEnv,\n ): Promise<unknown>;\n isBrowserWorkspaceBridgeConfigured(env?: NodeJS.ProcessEnv): boolean;\n listBrowserWorkspaceTabs(\n env?: NodeJS.ProcessEnv,\n ): Promise<BrowserWorkspaceTab[]>;\n navigateBrowserWorkspaceTab(\n request: NavigateBrowserWorkspaceTabRequest,\n env?: NodeJS.ProcessEnv,\n ): Promise<BrowserWorkspaceTab>;\n openBrowserWorkspaceTab(\n request: OpenBrowserWorkspaceTabRequest,\n env?: NodeJS.ProcessEnv,\n ): Promise<BrowserWorkspaceTab>;\n resolveBrowserWorkspaceConnectorPartition(\n provider: string,\n accountId: string,\n ): string;\n showBrowserWorkspaceTab(\n id: string,\n env?: NodeJS.ProcessEnv,\n ): Promise<BrowserWorkspaceTab>;\n}\n\nconst BROWSER_WORKSPACE_HOOKS = Symbol.for(\"elizaos.browser-workspace.hooks\");\n\ntype BrowserWorkspaceHooksGlobal = typeof globalThis & {\n [BROWSER_WORKSPACE_HOOKS]?: BrowserWorkspaceHooks;\n};\n\nfunction hooksGlobal(): BrowserWorkspaceHooksGlobal {\n return globalThis as BrowserWorkspaceHooksGlobal;\n}\n\nexport function registerBrowserWorkspaceHooks(\n hooks: BrowserWorkspaceHooks,\n): void {\n hooksGlobal()[BROWSER_WORKSPACE_HOOKS] = hooks;\n}\n\nexport function getBrowserWorkspaceHooks(): BrowserWorkspaceHooks | null {\n return hooksGlobal()[BROWSER_WORKSPACE_HOOKS] ?? null;\n}\n"],"mappings":"AAsCA,MAAM,0BAA0B,uBAAO,IAAI,iCAAiC;AAM5E,SAAS,cAA2C;AAClD,SAAO;AACT;AAEO,SAAS,8BACd,OACM;AACN,cAAY,EAAE,uBAAuB,IAAI;AAC3C;AAEO,SAAS,2BAAyD;AACvE,SAAO,YAAY,EAAE,uBAAuB,KAAK;AACnD;","names":[]}