diff --git a/extensions/cate.kitchensink/manifest.json b/extensions/cate.kitchensink/manifest.json index 32a03f8..3456376 100644 --- a/extensions/cate.kitchensink/manifest.json +++ b/extensions/cate.kitchensink/manifest.json @@ -9,5 +9,5 @@ "readyPath": "/health", "portEnv": "PORT" }, - "cateApi": ["storage", "editor", "canvas", "theme", "ui", "workspace.read", "agent"] + "cateApi": ["storage", "editor", "canvas", "theme", "ui", "workspace.read", "agent", "browser"] } diff --git a/extensions/cate.kitchensink/src/public/app.ts b/extensions/cate.kitchensink/src/public/app.ts index 531824f..eb2a384 100644 --- a/extensions/cate.kitchensink/src/public/app.ts +++ b/extensions/cate.kitchensink/src/public/app.ts @@ -326,6 +326,44 @@ function initAgent(): void { }) } +// --- browser ---------------------------------------------------------------- + +// Drive Cate's browser panels through window.cate.browser (needs the `browser` +// scope). These panels hold the user's real logged-in session, so this is a +// deliberately small demo: open a URL, and snapshot the focused panel. +function initBrowser(): void { + if (!window.cate) return + const out = byId('browser-out') + + byId('browser-open').addEventListener('click', async () => { + const url = byId('browser-url').value.trim() + if (!url) return + out.textContent = 'opening…' + try { + const res = await cate.browser.open({ url }) + out.textContent = 'opened ' + JSON.stringify(res, null, 2) + log('browser.open ->', res) + } catch (err) { + out.textContent = 'failed: ' + String(err) + log('browser.open failed:', String(err)) + } + }) + + byId('browser-snapshot').addEventListener('click', async () => { + out.textContent = 'snapshotting…' + try { + const snap = await cate.browser.snapshot() + out.textContent = + `${snap.title} — ${snap.url}\n${snap.refs.length} refs\n` + + JSON.stringify(snap.refs.slice(0, 20), null, 2) + log('browser.snapshot ->', snap) + } catch (err) { + out.textContent = 'failed: ' + String(err) + log('browser.snapshot failed:', String(err)) + } + }) +} + initBridge() initNotes() initStorageApi() @@ -334,3 +372,4 @@ initHttp() initWs() initRoundtrip() initAgent() +initBrowser() diff --git a/extensions/cate.kitchensink/src/public/cate-host.d.ts b/extensions/cate.kitchensink/src/public/cate-host.d.ts index f5b4693..5c43694 100644 --- a/extensions/cate.kitchensink/src/public/cate-host.d.ts +++ b/extensions/cate.kitchensink/src/public/cate-host.d.ts @@ -50,6 +50,22 @@ interface CatePanel { setTitle(title: string): Promise } +/** One interactable element in an accessibility `snapshot()`. `ref` is opaque and + * only valid for the snapshot it came from. */ +interface CateBrowserRef { + ref: string + role: string + name: string + value?: string +} + +/** Accessibility snapshot of a browser panel, from `cate.browser.snapshot()`. */ +interface CateBrowserSnapshot { + url: string + title: string + refs: CateBrowserRef[] +} + interface CateHost { /** API version int, for feature detection. */ version(): Promise @@ -95,6 +111,27 @@ interface CateHost { /** Abort the in-flight turn of this extension's session. */ cancel(): Promise } + /** Drive Cate's browser panels (requires the `browser` scope). These panels + * hold the user's real, logged-in browser session, so treat the access as + * sensitive. `panelId` targets a panel; omit it for the focused one. */ + browser: { + /** To enumerate open browser panels, use `cate.panel.list()`. */ + open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> + reload(opts?: { panelId?: string }): Promise<{ ok: true }> + /** Capture a screenshot; returns a host filesystem path in the OS temp dir + * (a webview guest can't read it directly; a server-backed extension can). */ + screenshot(opts?: { panelId?: string }): Promise<{ path: string }> + snapshot(opts?: { panelId?: string }): Promise + click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }> + type(opts: { ref: string; text: string; panelId?: string }): Promise<{ ok: true }> + /** Resolve once the panel stops loading (`timeoutMs` defaults to 5000, + * capped at 8000). Rejects in-band with `still-loading`. */ + wait(opts?: { panelId?: string; timeoutMs?: number }): Promise<{ url: string; title: string; loading: false }> + /** Press a named key (Enter, Tab, Escape, Backspace, Delete, Space, arrows, + * PageUp/PageDown, Home, End) as TRUSTED input, so Enter submits forms. + * With `ref` the element is focused first. */ + press(opts: { key: string; ref?: string; panelId?: string }): Promise<{ ok: true }> + } storage: CateHostStorage } diff --git a/extensions/cate.kitchensink/src/public/index.html b/extensions/cate.kitchensink/src/public/index.html index 546e1ab..1b2acf5 100644 --- a/extensions/cate.kitchensink/src/public/index.html +++ b/extensions/cate.kitchensink/src/public/index.html @@ -65,6 +65,16 @@

server to cate (CATE_API)


   
 
+  
+

browser

+
+ + + +
+

+  
+

agent