@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 +61 -25
- package/dist/src/bridge/ipc-server.d.ts +26 -0
- package/dist/src/bridge/ipc-server.js +116 -0
- package/dist/src/bridge/ipc-server.js.map +1 -0
- package/dist/src/bridge/main.d.ts +9 -0
- package/dist/src/bridge/main.js +146 -0
- package/dist/src/bridge/main.js.map +1 -0
- package/dist/src/bridge/native-messaging.d.ts +5 -0
- package/dist/src/bridge/native-messaging.js +31 -0
- package/dist/src/bridge/native-messaging.js.map +1 -0
- package/dist/src/bridge-registry.d.ts +13 -0
- package/dist/src/bridge-registry.js +73 -0
- package/dist/src/bridge-registry.js.map +1 -0
- package/dist/src/cli.js +9 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/doko.js +67 -13
- package/dist/src/doko.js.map +1 -1
- package/dist/src/install-bridge.d.ts +4 -0
- package/dist/src/install-bridge.js +145 -0
- package/dist/src/install-bridge.js.map +1 -0
- package/dist/src/local-client.d.ts +13 -0
- package/dist/src/local-client.js +70 -0
- package/dist/src/local-client.js.map +1 -0
- package/dist/src/server-client.d.ts +6 -0
- package/dist/src/server-client.js +15 -2
- package/dist/src/server-client.js.map +1 -1
- package/package.json +3 -2
- package/dist/src/crawl.d.ts +0 -2
- package/dist/src/crawl.js +0 -503
- package/dist/src/crawl.js.map +0 -1
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
|
|
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
|
-
### `
|
|
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
|
-
|
|
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
|
|
37
|
-
dokobot
|
|
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
|
-
|
|
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
|
-
|
|
49
|
+
### `doko read <url>`
|
|
49
50
|
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,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
|
}
|
package/dist/src/cli.js.map
CHANGED
|
@@ -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"}
|