@dokobot/cli 2.0.1 → 2.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/README.md CHANGED
@@ -10,59 +10,85 @@ npm i -g @dokobot/cli
10
10
 
11
11
  ## Quick Start
12
12
 
13
+ ### Local mode (free, unlimited)
14
+
15
+ ```bash
16
+ # Install the native messaging bridge (one-time setup)
17
+ dokobot install-bridge
18
+
19
+ # Read any page locally through your browser
20
+ dokobot doko read --local https://example.com
21
+ ```
22
+
23
+ ### Remote mode (cloud API)
24
+
13
25
  ```bash
14
26
  # Configure your API key
15
27
  dokobot config
16
28
 
17
- # Read any web page through your browser
29
+ # Read any page via cloud API
18
30
  dokobot doko read https://example.com
19
31
 
20
32
  # Search the web
21
33
  dokobot doko search "latest AI news"
22
-
23
- # Connect your Chrome browser for full MCP control
24
- dokobot doko connect
25
34
  ```
26
35
 
27
36
  ## Commands
28
37
 
29
- ### `doko`
30
-
31
- Manage doko devices — connect your Chrome browser so AI agents can access real web pages, including SPAs, login-gated sites, and dynamic content.
38
+ ### `install-bridge`
32
39
 
33
- #### `doko connect`
40
+ Install the native messaging bridge for local mode. This connects your CLI directly to the Chrome extension without going through the server.
34
41
 
35
42
  ```bash
36
- dokobot doko connect # Interactive setup
37
- dokobot doko connect --name "Work Chrome" # Name your doko
38
- dokobot doko connect --browser-url http://127.0.0.1:9222 # Custom Chrome instance
39
- dokobot doko connect --device-id <id> # Reconnect an existing doko
43
+ dokobot install-bridge # Install
44
+ dokobot install-bridge --uninstall # Remove
40
45
  ```
41
46
 
42
- **Prerequisites:** Chrome with remote debugging enabled:
43
- 1. Open `chrome://inspect/#remote-debugging`
44
- 2. Check **"Allow remote debugging for this browser instance"**
45
-
46
- #### `doko list`
47
+ After installing, restart Chrome or reload the extension.
47
48
 
48
- List all registered doko devices on this machine.
49
+ ### `doko read <url>`
49
50
 
50
- #### `doko read <url>`
51
-
52
- Read a web page through your connected browser and output clean Markdown text.
51
+ Read a web page and output clean text.
53
52
 
54
53
  ```bash
54
+ # Local mode (free, unlimited, no API key needed)
55
+ dokobot doko read --local https://example.com
56
+
57
+ # Remote mode (via cloud API)
55
58
  dokobot doko read https://example.com
56
- dokobot doko read https://example.com --screens 3 --timeout 30
59
+
60
+ # With options
61
+ dokobot doko read --local https://example.com --screens 3 --timeout 30
62
+ dokobot doko read https://example.com --device <id> --format chunks
57
63
  ```
58
64
 
59
65
  | Option | Description |
60
66
  |--------|-------------|
67
+ | `--local` | Use local bridge instead of remote server |
68
+ | `--device <id>` | Target device ID |
61
69
  | `--screens <n>` | Number of screens to capture (default: auto) |
62
70
  | `--timeout <seconds>` | Read timeout in seconds (default: 60) |
71
+ | `--format <type>` | Response format: `text` (default) or `chunks` |
63
72
  | `--reuse-tab` | Reuse existing tab instead of opening a new one |
73
+ | `--session-id <id>` | Continue from a previous session |
74
+
75
+ When a page has more content to scroll, the output includes a session ID. Use `--session-id` to continue reading.
64
76
 
65
- #### `doko search <query>`
77
+ ### `doko close-session <sessionId>`
78
+
79
+ Close an active read session and release the browser tab.
80
+
81
+ ```bash
82
+ dokobot doko close-session <sessionId>
83
+ dokobot doko close-session --local <sessionId>
84
+ ```
85
+
86
+ | Option | Description |
87
+ |--------|-------------|
88
+ | `--local` | Close a local session |
89
+ | `--device <id>` | Target device ID (local mode) |
90
+
91
+ ### `doko search <query>`
66
92
 
67
93
  Search the web and return results.
68
94
 
@@ -75,9 +101,19 @@ dokobot doko search "latest AI news" --num 10
75
101
  |--------|-------------|
76
102
  | `--num <n>` | Number of results, 1-10 (default: 5) |
77
103
 
78
- #### `doko remove <id>`
104
+ ### `doko connect`
105
+
106
+ Connect your Chrome browser for full MCP control.
107
+
108
+ ```bash
109
+ dokobot doko connect # Interactive setup
110
+ dokobot doko connect --name "Work Chrome" # Name your doko
111
+ dokobot doko connect --browser-url http://127.0.0.1:9222 # Custom Chrome instance
112
+ ```
113
+
114
+ ### `doko list` / `doko remove <id>`
79
115
 
80
- Remove a registered doko device.
116
+ List or remove registered doko devices.
81
117
 
82
118
  ### Other commands
83
119
 
@@ -0,0 +1,26 @@
1
+ import * as http from 'node:http';
2
+ export type ReadHandler = (params: {
3
+ url: string;
4
+ screens?: number;
5
+ timeout?: number;
6
+ reuseTab?: boolean;
7
+ }) => Promise<{
8
+ title?: string;
9
+ text?: string;
10
+ url?: string;
11
+ error?: string;
12
+ }>;
13
+ export type CloseSessionHandler = (sessionId: string) => Promise<{
14
+ error?: string;
15
+ }>;
16
+ export type StatusHandler = () => {
17
+ connected: boolean;
18
+ };
19
+ interface Handlers {
20
+ onRead: ReadHandler;
21
+ onCloseSession: CloseSessionHandler;
22
+ onStatus: StatusHandler;
23
+ }
24
+ export declare function getSocketPath(deviceId: string): string;
25
+ export declare function createIpcServer(socketPath: string, handlers: Handlers): Promise<http.Server>;
26
+ export {};
@@ -0,0 +1,116 @@
1
+ import * as http from 'node:http';
2
+ import * as net from 'node:net';
3
+ import * as fs from 'node:fs';
4
+ import * as os from 'node:os';
5
+ import * as path from 'node:path';
6
+ export function getSocketPath(deviceId) {
7
+ if (process.platform === 'win32') {
8
+ return `\\\\.\\pipe\\dokobot-bridge-${deviceId}`;
9
+ }
10
+ return path.join(os.homedir(), '.dokobot', 'bridges', `${deviceId}.sock`);
11
+ }
12
+ export async function createIpcServer(socketPath, handlers) {
13
+ const server = http.createServer(async (req, res) => {
14
+ res.setHeader('Content-Type', 'application/json');
15
+ if (req.method === 'GET' && req.url === '/status') {
16
+ const status = handlers.onStatus();
17
+ res.writeHead(200);
18
+ res.end(JSON.stringify(status));
19
+ return;
20
+ }
21
+ if (req.method === 'POST' && req.url === '/read') {
22
+ const body = await readBody(req);
23
+ if (!body) {
24
+ res.writeHead(400);
25
+ res.end(JSON.stringify({ error: 'Invalid JSON body' }));
26
+ return;
27
+ }
28
+ try {
29
+ const result = await handlers.onRead(body);
30
+ if (result.error) {
31
+ res.writeHead(502);
32
+ res.end(JSON.stringify({ error: result.error }));
33
+ }
34
+ else {
35
+ res.writeHead(200);
36
+ res.end(JSON.stringify(result));
37
+ }
38
+ }
39
+ catch (err) {
40
+ res.writeHead(500);
41
+ res.end(JSON.stringify({ error: err instanceof Error ? err.message : 'Internal error' }));
42
+ }
43
+ return;
44
+ }
45
+ if (req.method === 'POST' && req.url === '/close-session') {
46
+ const body = await readBody(req);
47
+ const sessionId = body?.sessionId;
48
+ if (!sessionId) {
49
+ res.writeHead(400);
50
+ res.end(JSON.stringify({ error: 'sessionId is required' }));
51
+ return;
52
+ }
53
+ try {
54
+ const result = await handlers.onCloseSession(sessionId);
55
+ if (result.error) {
56
+ res.writeHead(502);
57
+ res.end(JSON.stringify({ error: result.error }));
58
+ }
59
+ else {
60
+ res.writeHead(200);
61
+ res.end(JSON.stringify({ success: true }));
62
+ }
63
+ }
64
+ catch (err) {
65
+ res.writeHead(500);
66
+ res.end(JSON.stringify({ error: err instanceof Error ? err.message : 'Internal error' }));
67
+ }
68
+ return;
69
+ }
70
+ res.writeHead(404);
71
+ res.end(JSON.stringify({ error: 'Not found' }));
72
+ });
73
+ await cleanupStaleSocket(socketPath);
74
+ const dir = path.dirname(socketPath);
75
+ if (!fs.existsSync(dir)) {
76
+ fs.mkdirSync(dir, { recursive: true });
77
+ }
78
+ server.listen(socketPath);
79
+ return server;
80
+ }
81
+ function cleanupStaleSocket(socketPath) {
82
+ if (process.platform === 'win32')
83
+ return Promise.resolve();
84
+ if (!fs.existsSync(socketPath))
85
+ return Promise.resolve();
86
+ return new Promise((resolve) => {
87
+ const client = net.createConnection(socketPath);
88
+ client.on('connect', () => {
89
+ client.destroy();
90
+ resolve();
91
+ });
92
+ client.on('error', () => {
93
+ try {
94
+ fs.unlinkSync(socketPath);
95
+ }
96
+ catch { /* ignore */ }
97
+ resolve();
98
+ });
99
+ });
100
+ }
101
+ function readBody(req) {
102
+ return new Promise((resolve) => {
103
+ const chunks = [];
104
+ req.on('data', (chunk) => chunks.push(chunk));
105
+ req.on('end', () => {
106
+ try {
107
+ resolve(JSON.parse(Buffer.concat(chunks).toString('utf-8')));
108
+ }
109
+ catch {
110
+ resolve(null);
111
+ }
112
+ });
113
+ req.on('error', () => resolve(null));
114
+ });
115
+ }
116
+ //# sourceMappingURL=ipc-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipc-server.js","sourceRoot":"","sources":["../../../src/bridge/ipc-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Dokobot Local Bridge — Native Messaging Host
4
+ *
5
+ * Launched by Chrome when the extension calls connectNative('ai.dokobot.bridge').
6
+ * Communicates with the extension via stdin/stdout (Native Messaging protocol)
7
+ * and exposes an HTTP-over-IPC server for CLI access.
8
+ */
9
+ export {};
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Dokobot Local Bridge — Native Messaging Host
4
+ *
5
+ * Launched by Chrome when the extension calls connectNative('ai.dokobot.bridge').
6
+ * Communicates with the extension via stdin/stdout (Native Messaging protocol)
7
+ * and exposes an HTTP-over-IPC server for CLI access.
8
+ */
9
+ import * as fs from 'node:fs';
10
+ import * as path from 'node:path';
11
+ import * as crypto from 'node:crypto';
12
+ import { writeMessage, startReader } from './native-messaging.js';
13
+ import { createIpcServer, getSocketPath } from './ipc-server.js';
14
+ import { DOKOBOT_DIR, registerBridge, unregisterBridge } from '../bridge-registry.js';
15
+ const LOG_PATH = path.join(DOKOBOT_DIR, 'bridge.log');
16
+ function log(msg) {
17
+ fs.appendFileSync(LOG_PATH, `[${new Date().toISOString()}] ${msg}\n`);
18
+ }
19
+ let deviceId = null;
20
+ let extensionConnected = false;
21
+ const pendingRequests = new Map();
22
+ let server = null;
23
+ log('Bridge process started');
24
+ startReader((msg) => {
25
+ const m = msg;
26
+ log(`Received message: ${JSON.stringify(m).slice(0, 200)}`);
27
+ if (m.type === 'hello' && typeof m.deviceId === 'string') {
28
+ deviceId = m.deviceId;
29
+ extensionConnected = true;
30
+ startIpcServer();
31
+ writeMessage({ type: 'status', status: 'ready' });
32
+ log(`Hello from device ${deviceId}, IPC server started`);
33
+ return;
34
+ }
35
+ if (m.type === 'result' && typeof m.requestId === 'string') {
36
+ const pending = pendingRequests.get(m.requestId);
37
+ if (pending) {
38
+ clearTimeout(pending.timer);
39
+ pendingRequests.delete(m.requestId);
40
+ pending.resolve(m);
41
+ }
42
+ return;
43
+ }
44
+ });
45
+ process.stdin.on('end', () => {
46
+ log('stdin ended, shutting down');
47
+ shutdown();
48
+ });
49
+ process.on('uncaughtException', (err) => {
50
+ log(`Uncaught exception: ${err.message}\n${err.stack}`);
51
+ shutdown();
52
+ });
53
+ process.on('SIGTERM', shutdown);
54
+ process.on('SIGINT', shutdown);
55
+ async function startIpcServer() {
56
+ if (!deviceId || server)
57
+ return;
58
+ const socketPath = getSocketPath(deviceId);
59
+ server = await createIpcServer(socketPath, {
60
+ onRead: async (params) => {
61
+ if (!extensionConnected) {
62
+ return { error: 'Extension not connected' };
63
+ }
64
+ const requestId = crypto.randomUUID();
65
+ const result = await new Promise((resolve) => {
66
+ const timeoutMs = (params.timeout ?? 60) * 1000 + 30_000;
67
+ const timer = setTimeout(() => {
68
+ pendingRequests.delete(requestId);
69
+ resolve({ error: `Read timed out after ${params.timeout ?? 60}s` });
70
+ }, timeoutMs);
71
+ pendingRequests.set(requestId, { resolve, timer });
72
+ writeMessage({
73
+ type: 'command',
74
+ requestId,
75
+ command: 'readPage',
76
+ params: {
77
+ url: params.url,
78
+ screens: params.screens,
79
+ timeout: params.timeout,
80
+ reuseTab: params.reuseTab,
81
+ },
82
+ });
83
+ });
84
+ if (result.error) {
85
+ return { error: result.error };
86
+ }
87
+ const data = result.data;
88
+ const innerData = data?.data;
89
+ log(`sessionId=${innerData?.sessionId} canContinue=${innerData?.canContinue} stopReason=${innerData?.stats?.stopReason}`);
90
+ return {
91
+ title: innerData?.trace?.title,
92
+ text: innerData?.text,
93
+ url: innerData?.trace?.url,
94
+ sessionId: innerData?.sessionId,
95
+ canContinue: innerData?.canContinue,
96
+ };
97
+ },
98
+ onCloseSession: async (sessionId) => {
99
+ if (!extensionConnected) {
100
+ return { error: 'Extension not connected' };
101
+ }
102
+ const requestId = crypto.randomUUID();
103
+ const result = await new Promise((resolve) => {
104
+ const timer = setTimeout(() => {
105
+ pendingRequests.delete(requestId);
106
+ resolve({ error: 'Close session timed out' });
107
+ }, 10_000);
108
+ pendingRequests.set(requestId, { resolve, timer });
109
+ writeMessage({
110
+ type: 'command',
111
+ requestId,
112
+ command: 'closeSession',
113
+ params: { sessionId },
114
+ });
115
+ });
116
+ if (result.error) {
117
+ return { error: result.error };
118
+ }
119
+ return {};
120
+ },
121
+ onStatus: () => ({ connected: extensionConnected }),
122
+ });
123
+ registerBridge({ deviceId, pid: process.pid, socket: socketPath });
124
+ }
125
+ function shutdown() {
126
+ extensionConnected = false;
127
+ for (const [id, pending] of pendingRequests) {
128
+ clearTimeout(pending.timer);
129
+ pending.resolve({ error: 'Bridge shutting down' });
130
+ pendingRequests.delete(id);
131
+ }
132
+ if (server) {
133
+ server.close();
134
+ server = null;
135
+ }
136
+ if (deviceId) {
137
+ const socketPath = getSocketPath(deviceId);
138
+ try {
139
+ fs.unlinkSync(socketPath);
140
+ }
141
+ catch { /* ignore */ }
142
+ unregisterBridge(deviceId);
143
+ }
144
+ process.exit(0);
145
+ }
146
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/bridge/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Chrome Native Messaging protocol: 4-byte LE length prefix + UTF-8 JSON body.
3
+ */
4
+ export declare function writeMessage(msg: unknown): void;
5
+ export declare function startReader(onMessage: (msg: unknown) => void): void;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Chrome Native Messaging protocol: 4-byte LE length prefix + UTF-8 JSON body.
3
+ */
4
+ export function writeMessage(msg) {
5
+ const json = JSON.stringify(msg);
6
+ const buf = Buffer.from(json, 'utf-8');
7
+ const header = Buffer.alloc(4);
8
+ header.writeUInt32LE(buf.length, 0);
9
+ process.stdout.write(header);
10
+ process.stdout.write(buf);
11
+ }
12
+ export function startReader(onMessage) {
13
+ let buffer = Buffer.alloc(0);
14
+ process.stdin.on('data', (chunk) => {
15
+ buffer = Buffer.concat([buffer, chunk]);
16
+ while (buffer.length >= 4) {
17
+ const msgLen = buffer.readUInt32LE(0);
18
+ if (buffer.length < 4 + msgLen)
19
+ break;
20
+ const json = buffer.subarray(4, 4 + msgLen).toString('utf-8');
21
+ buffer = buffer.subarray(4 + msgLen);
22
+ try {
23
+ onMessage(JSON.parse(json));
24
+ }
25
+ catch {
26
+ // malformed JSON, skip
27
+ }
28
+ }
29
+ });
30
+ }
31
+ //# sourceMappingURL=native-messaging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-messaging.js","sourceRoot":"","sources":["../../../src/bridge/native-messaging.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;IAChC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACtC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAC9B,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAC5B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;AAC3B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAiC;IAC3D,IAAI,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAE5B,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;QACzC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;QAEvC,OAAO,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;YACrC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM;gBAAE,MAAK;YAErC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;YAC7D,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAA;YAEpC,IAAI,CAAC;gBACH,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;YAC7B,CAAC;YAAC,MAAM,CAAC;gBACP,uBAAuB;YACzB,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface BridgeEntry {
2
+ deviceId: string;
3
+ pid: number;
4
+ socket: string;
5
+ }
6
+ export declare const DOKOBOT_DIR: string;
7
+ export declare const REGISTRY_PATH: string;
8
+ export declare function readRegistry(): BridgeEntry[];
9
+ export declare function writeRegistry(entries: BridgeEntry[]): void;
10
+ export declare function registerBridge(entry: BridgeEntry): void;
11
+ export declare function unregisterBridge(deviceId: string): void;
12
+ export declare function getAliveBridges(): BridgeEntry[];
13
+ export declare function resolveBridge(deviceId?: string): BridgeEntry;
@@ -0,0 +1,73 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ export const DOKOBOT_DIR = path.join(os.homedir(), '.dokobot');
5
+ export const REGISTRY_PATH = path.join(DOKOBOT_DIR, 'bridges.json');
6
+ function isProcessAlive(pid) {
7
+ try {
8
+ process.kill(pid, 0);
9
+ return true;
10
+ }
11
+ catch {
12
+ return false;
13
+ }
14
+ }
15
+ export function readRegistry() {
16
+ try {
17
+ if (fs.existsSync(REGISTRY_PATH)) {
18
+ return JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf-8'));
19
+ }
20
+ }
21
+ catch { /* ignore */ }
22
+ return [];
23
+ }
24
+ export function writeRegistry(entries) {
25
+ const dir = path.dirname(REGISTRY_PATH);
26
+ if (!fs.existsSync(dir))
27
+ fs.mkdirSync(dir, { recursive: true });
28
+ fs.writeFileSync(REGISTRY_PATH, JSON.stringify(entries, null, 2));
29
+ }
30
+ export function registerBridge(entry) {
31
+ const entries = readRegistry().filter(e => e.deviceId !== entry.deviceId);
32
+ entries.push(entry);
33
+ writeRegistry(entries);
34
+ }
35
+ export function unregisterBridge(deviceId) {
36
+ const entries = readRegistry().filter(e => e.deviceId !== deviceId);
37
+ writeRegistry(entries);
38
+ }
39
+ export function getAliveBridges() {
40
+ return readRegistry().filter(e => isProcessAlive(e.pid));
41
+ }
42
+ export function resolveBridge(deviceId) {
43
+ const entries = getAliveBridges();
44
+ if (entries.length === 0) {
45
+ console.error('No local bridge running.\n');
46
+ console.error('To use --local mode:');
47
+ console.error(' 1. Install the bridge: dokobot install-bridge');
48
+ console.error(' 2. Open Chrome with the Dokobot extension enabled');
49
+ console.error(' 3. The bridge starts automatically when the extension loads');
50
+ process.exit(1);
51
+ }
52
+ if (deviceId) {
53
+ const match = entries.find(e => e.deviceId === deviceId);
54
+ if (!match) {
55
+ console.error(`Bridge for device "${deviceId}" not found.\n`);
56
+ console.error('Available bridges:');
57
+ for (const e of entries) {
58
+ console.error(` ${e.deviceId} (pid ${e.pid})`);
59
+ }
60
+ process.exit(1);
61
+ }
62
+ return match;
63
+ }
64
+ if (entries.length === 1) {
65
+ return entries[0];
66
+ }
67
+ console.error('Multiple bridges found. Use --device to select:\n');
68
+ for (const e of entries) {
69
+ console.error(` ${e.deviceId} (pid ${e.pid})`);
70
+ }
71
+ process.exit(1);
72
+ }
73
+ //# sourceMappingURL=bridge-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-registry.js","sourceRoot":"","sources":["../../src/bridge-registry.ts"],"names":[],"mappings":"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"}
package/dist/src/cli.js CHANGED
@@ -42,6 +42,15 @@ export function createProgram() {
42
42
  console.log(`You're on the latest version (${getCurrentVersion()}).`);
43
43
  }
44
44
  });
45
+ program
46
+ .command('install-bridge')
47
+ .description('Install native messaging bridge for --local mode')
48
+ .option('--extension-id <id>', 'Override extension ID (for dev builds)')
49
+ .option('--uninstall', 'Remove bridge installation')
50
+ .action(async (options) => {
51
+ const { installBridge } = await import('./install-bridge.js');
52
+ await installBridge(options);
53
+ });
45
54
  registerDokoCommands(program);
46
55
  return program;
47
56
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAEjD,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,SAAS,CAAC;SACf,WAAW,CAAC,iDAAiD,CAAC;SAC9D,OAAO,CAAC,iBAAiB,EAAE,CAAC;SAC5B,MAAM,CAAC,iBAAiB,EAAE,mCAAmC,CAAC;SAC9D,MAAM,CAAC,gBAAgB,EAAE,0CAA0C,CAAC;SACpE,MAAM,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAAC;IAEjD,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,kCAAkC,CAAC;SAC/C,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC;YACzB,OAAO,EAAE,UAAU;YACnB,OAAO,EAAE,MAAM,CAAC,MAAM;SACvB,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC;YAC5B,OAAO,EAAE,aAAa;YACtB,OAAO,EAAE,MAAM,CAAC,SAAS,IAAI,oBAAoB;SAClD,CAAC,CAAC;QACH,UAAU,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,uBAAuB,CAAC;SACpC,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACvC,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,iCAAiC,iBAAiB,EAAE,IAAI,CAAC,CAAC;QACxE,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAE9B,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAEjD,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,SAAS,CAAC;SACf,WAAW,CAAC,iDAAiD,CAAC;SAC9D,OAAO,CAAC,iBAAiB,EAAE,CAAC;SAC5B,MAAM,CAAC,iBAAiB,EAAE,mCAAmC,CAAC;SAC9D,MAAM,CAAC,gBAAgB,EAAE,0CAA0C,CAAC;SACpE,MAAM,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAAC;IAEjD,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,kCAAkC,CAAC;SAC/C,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC;YACzB,OAAO,EAAE,UAAU;YACnB,OAAO,EAAE,MAAM,CAAC,MAAM;SACvB,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC;YAC5B,OAAO,EAAE,aAAa;YACtB,OAAO,EAAE,MAAM,CAAC,SAAS,IAAI,oBAAoB;SAClD,CAAC,CAAC;QACH,UAAU,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,uBAAuB,CAAC;SACpC,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACvC,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,iCAAiC,iBAAiB,EAAE,IAAI,CAAC,CAAC;QACxE,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,gBAAgB,CAAC;SACzB,WAAW,CAAC,kDAAkD,CAAC;SAC/D,MAAM,CAAC,qBAAqB,EAAE,wCAAwC,CAAC;SACvE,MAAM,CAAC,aAAa,EAAE,4BAA4B,CAAC;SACnD,MAAM,CAAC,KAAK,EAAE,OAAsD,EAAE,EAAE;QACvE,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAC9D,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEL,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAE9B,OAAO,OAAO,CAAC;AACjB,CAAC"}