@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.
- package/LICENSE +21 -0
- package/README.md +106 -64
- package/dist/actions/browser-autofill-login.d.ts.map +1 -1
- package/dist/actions/browser-autofill-login.js.map +1 -1
- package/dist/actions/browser.d.ts +5 -6
- package/dist/actions/browser.d.ts.map +1 -1
- package/dist/actions/browser.js +312 -60
- package/dist/actions/browser.js.map +1 -1
- package/dist/actions/manage-browser-bridge.d.ts.map +1 -1
- package/dist/actions/manage-browser-bridge.js +10 -14
- package/dist/actions/manage-browser-bridge.js.map +1 -1
- package/dist/actions/wait-for-url-predicate.d.ts +34 -0
- package/dist/actions/wait-for-url-predicate.d.ts.map +1 -0
- package/dist/actions/wait-for-url-predicate.js +33 -0
- package/dist/actions/wait-for-url-predicate.js.map +1 -0
- package/dist/actions/wait-for-url.d.ts +64 -0
- package/dist/actions/wait-for-url.d.ts.map +1 -0
- package/dist/actions/wait-for-url.js +89 -0
- package/dist/actions/wait-for-url.js.map +1 -0
- package/dist/bridge-policy.d.ts +10 -0
- package/dist/bridge-policy.d.ts.map +1 -0
- package/dist/bridge-policy.js +37 -0
- package/dist/bridge-policy.js.map +1 -0
- package/dist/bridge-readiness.d.ts +16 -0
- package/dist/bridge-readiness.d.ts.map +1 -0
- package/dist/bridge-readiness.js +82 -0
- package/dist/bridge-readiness.js.map +1 -0
- package/dist/bridge-records.d.ts +9 -0
- package/dist/bridge-records.d.ts.map +1 -0
- package/dist/bridge-records.js +37 -0
- package/dist/bridge-records.js.map +1 -0
- package/dist/browser-capture-hooks.d.ts +9 -0
- package/dist/browser-capture-hooks.d.ts.map +1 -0
- package/dist/browser-capture-hooks.js +15 -0
- package/dist/browser-capture-hooks.js.map +1 -0
- package/dist/browser-service.d.ts +22 -4
- package/dist/browser-service.d.ts.map +1 -1
- package/dist/browser-service.js +63 -15
- package/dist/browser-service.js.map +1 -1
- package/dist/browser-workspace-hooks.d.ts +14 -0
- package/dist/browser-workspace-hooks.d.ts.map +1 -0
- package/dist/browser-workspace-hooks.js +15 -0
- package/dist/browser-workspace-hooks.js.map +1 -0
- package/dist/companion-auth.d.ts +34 -0
- package/dist/companion-auth.d.ts.map +1 -0
- package/dist/companion-auth.js +98 -0
- package/dist/companion-auth.js.map +1 -0
- package/dist/index.d.ts +12 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +66 -12
- package/dist/index.js.map +1 -1
- package/dist/message-adapter.d.ts +9 -0
- package/dist/message-adapter.d.ts.map +1 -0
- package/dist/message-adapter.js +104 -0
- package/dist/message-adapter.js.map +1 -0
- package/dist/packaging.d.ts.map +1 -1
- package/dist/packaging.js +2 -0
- package/dist/packaging.js.map +1 -1
- package/dist/parity/browser-matrix.d.ts +45 -0
- package/dist/parity/browser-matrix.d.ts.map +1 -0
- package/dist/parity/browser-matrix.js +361 -0
- package/dist/parity/browser-matrix.js.map +1 -0
- package/dist/parity/index.d.ts +5 -0
- package/dist/parity/index.d.ts.map +1 -0
- package/dist/parity/index.js +13 -0
- package/dist/parity/index.js.map +1 -0
- package/dist/password-manager-bridge.d.ts +50 -0
- package/dist/password-manager-bridge.d.ts.map +1 -0
- package/dist/password-manager-bridge.js +437 -0
- package/dist/password-manager-bridge.js.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +8 -4
- package/dist/plugin.js.map +1 -1
- package/dist/providers/workspace.d.ts +1 -1
- package/dist/providers/workspace.js.map +1 -1
- package/dist/routes/bridge.d.ts.map +1 -1
- package/dist/routes/bridge.js +63 -14
- package/dist/routes/bridge.js.map +1 -1
- package/dist/routes/workspace-setup.d.ts.map +1 -1
- package/dist/routes/workspace-setup.js +1 -1
- package/dist/routes/workspace-setup.js.map +1 -1
- package/dist/routes/workspace.d.ts +1 -2
- package/dist/routes/workspace.d.ts.map +1 -1
- package/dist/routes/workspace.js +104 -4
- package/dist/routes/workspace.js.map +1 -1
- package/dist/schema.d.ts +2 -2
- package/dist/schema.js.map +1 -1
- package/dist/service.d.ts +1 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js.map +1 -1
- package/dist/targets/bridge-target.d.ts +1 -1
- package/dist/targets/bridge-target.d.ts.map +1 -1
- package/dist/targets/bridge-target.js.map +1 -1
- package/dist/targets/stagehand-target.d.ts +3 -0
- package/dist/targets/stagehand-target.d.ts.map +1 -0
- package/dist/targets/stagehand-target.js +187 -0
- package/dist/targets/stagehand-target.js.map +1 -0
- package/dist/workspace/browser-capture.d.ts +1 -1
- package/dist/workspace/browser-capture.d.ts.map +1 -1
- package/dist/workspace/browser-capture.js +33 -1
- package/dist/workspace/browser-capture.js.map +1 -1
- package/dist/workspace/browser-workspace-desktop.d.ts +1 -1
- package/dist/workspace/browser-workspace-desktop.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-desktop.js +66 -30
- package/dist/workspace/browser-workspace-desktop.js.map +1 -1
- package/dist/workspace/browser-workspace-errors.d.ts +62 -0
- package/dist/workspace/browser-workspace-errors.d.ts.map +1 -0
- package/dist/workspace/browser-workspace-errors.js +69 -0
- package/dist/workspace/browser-workspace-errors.js.map +1 -0
- package/dist/workspace/browser-workspace-forms.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-forms.js +1 -1
- package/dist/workspace/browser-workspace-forms.js.map +1 -1
- package/dist/workspace/browser-workspace-helpers.d.ts +7 -0
- package/dist/workspace/browser-workspace-helpers.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-helpers.js +64 -6
- package/dist/workspace/browser-workspace-helpers.js.map +1 -1
- package/dist/workspace/browser-workspace-network.d.ts +1 -1
- package/dist/workspace/browser-workspace-network.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-types.d.ts +15 -0
- package/dist/workspace/browser-workspace-types.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-types.js.map +1 -1
- package/dist/workspace/browser-workspace-web.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-web.js +34 -93
- package/dist/workspace/browser-workspace-web.js.map +1 -1
- package/dist/workspace/browser-workspace.d.ts +1 -1
- package/dist/workspace/browser-workspace.d.ts.map +1 -1
- package/dist/workspace/browser-workspace.js +9 -4
- package/dist/workspace/browser-workspace.js.map +1 -1
- package/dist/workspace/index.d.ts +1 -0
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +1 -0
- package/dist/workspace/index.js.map +1 -1
- package/package.json +29 -7
- package/registry-entry.json +75 -0
- package/dist/actions/browser-autofill-login.d.js +0 -1
- package/dist/actions/browser-autofill-login.d.js.map +0 -1
- package/dist/actions/browser.d.js +0 -1
- package/dist/actions/browser.d.js.map +0 -1
- package/dist/actions/manage-browser-bridge.d.js +0 -1
- package/dist/actions/manage-browser-bridge.d.js.map +0 -1
- package/dist/ambient-jsdom.d.js +0 -1
- package/dist/ambient-jsdom.d.js.map +0 -1
- package/dist/browser-service.d.js +0 -1
- package/dist/browser-service.d.js.map +0 -1
- package/dist/contracts.d.js +0 -1
- package/dist/contracts.d.js.map +0 -1
- package/dist/index.d.js +0 -21
- package/dist/index.d.js.map +0 -1
- package/dist/lifeops-session-contracts.d.js +0 -1
- package/dist/lifeops-session-contracts.d.js.map +0 -1
- package/dist/packaging.d.js +0 -1
- package/dist/packaging.d.js.map +0 -1
- package/dist/plugin.d.js +0 -1
- package/dist/plugin.d.js.map +0 -1
- package/dist/providers/workspace.d.js +0 -1
- package/dist/providers/workspace.d.js.map +0 -1
- package/dist/routes/bridge.d.js +0 -1
- package/dist/routes/bridge.d.js.map +0 -1
- package/dist/routes/workspace-account-gate.d.js +0 -1
- package/dist/routes/workspace-account-gate.d.js.map +0 -1
- package/dist/routes/workspace-setup.d.js +0 -1
- package/dist/routes/workspace-setup.d.js.map +0 -1
- package/dist/routes/workspace.d.js +0 -1
- package/dist/routes/workspace.d.js.map +0 -1
- package/dist/schema.d.js +0 -1
- package/dist/schema.d.js.map +0 -1
- package/dist/service.d.js +0 -1
- package/dist/service.d.js.map +0 -1
- package/dist/targets/bridge-target.d.js +0 -1
- package/dist/targets/bridge-target.d.js.map +0 -1
- package/dist/workspace/browser-capture.d.js +0 -1
- package/dist/workspace/browser-capture.d.js.map +0 -1
- package/dist/workspace/browser-workspace-desktop.d.js +0 -1
- package/dist/workspace/browser-workspace-desktop.d.js.map +0 -1
- package/dist/workspace/browser-workspace-elements.d.js +0 -1
- package/dist/workspace/browser-workspace-elements.d.js.map +0 -1
- package/dist/workspace/browser-workspace-forms.d.js +0 -1
- package/dist/workspace/browser-workspace-forms.d.js.map +0 -1
- package/dist/workspace/browser-workspace-helpers.d.js +0 -1
- package/dist/workspace/browser-workspace-helpers.d.js.map +0 -1
- package/dist/workspace/browser-workspace-jsdom.d.js +0 -1
- package/dist/workspace/browser-workspace-jsdom.d.js.map +0 -1
- package/dist/workspace/browser-workspace-network.d.js +0 -1
- package/dist/workspace/browser-workspace-network.d.js.map +0 -1
- package/dist/workspace/browser-workspace-snapshots.d.js +0 -1
- package/dist/workspace/browser-workspace-snapshots.d.js.map +0 -1
- package/dist/workspace/browser-workspace-state.d.js +0 -1
- package/dist/workspace/browser-workspace-state.d.js.map +0 -1
- package/dist/workspace/browser-workspace-types.d.js +0 -1
- package/dist/workspace/browser-workspace-types.d.js.map +0 -1
- package/dist/workspace/browser-workspace-web.d.js +0 -1
- package/dist/workspace/browser-workspace-web.d.js.map +0 -1
- package/dist/workspace/browser-workspace.d.js +0 -11
- package/dist/workspace/browser-workspace.d.js.map +0 -1
- package/dist/workspace/index.d.js +0 -3
- 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
|
|
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
|
|
75
|
-
* targets
|
|
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
|
|
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"}
|
package/dist/browser-service.js
CHANGED
|
@@ -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
|
|
61
|
-
* targets
|
|
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
|
|
78
|
+
if (!target) return [];
|
|
68
79
|
try {
|
|
69
|
-
return await target.available() ? target :
|
|
80
|
+
return await target.available() ? [target] : [];
|
|
70
81
|
} catch {
|
|
71
|
-
return
|
|
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
|
-
|
|
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
|
|
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
|
|
90
|
-
if (
|
|
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
|
-
|
|
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":"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","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":"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","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":[]}
|