@jobshimo/browser-link 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/map/db.js CHANGED
@@ -42,34 +42,34 @@ function migrateLegacyDb(targetPath) {
42
42
  }
43
43
  }
44
44
  function runMigrations(db) {
45
- db.exec(`
46
- CREATE TABLE IF NOT EXISTS apps (
47
- id INTEGER PRIMARY KEY,
48
- origin TEXT NOT NULL,
49
- app_key TEXT NOT NULL,
50
- title TEXT,
51
- notes TEXT,
52
- created_at TEXT NOT NULL,
53
- last_seen_at TEXT NOT NULL,
54
- UNIQUE(origin, app_key)
55
- );
56
-
57
- CREATE TABLE IF NOT EXISTS entries (
58
- id INTEGER PRIMARY KEY,
59
- app_id INTEGER NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
60
- url_pattern TEXT NOT NULL,
61
- kind TEXT NOT NULL CHECK (kind IN ('selector', 'flow', 'gotcha')),
62
- purpose TEXT NOT NULL,
63
- payload TEXT NOT NULL,
64
- verified_at TEXT,
65
- failed_at TEXT,
66
- notes TEXT,
67
- created_at TEXT NOT NULL,
68
- updated_at TEXT NOT NULL,
69
- UNIQUE(app_id, url_pattern, kind, purpose)
70
- );
71
-
72
- CREATE INDEX IF NOT EXISTS idx_entries_lookup ON entries(app_id, url_pattern);
45
+ db.exec(`
46
+ CREATE TABLE IF NOT EXISTS apps (
47
+ id INTEGER PRIMARY KEY,
48
+ origin TEXT NOT NULL,
49
+ app_key TEXT NOT NULL,
50
+ title TEXT,
51
+ notes TEXT,
52
+ created_at TEXT NOT NULL,
53
+ last_seen_at TEXT NOT NULL,
54
+ UNIQUE(origin, app_key)
55
+ );
56
+
57
+ CREATE TABLE IF NOT EXISTS entries (
58
+ id INTEGER PRIMARY KEY,
59
+ app_id INTEGER NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
60
+ url_pattern TEXT NOT NULL,
61
+ kind TEXT NOT NULL CHECK (kind IN ('selector', 'flow', 'gotcha')),
62
+ purpose TEXT NOT NULL,
63
+ payload TEXT NOT NULL,
64
+ verified_at TEXT,
65
+ failed_at TEXT,
66
+ notes TEXT,
67
+ created_at TEXT NOT NULL,
68
+ updated_at TEXT NOT NULL,
69
+ UNIQUE(app_id, url_pattern, kind, purpose)
70
+ );
71
+
72
+ CREATE INDEX IF NOT EXISTS idx_entries_lookup ON entries(app_id, url_pattern);
73
73
  `);
74
74
  }
75
75
  export function closeDb() {
@@ -49,7 +49,7 @@ export function upsertApp(input) {
49
49
  return db.prepare('SELECT * FROM apps WHERE id = ?').get(existing.id);
50
50
  }
51
51
  const info = db
52
- .prepare(`INSERT INTO apps (origin, app_key, title, notes, created_at, last_seen_at)
52
+ .prepare(`INSERT INTO apps (origin, app_key, title, notes, created_at, last_seen_at)
53
53
  VALUES (?, ?, ?, ?, ?, ?)`)
54
54
  .run(input.origin, app_key, input.title ?? null, input.notes ?? null, ts, ts);
55
55
  return db.prepare('SELECT * FROM apps WHERE id = ?').get(info.lastInsertRowid);
@@ -67,8 +67,8 @@ export function saveEntry(input) {
67
67
  .prepare(`SELECT * FROM entries WHERE app_id = ? AND url_pattern = ? AND kind = ? AND purpose = ?`)
68
68
  .get(app.id, input.url_pattern, input.kind, input.purpose);
69
69
  if (existing) {
70
- db.prepare(`UPDATE entries
71
- SET payload = ?, notes = COALESCE(?, notes), verified_at = ?, failed_at = NULL, updated_at = ?
70
+ db.prepare(`UPDATE entries
71
+ SET payload = ?, notes = COALESCE(?, notes), verified_at = ?, failed_at = NULL, updated_at = ?
72
72
  WHERE id = ?`).run(payloadJson, input.notes ?? null, ts, ts, existing.id);
73
73
  const updated = db
74
74
  .prepare('SELECT * FROM entries WHERE id = ?')
@@ -76,7 +76,7 @@ export function saveEntry(input) {
76
76
  return { app, entry: hydrate(updated) };
77
77
  }
78
78
  const info = db
79
- .prepare(`INSERT INTO entries (app_id, url_pattern, kind, purpose, payload, verified_at, notes, created_at, updated_at)
79
+ .prepare(`INSERT INTO entries (app_id, url_pattern, kind, purpose, payload, verified_at, notes, created_at, updated_at)
80
80
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
81
81
  .run(app.id, input.url_pattern, input.kind, input.purpose, payloadJson, ts, input.notes ?? null, ts, ts);
82
82
  const inserted = db
@@ -1,50 +1,50 @@
1
1
  /** Usage protocol pushed to the MCP client on `initialize`. Plain string,
2
2
  * intentionally kept short. Edit here when the protocol changes. */
3
- export const SERVER_INSTRUCTIONS = `browser-link bridges Claude Code to the Chrome tabs the user has
4
- explicitly connected through the companion extension, and ships a
5
- persistent UI map backed by a local SQLite DB. The data dir resolves
6
- per-OS via env-paths (\$XDG_DATA_HOME/browser-link on Linux,
7
- ~/Library/Application Support/browser-link on macOS, %APPDATA%/browser-link
8
- on Windows). Override with \$BROWSER_LINK_DATA_DIR. The map is private
9
- and per-machine; never persisted in any repo.
10
-
11
- ## When operating on a tab
12
-
13
- 1. Before doing anything on a tab whose URL you don't already know,
14
- call \`browser.map.recall\` with { origin } (and optionally url) to load
15
- selectors, flows and gotchas previously learned for that app.
16
- 2. If recall returns entries with \`failed_at\` more recent than
17
- \`verified_at\`, treat them as suspect: re-verify (snapshot / evaluate)
18
- before reusing, or replace them.
19
- 3. After every interaction that used a map entry, call
20
- \`browser.map.record_use\` with { entry_id, ok }. ok=true updates
21
- verified_at; ok=false updates failed_at. Keep the map honest.
22
- 4. After a non-trivial flow that worked end-to-end, persist it with
23
- \`browser.map.save\`. Three \`kind\` values:
24
- - selector: { selector, evidence? } — a CSS selector tied to a purpose.
25
- - flow: { steps: [...] } — an ordered list of actions to reach an outcome.
26
- - gotcha: { body } — free-form note about something non-obvious.
27
- Use \`url_pattern\` = pathname (exact). Promote to glob only if you have
28
- evidence of a parametric route. Provide \`purpose\` as a stable, reusable
29
- label ("open task detail dialog", not "open IB0311 detail").
30
- 5. Never save selectors or flows you have not just successfully executed.
31
- 6. Never store domain data (IDs, user names, dates, etc.). The map captures
32
- UI structure only.
33
-
34
- ## Identifying the app
35
-
36
- - \`origin\` = scheme://host:port of the tab.
37
- - \`app_key\` distinguishes apps that share an origin over time. On first
38
- save you may omit it; it will be derived from the page title (slugified).
39
- Use \`browser.map.rename_app\` if that initial guess is poor.
40
-
41
- ## When something is wrong
42
-
43
- - A selector from recall fails → record_use({ok:false}), learn the new
44
- one, save it (upsert on purpose).
45
- - A whole app got refactored → \`browser.map.forget\` the app_id and let
46
- the map repopulate as you learn the new structure.
47
-
48
- The map is a cache of navigation, not a substitute for \`browser.snapshot\`.
3
+ export const SERVER_INSTRUCTIONS = `browser-link bridges Claude Code to the Chrome tabs the user has
4
+ explicitly connected through the companion extension, and ships a
5
+ persistent UI map backed by a local SQLite DB. The data dir resolves
6
+ per-OS via env-paths (\$XDG_DATA_HOME/browser-link on Linux,
7
+ ~/Library/Application Support/browser-link on macOS, %APPDATA%/browser-link
8
+ on Windows). Override with \$BROWSER_LINK_DATA_DIR. The map is private
9
+ and per-machine; never persisted in any repo.
10
+
11
+ ## When operating on a tab
12
+
13
+ 1. Before doing anything on a tab whose URL you don't already know,
14
+ call \`browser.map.recall\` with { origin } (and optionally url) to load
15
+ selectors, flows and gotchas previously learned for that app.
16
+ 2. If recall returns entries with \`failed_at\` more recent than
17
+ \`verified_at\`, treat them as suspect: re-verify (snapshot / evaluate)
18
+ before reusing, or replace them.
19
+ 3. After every interaction that used a map entry, call
20
+ \`browser.map.record_use\` with { entry_id, ok }. ok=true updates
21
+ verified_at; ok=false updates failed_at. Keep the map honest.
22
+ 4. After a non-trivial flow that worked end-to-end, persist it with
23
+ \`browser.map.save\`. Three \`kind\` values:
24
+ - selector: { selector, evidence? } — a CSS selector tied to a purpose.
25
+ - flow: { steps: [...] } — an ordered list of actions to reach an outcome.
26
+ - gotcha: { body } — free-form note about something non-obvious.
27
+ Use \`url_pattern\` = pathname (exact). Promote to glob only if you have
28
+ evidence of a parametric route. Provide \`purpose\` as a stable, reusable
29
+ label ("open task detail dialog", not "open IB0311 detail").
30
+ 5. Never save selectors or flows you have not just successfully executed.
31
+ 6. Never store domain data (IDs, user names, dates, etc.). The map captures
32
+ UI structure only.
33
+
34
+ ## Identifying the app
35
+
36
+ - \`origin\` = scheme://host:port of the tab.
37
+ - \`app_key\` distinguishes apps that share an origin over time. On first
38
+ save you may omit it; it will be derived from the page title (slugified).
39
+ Use \`browser.map.rename_app\` if that initial guess is poor.
40
+
41
+ ## When something is wrong
42
+
43
+ - A selector from recall fails → record_use({ok:false}), learn the new
44
+ one, save it (upsert on purpose).
45
+ - A whole app got refactored → \`browser.map.forget\` the app_id and let
46
+ the map repopulate as you learn the new structure.
47
+
48
+ The map is a cache of navigation, not a substitute for \`browser.snapshot\`.
49
49
  The live snapshot is always the source of truth.`;
50
50
  //# sourceMappingURL=server-instructions.js.map
package/package.json CHANGED
@@ -1,61 +1,64 @@
1
- {
2
- "name": "@jobshimo/browser-link",
3
- "version": "0.0.1",
4
- "description": "MCP server that bridges Claude (and other MCP clients) to a Chrome tab, with a persistent UI map of selectors, flows and gotchas across sessions.",
5
- "keywords": [
6
- "mcp",
7
- "model-context-protocol",
8
- "claude",
9
- "chrome",
10
- "browser-automation",
11
- "ai-tools"
12
- ],
13
- "homepage": "https://github.com/jobshimo/browser-link#readme",
14
- "bugs": {
15
- "url": "https://github.com/jobshimo/browser-link/issues"
16
- },
17
- "repository": {
18
- "type": "git",
19
- "url": "git+https://github.com/jobshimo/browser-link.git",
20
- "directory": "packages/server"
21
- },
22
- "license": "MIT",
23
- "author": "Martín Miguel Bernal",
24
- "type": "module",
25
- "main": "./dist/index.js",
26
- "bin": {
27
- "browser-link": "./dist/cli.js"
28
- },
29
- "files": [
30
- "dist",
31
- "README.md",
32
- "LICENSE"
33
- ],
34
- "publishConfig": {
35
- "access": "public"
36
- },
37
- "scripts": {
38
- "build": "tsc",
39
- "dev": "tsx watch src/cli.ts",
40
- "start": "node dist/cli.js",
41
- "typecheck": "tsc --noEmit",
42
- "clean": "rm -rf dist",
43
- "test": "vitest run",
44
- "test:watch": "vitest",
45
- "prepublishOnly": "npm run clean && npm run build && node ./scripts/prepare-publish.mjs"
46
- },
47
- "dependencies": {
48
- "@modelcontextprotocol/sdk": "^1.0.0",
49
- "better-sqlite3": "^11.5.0",
50
- "env-paths": "^3.0.0",
51
- "ws": "^8.18.0"
52
- },
53
- "devDependencies": {
54
- "@types/better-sqlite3": "^7.6.11",
55
- "@types/node": "^22.0.0",
56
- "@types/ws": "^8.5.13",
57
- "tsx": "^4.19.0",
58
- "typescript": "^5.6.0",
59
- "vitest": "^3.1.4"
60
- }
61
- }
1
+ {
2
+ "name": "@jobshimo/browser-link",
3
+ "version": "0.1.0",
4
+ "description": "MCP server that bridges Claude Code, OpenCode and other MCP clients to a Chrome tab, with a persistent UI map of selectors, flows and gotchas across sessions.",
5
+ "keywords": [
6
+ "mcp",
7
+ "model-context-protocol",
8
+ "claude",
9
+ "claude-code",
10
+ "opencode",
11
+ "chrome",
12
+ "browser-automation",
13
+ "ai-tools"
14
+ ],
15
+ "homepage": "https://github.com/jobshimo/browser-link#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/jobshimo/browser-link/issues"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/jobshimo/browser-link.git",
22
+ "directory": "packages/server"
23
+ },
24
+ "license": "MIT",
25
+ "author": "Martín Miguel Bernal",
26
+ "type": "module",
27
+ "main": "./dist/index.js",
28
+ "bin": {
29
+ "browser-link": "./dist/cli.js"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "scripts": {
40
+ "build": "tsc",
41
+ "dev": "tsx watch src/cli.ts",
42
+ "start": "node dist/cli.js",
43
+ "typecheck": "tsc --noEmit",
44
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "prepublishOnly": "npm run clean && npm run build && node ./scripts/prepare-publish.mjs"
48
+ },
49
+ "dependencies": {
50
+ "@clack/prompts": "^1.3.0",
51
+ "@modelcontextprotocol/sdk": "^1.0.0",
52
+ "better-sqlite3": "^11.5.0",
53
+ "env-paths": "^3.0.0",
54
+ "ws": "^8.18.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/better-sqlite3": "^7.6.11",
58
+ "@types/node": "^22.0.0",
59
+ "@types/ws": "^8.5.13",
60
+ "tsx": "^4.19.0",
61
+ "typescript": "^5.6.0",
62
+ "vitest": "^3.1.4"
63
+ }
64
+ }
@@ -1,51 +0,0 @@
1
- export declare const ansi: {
2
- reset: string;
3
- bold: string;
4
- dim: string;
5
- italic: string;
6
- underline: string;
7
- black: string;
8
- red: string;
9
- green: string;
10
- yellow: string;
11
- blue: string;
12
- magenta: string;
13
- cyan: string;
14
- white: string;
15
- gray: string;
16
- bgYellow: string;
17
- bgRed: string;
18
- clearScreen: string;
19
- hideCursor: string;
20
- showCursor: string;
21
- };
22
- export declare const KEY: {
23
- CTRL_C: string;
24
- ESC: string;
25
- ENTER_CR: string;
26
- ENTER_LF: string;
27
- UP: string;
28
- DOWN: string;
29
- RIGHT: string;
30
- LEFT: string;
31
- };
32
- /** Visible width of a string (ignores ANSI escape sequences). Treats every
33
- * other code point as 1 column — fine for ASCII + the Latin range we use. */
34
- export declare function visibleWidth(s: string): number;
35
- export declare function padRight(s: string, width: number): string;
36
- /** Wrap a line at the given width, preserving simple ANSI runs. */
37
- export declare function wrap(text: string, width: number): string[];
38
- export interface BoxOptions {
39
- width?: number;
40
- borderColor?: string;
41
- }
42
- /** Render an array of lines inside a rounded Unicode box. */
43
- export declare function renderBox(lines: string[], opts?: BoxOptions): string;
44
- /** Read a single keypress from stdin without echoing. Returns the raw key
45
- * (a control sequence for arrows, '\r' for enter, etc.). */
46
- export declare function readKey(): Promise<string>;
47
- /** Classify a raw key into a logical name. */
48
- export declare function classifyKey(raw: string): string;
49
- export declare function clearScreen(): void;
50
- export declare function hideCursor(): void;
51
- export declare function showCursor(): void;
@@ -1,148 +0,0 @@
1
- import { stdin, stdout } from 'node:process';
2
- /* ANSI helpers — no dependencies. Modern terminals (macOS Terminal, iTerm,
3
- * Windows Terminal, PowerShell 7+, every Linux TTY) understand these.
4
- * Old cmd.exe may render the box characters poorly but the flow still works. */
5
- const ESC = '\x1b';
6
- export const ansi = {
7
- reset: `${ESC}[0m`,
8
- bold: `${ESC}[1m`,
9
- dim: `${ESC}[2m`,
10
- italic: `${ESC}[3m`,
11
- underline: `${ESC}[4m`,
12
- black: `${ESC}[30m`,
13
- red: `${ESC}[31m`,
14
- green: `${ESC}[32m`,
15
- yellow: `${ESC}[33m`,
16
- blue: `${ESC}[34m`,
17
- magenta: `${ESC}[35m`,
18
- cyan: `${ESC}[36m`,
19
- white: `${ESC}[37m`,
20
- gray: `${ESC}[90m`,
21
- bgYellow: `${ESC}[43m`,
22
- bgRed: `${ESC}[41m`,
23
- clearScreen: `${ESC}[2J${ESC}[H`,
24
- hideCursor: `${ESC}[?25l`,
25
- showCursor: `${ESC}[?25h`,
26
- };
27
- export const KEY = {
28
- CTRL_C: '\x03',
29
- ESC: '\x1b',
30
- ENTER_CR: '\r',
31
- ENTER_LF: '\n',
32
- UP: '\x1b[A',
33
- DOWN: '\x1b[B',
34
- RIGHT: '\x1b[C',
35
- LEFT: '\x1b[D',
36
- };
37
- /** Visible width of a string (ignores ANSI escape sequences). Treats every
38
- * other code point as 1 column — fine for ASCII + the Latin range we use. */
39
- export function visibleWidth(s) {
40
- // eslint-disable-next-line no-control-regex
41
- return s.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '').length;
42
- }
43
- export function padRight(s, width) {
44
- const diff = width - visibleWidth(s);
45
- return diff > 0 ? s + ' '.repeat(diff) : s;
46
- }
47
- /** Wrap a line at the given width, preserving simple ANSI runs. */
48
- export function wrap(text, width) {
49
- if (width <= 0)
50
- return [text];
51
- const lines = [];
52
- for (const paragraph of text.split('\n')) {
53
- if (visibleWidth(paragraph) <= width) {
54
- lines.push(paragraph);
55
- continue;
56
- }
57
- const words = paragraph.split(/\s+/);
58
- let cur = '';
59
- for (const w of words) {
60
- if (cur.length === 0) {
61
- cur = w;
62
- }
63
- else if (visibleWidth(cur) + 1 + visibleWidth(w) > width) {
64
- lines.push(cur);
65
- cur = w;
66
- }
67
- else {
68
- cur += ' ' + w;
69
- }
70
- }
71
- if (cur.length > 0)
72
- lines.push(cur);
73
- }
74
- return lines;
75
- }
76
- const B = {
77
- tl: '╭',
78
- tr: '╮',
79
- bl: '╰',
80
- br: '╯',
81
- h: '─',
82
- v: '│',
83
- };
84
- /** Render an array of lines inside a rounded Unicode box. */
85
- export function renderBox(lines, opts = {}) {
86
- const termWidth = stdout.columns ?? 80;
87
- // 86 cols comfortably fits the longest pre-formatted line in About
88
- // (tool name + padded description). Smaller terminals still get the cap.
89
- const width = Math.min(opts.width ?? 86, Math.max(40, termWidth - 2));
90
- const inner = width - 2; // 2 columns for the vertical borders
91
- const wrapped = lines.flatMap((l) => wrap(l, inner - 2)); // padding 1 each side
92
- const color = opts.borderColor ?? '';
93
- const reset = color ? ansi.reset : '';
94
- const top = `${color}${B.tl}${B.h.repeat(inner)}${B.tr}${reset}`;
95
- const bottom = `${color}${B.bl}${B.h.repeat(inner)}${B.br}${reset}`;
96
- const body = wrapped.map((l) => `${color}${B.v}${reset} ${padRight(l, inner - 2)} ${color}${B.v}${reset}`);
97
- return [top, ...body, bottom].join('\n');
98
- }
99
- /** Read a single keypress from stdin without echoing. Returns the raw key
100
- * (a control sequence for arrows, '\r' for enter, etc.). */
101
- export function readKey() {
102
- return new Promise((resolve) => {
103
- const wasRaw = stdin.isTTY ? stdin.isRaw : false;
104
- if (stdin.isTTY)
105
- stdin.setRawMode(true);
106
- stdin.resume();
107
- stdin.setEncoding('utf8');
108
- const onData = (data) => {
109
- const key = data.toString();
110
- stdin.off('data', onData);
111
- if (stdin.isTTY)
112
- stdin.setRawMode(wasRaw);
113
- stdin.pause();
114
- resolve(key);
115
- };
116
- stdin.on('data', onData);
117
- });
118
- }
119
- /** Classify a raw key into a logical name. */
120
- export function classifyKey(raw) {
121
- if (raw === KEY.CTRL_C)
122
- return 'ctrl-c';
123
- if (raw === KEY.ESC)
124
- return 'esc';
125
- if (raw === KEY.ENTER_CR || raw === KEY.ENTER_LF)
126
- return 'enter';
127
- if (raw === KEY.UP)
128
- return 'up';
129
- if (raw === KEY.DOWN)
130
- return 'down';
131
- if (raw === KEY.LEFT)
132
- return 'left';
133
- if (raw === KEY.RIGHT)
134
- return 'right';
135
- if (raw.length === 1)
136
- return raw.toLowerCase();
137
- return raw;
138
- }
139
- export function clearScreen() {
140
- stdout.write(ansi.clearScreen);
141
- }
142
- export function hideCursor() {
143
- stdout.write(ansi.hideCursor);
144
- }
145
- export function showCursor() {
146
- stdout.write(ansi.showCursor);
147
- }
148
- //# sourceMappingURL=tty.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tty.js","sourceRoot":"","sources":["../../src/commands/tty.ts"],"names":[],"mappings":"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"}