@jobshimo/browser-link 0.33.0 → 0.34.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/map/tools.js CHANGED
@@ -1,9 +1,9 @@
1
- import { forget, listApps, recall, recordUse, renameApp, saveEntry, saveFlow, } from './queries.js';
1
+ import { forget, listApps, recall, recordUse, renameApp, saveEntry, saveFlow, inTransaction, saveInsight, upsertApp, } from './queries.js';
2
2
  import { validateFlowSteps } from '../tools/browser-dispatch.js';
3
3
  export const MAP_TOOL_DEFINITIONS = [
4
4
  {
5
5
  name: 'browser.map.recall',
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
+ 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 sections, in order: `pitfalls` (what does NOT work — read these first), `mechanisms` (how the app behaves underneath), `entries`, `flows`. `flows` are 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.',
7
7
  inputSchema: {
8
8
  type: 'object',
9
9
  properties: {
@@ -36,7 +36,7 @@ export const MAP_TOOL_DEFINITIONS = [
36
36
  },
37
37
  {
38
38
  name: 'browser.map.save',
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.',
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). The entry fields (url_pattern, kind, purpose, payload) are optional AS A GROUP: pass all four, or omit all four to save only `flows`, `mechanisms` or `pitfalls` (no throwaway entry needed); a partial entry or a call carrying nothing to save is rejected. 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. Also optionally pass `mechanisms` (`{ key, description }`: how the app behaves underneath) and `pitfalls` (`{ key, description, symptom? }`: what does NOT work) — behaviour, never content.',
40
40
  inputSchema: {
41
41
  type: 'object',
42
42
  properties: {
@@ -83,14 +83,54 @@ export const MAP_TOOL_DEFINITIONS = [
83
83
  additionalProperties: false,
84
84
  },
85
85
  },
86
+ mechanisms: {
87
+ type: 'array',
88
+ description: 'Optional. How the app behaves underneath — behaviour, never content. Allowed: "a GET of an item URL marks it complete server-side", "progress % is rendered client-side, not in the fetched HTML". Forbidden: real IDs, names, dates, URLs with real values, message text. Upserts on (app, key).',
89
+ items: {
90
+ type: 'object',
91
+ properties: {
92
+ key: {
93
+ type: 'string',
94
+ description: 'Stable slug-like label (e.g. "get-marks-item-complete"), max 100 chars.',
95
+ },
96
+ description: { type: 'string', description: 'The behaviour, max 1000 chars.' },
97
+ },
98
+ required: ['key', 'description'],
99
+ additionalProperties: false,
100
+ },
101
+ },
102
+ pitfalls: {
103
+ type: 'array',
104
+ description: 'Optional. What does NOT work or misleads — the most expensive thing to rediscover. Allowed: "click on the next-lesson link returns ok but does not navigate (onmouseup handler)", "the text \'lessons completed\' is always in the raw HTML (JS template) — false positive for enrolment detection". Forbidden: real IDs, names, dates or any domain data. Upserts on (app, key).',
105
+ items: {
106
+ type: 'object',
107
+ properties: {
108
+ key: {
109
+ type: 'string',
110
+ description: 'Stable slug-like label (e.g. "next-link-click-noop"), max 100 chars.',
111
+ },
112
+ description: {
113
+ type: 'string',
114
+ description: 'What fails or misleads, max 1000 chars.',
115
+ },
116
+ symptom: {
117
+ type: 'string',
118
+ description: 'Optional. What you observe when you hit it, max 1000 chars.',
119
+ },
120
+ },
121
+ required: ['key', 'description'],
122
+ additionalProperties: false,
123
+ },
124
+ },
86
125
  },
87
- required: ['origin', 'url_pattern', 'kind', 'purpose', 'payload'],
126
+ required: ['origin'],
88
127
  additionalProperties: false,
89
128
  },
90
129
  doc: {
91
130
  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).',
92
131
  when_to_use: [
93
132
  'After a non-trivial flow worked end-to-end (opened a dialog, completed a form, found a setting).',
133
+ 'You learned HOW the app works underneath, or hit something that does NOT work → save a `mechanisms` / `pitfalls` item on its own (omit url_pattern/kind/purpose/payload), not just a selector. A pitfall costs far more to rediscover than a selector.',
94
134
  '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.',
95
135
  ],
96
136
  gotchas: [
@@ -98,6 +138,7 @@ export const MAP_TOOL_DEFINITIONS = [
98
138
  '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.',
99
139
  'Use url_pattern = exact pathname by default; only promote to a glob if you have evidence of a parametric route.',
100
140
  'Give `purpose` a stable, reusable label ("open task detail dialog", not "open IB0311 detail"). Same rule for a flow recipe\'s `name`.',
141
+ '`mechanisms` and `pitfalls` describe behaviour, never content. Allowed: "a GET of an item URL marks it complete server-side"; "click on the next link returns ok but does not navigate". Forbidden: "item 4812 is complete", "user jane@x.com is enrolled", any real ID, name, date or value. Use a stable `key`; re-saving the same key replaces it.',
101
142
  "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.",
102
143
  ],
103
144
  },
@@ -128,12 +169,23 @@ export const MAP_TOOL_DEFINITIONS = [
128
169
  },
129
170
  {
130
171
  name: 'browser.map.forget',
131
- description: 'Permanently delete an entry (entry_id) or a whole app and all its entries (app_id). Use when a refactor or app change makes the saved knowledge actively wrong.',
172
+ description: 'Permanently delete an entry (entry_id), ONE mechanism/pitfall (insight: { app_id, kind, key } — the app_id, kind and key come straight from a browser.map.recall item), or a whole app and everything in it (app_id). Use when a refactor or app change makes the saved knowledge actively wrong.',
132
173
  inputSchema: {
133
174
  type: 'object',
134
175
  properties: {
135
176
  entry_id: { type: 'number' },
136
177
  app_id: { type: 'number' },
178
+ insight: {
179
+ type: 'object',
180
+ description: 'Delete a single saved mechanism or pitfall.',
181
+ properties: {
182
+ app_id: { type: 'number' },
183
+ kind: { type: 'string', enum: ['mechanism', 'pitfall'] },
184
+ key: { type: 'string' },
185
+ },
186
+ required: ['app_id', 'kind', 'key'],
187
+ additionalProperties: false,
188
+ },
137
189
  reason: { type: 'string' },
138
190
  },
139
191
  additionalProperties: false,
@@ -172,6 +224,64 @@ export const MAP_TOOL_DEFINITIONS = [
172
224
  },
173
225
  },
174
226
  ];
227
+ const ENTRY_KINDS = ['selector', 'flow', 'gotcha'];
228
+ export const MAX_INSIGHT_KEY = 100;
229
+ export const MAX_INSIGHT_TEXT = 1000;
230
+ /** Validate ONE mechanism/pitfall. Shared by `browser.map.save` and
231
+ * `browser-link map import`, so an import file can never smuggle in what
232
+ * save would reject. Throws an unprefixed message. `symptom: null` counts as
233
+ * absent: recall returns `symptom: null`, and a recalled item must re-save
234
+ * unchanged. */
235
+ export function parseInsight(item, kind) {
236
+ if (typeof item !== 'object' || item === null) {
237
+ throw new Error(`each ${kind} must be an object`);
238
+ }
239
+ const i = item;
240
+ if (typeof i.key !== 'string' || i.key.trim().length === 0) {
241
+ throw new Error(`each ${kind} requires a non-empty key`);
242
+ }
243
+ const key = i.key.trim();
244
+ if (key.length > MAX_INSIGHT_KEY) {
245
+ throw new Error(`${kind} key must be at most ${MAX_INSIGHT_KEY} chars`);
246
+ }
247
+ if (typeof i.description !== 'string' || i.description.trim().length === 0) {
248
+ throw new Error(`${kind} "${key}" requires a non-empty description`);
249
+ }
250
+ if (i.description.length > MAX_INSIGHT_TEXT) {
251
+ throw new Error(`${kind} "${key}" description must be at most ${MAX_INSIGHT_TEXT} chars`);
252
+ }
253
+ let symptom = null;
254
+ if (i.symptom !== undefined && i.symptom !== null) {
255
+ if (kind !== 'pitfall') {
256
+ throw new Error(`only a pitfall takes a symptom (mechanism "${key}")`);
257
+ }
258
+ if (typeof i.symptom !== 'string') {
259
+ throw new Error(`pitfall "${key}" symptom must be a string`);
260
+ }
261
+ if (i.symptom.length > MAX_INSIGHT_TEXT) {
262
+ throw new Error(`pitfall "${key}" symptom must be at most ${MAX_INSIGHT_TEXT} chars`);
263
+ }
264
+ symptom = i.symptom;
265
+ }
266
+ return { key, description: i.description, symptom };
267
+ }
268
+ /** Shape-check `mechanisms` / `pitfalls` BEFORE anything is written (same
269
+ * all-or-nothing rule as `flows`). Runs here, not only in the JSON schema,
270
+ * because the IPC/proxy path in multi-agent mode skips schema validation. */
271
+ function validateInsights(raw, kind) {
272
+ if (raw === undefined)
273
+ return [];
274
+ if (!Array.isArray(raw))
275
+ throw new Error(`browser.map.save: ${kind}s must be an array`);
276
+ return raw.map((item) => {
277
+ try {
278
+ return parseInsight(item, kind);
279
+ }
280
+ catch (err) {
281
+ throw new Error(`browser.map.save: ${err.message}`, { cause: err });
282
+ }
283
+ });
284
+ }
175
285
  export function isMapTool(name) {
176
286
  return name.startsWith('browser.map.');
177
287
  }
@@ -207,27 +317,78 @@ export function handleMapTool(name, args) {
207
317
  }
208
318
  return { name: f.name, description: f.description ?? null, steps: validated.steps };
209
319
  });
210
- const savedEntry = saveEntry({
211
- origin: a.origin,
212
- app_key: a.app_key ?? null,
213
- title: a.title ?? null,
214
- url_pattern: a.url_pattern,
215
- kind: a.kind,
216
- purpose: a.purpose,
217
- payload: a.payload,
218
- notes: a.notes ?? null,
320
+ const validatedMechanisms = validateInsights(a.mechanisms, 'mechanism');
321
+ const validatedPitfalls = validateInsights(a.pitfalls, 'pitfall');
322
+ if (typeof a.origin !== 'string' || a.origin.trim().length === 0) {
323
+ throw new Error('browser.map.save: origin is required');
324
+ }
325
+ // The base entry is all-or-nothing: either all four of url_pattern /
326
+ // kind / purpose / payload, or none of them (then the call must carry
327
+ // flows, mechanisms or pitfalls). A half-filled entry is almost
328
+ // certainly a mistake and would store junk.
329
+ const entryFields = ['url_pattern', 'kind', 'purpose', 'payload'];
330
+ const present = entryFields.filter((f) => a[f] !== undefined);
331
+ const hasEntry = present.length > 0;
332
+ if (hasEntry && present.length < entryFields.length) {
333
+ const missing = entryFields.filter((f) => a[f] === undefined);
334
+ throw new Error(`browser.map.save: an entry requires url_pattern, kind, purpose and payload together (missing: ${missing.join(', ')})`);
335
+ }
336
+ if (hasEntry && !ENTRY_KINDS.includes(a.kind)) {
337
+ throw new Error(`browser.map.save: kind must be one of ${ENTRY_KINDS.join(' | ')}`);
338
+ }
339
+ if (!hasEntry &&
340
+ validatedFlows.length + validatedMechanisms.length + validatedPitfalls.length === 0) {
341
+ throw new Error('browser.map.save: nothing to save — pass an entry (url_pattern, kind, purpose, payload) and/or flows, mechanisms or pitfalls');
342
+ }
343
+ // One transaction for the whole write phase: a failure part-way must
344
+ // not leave an entry saved without the pitfalls that came with it.
345
+ return inTransaction(() => {
346
+ const savedEntry = hasEntry
347
+ ? saveEntry({
348
+ origin: a.origin,
349
+ app_key: a.app_key ?? null,
350
+ title: a.title ?? null,
351
+ url_pattern: a.url_pattern,
352
+ kind: a.kind,
353
+ purpose: a.purpose,
354
+ payload: a.payload,
355
+ notes: a.notes ?? null,
356
+ })
357
+ : {
358
+ app: upsertApp({
359
+ origin: a.origin,
360
+ app_key: a.app_key ?? null,
361
+ title: a.title ?? null,
362
+ }),
363
+ };
364
+ const extras = {};
365
+ if (validatedFlows.length > 0) {
366
+ extras.flows = validatedFlows.map((f) => saveFlow({
367
+ origin: a.origin,
368
+ app_key: a.app_key ?? null,
369
+ title: a.title ?? null,
370
+ name: f.name,
371
+ description: f.description,
372
+ steps: f.steps,
373
+ }).flow);
374
+ }
375
+ const saveInsights = (kind, items) => items.map((i) => saveInsight({
376
+ origin: a.origin,
377
+ app_key: a.app_key ?? null,
378
+ title: a.title ?? null,
379
+ kind,
380
+ key: i.key,
381
+ description: i.description,
382
+ symptom: i.symptom,
383
+ }).insight);
384
+ if (validatedMechanisms.length > 0) {
385
+ extras.mechanisms = saveInsights('mechanism', validatedMechanisms);
386
+ }
387
+ if (validatedPitfalls.length > 0) {
388
+ extras.pitfalls = saveInsights('pitfall', validatedPitfalls);
389
+ }
390
+ return Object.keys(extras).length === 0 ? savedEntry : { ...savedEntry, ...extras };
219
391
  });
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 };
231
392
  }
232
393
  case 'browser.map.record_use': {
233
394
  const a = args;
@@ -235,6 +396,21 @@ export function handleMapTool(name, args) {
235
396
  }
236
397
  case 'browser.map.forget': {
237
398
  const a = args;
399
+ if (a.insight !== undefined) {
400
+ const ins = a.insight;
401
+ if (typeof ins !== 'object' ||
402
+ ins === null ||
403
+ typeof ins.app_id !== 'number' ||
404
+ (ins.kind !== 'mechanism' && ins.kind !== 'pitfall') ||
405
+ typeof ins.key !== 'string' ||
406
+ ins.key.trim().length === 0) {
407
+ throw new Error('browser.map.forget: insight must be { app_id: number, kind: "mechanism" | "pitfall", key: string }');
408
+ }
409
+ return forget({
410
+ insight: { app_id: ins.app_id, kind: ins.kind, key: ins.key.trim() },
411
+ reason: a.reason,
412
+ });
413
+ }
238
414
  return forget({ entry_id: a.entry_id, app_id: a.app_id, reason: a.reason });
239
415
  }
240
416
  case 'browser.map.rename_app': {
@@ -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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"}
@@ -32,10 +32,12 @@ change. If a call returns "Tab not connected", call \`browser.events\`
32
32
  to find the new tab_id before retrying. After a non-trivial flow worked
33
33
  end-to-end, persist UI structure (NEVER domain data) with
34
34
  \`browser.map.save\`. When \`list_tabs\` shows a \`map\` hint on a tab
35
- (\`{ app_key, entries, flows }\`), call \`browser.map.recall\` BEFORE
36
- snapshotting — the map already has selectors, gotchas or flow recipes
37
- for that origin, so recall first instead of rediscovering them from
38
- scratch. When list_tabs is empty ([]) a second text block "hint: ..."
35
+ (\`{ app_key, entries, flows, mechanisms, pitfalls, inline? }\`): if
36
+ \`inline\` is present the small map is already in the hint — read its
37
+ pitfalls first and skip the round trip; otherwise call
38
+ \`browser.map.recall\` BEFORE snapshotting — the map already has
39
+ selectors, gotchas, flow recipes, mechanisms or pitfalls for that origin,
40
+ so recall first instead of rediscovering them from scratch. When list_tabs is empty ([]) a second text block "hint: ..."
39
41
  follows the result — read it: it says whether to wait for the extension's
40
42
  auto-reconnect (up to 10 minutes after a drop) or ask the user to press
41
43
  Connect.
@@ -111,6 +113,19 @@ only, never domain data (real names, IDs, dates, message content) —
111
113
  placeholders exist precisely so a flow can be saved without ever writing
112
114
  that data to disk.
113
115
 
116
+ ## Saving mechanisms and pitfalls
117
+
118
+ After a non-trivial discovery, save more than selectors.
119
+ \`browser.map.save\` also takes \`mechanisms: [{ key, description }]\` (how
120
+ the app behaves underneath — "a GET of an item URL marks it complete
121
+ server-side", "progress % is client-rendered, not in the fetched HTML")
122
+ and \`pitfalls: [{ key, description, symptom? }]\` (what does NOT work —
123
+ "click on the next link returns ok but does not navigate (onmouseup
124
+ handler)"). \`browser.map.recall\` returns \`pitfalls\` first, then
125
+ \`mechanisms\`. A pitfall is the most expensive thing to rediscover, so
126
+ write them down. Same privacy rule as the rest of the map: behaviour,
127
+ never content — no IDs, names, dates or real values.
128
+
114
129
  ## Token-efficient patterns (READ THIS BEFORE REACHING FOR EVALUATE)
115
130
 
116
131
  \`browser.evaluate\` is the all-purpose escape hatch. Reach for it only
@@ -1 +1 @@
1
- {"version":3,"file":"server-instructions.js","sourceRoot":"","sources":["../../src/tools/server-instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAGvD;;sDAEsD;AACtD,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6EAkO4D,CAAC;AAE9E;;mEAEmE;AACnE,SAAS,UAAU,CAAC,GAAmB;IACrC,IAAI,CAAC,GAAG,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC;IAC3D,MAAM,KAAK,GAAa,CAAC,OAAO,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,kBAAkB,CAAC,CAAC;IACjF,KAAK,MAAM,CAAC,IAAI,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClD,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;QAC/B,KAAK,MAAM,CAAC,IAAI,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;iEAEiE;AACjE,MAAM,UAAU,uBAAuB;IACrC,MAAM,GAAG,GAAqB,CAAC,GAAG,wBAAwB,EAAE,GAAG,oBAAoB,CAAC,CAAC;IACrF,MAAM,QAAQ,GAAa,CAAC,QAAQ,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC;IACtD,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED;8DAC8D;AAC9D,MAAM,CAAC,MAAM,mBAAmB,GAAG,uBAAuB,EAAE,CAAC"}
1
+ {"version":3,"file":"server-instructions.js","sourceRoot":"","sources":["../../src/tools/server-instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAGvD;;sDAEsD;AACtD,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6EAiP4D,CAAC;AAE9E;;mEAEmE;AACnE,SAAS,UAAU,CAAC,GAAmB;IACrC,IAAI,CAAC,GAAG,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC;IAC3D,MAAM,KAAK,GAAa,CAAC,OAAO,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,kBAAkB,CAAC,CAAC;IACjF,KAAK,MAAM,CAAC,IAAI,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClD,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;QAC/B,KAAK,MAAM,CAAC,IAAI,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;iEAEiE;AACjE,MAAM,UAAU,uBAAuB;IACrC,MAAM,GAAG,GAAqB,CAAC,GAAG,wBAAwB,EAAE,GAAG,oBAAoB,CAAC,CAAC;IACrF,MAAM,QAAQ,GAAa,CAAC,QAAQ,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC;IACtD,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED;8DAC8D;AAC9D,MAAM,CAAC,MAAM,mBAAmB,GAAG,uBAAuB,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobshimo/browser-link",
3
- "version": "0.33.0",
3
+ "version": "0.34.1",
4
4
  "description": "MCP server that bridges Claude Code, OpenCode, GitHub Copilot CLI and other MCP clients to a Chrome tab. Per-tool permissions, multi-agent mode (multiple MCP clients sharing one bridge), persistent UI map across sessions.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -60,17 +60,19 @@
60
60
  "env-paths": "^4.0.0",
61
61
  "ink": "^7.1.1",
62
62
  "react": "^19.3.1",
63
- "ws": "^8.22.1"
63
+ "ws": "^8.22.1",
64
+ "zod": "^4.6.7"
64
65
  },
65
66
  "devDependencies": {
66
67
  "@types/better-sqlite3": "^9.6.0",
67
68
  "@types/node": "^22.20.5",
68
69
  "@types/react": "^19.3.0",
69
70
  "@types/ws": "^8.18.2",
70
- "@vitest/coverage-v8": "^4.1.10",
71
+ "@vitest/coverage-v8": "^4.1.12",
71
72
  "ink-testing-library": "^4.0.0",
72
73
  "tsx": "^4.23.15",
73
74
  "typescript": "^6.0.3",
74
- "vitest": "^4.1.11"
75
+ "vite": "^8.3.6",
76
+ "vitest": "^4.1.12"
75
77
  }
76
78
  }