@jobshimo/browser-link 0.17.0 → 0.18.1
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/agent-instructions/content.js +20 -1
- package/dist/agent-instructions/content.js.map +1 -1
- package/dist/commands/about.js +2 -0
- package/dist/commands/about.js.map +1 -1
- package/dist/extension/background.js +13 -3
- package/dist/extension/background.js.map +1 -1
- package/dist/extension/flow.d.ts +13 -0
- package/dist/extension/flow.js.map +1 -1
- package/dist/extension/inpage/builders.d.ts +56 -2
- package/dist/extension/inpage/builders.js +201 -4
- package/dist/extension/inpage/builders.js.map +1 -1
- package/dist/extension/manifest.json +1 -1
- package/dist/map/db.js +26 -0
- package/dist/map/db.js.map +1 -1
- package/dist/map/queries.d.ts +29 -0
- package/dist/map/queries.js +44 -2
- package/dist/map/queries.js.map +1 -1
- package/dist/map/tools.js +71 -8
- package/dist/map/tools.js.map +1 -1
- package/dist/permissions.js +6 -0
- package/dist/permissions.js.map +1 -1
- package/dist/tools/browser-definitions.js +30 -1
- package/dist/tools/browser-definitions.js.map +1 -1
- package/dist/tools/browser-dispatch.d.ts +37 -0
- package/dist/tools/browser-dispatch.js +11 -1
- package/dist/tools/browser-dispatch.js.map +1 -1
- package/dist/tools/server-instructions.js +28 -3
- package/dist/tools/server-instructions.js.map +1 -1
- package/package.json +1 -1
|
@@ -121,6 +121,7 @@ export function buildFindJs(opts) {
|
|
|
121
121
|
role: typeof opts.role === 'string' && opts.role.length > 0 ? opts.role : null,
|
|
122
122
|
exact: opts.exact === true,
|
|
123
123
|
candidateLimit: 5,
|
|
124
|
+
nearMissLimit: 3,
|
|
124
125
|
});
|
|
125
126
|
return `
|
|
126
127
|
(() => {
|
|
@@ -128,6 +129,7 @@ export function buildFindJs(opts) {
|
|
|
128
129
|
${DOM_HELPERS_JS}
|
|
129
130
|
const opts = ${optsJson};
|
|
130
131
|
const needle = opts.text.toLowerCase();
|
|
132
|
+
const needleTokens = needle.split(/[^a-z0-9]+/).filter(Boolean);
|
|
131
133
|
const ROLE_SELECTORS = {
|
|
132
134
|
button: 'button, [role="button"], input[type="button"], input[type="submit"], input[type="reset"]',
|
|
133
135
|
link: 'a[href], [role="link"]',
|
|
@@ -136,9 +138,10 @@ export function buildFindJs(opts) {
|
|
|
136
138
|
tab: '[role="tab"]',
|
|
137
139
|
menuitem: '[role="menuitem"]',
|
|
138
140
|
};
|
|
141
|
+
const BROAD_SELECTOR = 'button, a, input, textarea, select, [role], [onclick], [contenteditable="true"], [tabindex]';
|
|
139
142
|
const selectorSet = opts.role && ROLE_SELECTORS[opts.role]
|
|
140
143
|
? ROLE_SELECTORS[opts.role]
|
|
141
|
-
:
|
|
144
|
+
: BROAD_SELECTOR;
|
|
142
145
|
const all = deepQueryAll(selectorSet);
|
|
143
146
|
const matches = [];
|
|
144
147
|
for (const el of all) {
|
|
@@ -150,7 +153,79 @@ export function buildFindJs(opts) {
|
|
|
150
153
|
if (ok) matches.push(el);
|
|
151
154
|
}
|
|
152
155
|
if (matches.length === 0) {
|
|
153
|
-
|
|
156
|
+
// Near-miss ranking: containment beats token overlap; anything scoring
|
|
157
|
+
// 0 is dropped so the suggestion list never pads itself with noise.
|
|
158
|
+
function snippetOf(el) {
|
|
159
|
+
const t = accessibleText(el).trim();
|
|
160
|
+
return t.length > 60 ? t.slice(0, 60) + '...' : t;
|
|
161
|
+
}
|
|
162
|
+
function scoreText(text) {
|
|
163
|
+
const lower = text.toLowerCase();
|
|
164
|
+
const contains = lower.includes(needle);
|
|
165
|
+
const candTokens = lower.split(/[^a-z0-9]+/).filter(Boolean);
|
|
166
|
+
let overlap = 0;
|
|
167
|
+
for (let i = 0; i < needleTokens.length; i++) {
|
|
168
|
+
if (candTokens.indexOf(needleTokens[i]) !== -1) overlap++;
|
|
169
|
+
}
|
|
170
|
+
if (!contains && overlap === 0) return -1;
|
|
171
|
+
return (contains ? 1000 : 0) + overlap * 10;
|
|
172
|
+
}
|
|
173
|
+
function rankCandidates(elements) {
|
|
174
|
+
const scored = [];
|
|
175
|
+
for (const el of elements) {
|
|
176
|
+
if (!isVisible(el)) continue;
|
|
177
|
+
const text = accessibleText(el).trim();
|
|
178
|
+
if (text.length === 0) continue;
|
|
179
|
+
const score = scoreText(text);
|
|
180
|
+
if (score < 0) continue;
|
|
181
|
+
scored.push({ el: el, score: score });
|
|
182
|
+
}
|
|
183
|
+
scored.sort((a, b) => b.score - a.score);
|
|
184
|
+
return scored.slice(0, opts.nearMissLimit).map((s) => s.el);
|
|
185
|
+
}
|
|
186
|
+
function toNearMiss(el) {
|
|
187
|
+
const entry = { text: snippetOf(el), selector: genSelector(el) };
|
|
188
|
+
const roleAttr = el.getAttribute('role');
|
|
189
|
+
if (roleAttr) entry.role = roleAttr;
|
|
190
|
+
return entry;
|
|
191
|
+
}
|
|
192
|
+
function describeForError(el) {
|
|
193
|
+
const tag = el.tagName.toLowerCase();
|
|
194
|
+
if (el.hasAttribute('onclick')) return '<' + tag + ' onclick>';
|
|
195
|
+
const role = el.getAttribute('role');
|
|
196
|
+
if (role) return '<' + tag + ' role="' + role + '">';
|
|
197
|
+
return '<' + tag + '>';
|
|
198
|
+
}
|
|
199
|
+
const result = { matched: false, reason: 'not-found' };
|
|
200
|
+
if (opts.role) {
|
|
201
|
+
// Role narrowed the scan — check whether the text exists OUTSIDE the
|
|
202
|
+
// role filter so the error can name the exclusion explicitly instead
|
|
203
|
+
// of reporting a bare not-found.
|
|
204
|
+
const broadAll = deepQueryAll(BROAD_SELECTOR);
|
|
205
|
+
const broadTextMatches = [];
|
|
206
|
+
for (const el of broadAll) {
|
|
207
|
+
if (!isVisible(el)) continue;
|
|
208
|
+
const text = accessibleText(el).trim();
|
|
209
|
+
if (text.length === 0) continue;
|
|
210
|
+
if (text.toLowerCase().includes(needle)) broadTextMatches.push(el);
|
|
211
|
+
}
|
|
212
|
+
if (broadTextMatches.length > 0) {
|
|
213
|
+
const ranked = rankCandidates(broadTextMatches);
|
|
214
|
+
const closest = ranked[0];
|
|
215
|
+
result.error = 'text matched ' + broadTextMatches.length + ' element' +
|
|
216
|
+
(broadTextMatches.length === 1 ? '' : 's') +
|
|
217
|
+
' but none with role "' + opts.role + '"' +
|
|
218
|
+
(closest ? ' — closest: ' + describeForError(closest) + ' "' + snippetOf(closest) + '"' : '');
|
|
219
|
+
result.near_misses = ranked.map(toNearMiss);
|
|
220
|
+
return result;
|
|
221
|
+
}
|
|
222
|
+
const ranked = rankCandidates(broadAll);
|
|
223
|
+
if (ranked.length > 0) result.near_misses = ranked.map(toNearMiss);
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
const ranked = rankCandidates(all);
|
|
227
|
+
if (ranked.length > 0) result.near_misses = ranked.map(toNearMiss);
|
|
228
|
+
return result;
|
|
154
229
|
}
|
|
155
230
|
if (matches.length > 1) {
|
|
156
231
|
return {
|
|
@@ -189,6 +264,11 @@ export function buildClickResolveJs(opts) {
|
|
|
189
264
|
(() => {
|
|
190
265
|
${DEEP_QUERY_JS}
|
|
191
266
|
const opts = ${optsJson};
|
|
267
|
+
try {
|
|
268
|
+
document.querySelector(opts.selector);
|
|
269
|
+
} catch (e) {
|
|
270
|
+
return { ok: false, reason: 'invalid-selector', error: e && e.message ? e.message : String(e) };
|
|
271
|
+
}
|
|
192
272
|
const el = deepQueryFirst(opts.selector);
|
|
193
273
|
if (!el) return { ok: false, reason: 'not-found' };
|
|
194
274
|
const chain = frameElementChain(el);
|
|
@@ -214,11 +294,16 @@ export function buildTypeResolveJs(opts) {
|
|
|
214
294
|
(() => {
|
|
215
295
|
${DEEP_QUERY_JS}
|
|
216
296
|
const opts = ${optsJson};
|
|
297
|
+
try {
|
|
298
|
+
document.querySelector(opts.selector);
|
|
299
|
+
} catch (e) {
|
|
300
|
+
return { ok: false, reason: 'invalid-selector', error: e && e.message ? e.message : String(e) };
|
|
301
|
+
}
|
|
217
302
|
const el = deepQueryFirst(opts.selector);
|
|
218
|
-
if (!el) return false;
|
|
303
|
+
if (!el) return { ok: false, reason: 'not-found' };
|
|
219
304
|
el.focus();
|
|
220
305
|
${opts.clear ? "if ('value' in el) { el.value = ''; el.dispatchEvent(new Event('input', { bubbles: true })); }" : ''}
|
|
221
|
-
return true;
|
|
306
|
+
return { ok: true };
|
|
222
307
|
})()`;
|
|
223
308
|
}
|
|
224
309
|
export function buildFocusJs(opts) {
|
|
@@ -287,4 +372,116 @@ export function buildSettleJs(opts) {
|
|
|
287
372
|
});
|
|
288
373
|
})()`;
|
|
289
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* Build the `browser.state` expression: a compact orientation snapshot —
|
|
377
|
+
* current url/title, the deep-resolved focused element, visible dialog-role
|
|
378
|
+
* elements, scroll position, and viewport size. Cheaper than a full
|
|
379
|
+
* `browser.snapshot` when the agent only needs "where am I right now".
|
|
380
|
+
*
|
|
381
|
+
* `focused` descends through `document.activeElement` past shadow-root and
|
|
382
|
+
* same-origin-iframe boundaries to the real innermost focused element (a
|
|
383
|
+
* shadow host or an <iframe> being "active" at one level is not the actual
|
|
384
|
+
* focus target the agent cares about). Its `selector` goes through the same
|
|
385
|
+
* `genSelectorInfo` uniqueness check `snapshot`/`find` use, so it carries
|
|
386
|
+
* `ambiguous: true` under the same structurally-identical-twins condition.
|
|
387
|
+
*
|
|
388
|
+
* `dialogs` matches visible `[role=dialog]`, `[role=alertdialog]` and open
|
|
389
|
+
* `<dialog>` elements found via the same deep walk, with a best-effort
|
|
390
|
+
* `label` resolved from aria-label / aria-labelledby / the first heading
|
|
391
|
+
* inside the dialog.
|
|
392
|
+
*
|
|
393
|
+
* Every optional field is omitted when there is nothing to report: no
|
|
394
|
+
* `focused` beyond `<body>`, no `dialogs` when none are open, and no
|
|
395
|
+
* `scroll` when the page is at its default (0,0) position — token-lean by
|
|
396
|
+
* construction, matching the omit-falsy convention the other builders use.
|
|
397
|
+
*/
|
|
398
|
+
export function buildStateJs() {
|
|
399
|
+
return `
|
|
400
|
+
(() => {
|
|
401
|
+
${DEEP_QUERY_JS}
|
|
402
|
+
${DOM_HELPERS_JS}
|
|
403
|
+
function deepActiveElement() {
|
|
404
|
+
let el = document.activeElement;
|
|
405
|
+
let guard = 0;
|
|
406
|
+
while (el && guard < 20) {
|
|
407
|
+
guard++;
|
|
408
|
+
if (el.shadowRoot && el.shadowRoot.activeElement) {
|
|
409
|
+
el = el.shadowRoot.activeElement;
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
if (el.tagName === 'IFRAME') {
|
|
413
|
+
let innerDoc = null;
|
|
414
|
+
try {
|
|
415
|
+
innerDoc = el.contentDocument;
|
|
416
|
+
} catch (_) {
|
|
417
|
+
innerDoc = null;
|
|
418
|
+
}
|
|
419
|
+
if (innerDoc && innerDoc.activeElement && innerDoc.activeElement !== innerDoc.body) {
|
|
420
|
+
el = innerDoc.activeElement;
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
return el;
|
|
427
|
+
}
|
|
428
|
+
function dialogLabel(el) {
|
|
429
|
+
const aria = el.getAttribute('aria-label');
|
|
430
|
+
if (aria) return aria;
|
|
431
|
+
const labelledby = el.getAttribute('aria-labelledby');
|
|
432
|
+
if (labelledby) {
|
|
433
|
+
const ids = labelledby.split(/\\s+/);
|
|
434
|
+
const parts = [];
|
|
435
|
+
for (let i = 0; i < ids.length; i++) {
|
|
436
|
+
if (!ids[i]) continue;
|
|
437
|
+
const target = deepQueryFirst('#' + CSS.escape(ids[i]));
|
|
438
|
+
if (target) {
|
|
439
|
+
const t = shortText(target);
|
|
440
|
+
if (t) parts.push(t);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
const joined = parts.join(' ').trim();
|
|
444
|
+
if (joined) return joined;
|
|
445
|
+
}
|
|
446
|
+
const heading = deepQueryFirst('h1, h2, h3', el);
|
|
447
|
+
if (heading) {
|
|
448
|
+
const t = shortText(heading);
|
|
449
|
+
if (t) return t;
|
|
450
|
+
}
|
|
451
|
+
return '';
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const result = {
|
|
455
|
+
url: location.href,
|
|
456
|
+
title: document.title,
|
|
457
|
+
viewport: { w: window.innerWidth, h: window.innerHeight },
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const active = deepActiveElement();
|
|
461
|
+
if (active && active !== document.body && active.tagName) {
|
|
462
|
+
const selInfo = genSelectorInfo(active);
|
|
463
|
+
const focused = { selector: selInfo.selector, tag: active.tagName.toLowerCase() };
|
|
464
|
+
if (selInfo.ambiguous) focused.ambiguous = true;
|
|
465
|
+
result.focused = focused;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const dialogs = [];
|
|
469
|
+
deepQueryAll('[role="dialog"], [role="alertdialog"], dialog[open]').forEach((el) => {
|
|
470
|
+
if (!isVisible(el)) return;
|
|
471
|
+
const role = el.getAttribute('role') || 'dialog';
|
|
472
|
+
const entry = { selector: genSelector(el), role: role };
|
|
473
|
+
const label = dialogLabel(el);
|
|
474
|
+
if (label) entry.label = label;
|
|
475
|
+
dialogs.push(entry);
|
|
476
|
+
});
|
|
477
|
+
if (dialogs.length > 0) result.dialogs = dialogs;
|
|
478
|
+
|
|
479
|
+
const scrollX = Math.round(window.scrollX);
|
|
480
|
+
const scrollY = Math.round(window.scrollY);
|
|
481
|
+
if (scrollX !== 0 || scrollY !== 0) result.scroll = { x: scrollX, y: scrollY };
|
|
482
|
+
|
|
483
|
+
return result;
|
|
484
|
+
})()
|
|
485
|
+
`;
|
|
486
|
+
}
|
|
290
487
|
//# sourceMappingURL=builders.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builders.js","sourceRoot":"","sources":["../../src/inpage/builders.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAwBlD,MAAM,UAAU,eAAe,CAAC,OAAqB,EAAE;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;QAC9B,cAAc,EAAE,OAAO,IAAI,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI;QACtF,eAAe,EAAE,IAAI,CAAC,gBAAgB,KAAK,IAAI;QAC/C,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;QACxD,cAAc,EACZ,OAAO,IAAI,CAAC,eAAe,KAAK,QAAQ,IAAI,IAAI,CAAC,eAAe,GAAG,CAAC;YAClE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,CAAC;YACrC,CAAC,CAAC,GAAG;KACV,CAAC,CAAC;IACH,OAAO;;IAEL,aAAa;IACb,cAAc;iBACD,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4FxB,CAAC;AACF,CAAC;
|
|
1
|
+
{"version":3,"file":"builders.js","sourceRoot":"","sources":["../../src/inpage/builders.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "browser-link",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.18.1",
|
|
5
5
|
"description": "Bridge between Chrome and an MCP client (Claude Code, OpenCode, GitHub Copilot CLI, …). Per-tab manual activation.",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"debugger",
|
package/dist/map/db.js
CHANGED
|
@@ -71,6 +71,32 @@ function runMigrations(db) {
|
|
|
71
71
|
|
|
72
72
|
CREATE INDEX IF NOT EXISTS idx_entries_lookup ON entries(app_id, url_pattern);
|
|
73
73
|
`);
|
|
74
|
+
// v0.18.0: named, replayable browser.flow recipes per app. Deliberately a
|
|
75
|
+
// separate table from `entries` — the pre-existing `entries.kind='flow'`
|
|
76
|
+
// is a free-form payload with no shape guarantee, while a row here always
|
|
77
|
+
// holds a `steps_json` array validated against the EXACT browser.flow step
|
|
78
|
+
// grammar (see `validateFlowSteps` in tools/browser-dispatch.ts, reused by
|
|
79
|
+
// map/tools.ts). Not scoped by url_pattern like entries — a flow recipe
|
|
80
|
+
// is identified by (app, name) only, since a multi-step recipe is not
|
|
81
|
+
// tied to a single route the way a selector or gotcha usually is.
|
|
82
|
+
// Same idempotent CREATE-TABLE-IF-NOT-EXISTS pattern as above, so an
|
|
83
|
+
// existing DB from before this table existed picks it up on next getDb()
|
|
84
|
+
// without touching any pre-existing row.
|
|
85
|
+
db.exec(`
|
|
86
|
+
CREATE TABLE IF NOT EXISTS flows (
|
|
87
|
+
id INTEGER PRIMARY KEY,
|
|
88
|
+
app_id INTEGER NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
|
|
89
|
+
name TEXT NOT NULL,
|
|
90
|
+
description TEXT,
|
|
91
|
+
steps_json TEXT NOT NULL,
|
|
92
|
+
created_at TEXT NOT NULL,
|
|
93
|
+
updated_at TEXT NOT NULL,
|
|
94
|
+
use_count INTEGER NOT NULL DEFAULT 0,
|
|
95
|
+
UNIQUE(app_id, name)
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
CREATE INDEX IF NOT EXISTS idx_flows_app ON flows(app_id);
|
|
99
|
+
`);
|
|
74
100
|
}
|
|
75
101
|
export function closeDb() {
|
|
76
102
|
if (dbInstance) {
|
package/dist/map/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/map/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,IAAI,UAAU,GAA6B,IAAI,CAAC;AAEhD,MAAM,UAAU,KAAK;IACnB,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAClC,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;IACzB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,eAAe,CAAC,IAAI,CAAC,CAAC;IACtB,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9B,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/B,aAAa,CAAC,EAAE,CAAC,CAAC;IAClB,UAAU,GAAG,EAAE,CAAC;IAChB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,eAAe,CAAC,UAAkB;IACzC,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7F,OAAO;IACT,CAAC;IACD,IAAI,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO;IACnC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;IAC1D,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO;IAChC,IAAI,CAAC;QACH,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;IAC3D,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,EAAqB;IAC1C,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BP,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,IAAI,UAAU,EAAE,CAAC;QACf,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/map/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,IAAI,UAAU,GAA6B,IAAI,CAAC;AAEhD,MAAM,UAAU,KAAK;IACnB,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAClC,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;IACzB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,eAAe,CAAC,IAAI,CAAC,CAAC;IACtB,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9B,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/B,aAAa,CAAC,EAAE,CAAC,CAAC;IAClB,UAAU,GAAG,EAAE,CAAC;IAChB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,eAAe,CAAC,UAAkB;IACzC,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7F,OAAO;IACT,CAAC;IACD,IAAI,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO;IACnC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;IAC1D,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO;IAChC,IAAI,CAAC;QACH,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;IAC3D,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,EAAqB;IAC1C,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BP,CAAC,CAAC;IAEH,0EAA0E;IAC1E,yEAAyE;IACzE,0EAA0E;IAC1E,2EAA2E;IAC3E,2EAA2E;IAC3E,wEAAwE;IACxE,sEAAsE;IACtE,kEAAkE;IAClE,qEAAqE;IACrE,yEAAyE;IACzE,yCAAyC;IACzC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;GAcP,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,IAAI,UAAU,EAAE,CAAC;QACf,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;AACH,CAAC"}
|
package/dist/map/queries.d.ts
CHANGED
|
@@ -50,6 +50,7 @@ export interface RecallInput {
|
|
|
50
50
|
export interface RecallResult {
|
|
51
51
|
app: AppRow | null;
|
|
52
52
|
entries: EntryRow[];
|
|
53
|
+
flows: FlowRow[];
|
|
53
54
|
}
|
|
54
55
|
export declare function recall(input: RecallInput): RecallResult;
|
|
55
56
|
export interface RecordUseInput {
|
|
@@ -70,3 +71,31 @@ export interface ForgetResult {
|
|
|
70
71
|
export declare function forget(input: ForgetInput): ForgetResult;
|
|
71
72
|
export declare function renameApp(app_id: number, new_app_key: string): AppRow | null;
|
|
72
73
|
export declare function listApps(): AppRow[];
|
|
74
|
+
export interface FlowRow {
|
|
75
|
+
id: number;
|
|
76
|
+
app_id: number;
|
|
77
|
+
name: string;
|
|
78
|
+
description: string | null;
|
|
79
|
+
steps: unknown;
|
|
80
|
+
created_at: string;
|
|
81
|
+
updated_at: string;
|
|
82
|
+
use_count: number;
|
|
83
|
+
}
|
|
84
|
+
export interface SaveFlowInput {
|
|
85
|
+
origin: string;
|
|
86
|
+
app_key?: string | null;
|
|
87
|
+
title?: string | null;
|
|
88
|
+
name: string;
|
|
89
|
+
description?: string | null;
|
|
90
|
+
steps: unknown;
|
|
91
|
+
}
|
|
92
|
+
/** Upsert on (app, name) — saving an existing name replaces its
|
|
93
|
+
* description/steps and bumps updated_at, mirroring saveEntry's upsert
|
|
94
|
+
* semantics. `use_count` is left untouched on update (re-saving is not a
|
|
95
|
+
* "use"). */
|
|
96
|
+
export declare function saveFlow(input: SaveFlowInput): {
|
|
97
|
+
app: AppRow;
|
|
98
|
+
flow: FlowRow;
|
|
99
|
+
};
|
|
100
|
+
/** Every flow recipe saved for an app, alphabetical by name. */
|
|
101
|
+
export declare function listFlows(app_id: number): FlowRow[];
|
package/dist/map/queries.js
CHANGED
|
@@ -99,7 +99,7 @@ export function recall(input) {
|
|
|
99
99
|
.get(input.origin);
|
|
100
100
|
}
|
|
101
101
|
if (!app)
|
|
102
|
-
return { app: null, entries: [] };
|
|
102
|
+
return { app: null, entries: [], flows: [] };
|
|
103
103
|
// Touch last_seen_at so apps that are actively used bubble up.
|
|
104
104
|
db.prepare('UPDATE apps SET last_seen_at = ? WHERE id = ?').run(now(), app.id);
|
|
105
105
|
const pathname = input.url ? extractPathname(input.url) : null;
|
|
@@ -110,7 +110,10 @@ export function recall(input) {
|
|
|
110
110
|
: db
|
|
111
111
|
.prepare(`SELECT * FROM entries WHERE app_id = ? ORDER BY url_pattern, updated_at DESC`)
|
|
112
112
|
.all(app.id));
|
|
113
|
-
|
|
113
|
+
// Flow recipes are not url_pattern-scoped (see the schema comment in
|
|
114
|
+
// db.ts) — recall always returns every flow saved for the app, regardless
|
|
115
|
+
// of the optional `url` filter.
|
|
116
|
+
return { app, entries: rows.map(hydrate), flows: listFlows(app.id) };
|
|
114
117
|
}
|
|
115
118
|
function extractPathname(url) {
|
|
116
119
|
try {
|
|
@@ -159,4 +162,43 @@ export function renameApp(app_id, new_app_key) {
|
|
|
159
162
|
export function listApps() {
|
|
160
163
|
return getDb().prepare('SELECT * FROM apps ORDER BY last_seen_at DESC').all();
|
|
161
164
|
}
|
|
165
|
+
function hydrateFlow(row) {
|
|
166
|
+
const { steps_json, ...rest } = row;
|
|
167
|
+
return { ...rest, steps: parsePayload(steps_json) };
|
|
168
|
+
}
|
|
169
|
+
/** Upsert on (app, name) — saving an existing name replaces its
|
|
170
|
+
* description/steps and bumps updated_at, mirroring saveEntry's upsert
|
|
171
|
+
* semantics. `use_count` is left untouched on update (re-saving is not a
|
|
172
|
+
* "use"). */
|
|
173
|
+
export function saveFlow(input) {
|
|
174
|
+
const app = upsertApp({
|
|
175
|
+
origin: input.origin,
|
|
176
|
+
app_key: input.app_key ?? null,
|
|
177
|
+
title: input.title ?? null,
|
|
178
|
+
});
|
|
179
|
+
const ts = now();
|
|
180
|
+
const stepsJson = JSON.stringify(input.steps);
|
|
181
|
+
const db = getDb();
|
|
182
|
+
const existing = db
|
|
183
|
+
.prepare('SELECT * FROM flows WHERE app_id = ? AND name = ?')
|
|
184
|
+
.get(app.id, input.name);
|
|
185
|
+
if (existing) {
|
|
186
|
+
db.prepare('UPDATE flows SET description = ?, steps_json = ?, updated_at = ? WHERE id = ?').run(input.description ?? null, stepsJson, ts, existing.id);
|
|
187
|
+
const updated = db.prepare('SELECT * FROM flows WHERE id = ?').get(existing.id);
|
|
188
|
+
return { app, flow: hydrateFlow(updated) };
|
|
189
|
+
}
|
|
190
|
+
const info = db
|
|
191
|
+
.prepare(`INSERT INTO flows (app_id, name, description, steps_json, created_at, updated_at, use_count)
|
|
192
|
+
VALUES (?, ?, ?, ?, ?, ?, 0)`)
|
|
193
|
+
.run(app.id, input.name, input.description ?? null, stepsJson, ts, ts);
|
|
194
|
+
const inserted = db.prepare('SELECT * FROM flows WHERE id = ?').get(info.lastInsertRowid);
|
|
195
|
+
return { app, flow: hydrateFlow(inserted) };
|
|
196
|
+
}
|
|
197
|
+
/** Every flow recipe saved for an app, alphabetical by name. */
|
|
198
|
+
export function listFlows(app_id) {
|
|
199
|
+
const rows = getDb()
|
|
200
|
+
.prepare('SELECT * FROM flows WHERE app_id = ? ORDER BY name')
|
|
201
|
+
.all(app_id);
|
|
202
|
+
return rows.map(hydrateFlow);
|
|
203
|
+
}
|
|
162
204
|
//# sourceMappingURL=queries.js.map
|
package/dist/map/queries.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/map/queries.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/map/queries.ts"],"names":[],"mappings":"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"}
|
package/dist/map/tools.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { forget, listApps, recall, recordUse, renameApp, saveEntry, } from './queries.js';
|
|
1
|
+
import { forget, listApps, recall, recordUse, renameApp, saveEntry, saveFlow, } from './queries.js';
|
|
2
|
+
import { validateFlowSteps } from '../tools/browser-dispatch.js';
|
|
2
3
|
export const MAP_TOOL_DEFINITIONS = [
|
|
3
4
|
{
|
|
4
5
|
name: 'browser.map.recall',
|
|
5
|
-
description: 'Recall what is already known about a browser app/route from the local map DB. Call this first when you arrive at a tab so you can reuse known selectors, flows and gotchas instead of rediscovering them. Pass origin (scheme://host:port). Optionally pass app_key to disambiguate when multiple apps share an origin, and url to filter entries down to a specific pathname.',
|
|
6
|
+
description: 'Recall what is already known about a browser app/route from the local map DB. Call this first when you arrive at a tab so you can reuse known selectors, flows and gotchas instead of rediscovering them. Pass origin (scheme://host:port). Optionally pass app_key to disambiguate when multiple apps share an origin, and url to filter entries down to a specific pathname. Response includes `flows`: named browser.flow-replayable recipes saved for the app (`{ name, description, steps, use_count }`, not filtered by url) — adapt any placeholder text in `steps` (e.g. `type text "<QUERY>"`) to the real value before replaying with browser.flow.',
|
|
6
7
|
inputSchema: {
|
|
7
8
|
type: 'object',
|
|
8
9
|
properties: {
|
|
@@ -28,13 +29,14 @@ export const MAP_TOOL_DEFINITIONS = [
|
|
|
28
29
|
gotchas: [
|
|
29
30
|
'If recall returns entries with failed_at more recent than verified_at, treat them as suspect — re-verify with snapshot before reusing.',
|
|
30
31
|
'After every interaction that used a map entry, call browser.map.record_use with { entry_id, ok } so the map stays honest.',
|
|
32
|
+
'For a saved flow recipe: ADAPT its steps (substitute placeholder text for the real value) BEFORE calling browser.flow — never replay a placeholder-bearing recipe verbatim.',
|
|
31
33
|
],
|
|
32
34
|
example: 'browser.map.recall({ origin: "https://app.example.com" })',
|
|
33
35
|
},
|
|
34
36
|
},
|
|
35
37
|
{
|
|
36
38
|
name: 'browser.map.save',
|
|
37
|
-
description: 'Persist something learned about a browser app. Three kinds are supported: "selector" (a CSS selector tied to a purpose), "flow" (
|
|
39
|
+
description: 'Persist something learned about a browser app. Three entry kinds are supported: "selector" (a CSS selector tied to a purpose), "flow" (a free-form note about a multi-step path), and "gotcha" (a free-form note). Saving auto-creates the app row if needed (app_key is derived from title when not provided). Upsert on (app, url_pattern, kind, purpose). Marks verified_at = now and clears failed_at. Never save selectors or flows you have not just successfully executed. Optionally pass `flows`: an array of NAMED, REPLAYABLE flow recipes (`{ name, description?, steps }`) — `steps` must follow the EXACT browser.flow step grammar and is validated with the same rules browser.flow itself enforces. Upserts on (app, name); saving an existing name replaces its steps/description. Distinct from the "flow" entry kind above: that is a free-form note, this is a structured, browser.flow-replayable recipe returned by browser.map.recall.',
|
|
38
40
|
inputSchema: {
|
|
39
41
|
type: 'object',
|
|
40
42
|
properties: {
|
|
@@ -60,20 +62,43 @@ export const MAP_TOOL_DEFINITIONS = [
|
|
|
60
62
|
description: 'Kind-specific payload. selector: { selector: string, evidence?: string }. flow: { steps: array<{action,...}>}. gotcha: { body: string }.',
|
|
61
63
|
},
|
|
62
64
|
notes: { type: 'string' },
|
|
65
|
+
flows: {
|
|
66
|
+
type: 'array',
|
|
67
|
+
description: 'Optional named, replayable flow recipes for this app. Each `steps` array follows the EXACT browser.flow step grammar (find/click/type/press/wait_for) and is rejected with the same validation browser.flow applies. Use placeholder text for free-text values instead of real domain data (e.g. `type text "<QUERY>"`) — agents substitute the placeholder before calling browser.flow. Upserts on (app, name).',
|
|
68
|
+
items: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
name: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
description: 'Stable, reusable name for this recipe (e.g. "open task detail dialog").',
|
|
74
|
+
},
|
|
75
|
+
description: { type: 'string' },
|
|
76
|
+
steps: {
|
|
77
|
+
type: 'array',
|
|
78
|
+
description: 'Ordered find/click/type/press/wait_for steps — browser.flow grammar.',
|
|
79
|
+
items: { type: 'object' },
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
required: ['name', 'steps'],
|
|
83
|
+
additionalProperties: false,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
63
86
|
},
|
|
64
87
|
required: ['origin', 'url_pattern', 'kind', 'purpose', 'payload'],
|
|
65
88
|
additionalProperties: false,
|
|
66
89
|
},
|
|
67
90
|
doc: {
|
|
68
|
-
purpose: 'Persist UI structure you just discovered — a selector, a
|
|
91
|
+
purpose: 'Persist UI structure you just discovered — a selector, a note, a gotcha, and/or one or more named replayable flow recipes — keyed by app (and, for the entry, a url pattern).',
|
|
69
92
|
when_to_use: [
|
|
70
93
|
'After a non-trivial flow worked end-to-end (opened a dialog, completed a form, found a setting).',
|
|
94
|
+
'The multi-step path you just ran is worth replaying later as-is → attach it via `flows` so a future browser.map.recall can hand it straight to browser.flow.',
|
|
71
95
|
],
|
|
72
96
|
gotchas: [
|
|
73
97
|
'NEVER save selectors or flows you have not just successfully executed.',
|
|
74
|
-
'NEVER store domain data (IDs, user names, dates, etc.). The map captures UI structure only.',
|
|
98
|
+
'NEVER store domain data (IDs, user names, dates, etc.). The map captures UI structure only — `flows` steps must use placeholders (`<QUERY>`, `<NAME>`) for any free-text value, never the real value you just typed.',
|
|
75
99
|
'Use url_pattern = exact pathname by default; only promote to a glob if you have evidence of a parametric route.',
|
|
76
|
-
'Give `purpose` a stable, reusable label ("open task detail dialog", not "open IB0311 detail").',
|
|
100
|
+
'Give `purpose` a stable, reusable label ("open task detail dialog", not "open IB0311 detail"). Same rule for a flow recipe\'s `name`.',
|
|
101
|
+
"A `flows` entry's `steps` is validated with the SAME rules browser.flow enforces — an invalid steps array is rejected before anything is written, including the base entry.",
|
|
77
102
|
],
|
|
78
103
|
},
|
|
79
104
|
},
|
|
@@ -92,9 +117,12 @@ export const MAP_TOOL_DEFINITIONS = [
|
|
|
92
117
|
},
|
|
93
118
|
doc: {
|
|
94
119
|
purpose: 'Update verified_at / failed_at on a map entry after you reused it, so the next agent knows whether it still works.',
|
|
95
|
-
when_to_use: [
|
|
120
|
+
when_to_use: [
|
|
121
|
+
'Right after using any selector or gotcha entry you got from browser.map.recall.',
|
|
122
|
+
],
|
|
96
123
|
gotchas: [
|
|
97
124
|
'ok:true updates verified_at and clears failed_at. ok:false updates failed_at — keep the map honest about what works today.',
|
|
125
|
+
"Scoped to entries (selector/flow-note/gotcha rows), NOT to the named flow recipes in `flows` — those track only `use_count` (no verified/failed distinction), a deliberately simpler model that this tool's ok:true/false contract does not map onto cleanly. Re-`browser.map.save` a recipe with the same name to refresh it.",
|
|
98
126
|
],
|
|
99
127
|
},
|
|
100
128
|
},
|
|
@@ -155,7 +183,31 @@ export function handleMapTool(name, args) {
|
|
|
155
183
|
}
|
|
156
184
|
case 'browser.map.save': {
|
|
157
185
|
const a = args;
|
|
158
|
-
|
|
186
|
+
// Validate every flow recipe BEFORE writing anything — a steps array
|
|
187
|
+
// browser.flow would reject must fail browser.map.save cleanly too,
|
|
188
|
+
// without leaving a half-saved entry behind (see the reuse note on
|
|
189
|
+
// `validateFlowSteps` in browser-dispatch.ts). The shape guards run
|
|
190
|
+
// here and not just in the JSON schema because the IPC/proxy path in
|
|
191
|
+
// multi-agent mode reaches handleMapTool without schema validation.
|
|
192
|
+
if (a.flows !== undefined && !Array.isArray(a.flows)) {
|
|
193
|
+
throw new Error('browser.map.save: flows must be an array');
|
|
194
|
+
}
|
|
195
|
+
const rawFlows = a.flows ?? [];
|
|
196
|
+
const validatedFlows = rawFlows.map((raw) => {
|
|
197
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
198
|
+
throw new Error('browser.map.save: each flow must be an object');
|
|
199
|
+
}
|
|
200
|
+
const f = raw;
|
|
201
|
+
if (typeof f.name !== 'string' || f.name.trim().length === 0) {
|
|
202
|
+
throw new Error('browser.map.save: each flow requires a non-empty name');
|
|
203
|
+
}
|
|
204
|
+
const validated = validateFlowSteps(f.steps);
|
|
205
|
+
if (!validated.ok) {
|
|
206
|
+
throw new Error(`browser.map.save: flow "${f.name}": ${validated.error}`);
|
|
207
|
+
}
|
|
208
|
+
return { name: f.name, description: f.description ?? null, steps: validated.steps };
|
|
209
|
+
});
|
|
210
|
+
const savedEntry = saveEntry({
|
|
159
211
|
origin: a.origin,
|
|
160
212
|
app_key: a.app_key ?? null,
|
|
161
213
|
title: a.title ?? null,
|
|
@@ -165,6 +217,17 @@ export function handleMapTool(name, args) {
|
|
|
165
217
|
payload: a.payload,
|
|
166
218
|
notes: a.notes ?? null,
|
|
167
219
|
});
|
|
220
|
+
if (validatedFlows.length === 0)
|
|
221
|
+
return savedEntry;
|
|
222
|
+
const flows = validatedFlows.map((f) => saveFlow({
|
|
223
|
+
origin: a.origin,
|
|
224
|
+
app_key: a.app_key ?? null,
|
|
225
|
+
title: a.title ?? null,
|
|
226
|
+
name: f.name,
|
|
227
|
+
description: f.description,
|
|
228
|
+
steps: f.steps,
|
|
229
|
+
}).flow);
|
|
230
|
+
return { ...savedEntry, flows };
|
|
168
231
|
}
|
|
169
232
|
case 'browser.map.record_use': {
|
|
170
233
|
const a = args;
|
package/dist/map/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/map/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,QAAQ,EACR,MAAM,EACN,SAAS,EACT,SAAS,EACT,SAAS,
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/map/tools.ts"],"names":[],"mappings":"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"}
|
package/dist/permissions.js
CHANGED
|
@@ -41,6 +41,12 @@ export const TOOL_CATALOGUE = [
|
|
|
41
41
|
category: 'read',
|
|
42
42
|
summary: 'Find one element by visible text and return a stable selector + coords',
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
name: 'browser.state',
|
|
46
|
+
family: 'bridge',
|
|
47
|
+
category: 'read',
|
|
48
|
+
summary: 'Compact orientation snapshot: focused element, open dialogs, scroll, viewport',
|
|
49
|
+
},
|
|
44
50
|
{
|
|
45
51
|
name: 'browser.canvas_screenshot',
|
|
46
52
|
family: 'bridge',
|