@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.
@@ -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
- : 'button, a, input, textarea, select, [role], [onclick], [contenteditable="true"], [tabindex]';
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
- return { matched: false, reason: 'not-found' };
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":"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"}
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.17.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) {
@@ -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"}
@@ -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[];
@@ -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
- return { app, entries: rows.map(hydrate) };
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
@@ -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" (an ordered list of steps to reach an outcome), 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.',
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 multi-step flow, or a gotcha — keyed by app and url pattern.',
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: ['Right after using any selector or flow you got from browser.map.recall.'],
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
- return saveEntry({
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;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/map/tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/map/tools.ts"],"names":[],"mappings":"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"}
@@ -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',