@lobu/cli 7.1.0 → 7.2.0
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/dist/commands/context.d.ts +7 -0
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +19 -2
- package/dist/commands/context.js.map +1 -1
- package/dist/connectors/chrome.ts +351 -0
- package/dist/connectors/chrome_bookmarks.ts +79 -0
- package/dist/connectors/chrome_downloads.ts +80 -0
- package/dist/connectors/chrome_history.ts +80 -0
- package/dist/connectors/index.ts +14 -8
- package/dist/db/migrations/20260518020000_runs_heartbeat_inflight_narrow.sql +36 -0
- package/dist/db/migrations/20260518040000_agent_transcript_snapshot.sql +54 -0
- package/dist/db/migrations/20260518050000_runs_denormalize_agent_conversation.sql +36 -0
- package/dist/db/migrations/20260518060000_revert_runs_denormalize.sql +29 -0
- package/dist/db/migrations/20260518070000_runs_heartbeat_inflight_widen.sql +33 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/context.d.ts +4 -1
- package/dist/internal/context.d.ts.map +1 -1
- package/dist/internal/context.js +43 -3
- package/dist/internal/context.js.map +1 -1
- package/dist/internal/index.d.ts +1 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +1 -1
- package/dist/internal/index.js.map +1 -1
- package/dist/server.bundle.mjs +1520 -568
- package/dist/start-local.bundle.mjs +1556 -570
- package/package.json +6 -6
- package/dist/connectors/browser/evaluate.ts +0 -120
- package/dist/connectors/browser/fill_form.ts +0 -107
- package/dist/connectors/browser/page_text.ts +0 -108
- package/dist/connectors/chrome_tabs.ts +0 -74
|
@@ -3,6 +3,13 @@ export declare function contextCurrentCommand(): Promise<void>;
|
|
|
3
3
|
export declare function contextAddCommand(options: {
|
|
4
4
|
name: string;
|
|
5
5
|
apiUrl: string;
|
|
6
|
+
port?: number;
|
|
7
|
+
host?: string;
|
|
8
|
+
databaseUrl?: string;
|
|
9
|
+
dataDir?: string;
|
|
10
|
+
cwd?: string;
|
|
11
|
+
lifecycle?: "managed" | "external";
|
|
6
12
|
}): Promise<void>;
|
|
13
|
+
export declare function contextRmCommand(name: string): Promise<void>;
|
|
7
14
|
export declare function contextUseCommand(name: string): Promise<void>;
|
|
8
15
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAWA,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBxD;AAED,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAU3D;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;CACpC,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWnE"}
|
package/dist/commands/context.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import { addContext, getCurrentContextName, loadContextConfig, resolveContext, setCurrentContext, } from "../internal/index.js";
|
|
2
|
+
import { addContext, getCurrentContextName, loadContextConfig, removeContext, resolveContext, setCurrentContext, } from "../internal/index.js";
|
|
3
3
|
export async function contextListCommand() {
|
|
4
4
|
const config = await loadContextConfig();
|
|
5
5
|
const currentContext = await getCurrentContextName();
|
|
@@ -30,9 +30,26 @@ export async function contextCurrentCommand() {
|
|
|
30
30
|
console.log();
|
|
31
31
|
}
|
|
32
32
|
export async function contextAddCommand(options) {
|
|
33
|
-
|
|
33
|
+
const server = {};
|
|
34
|
+
if (options.port !== undefined)
|
|
35
|
+
server.port = options.port;
|
|
36
|
+
if (options.host)
|
|
37
|
+
server.host = options.host;
|
|
38
|
+
if (options.databaseUrl)
|
|
39
|
+
server.databaseUrl = options.databaseUrl;
|
|
40
|
+
if (options.dataDir)
|
|
41
|
+
server.dataDir = options.dataDir;
|
|
42
|
+
if (options.cwd)
|
|
43
|
+
server.cwd = options.cwd;
|
|
44
|
+
if (options.lifecycle)
|
|
45
|
+
server.lifecycle = options.lifecycle;
|
|
46
|
+
await addContext(options.name, options.apiUrl, Object.keys(server).length === 0 ? undefined : server);
|
|
34
47
|
console.log(chalk.green(`\n Saved context ${options.name} -> ${options.apiUrl}\n`));
|
|
35
48
|
}
|
|
49
|
+
export async function contextRmCommand(name) {
|
|
50
|
+
await removeContext(name);
|
|
51
|
+
console.log(chalk.dim(`\n Removed context ${name}\n`));
|
|
52
|
+
}
|
|
36
53
|
export async function contextUseCommand(name) {
|
|
37
54
|
const trimmedName = name.trim();
|
|
38
55
|
const config = await setCurrentContext(trimmedName);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EACL,UAAU,EACV,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EACL,UAAU,EACV,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAG9B,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAC;IACzC,MAAM,cAAc,GAAG,MAAM,qBAAqB,EAAE,CAAC;IAErD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9D,MAAM,MAAM,GAAG,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAClE,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,IAAI,KAAK,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC,CAAC;QAC9D,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QACvE,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,MAAM,OAAO,GAAG,MAAM,cAAc,EAAE,CAAC;IAEvC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvD,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,CAAC,GAAG,EAAE,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OASvC;IACC,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;QAAE,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3D,IAAI,OAAO,CAAC,IAAI;QAAE,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7C,IAAI,OAAO,CAAC,WAAW;QAAE,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAClE,IAAI,OAAO,CAAC,OAAO;QAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACtD,IAAI,OAAO,CAAC,GAAG;QAAE,MAAM,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAC1C,IAAI,OAAO,CAAC,SAAS;QAAE,MAAM,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IAE5D,MAAM,UAAU,CACd,OAAO,CAAC,IAAI,EACZ,OAAO,CAAC,MAAM,EACd,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CACtD,CAAC;IACF,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,KAAK,CAAC,qBAAqB,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,MAAM,IAAI,CAAC,CACxE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY;IACjD,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,uBAAuB,IAAI,IAAI,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAClD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAChC,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAE7C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,WAAW,WAAW,8BAA8B,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,2BAA2B,WAAW,EAAE,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome Connector — Owletto for Chrome only.
|
|
3
|
+
*
|
|
4
|
+
* One connector per paired Chrome profile. The cloud-side definition is
|
|
5
|
+
* pure metadata; all execution happens in the extension's service worker
|
|
6
|
+
* (apps/chrome/background.js: dispatchToolRun) against the user's signed-in
|
|
7
|
+
* Chrome via chrome.debugger + chrome.scripting + chrome.tabs.
|
|
8
|
+
*
|
|
9
|
+
* Surface:
|
|
10
|
+
*
|
|
11
|
+
* feeds.open_tabs
|
|
12
|
+
* Auto-wired snapshot feed. The extension emits one event per open tab
|
|
13
|
+
* each sync cycle. Cheap and read-only.
|
|
14
|
+
*
|
|
15
|
+
* actions.navigate
|
|
16
|
+
* Page.navigate the target tab (default: a fresh background tab; opt
|
|
17
|
+
* out via open_in_new_tab=false) to `url`.
|
|
18
|
+
*
|
|
19
|
+
* actions.get_accessibility_tree
|
|
20
|
+
* Inject the bundled accessibility-tree.js content script and return a
|
|
21
|
+
* structured snapshot of the visible interactive nodes, each with a
|
|
22
|
+
* stable {frame_id, document_epoch, ref_id} that subsequent
|
|
23
|
+
* click_ref/type_ref calls can target. Sensitive fields (password,
|
|
24
|
+
* one-time-code, credit-card autocomplete) are redacted in the page
|
|
25
|
+
* before the snapshot leaves it.
|
|
26
|
+
*
|
|
27
|
+
* actions.click_ref / actions.type_ref
|
|
28
|
+
* Act on a ref returned by get_accessibility_tree, in the same tab,
|
|
29
|
+
* using chrome.debugger Input.dispatchMouseEvent / dispatchKeyEvent /
|
|
30
|
+
* insertText. Refs become stale on navigation or DOM replacement; the
|
|
31
|
+
* extension surfaces a clear error and the caller re-snapshots.
|
|
32
|
+
*
|
|
33
|
+
* actions.wait_for_selector
|
|
34
|
+
* Poll the page for a CSS selector via Runtime.evaluate. Returns when
|
|
35
|
+
* it appears or rejects on timeout (default 10s).
|
|
36
|
+
*
|
|
37
|
+
* actions.screenshot
|
|
38
|
+
* Page.captureScreenshot. PNG, base64-encoded.
|
|
39
|
+
*
|
|
40
|
+
* actions.evaluate
|
|
41
|
+
* Runtime.evaluate(expression). Returns the JSON-serialised result.
|
|
42
|
+
* Last-resort escape hatch — prefer ref-based actions because the
|
|
43
|
+
* script string is harder to audit.
|
|
44
|
+
*
|
|
45
|
+
* The connector author writes a normal server-side sync() that sequences
|
|
46
|
+
* these actions through `ctx.chrome.<tool>(args)` (helper added in a
|
|
47
|
+
* later PR — for v1 the actions are reachable directly via the run
|
|
48
|
+
* scheduling API). No bespoke executor code lives in the extension; new
|
|
49
|
+
* connectors compose existing tools.
|
|
50
|
+
*
|
|
51
|
+
* URL allowlist: each connector that runs on top of this dispatcher
|
|
52
|
+
* declares `allowedOrigins` on its own definition. The extension refuses
|
|
53
|
+
* any tool call whose target URL is outside the allowlist.
|
|
54
|
+
*
|
|
55
|
+
* Required worker capability is `browser.debugger`.
|
|
56
|
+
*
|
|
57
|
+
* Cloud-side `sync()` / `execute()` throw — actual work happens in the
|
|
58
|
+
* extension's service worker.
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
import {
|
|
62
|
+
type ActionResult,
|
|
63
|
+
type ConnectorDefinition,
|
|
64
|
+
ConnectorRuntime,
|
|
65
|
+
type SyncContext,
|
|
66
|
+
type SyncResult,
|
|
67
|
+
} from '@lobu/connector-sdk';
|
|
68
|
+
|
|
69
|
+
const BRIDGE_ONLY =
|
|
70
|
+
'chrome runs only on a worker advertising capability "browser.debugger" (Owletto for Chrome).';
|
|
71
|
+
|
|
72
|
+
const tabIdSchema = {
|
|
73
|
+
type: 'integer',
|
|
74
|
+
description: 'Tab to act on. Defaults to the run-scoped scratch tab.',
|
|
75
|
+
} as const;
|
|
76
|
+
|
|
77
|
+
const refIdSchema = {
|
|
78
|
+
type: 'object',
|
|
79
|
+
required: ['document_epoch', 'ref_id'],
|
|
80
|
+
properties: {
|
|
81
|
+
document_epoch: { type: 'integer' },
|
|
82
|
+
ref_id: { type: 'integer' },
|
|
83
|
+
},
|
|
84
|
+
description:
|
|
85
|
+
'Element reference returned by a prior get_accessibility_tree call on the same tab + document. frame_id is reserved for future iframe support; v1 dispatches against the main frame.',
|
|
86
|
+
} as const;
|
|
87
|
+
|
|
88
|
+
export default class ChromeConnector extends ConnectorRuntime {
|
|
89
|
+
readonly definition: ConnectorDefinition = {
|
|
90
|
+
key: 'chrome',
|
|
91
|
+
name: 'Chrome',
|
|
92
|
+
description:
|
|
93
|
+
'Paired Chrome profile. Tab snapshots + a fixed set of typed browser actions (navigate, click, type, wait, screenshot, accessibility snapshot, evaluate) that connectors compose without shipping per-connector code into the extension.',
|
|
94
|
+
version: '0.2.0',
|
|
95
|
+
faviconDomain: 'google.com',
|
|
96
|
+
requiredCapability: 'browser.debugger',
|
|
97
|
+
runtime: { platforms: ['chrome-extension'] as unknown as ['macos'] },
|
|
98
|
+
authSchema: { methods: [{ type: 'none' }] },
|
|
99
|
+
feeds: {
|
|
100
|
+
open_tabs: {
|
|
101
|
+
key: 'open_tabs',
|
|
102
|
+
name: 'Open tabs',
|
|
103
|
+
description: 'Snapshot of the tabs currently open in this Chrome profile.',
|
|
104
|
+
configSchema: { type: 'object', properties: {} },
|
|
105
|
+
eventKinds: {
|
|
106
|
+
tab_snapshot: {
|
|
107
|
+
description: 'One row per tab observed in the active poll cycle.',
|
|
108
|
+
metadataSchema: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
required: ['source', 'origin_id', 'url'],
|
|
111
|
+
properties: {
|
|
112
|
+
source: { type: 'string', const: 'chrome_tabs' },
|
|
113
|
+
origin_id: { type: 'string' },
|
|
114
|
+
url: { type: 'string', format: 'uri' },
|
|
115
|
+
title: { type: 'string' },
|
|
116
|
+
window_id: { type: 'integer' },
|
|
117
|
+
active: { type: 'boolean' },
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
tab_events: {
|
|
124
|
+
key: 'tab_events',
|
|
125
|
+
name: 'Tab events',
|
|
126
|
+
description:
|
|
127
|
+
'Live stream of tab creates / closes / URL changes / focus changes. Each event has a timestamp, so this is the lossless "browsing timeline" companion to the open_tabs snapshot. No extra permission required (baseline `tabs`).',
|
|
128
|
+
configSchema: { type: 'object', properties: {} },
|
|
129
|
+
eventKinds: {
|
|
130
|
+
tab_event: {
|
|
131
|
+
description:
|
|
132
|
+
'One row per tab lifecycle event. event_type is one of: created, removed, updated, activated.',
|
|
133
|
+
metadataSchema: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
required: ['source', 'origin_id', 'event_type'],
|
|
136
|
+
properties: {
|
|
137
|
+
source: { type: 'string', const: 'chrome_tab_events' },
|
|
138
|
+
origin_id: { type: 'string' },
|
|
139
|
+
event_type: {
|
|
140
|
+
enum: ['created', 'removed', 'updated', 'activated'],
|
|
141
|
+
},
|
|
142
|
+
tab_id: { type: 'integer' },
|
|
143
|
+
url: { type: 'string' },
|
|
144
|
+
title: { type: 'string' },
|
|
145
|
+
window_id: { type: 'integer' },
|
|
146
|
+
from_url: {
|
|
147
|
+
type: 'string',
|
|
148
|
+
description: 'For updated events, the URL the tab was on before the change.',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
actions: {
|
|
157
|
+
navigate: {
|
|
158
|
+
key: 'navigate',
|
|
159
|
+
name: 'Navigate',
|
|
160
|
+
description: 'Open a URL in a fresh background tab (default) or an existing tab.',
|
|
161
|
+
requiresApproval: false,
|
|
162
|
+
inputSchema: {
|
|
163
|
+
type: 'object',
|
|
164
|
+
required: ['url'],
|
|
165
|
+
properties: {
|
|
166
|
+
url: { type: 'string', format: 'uri' },
|
|
167
|
+
tab_id: tabIdSchema,
|
|
168
|
+
open_in_new_tab: {
|
|
169
|
+
type: 'boolean',
|
|
170
|
+
description: 'Default true. Opt out for active-tab control.',
|
|
171
|
+
},
|
|
172
|
+
wait_for_load: {
|
|
173
|
+
type: 'boolean',
|
|
174
|
+
description:
|
|
175
|
+
'Wait for Page.frameStoppedLoading on the main frame before returning. Default true.',
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
outputSchema: {
|
|
180
|
+
type: 'object',
|
|
181
|
+
properties: {
|
|
182
|
+
tab_id: { type: 'integer' },
|
|
183
|
+
current_url: { type: 'string' },
|
|
184
|
+
title: { type: 'string' },
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
get_accessibility_tree: {
|
|
189
|
+
key: 'get_accessibility_tree',
|
|
190
|
+
name: 'Get accessibility tree',
|
|
191
|
+
description:
|
|
192
|
+
'Return a structured snapshot of the visible interactive elements on the page, with stable refs for click_ref/type_ref. Sensitive fields are redacted.',
|
|
193
|
+
requiresApproval: false,
|
|
194
|
+
inputSchema: {
|
|
195
|
+
type: 'object',
|
|
196
|
+
properties: {
|
|
197
|
+
tab_id: tabIdSchema,
|
|
198
|
+
filter: {
|
|
199
|
+
enum: ['interactive', 'visible', 'all'],
|
|
200
|
+
description: 'Default "interactive". "all" is for debugging only.',
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
outputSchema: {
|
|
205
|
+
type: 'object',
|
|
206
|
+
properties: {
|
|
207
|
+
document_epoch: { type: 'integer' },
|
|
208
|
+
current_url: { type: 'string' },
|
|
209
|
+
title: { type: 'string' },
|
|
210
|
+
tree: { type: 'array' },
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
click_ref: {
|
|
215
|
+
key: 'click_ref',
|
|
216
|
+
name: 'Click element by ref',
|
|
217
|
+
description:
|
|
218
|
+
'Dispatch a mouse click on the element identified by a ref from a prior accessibility snapshot of the same tab + document.',
|
|
219
|
+
requiresApproval: false,
|
|
220
|
+
inputSchema: {
|
|
221
|
+
type: 'object',
|
|
222
|
+
required: ['ref'],
|
|
223
|
+
properties: {
|
|
224
|
+
ref: refIdSchema,
|
|
225
|
+
tab_id: tabIdSchema,
|
|
226
|
+
button: {
|
|
227
|
+
enum: ['left', 'right', 'middle'],
|
|
228
|
+
description: 'Default "left".',
|
|
229
|
+
},
|
|
230
|
+
click_count: {
|
|
231
|
+
type: 'integer',
|
|
232
|
+
minimum: 1,
|
|
233
|
+
maximum: 3,
|
|
234
|
+
description: 'Default 1. Use 2 for double-click, 3 for triple-click.',
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
type_ref: {
|
|
240
|
+
key: 'type_ref',
|
|
241
|
+
name: 'Type into element by ref',
|
|
242
|
+
description:
|
|
243
|
+
'Focus the element identified by a ref and dispatch keystrokes to enter the given text. Existing value is replaced by default.',
|
|
244
|
+
requiresApproval: false,
|
|
245
|
+
inputSchema: {
|
|
246
|
+
type: 'object',
|
|
247
|
+
required: ['ref', 'text'],
|
|
248
|
+
properties: {
|
|
249
|
+
ref: refIdSchema,
|
|
250
|
+
tab_id: tabIdSchema,
|
|
251
|
+
text: { type: 'string' },
|
|
252
|
+
clear_first: {
|
|
253
|
+
type: 'boolean',
|
|
254
|
+
description: 'Default true. Selects all + deletes before typing.',
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
wait_for_selector: {
|
|
260
|
+
key: 'wait_for_selector',
|
|
261
|
+
name: 'Wait for selector',
|
|
262
|
+
description:
|
|
263
|
+
'Poll the page for the first match of a CSS selector and return when it appears.',
|
|
264
|
+
requiresApproval: false,
|
|
265
|
+
inputSchema: {
|
|
266
|
+
type: 'object',
|
|
267
|
+
required: ['selector'],
|
|
268
|
+
properties: {
|
|
269
|
+
selector: { type: 'string' },
|
|
270
|
+
tab_id: tabIdSchema,
|
|
271
|
+
timeout_ms: {
|
|
272
|
+
type: 'integer',
|
|
273
|
+
minimum: 100,
|
|
274
|
+
maximum: 60_000,
|
|
275
|
+
description: 'Default 10000.',
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
screenshot: {
|
|
281
|
+
key: 'screenshot',
|
|
282
|
+
name: 'Screenshot',
|
|
283
|
+
description: 'Capture the visible viewport as a PNG.',
|
|
284
|
+
requiresApproval: false,
|
|
285
|
+
inputSchema: {
|
|
286
|
+
type: 'object',
|
|
287
|
+
properties: {
|
|
288
|
+
tab_id: tabIdSchema,
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
outputSchema: {
|
|
292
|
+
type: 'object',
|
|
293
|
+
properties: {
|
|
294
|
+
data_url: {
|
|
295
|
+
type: 'string',
|
|
296
|
+
description: 'data:image/png;base64,... — caller decodes.',
|
|
297
|
+
},
|
|
298
|
+
width: { type: 'integer' },
|
|
299
|
+
height: { type: 'integer' },
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
close_tab: {
|
|
304
|
+
key: 'close_tab',
|
|
305
|
+
name: 'Close tab',
|
|
306
|
+
description:
|
|
307
|
+
'Close a tab the extension created for this connector. Required at the end of any multi-step session — tabs the extension owned across navigate / get_accessibility_tree / click_ref / etc. are NOT auto-disposed (that would break the natural flow). A reaper closes orphaned owned tabs after 30 minutes.',
|
|
308
|
+
requiresApproval: false,
|
|
309
|
+
inputSchema: {
|
|
310
|
+
type: 'object',
|
|
311
|
+
required: ['tab_id'],
|
|
312
|
+
properties: { tab_id: { type: 'integer' } },
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
evaluate: {
|
|
316
|
+
key: 'evaluate',
|
|
317
|
+
name: 'Evaluate JS',
|
|
318
|
+
description:
|
|
319
|
+
'Last-resort escape hatch: run a JS expression with Runtime.evaluate and return the JSON-serialised result. Prefer ref-based actions when possible — scripts are harder to audit.',
|
|
320
|
+
requiresApproval: false,
|
|
321
|
+
inputSchema: {
|
|
322
|
+
type: 'object',
|
|
323
|
+
required: ['expression'],
|
|
324
|
+
properties: {
|
|
325
|
+
expression: { type: 'string' },
|
|
326
|
+
tab_id: tabIdSchema,
|
|
327
|
+
await_promise: {
|
|
328
|
+
type: 'boolean',
|
|
329
|
+
description: 'Default true.',
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
outputSchema: {
|
|
334
|
+
type: 'object',
|
|
335
|
+
properties: {
|
|
336
|
+
value: {},
|
|
337
|
+
exception: { type: 'string' },
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
async sync(_ctx: SyncContext): Promise<SyncResult> {
|
|
345
|
+
throw new Error(BRIDGE_ONLY);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async execute(): Promise<ActionResult> {
|
|
349
|
+
throw new Error(BRIDGE_ONLY);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome Bookmarks Connector — Owletto for Chrome only.
|
|
3
|
+
*
|
|
4
|
+
* Opt-in ambient feed. The Chrome extension advertises capability
|
|
5
|
+
* `browser.bookmarks` when the user grants the `bookmarks` Chrome
|
|
6
|
+
* permission via the sidepanel Permissions panel; the gateway then
|
|
7
|
+
* auto-wires this connector.
|
|
8
|
+
*
|
|
9
|
+
* Emits one event per bookmark (with its folder path). Backfills the
|
|
10
|
+
* full tree on first sync, then streams `bookmarks.onCreated/onRemoved/
|
|
11
|
+
* onChanged/onMoved` thereafter.
|
|
12
|
+
*
|
|
13
|
+
* Cloud-side `sync()` / `execute()` throw — actual work happens in
|
|
14
|
+
* apps/chrome/feeds-bookmarks.js in the extension.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
type ActionResult,
|
|
19
|
+
type ConnectorDefinition,
|
|
20
|
+
ConnectorRuntime,
|
|
21
|
+
type SyncContext,
|
|
22
|
+
type SyncResult,
|
|
23
|
+
} from '@lobu/connector-sdk';
|
|
24
|
+
|
|
25
|
+
const BRIDGE_ONLY =
|
|
26
|
+
'chrome.bookmarks runs only on a worker advertising capability "browser.bookmarks" (Owletto for Chrome with bookmarks permission granted).';
|
|
27
|
+
|
|
28
|
+
export default class ChromeBookmarksConnector extends ConnectorRuntime {
|
|
29
|
+
readonly definition: ConnectorDefinition = {
|
|
30
|
+
key: 'chrome.bookmarks',
|
|
31
|
+
name: 'Chrome bookmarks',
|
|
32
|
+
description:
|
|
33
|
+
"Bookmarks (and folder structure) from the paired Chrome profile. Opt-in — requires the user to grant the extension's optional `bookmarks` permission.",
|
|
34
|
+
version: '0.1.0',
|
|
35
|
+
faviconDomain: 'google.com',
|
|
36
|
+
requiredCapability: 'browser.bookmarks',
|
|
37
|
+
runtime: { platforms: ['chrome-extension'] as unknown as ['macos'] },
|
|
38
|
+
authSchema: { methods: [{ type: 'none' }] },
|
|
39
|
+
feeds: {
|
|
40
|
+
bookmarks: {
|
|
41
|
+
key: 'bookmarks',
|
|
42
|
+
name: 'Bookmarks',
|
|
43
|
+
description:
|
|
44
|
+
'One event per bookmark. Backfills the full tree on first sync via chrome.bookmarks.getTree, then streams onCreated / onRemoved / onChanged / onMoved.',
|
|
45
|
+
configSchema: { type: 'object', properties: {} },
|
|
46
|
+
eventKinds: {
|
|
47
|
+
bookmark: {
|
|
48
|
+
description: 'One row per bookmark add/remove/edit.',
|
|
49
|
+
metadataSchema: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
required: ['source', 'origin_id', 'event_type'],
|
|
52
|
+
properties: {
|
|
53
|
+
source: { type: 'string', const: 'chrome_bookmarks' },
|
|
54
|
+
origin_id: { type: 'string' },
|
|
55
|
+
event_type: {
|
|
56
|
+
enum: ['created', 'removed', 'changed', 'moved'],
|
|
57
|
+
},
|
|
58
|
+
bookmark_id: { type: 'string' },
|
|
59
|
+
title: { type: 'string' },
|
|
60
|
+
url: { type: 'string' },
|
|
61
|
+
parent_folder_id: { type: 'string' },
|
|
62
|
+
parent_folder_path: { type: 'string' },
|
|
63
|
+
date_added: { type: 'string', format: 'date-time' },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
async sync(_ctx: SyncContext): Promise<SyncResult> {
|
|
73
|
+
throw new Error(BRIDGE_ONLY);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async execute(): Promise<ActionResult> {
|
|
77
|
+
throw new Error(BRIDGE_ONLY);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome Downloads Connector — Owletto for Chrome only.
|
|
3
|
+
*
|
|
4
|
+
* Opt-in ambient feed. The Chrome extension advertises capability
|
|
5
|
+
* `browser.downloads` when the user grants the `downloads` Chrome
|
|
6
|
+
* permission via the sidepanel Permissions panel; the gateway then
|
|
7
|
+
* auto-wires this connector.
|
|
8
|
+
*
|
|
9
|
+
* Emits one event per download (filename, source URL, mime type, size,
|
|
10
|
+
* finish time). Backfills recent downloads on first sync via
|
|
11
|
+
* chrome.downloads.search({}), then streams chrome.downloads.onCreated /
|
|
12
|
+
* onChanged.
|
|
13
|
+
*
|
|
14
|
+
* Cloud-side `sync()` / `execute()` throw — actual work happens in
|
|
15
|
+
* apps/chrome/feeds-downloads.js in the extension.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
type ActionResult,
|
|
20
|
+
type ConnectorDefinition,
|
|
21
|
+
ConnectorRuntime,
|
|
22
|
+
type SyncContext,
|
|
23
|
+
type SyncResult,
|
|
24
|
+
} from '@lobu/connector-sdk';
|
|
25
|
+
|
|
26
|
+
const BRIDGE_ONLY =
|
|
27
|
+
'chrome.downloads runs only on a worker advertising capability "browser.downloads" (Owletto for Chrome with downloads permission granted).';
|
|
28
|
+
|
|
29
|
+
export default class ChromeDownloadsConnector extends ConnectorRuntime {
|
|
30
|
+
readonly definition: ConnectorDefinition = {
|
|
31
|
+
key: 'chrome.downloads',
|
|
32
|
+
name: 'Chrome downloads',
|
|
33
|
+
description:
|
|
34
|
+
"Files downloaded in the paired Chrome profile, with their source URLs. Opt-in — requires the user to grant the extension's optional `downloads` permission.",
|
|
35
|
+
version: '0.1.0',
|
|
36
|
+
faviconDomain: 'google.com',
|
|
37
|
+
requiredCapability: 'browser.downloads',
|
|
38
|
+
runtime: { platforms: ['chrome-extension'] as unknown as ['macos'] },
|
|
39
|
+
authSchema: { methods: [{ type: 'none' }] },
|
|
40
|
+
feeds: {
|
|
41
|
+
downloads: {
|
|
42
|
+
key: 'downloads',
|
|
43
|
+
name: 'Downloads',
|
|
44
|
+
description:
|
|
45
|
+
'One event per download. Backfills via chrome.downloads.search({}), then streams onCreated / onChanged (state=complete).',
|
|
46
|
+
configSchema: { type: 'object', properties: {} },
|
|
47
|
+
eventKinds: {
|
|
48
|
+
download: {
|
|
49
|
+
description: 'One row per file the user downloaded.',
|
|
50
|
+
metadataSchema: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
required: ['source', 'origin_id'],
|
|
53
|
+
properties: {
|
|
54
|
+
source: { type: 'string', const: 'chrome_downloads' },
|
|
55
|
+
origin_id: { type: 'string' },
|
|
56
|
+
download_id: { type: 'integer' },
|
|
57
|
+
filename: { type: 'string' },
|
|
58
|
+
source_url: { type: 'string', format: 'uri' },
|
|
59
|
+
referrer: { type: 'string' },
|
|
60
|
+
mime: { type: 'string' },
|
|
61
|
+
bytes: { type: 'integer' },
|
|
62
|
+
started_at: { type: 'string', format: 'date-time' },
|
|
63
|
+
finished_at: { type: 'string', format: 'date-time' },
|
|
64
|
+
state: { type: 'string' },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
async sync(_ctx: SyncContext): Promise<SyncResult> {
|
|
74
|
+
throw new Error(BRIDGE_ONLY);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async execute(): Promise<ActionResult> {
|
|
78
|
+
throw new Error(BRIDGE_ONLY);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome History Connector — Owletto for Chrome only.
|
|
3
|
+
*
|
|
4
|
+
* Opt-in ambient feed. The Chrome extension advertises capability
|
|
5
|
+
* `browser.history` when the user grants the `history` Chrome permission
|
|
6
|
+
* via the sidepanel Permissions panel; the gateway then auto-wires this
|
|
7
|
+
* connector to the paired chrome-extension device.
|
|
8
|
+
*
|
|
9
|
+
* Emits one event per page load (URL, title, visit time, transition type).
|
|
10
|
+
* The backfill feed seeds with up to 90 days of history on first sync; the
|
|
11
|
+
* live feed streams `history.onVisited` thereafter.
|
|
12
|
+
*
|
|
13
|
+
* Cloud-side `sync()` / `execute()` throw — actual work happens in
|
|
14
|
+
* apps/chrome/feeds-history.js in the extension.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
type ActionResult,
|
|
19
|
+
type ConnectorDefinition,
|
|
20
|
+
ConnectorRuntime,
|
|
21
|
+
type SyncContext,
|
|
22
|
+
type SyncResult,
|
|
23
|
+
} from '@lobu/connector-sdk';
|
|
24
|
+
|
|
25
|
+
const BRIDGE_ONLY =
|
|
26
|
+
'chrome.history runs only on a worker advertising capability "browser.history" (Owletto for Chrome with history permission granted).';
|
|
27
|
+
|
|
28
|
+
export default class ChromeHistoryConnector extends ConnectorRuntime {
|
|
29
|
+
readonly definition: ConnectorDefinition = {
|
|
30
|
+
key: 'chrome.history',
|
|
31
|
+
name: 'Chrome history',
|
|
32
|
+
description:
|
|
33
|
+
"Every page the user visits in their paired Chrome profile, with visit time + transition type. Opt-in — requires the user to grant the extension's optional `history` permission.",
|
|
34
|
+
version: '0.1.0',
|
|
35
|
+
faviconDomain: 'google.com',
|
|
36
|
+
requiredCapability: 'browser.history',
|
|
37
|
+
runtime: { platforms: ['chrome-extension'] as unknown as ['macos'] },
|
|
38
|
+
authSchema: { methods: [{ type: 'none' }] },
|
|
39
|
+
feeds: {
|
|
40
|
+
visits: {
|
|
41
|
+
key: 'visits',
|
|
42
|
+
name: 'Visits',
|
|
43
|
+
description:
|
|
44
|
+
'One event per page visit. Backfills ~90 days on first sync (chrome.history.search), then streams new visits via the chrome.history.onVisited listener.',
|
|
45
|
+
configSchema: { type: 'object', properties: {} },
|
|
46
|
+
eventKinds: {
|
|
47
|
+
page_visit: {
|
|
48
|
+
description: 'One row per visit observed.',
|
|
49
|
+
metadataSchema: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
required: ['source', 'origin_id', 'url', 'visit_time'],
|
|
52
|
+
properties: {
|
|
53
|
+
source: { type: 'string', const: 'chrome_history' },
|
|
54
|
+
origin_id: { type: 'string' },
|
|
55
|
+
url: { type: 'string', format: 'uri' },
|
|
56
|
+
title: { type: 'string' },
|
|
57
|
+
visit_time: { type: 'string', format: 'date-time' },
|
|
58
|
+
transition_type: {
|
|
59
|
+
description:
|
|
60
|
+
'How the user got to the page: link, typed, auto_bookmark, auto_subframe, manual_subframe, generated, start_page, form_submit, reload, keyword, keyword_generated.',
|
|
61
|
+
type: 'string',
|
|
62
|
+
},
|
|
63
|
+
visit_id: { type: 'integer' },
|
|
64
|
+
visit_count: { type: 'integer' },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
async sync(_ctx: SyncContext): Promise<SyncResult> {
|
|
74
|
+
throw new Error(BRIDGE_ONLY);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async execute(): Promise<ActionResult> {
|
|
78
|
+
throw new Error(BRIDGE_ONLY);
|
|
79
|
+
}
|
|
80
|
+
}
|