feat(record): add popup quick-actions launcher and make record --url optional

Add a popup launcher + action-recording panel that builds a copyable
`bsk record start` prompt, add `--browser` targeting, and default `--url`
to https://example.com/ when omitted. Drop the popup browser-alias field.
This commit is contained in:
“haonan”
2026-07-22 13:27:30 +08:00
parent ac028a7a18
commit 585308df45
18 changed files with 546 additions and 177 deletions
@@ -2,11 +2,18 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import {
ensureBrowserObservationListeners,
isBrowserObservationAttachedForTests,
RECORD_DEFAULT_START_URL,
releaseBrowserObservationListenersIfIdle,
resetBrowserObservationForTests,
setBrowserObservationAttachForTests,
} from "../record";
describe("record defaults", () => {
it("uses Baidu as the default injectable start URL", () => {
expect(RECORD_DEFAULT_START_URL).toBe("https://www.baidu.com/");
});
});
describe("browser observation lifecycle", () => {
afterEach(() => {
resetBrowserObservationForTests();
+11 -8
View File
@@ -77,6 +77,9 @@ function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/** Injectable http(s) landing page when `tool.record_start` omits `url`. */
export const RECORD_DEFAULT_START_URL = "https://www.baidu.com/";
/** Pages where MV3 content scripts cannot attach (Agent Window boots here). */
function isContentScriptRestrictedUrl(url: string | undefined): boolean {
if (!url) return true;
@@ -568,12 +571,12 @@ export async function handleRecordStart(
resolveFinish = resolve;
rejectFinish = reject;
});
const provisionalUrl = params.url;
const navigateUrl = params.url ?? RECORD_DEFAULT_START_URL;
recordings.set(params.session_id, {
requestId,
tabId: target.tabId,
agentWindowId: ctx.agentWindowId,
startUrl: provisionalUrl,
startUrl: navigateUrl,
...(params.purpose ? { purpose: params.purpose } : {}),
steps: [],
startedAt: new Date().toISOString(),
@@ -582,7 +585,7 @@ export async function handleRecordStart(
rejectFinish,
settled: false,
finishing: false,
currentUrl: provisionalUrl,
currentUrl: navigateUrl,
pendingNavigation: false,
pendingNavigationDeadline: undefined,
});
@@ -629,12 +632,12 @@ export async function handleRecordStart(
if (cancelled) return cancelled;
}
if (params.url && deps.cdp) {
if (deps.cdp) {
const nav = await handleNavigate(
manager,
{
session_id: params.session_id,
url: params.url,
url: navigateUrl,
tab_id: target.tabId,
},
{ cdp: deps.cdp, tabsApi: deps.tabsApi, signal: deps.signal },
@@ -663,7 +666,7 @@ export async function handleRecordStart(
const tab = await deps.tabsApi.get(target.tabId);
startUrl = tab.url;
} catch {
startUrl = params.url;
startUrl = navigateUrl;
}
const active = recordings.get(params.session_id);
@@ -680,7 +683,7 @@ export async function handleRecordStart(
code: "invalid_params",
message: params.url
? `cannot record on restricted URL (${startUrl}); use an http(s) page`
: "Agent Window starts on about:blank where the content script cannot inject — pass --url https://… before recording",
: `cannot record on restricted URL (${startUrl}); default start page must be injectable http(s)`,
};
}
@@ -713,7 +716,7 @@ export async function handleRecordStart(
return {
code: "protocol_error",
message:
"failed to start recording in content script — reload the BrowserSkill extension, then retry with --url https://…",
"failed to start recording in content script — reload the BrowserSkill extension, then retry",
};
}