@dokobot/cli 1.0.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/bin/dokobot.d.ts +2 -0
- package/dist/bin/dokobot.js +12 -0
- package/dist/bin/dokobot.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +256 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/command-poller.d.ts +14 -0
- package/dist/src/command-poller.js +59 -0
- package/dist/src/command-poller.js.map +1 -0
- package/dist/src/config.d.ts +19 -0
- package/dist/src/config.js +59 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/executor.d.ts +16 -0
- package/dist/src/executor.js +36 -0
- package/dist/src/executor.js.map +1 -0
- package/dist/src/heartbeat.d.ts +10 -0
- package/dist/src/heartbeat.js +30 -0
- package/dist/src/heartbeat.js.map +1 -0
- package/dist/src/logger.d.ts +15 -0
- package/dist/src/logger.js +23 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/mcp-client.d.ts +17 -0
- package/dist/src/mcp-client.js +49 -0
- package/dist/src/mcp-client.js.map +1 -0
- package/dist/src/server-client.d.ts +27 -0
- package/dist/src/server-client.js +84 -0
- package/dist/src/server-client.js.map +1 -0
- package/dist/src/update-checker.d.ts +8 -0
- package/dist/src/update-checker.js +106 -0
- package/dist/src/update-checker.js.map +1 -0
- package/dist/src/version.d.ts +2 -0
- package/dist/src/version.js +29 -0
- package/dist/src/version.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createProgram } from '../src/cli.js';
|
|
3
|
+
import { initUpdateCheck, getUpdateResult, formatUpdateNotification } from '../src/update-checker.js';
|
|
4
|
+
initUpdateCheck();
|
|
5
|
+
const program = createProgram();
|
|
6
|
+
program.parseAsync().then(() => {
|
|
7
|
+
const info = getUpdateResult();
|
|
8
|
+
if (info) {
|
|
9
|
+
console.error(formatUpdateNotification(info));
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=dokobot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dokobot.js","sourceRoot":"","sources":["../../bin/dokobot.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAEtG,eAAe,EAAE,CAAC;AAElB,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;AAChC,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;IAC7B,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC;IAChD,CAAC;AACH,CAAC,CAAC,CAAC"}
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { input, select } from '@inquirer/prompts';
|
|
3
|
+
import { McpClient } from './mcp-client.js';
|
|
4
|
+
import { ServerClient } from './server-client.js';
|
|
5
|
+
import { CommandPoller } from './command-poller.js';
|
|
6
|
+
import { Heartbeat } from './heartbeat.js';
|
|
7
|
+
import { Executor } from './executor.js';
|
|
8
|
+
import { getApiKey, getServerUrl, loadConfig, saveConfig, saveDevice, listDevices, removeDevice, findDeviceByName, } from './config.js';
|
|
9
|
+
import { setLogLevel } from './logger.js';
|
|
10
|
+
import { getCurrentVersion } from './version.js';
|
|
11
|
+
import { getUpdateResult, checkForUpdateNow, formatUpdateNotification } from './update-checker.js';
|
|
12
|
+
export function createProgram() {
|
|
13
|
+
const program = new Command();
|
|
14
|
+
program
|
|
15
|
+
.name('dokobot')
|
|
16
|
+
.description('Dokobot CLI - Connect Chrome browser as a doko')
|
|
17
|
+
.version(getCurrentVersion())
|
|
18
|
+
.option('--api-key <key>', 'API key (or set DOKO_API_KEY env)')
|
|
19
|
+
.option('--server <url>', 'Server URL (default: https://dokobot.ai)')
|
|
20
|
+
.option('--verbose', 'Enable verbose logging');
|
|
21
|
+
program
|
|
22
|
+
.command('connect')
|
|
23
|
+
.description('Connect a Chrome browser via Chrome DevTools MCP')
|
|
24
|
+
.option('--name <name>', 'Name for the doko')
|
|
25
|
+
.option('--device-id <id>', 'Reuse an existing device ID')
|
|
26
|
+
.option('--browser-url <url>', 'Connect to a running Chrome instance (e.g. http://127.0.0.1:9222)')
|
|
27
|
+
.option('--mcp-args <args>', 'Extra arguments for chrome-devtools-mcp (comma-separated)')
|
|
28
|
+
.action(async (options) => {
|
|
29
|
+
const globalOpts = program.opts();
|
|
30
|
+
if (globalOpts.verbose)
|
|
31
|
+
setLogLevel('debug');
|
|
32
|
+
await connectCommand(globalOpts, options);
|
|
33
|
+
});
|
|
34
|
+
program
|
|
35
|
+
.command('list')
|
|
36
|
+
.description('List registered devices')
|
|
37
|
+
.action(() => {
|
|
38
|
+
const devices = listDevices();
|
|
39
|
+
if (devices.length === 0) {
|
|
40
|
+
console.log('No registered devices.');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
console.log('Registered devices:');
|
|
44
|
+
for (const d of devices) {
|
|
45
|
+
console.log(` ${d.name} (${d.id}) [${d.type}]`);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
program
|
|
49
|
+
.command('remove <id>')
|
|
50
|
+
.description('Remove a registered device')
|
|
51
|
+
.action((id) => {
|
|
52
|
+
if (removeDevice(id)) {
|
|
53
|
+
console.log(`Removed device: ${id}`);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.log(`Device not found: ${id}`);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
program
|
|
60
|
+
.command('config')
|
|
61
|
+
.description('Configure API key and server URL')
|
|
62
|
+
.action(async () => {
|
|
63
|
+
const config = loadConfig();
|
|
64
|
+
const apiKey = await input({
|
|
65
|
+
message: 'API Key:',
|
|
66
|
+
default: config.apiKey,
|
|
67
|
+
});
|
|
68
|
+
const serverUrl = await input({
|
|
69
|
+
message: 'Server URL:',
|
|
70
|
+
default: config.serverUrl || 'https://dokobot.ai',
|
|
71
|
+
});
|
|
72
|
+
saveConfig({ apiKey, serverUrl });
|
|
73
|
+
console.log('Configuration saved.');
|
|
74
|
+
});
|
|
75
|
+
program
|
|
76
|
+
.command('update')
|
|
77
|
+
.description('Check for CLI updates')
|
|
78
|
+
.action(async () => {
|
|
79
|
+
console.log('Checking for updates...');
|
|
80
|
+
const info = await checkForUpdateNow();
|
|
81
|
+
if (info) {
|
|
82
|
+
console.error(formatUpdateNotification(info));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
console.log(`You're on the latest version (${getCurrentVersion()}).`);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return program;
|
|
89
|
+
}
|
|
90
|
+
const G = '\x1b[32m';
|
|
91
|
+
const W = '\x1b[97;1m';
|
|
92
|
+
const B = '\x1b[1m';
|
|
93
|
+
const D = '\x1b[2m';
|
|
94
|
+
const R = '\x1b[0m';
|
|
95
|
+
const BANNER = `
|
|
96
|
+
${G}▄████████▄${R}
|
|
97
|
+
${G}███${W}██${R}${G}█${W}██${R}${G}███${R} ${B}Dokobot CLI${R} ${D}v${getCurrentVersion()}${R}
|
|
98
|
+
${G}███████████${R} ${D}Open the full web to AI agent${R}
|
|
99
|
+
${G}▀████████▀${R}
|
|
100
|
+
`;
|
|
101
|
+
async function connectCommand(globalOpts, options) {
|
|
102
|
+
console.log(BANNER);
|
|
103
|
+
const serverUrl = getServerUrl(globalOpts.server);
|
|
104
|
+
let deviceId = options.deviceId;
|
|
105
|
+
let deviceName = options.name;
|
|
106
|
+
let apiKey = getApiKey(globalOpts.apiKey);
|
|
107
|
+
if (!apiKey) {
|
|
108
|
+
if (deviceId) {
|
|
109
|
+
const guideUrl = `${serverUrl}/guide?source=cli&deviceId=${deviceId}`;
|
|
110
|
+
console.log(` No API key found. Opening setup guide...`);
|
|
111
|
+
console.log(` ${D}${guideUrl}${R}`);
|
|
112
|
+
console.log('');
|
|
113
|
+
const { exec } = await import('child_process');
|
|
114
|
+
exec(`open "${guideUrl}"`);
|
|
115
|
+
apiKey = await input({ message: 'Paste your API key:' });
|
|
116
|
+
if (!apiKey) {
|
|
117
|
+
console.error('API key is required.');
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
const config = loadConfig();
|
|
121
|
+
saveConfig({ ...config, apiKey });
|
|
122
|
+
console.log(' ✓ API key saved');
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const { randomUUID } = await import('crypto');
|
|
126
|
+
const preferredId = randomUUID();
|
|
127
|
+
const guideUrl = `${serverUrl}/guide?source=cli&deviceId=${preferredId}`;
|
|
128
|
+
console.log(` No API key found. Opening setup guide...`);
|
|
129
|
+
console.log(` ${D}${guideUrl}${R}`);
|
|
130
|
+
console.log('');
|
|
131
|
+
console.log(' Follow the guide to complete the setup.');
|
|
132
|
+
const { exec } = await import('child_process');
|
|
133
|
+
exec(`open "${guideUrl}"`);
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const serverClient = new ServerClient(serverUrl, apiKey);
|
|
138
|
+
if (!deviceId && deviceName) {
|
|
139
|
+
const existing = findDeviceByName(deviceName);
|
|
140
|
+
if (existing) {
|
|
141
|
+
deviceId = existing.id;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (!deviceId && !deviceName) {
|
|
145
|
+
const devices = listDevices();
|
|
146
|
+
const choices = [
|
|
147
|
+
{ name: '+ Create new doko', value: '__new__' },
|
|
148
|
+
...devices.map(d => ({ name: `${d.name} (${d.id})`, value: d.id })),
|
|
149
|
+
];
|
|
150
|
+
const selected = await select({
|
|
151
|
+
message: 'Choose a doko:',
|
|
152
|
+
choices,
|
|
153
|
+
});
|
|
154
|
+
if (selected === '__new__') {
|
|
155
|
+
deviceName = await input({
|
|
156
|
+
message: 'Name your new doko:',
|
|
157
|
+
default: 'My Chrome',
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
deviceId = selected;
|
|
162
|
+
const existing = devices.find(d => d.id === deviceId);
|
|
163
|
+
deviceName = existing?.name;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const mcpExtraArgs = options.mcpArgs ? options.mcpArgs.split(',') : [];
|
|
167
|
+
const mcpClient = new McpClient();
|
|
168
|
+
console.log(' Connecting to Chrome...');
|
|
169
|
+
try {
|
|
170
|
+
await mcpClient.connect({
|
|
171
|
+
browserUrl: options.browserUrl,
|
|
172
|
+
extraArgs: mcpExtraArgs,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.error('Failed to connect to Chrome DevTools MCP:', error instanceof Error ? error.message : error);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
let tools;
|
|
180
|
+
try {
|
|
181
|
+
tools = await mcpClient.listTools();
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
console.error(' ✗ Failed to connect to Chrome DevTools MCP:', error instanceof Error ? error.message : error);
|
|
185
|
+
await mcpClient.disconnect();
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const check = await mcpClient.callTool('list_pages', {});
|
|
190
|
+
if (check.isError) {
|
|
191
|
+
const msg = check.content?.[0]?.text ?? '';
|
|
192
|
+
console.error(' ✗ Chrome is not reachable.');
|
|
193
|
+
console.error('');
|
|
194
|
+
console.error(' Please enable remote debugging in Chrome:');
|
|
195
|
+
console.error(' 1. Open chrome://inspect/#remote-debugging');
|
|
196
|
+
console.error(' 2. Check "Allow remote debugging for this browser instance"');
|
|
197
|
+
console.error(' 3. Re-run this command');
|
|
198
|
+
console.error('');
|
|
199
|
+
console.error(` Detail: ${msg}`);
|
|
200
|
+
await mcpClient.disconnect();
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
console.log(` ✓ Connected to Chrome (${tools.length} tools available)`);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
console.error(' ✗ Chrome health check failed:', error instanceof Error ? error.message : error);
|
|
207
|
+
await mcpClient.disconnect();
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const result = await serverClient.connect({
|
|
212
|
+
type: 'chrome',
|
|
213
|
+
deviceId,
|
|
214
|
+
name: deviceName,
|
|
215
|
+
tools,
|
|
216
|
+
});
|
|
217
|
+
deviceId = result.deviceId;
|
|
218
|
+
deviceName = result.name;
|
|
219
|
+
saveDevice({
|
|
220
|
+
id: result.deviceId,
|
|
221
|
+
name: result.name,
|
|
222
|
+
type: result.type,
|
|
223
|
+
createdAt: Date.now(),
|
|
224
|
+
});
|
|
225
|
+
console.log(` ✓ Registered "${deviceName}" (${deviceId})`);
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
console.error('Registration failed:', error instanceof Error ? error.message : error);
|
|
229
|
+
await mcpClient.disconnect();
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
const executor = new Executor(mcpClient);
|
|
233
|
+
const heartbeat = new Heartbeat(serverClient, deviceId);
|
|
234
|
+
const poller = new CommandPoller(serverClient, executor, deviceId);
|
|
235
|
+
heartbeat.start();
|
|
236
|
+
poller.start();
|
|
237
|
+
const updateInfo = getUpdateResult();
|
|
238
|
+
if (updateInfo) {
|
|
239
|
+
console.error(formatUpdateNotification(updateInfo));
|
|
240
|
+
}
|
|
241
|
+
console.log('');
|
|
242
|
+
process.stdout.write(` ${G}●${R} Listening for commands... (Ctrl+C to quit)`);
|
|
243
|
+
const shutdown = async () => {
|
|
244
|
+
process.stdout.write(`\r\x1b[2K`);
|
|
245
|
+
console.log(' Shutting down...');
|
|
246
|
+
poller.stop();
|
|
247
|
+
heartbeat.stop();
|
|
248
|
+
await serverClient.disconnect(deviceId);
|
|
249
|
+
await mcpClient.disconnect();
|
|
250
|
+
process.exit(0);
|
|
251
|
+
};
|
|
252
|
+
process.on('SIGINT', shutdown);
|
|
253
|
+
process.on('SIGTERM', shutdown);
|
|
254
|
+
await new Promise(() => { });
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ServerClient } from './server-client.js';
|
|
2
|
+
import { Executor } from './executor.js';
|
|
3
|
+
export declare class CommandPoller {
|
|
4
|
+
private serverClient;
|
|
5
|
+
private executor;
|
|
6
|
+
private deviceId;
|
|
7
|
+
private polling;
|
|
8
|
+
private abortController;
|
|
9
|
+
private backoffMs;
|
|
10
|
+
constructor(serverClient: ServerClient, executor: Executor, deviceId: string);
|
|
11
|
+
start(): void;
|
|
12
|
+
stop(): void;
|
|
13
|
+
private pollLoop;
|
|
14
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { logger } from './logger.js';
|
|
2
|
+
const MAX_BACKOFF_MS = 60_000;
|
|
3
|
+
export class CommandPoller {
|
|
4
|
+
serverClient;
|
|
5
|
+
executor;
|
|
6
|
+
deviceId;
|
|
7
|
+
polling = false;
|
|
8
|
+
abortController = null;
|
|
9
|
+
backoffMs = 1000;
|
|
10
|
+
constructor(serverClient, executor, deviceId) {
|
|
11
|
+
this.serverClient = serverClient;
|
|
12
|
+
this.executor = executor;
|
|
13
|
+
this.deviceId = deviceId;
|
|
14
|
+
}
|
|
15
|
+
start() {
|
|
16
|
+
if (this.polling)
|
|
17
|
+
return;
|
|
18
|
+
this.polling = true;
|
|
19
|
+
this.backoffMs = 1000;
|
|
20
|
+
this.pollLoop();
|
|
21
|
+
}
|
|
22
|
+
stop() {
|
|
23
|
+
this.polling = false;
|
|
24
|
+
if (this.abortController) {
|
|
25
|
+
this.abortController.abort();
|
|
26
|
+
this.abortController = null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async pollLoop() {
|
|
30
|
+
while (this.polling) {
|
|
31
|
+
try {
|
|
32
|
+
this.abortController = new AbortController();
|
|
33
|
+
const command = await this.serverClient.pollCommand(this.deviceId, this.abortController.signal);
|
|
34
|
+
if (!this.polling)
|
|
35
|
+
break;
|
|
36
|
+
if (command === null) {
|
|
37
|
+
this.backoffMs = 1000;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
this.backoffMs = 1000;
|
|
41
|
+
const serverCommand = command;
|
|
42
|
+
const result = await this.executor.execute(serverCommand);
|
|
43
|
+
await this.serverClient.sendResult(serverCommand.requestId, result);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (!this.polling)
|
|
47
|
+
break;
|
|
48
|
+
if (error instanceof DOMException && error.name === 'AbortError')
|
|
49
|
+
continue;
|
|
50
|
+
logger.warn('Poll failed', error);
|
|
51
|
+
}
|
|
52
|
+
if (!this.polling)
|
|
53
|
+
break;
|
|
54
|
+
await new Promise(r => setTimeout(r, this.backoffMs));
|
|
55
|
+
this.backoffMs = Math.min(this.backoffMs * 2, MAX_BACKOFF_MS);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=command-poller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-poller.js","sourceRoot":"","sources":["../../src/command-poller.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,cAAc,GAAG,MAAM,CAAC;AAE9B,MAAM,OAAO,aAAa;IAMd;IACA;IACA;IAPF,OAAO,GAAG,KAAK,CAAC;IAChB,eAAe,GAA2B,IAAI,CAAC;IAC/C,SAAS,GAAG,IAAI,CAAC;IAEzB,YACU,YAA0B,EAC1B,QAAkB,EAClB,QAAgB;QAFhB,iBAAY,GAAZ,YAAY,CAAc;QAC1B,aAAQ,GAAR,QAAQ,CAAU;QAClB,aAAQ,GAAR,QAAQ,CAAQ;IACvB,CAAC;IAEJ,KAAK;QACH,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,QAAQ;QACpB,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;YACpB,IAAI,CAAC;gBACH,IAAI,CAAC,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;gBAC7C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,WAAW,CACjD,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,eAAe,CAAC,MAAM,CAC5B,CAAC;gBAEF,IAAI,CAAC,IAAI,CAAC,OAAO;oBAAE,MAAM;gBAEzB,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;oBACrB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;oBACtB,SAAS;gBACX,CAAC;gBAED,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;gBAEtB,MAAM,aAAa,GAAG,OAKrB,CAAC;gBAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;gBAC1D,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACtE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,IAAI,CAAC,OAAO;oBAAE,MAAM;gBACzB,IAAI,KAAK,YAAY,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;oBAAE,SAAS;gBAC3E,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;YACpC,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,OAAO;gBAAE,MAAM;YACzB,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;YACtD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,EAAE,cAAc,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface Config {
|
|
2
|
+
apiKey?: string;
|
|
3
|
+
serverUrl?: string;
|
|
4
|
+
}
|
|
5
|
+
interface DeviceInfo {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
type: string;
|
|
9
|
+
createdAt: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function loadConfig(): Config;
|
|
12
|
+
export declare function saveConfig(config: Config): void;
|
|
13
|
+
export declare function saveDevice(device: DeviceInfo): void;
|
|
14
|
+
export declare function listDevices(): DeviceInfo[];
|
|
15
|
+
export declare function removeDevice(id: string): boolean;
|
|
16
|
+
export declare function findDeviceByName(name: string): DeviceInfo | undefined;
|
|
17
|
+
export declare function getApiKey(cliApiKey?: string): string | undefined;
|
|
18
|
+
export declare function getServerUrl(cliServerUrl?: string): string;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
const CONFIG_DIR = path.join(os.homedir(), '.dokobot');
|
|
5
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
6
|
+
const DEVICES_DIR = path.join(CONFIG_DIR, 'devices');
|
|
7
|
+
function ensureDir(dir) {
|
|
8
|
+
if (!fs.existsSync(dir)) {
|
|
9
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function loadConfig() {
|
|
13
|
+
try {
|
|
14
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
15
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch { /* ignore */ }
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
export function saveConfig(config) {
|
|
22
|
+
ensureDir(CONFIG_DIR);
|
|
23
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
24
|
+
}
|
|
25
|
+
export function saveDevice(device) {
|
|
26
|
+
ensureDir(DEVICES_DIR);
|
|
27
|
+
const filePath = path.join(DEVICES_DIR, `${device.id}.json`);
|
|
28
|
+
fs.writeFileSync(filePath, JSON.stringify(device, null, 2));
|
|
29
|
+
}
|
|
30
|
+
export function listDevices() {
|
|
31
|
+
ensureDir(DEVICES_DIR);
|
|
32
|
+
const files = fs.readdirSync(DEVICES_DIR).filter(f => f.endsWith('.json'));
|
|
33
|
+
return files.map(f => {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(fs.readFileSync(path.join(DEVICES_DIR, f), 'utf-8'));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}).filter((d) => d !== null);
|
|
41
|
+
}
|
|
42
|
+
export function removeDevice(id) {
|
|
43
|
+
const filePath = path.join(DEVICES_DIR, `${id}.json`);
|
|
44
|
+
if (fs.existsSync(filePath)) {
|
|
45
|
+
fs.unlinkSync(filePath);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
export function findDeviceByName(name) {
|
|
51
|
+
return listDevices().find(d => d.name === name);
|
|
52
|
+
}
|
|
53
|
+
export function getApiKey(cliApiKey) {
|
|
54
|
+
return cliApiKey || process.env.DOKO_API_KEY || loadConfig().apiKey;
|
|
55
|
+
}
|
|
56
|
+
export function getServerUrl(cliServerUrl) {
|
|
57
|
+
return cliServerUrl || process.env.DOKO_SERVER_URL || loadConfig().serverUrl || 'https://dokobot.ai';
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { McpClient } from './mcp-client.js';
|
|
2
|
+
interface ServerCommand {
|
|
3
|
+
type: string;
|
|
4
|
+
requestId: string;
|
|
5
|
+
command: string;
|
|
6
|
+
params: {
|
|
7
|
+
tool: string;
|
|
8
|
+
arguments: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export declare class Executor {
|
|
12
|
+
private mcpClient;
|
|
13
|
+
constructor(mcpClient: McpClient);
|
|
14
|
+
execute(command: ServerCommand): Promise<Record<string, unknown>>;
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const STATUS_LINE = ` \x1b[32m●\x1b[0m Listening for commands... (Ctrl+C to quit)`;
|
|
2
|
+
function clearLine() {
|
|
3
|
+
process.stdout.write(`\r\x1b[2K`);
|
|
4
|
+
}
|
|
5
|
+
function showStatus() {
|
|
6
|
+
process.stdout.write(STATUS_LINE);
|
|
7
|
+
}
|
|
8
|
+
export class Executor {
|
|
9
|
+
mcpClient;
|
|
10
|
+
constructor(mcpClient) {
|
|
11
|
+
this.mcpClient = mcpClient;
|
|
12
|
+
}
|
|
13
|
+
async execute(command) {
|
|
14
|
+
if (command.command !== 'mcpToolCall') {
|
|
15
|
+
return { success: false, error: `Unknown command: ${command.command}` };
|
|
16
|
+
}
|
|
17
|
+
const { tool, arguments: args } = command.params;
|
|
18
|
+
const argsStr = args && Object.keys(args).length > 0 ? ` ${JSON.stringify(args)}` : '';
|
|
19
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
20
|
+
clearLine();
|
|
21
|
+
process.stdout.write(` \x1b[2m${timestamp}\x1b[0m \x1b[32m→\x1b[0m ${tool}${argsStr}`);
|
|
22
|
+
try {
|
|
23
|
+
const result = await this.mcpClient.callTool(tool, args || {});
|
|
24
|
+
console.log(` \x1b[32m✓\x1b[0m`);
|
|
25
|
+
showStatus();
|
|
26
|
+
return { success: true, data: result };
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
30
|
+
console.log(` \x1b[31m✗\x1b[0m ${message}`);
|
|
31
|
+
showStatus();
|
|
32
|
+
return { success: false, error: message };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/executor.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAAG,+DAA+D,CAAC;AAEpF,SAAS,SAAS;IAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AACpC,CAAC;AAYD,MAAM,OAAO,QAAQ;IACC;IAApB,YAAoB,SAAoB;QAApB,cAAS,GAAT,SAAS,CAAW;IAAG,CAAC;IAE5C,KAAK,CAAC,OAAO,CAAC,OAAsB;QAClC,IAAI,OAAO,CAAC,OAAO,KAAK,aAAa,EAAE,CAAC;YACtC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAC1E,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACjD,MAAM,OAAO,GAAG,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvF,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,kBAAkB,EAAE,CAAC;QAElD,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,SAAS,4BAA4B,IAAI,GAAG,OAAO,EAAE,CAAC,CAAC;QAExF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;YAC/D,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;YACjC,UAAU,EAAE,CAAC;YACb,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO,CAAC,GAAG,CAAC,qBAAqB,OAAO,EAAE,CAAC,CAAC;YAC5C,UAAU,EAAE,CAAC;YACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QAC5C,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ServerClient } from './server-client.js';
|
|
2
|
+
export declare class Heartbeat {
|
|
3
|
+
private serverClient;
|
|
4
|
+
private deviceId;
|
|
5
|
+
private timer;
|
|
6
|
+
constructor(serverClient: ServerClient, deviceId: string);
|
|
7
|
+
start(): void;
|
|
8
|
+
stop(): void;
|
|
9
|
+
private send;
|
|
10
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { logger } from './logger.js';
|
|
2
|
+
const HEARTBEAT_INTERVAL_MS = 60_000;
|
|
3
|
+
export class Heartbeat {
|
|
4
|
+
serverClient;
|
|
5
|
+
deviceId;
|
|
6
|
+
timer = null;
|
|
7
|
+
constructor(serverClient, deviceId) {
|
|
8
|
+
this.serverClient = serverClient;
|
|
9
|
+
this.deviceId = deviceId;
|
|
10
|
+
}
|
|
11
|
+
start() {
|
|
12
|
+
this.send();
|
|
13
|
+
this.timer = setInterval(() => this.send(), HEARTBEAT_INTERVAL_MS);
|
|
14
|
+
}
|
|
15
|
+
stop() {
|
|
16
|
+
if (this.timer) {
|
|
17
|
+
clearInterval(this.timer);
|
|
18
|
+
this.timer = null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async send() {
|
|
22
|
+
try {
|
|
23
|
+
await this.serverClient.heartbeat(this.deviceId);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
logger.warn('Heartbeat failed', error);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../src/heartbeat.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAErC,MAAM,OAAO,SAAS;IAIV;IACA;IAJF,KAAK,GAA0C,IAAI,CAAC;IAE5D,YACU,YAA0B,EAC1B,QAAgB;QADhB,iBAAY,GAAZ,YAAY,CAAc;QAC1B,aAAQ,GAAR,QAAQ,CAAQ;IACvB,CAAC;IAEJ,KAAK;QACH,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,qBAAqB,CAAC,CAAC;IACrE,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare const LEVELS: {
|
|
2
|
+
readonly debug: 0;
|
|
3
|
+
readonly info: 1;
|
|
4
|
+
readonly warn: 2;
|
|
5
|
+
readonly error: 3;
|
|
6
|
+
};
|
|
7
|
+
type Level = keyof typeof LEVELS;
|
|
8
|
+
export declare function setLogLevel(level: Level): void;
|
|
9
|
+
export declare const logger: {
|
|
10
|
+
debug: (msg: string, data?: unknown) => void;
|
|
11
|
+
info: (msg: string, data?: unknown) => void;
|
|
12
|
+
warn: (msg: string, data?: unknown) => void;
|
|
13
|
+
error: (msg: string, data?: unknown) => void;
|
|
14
|
+
};
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
2
|
+
let currentLevel = 'info';
|
|
3
|
+
export function setLogLevel(level) {
|
|
4
|
+
currentLevel = level;
|
|
5
|
+
}
|
|
6
|
+
function log(level, message, data) {
|
|
7
|
+
if (LEVELS[level] < LEVELS[currentLevel])
|
|
8
|
+
return;
|
|
9
|
+
const prefix = `[${new Date().toISOString()}] [${level.toUpperCase()}]`;
|
|
10
|
+
if (data !== undefined) {
|
|
11
|
+
console.error(`${prefix} ${message}`, data);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
console.error(`${prefix} ${message}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export const logger = {
|
|
18
|
+
debug: (msg, data) => log('debug', msg, data),
|
|
19
|
+
info: (msg, data) => log('info', msg, data),
|
|
20
|
+
warn: (msg, data) => log('warn', msg, data),
|
|
21
|
+
error: (msg, data) => log('error', msg, data),
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAW,CAAC;AAGjE,IAAI,YAAY,GAAU,MAAM,CAAC;AAEjC,MAAM,UAAU,WAAW,CAAC,KAAY;IACtC,YAAY,GAAG,KAAK,CAAC;AACvB,CAAC;AAED,SAAS,GAAG,CAAC,KAAY,EAAE,OAAe,EAAE,IAAc;IACxD,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC;QAAE,OAAO;IACjD,MAAM,MAAM,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC;IACxE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;IAC/D,IAAI,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;IAC7D,IAAI,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;IAC7D,KAAK,EAAE,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;CAChE,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface McpTool {
|
|
2
|
+
name: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
inputSchema?: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
export declare class McpClient {
|
|
7
|
+
private client;
|
|
8
|
+
private transport;
|
|
9
|
+
connect(options?: {
|
|
10
|
+
browserUrl?: string;
|
|
11
|
+
extraArgs?: string[];
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
listTools(): Promise<McpTool[]>;
|
|
14
|
+
callTool(name: string, args: Record<string, unknown>): Promise<unknown>;
|
|
15
|
+
disconnect(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
+
import { getCurrentVersion } from './version.js';
|
|
4
|
+
export class McpClient {
|
|
5
|
+
client = null;
|
|
6
|
+
transport = null;
|
|
7
|
+
async connect(options = {}) {
|
|
8
|
+
const args = ['chrome-devtools-mcp@latest'];
|
|
9
|
+
if (options.browserUrl) {
|
|
10
|
+
args.push('--browserUrl', options.browserUrl);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
args.push('--autoConnect');
|
|
14
|
+
}
|
|
15
|
+
if (options.extraArgs) {
|
|
16
|
+
args.push(...options.extraArgs);
|
|
17
|
+
}
|
|
18
|
+
this.transport = new StdioClientTransport({
|
|
19
|
+
command: 'npx',
|
|
20
|
+
args,
|
|
21
|
+
stderr: 'pipe',
|
|
22
|
+
});
|
|
23
|
+
this.client = new Client({ name: 'dokobot-cli', version: getCurrentVersion() }, { capabilities: {} });
|
|
24
|
+
await this.client.connect(this.transport);
|
|
25
|
+
}
|
|
26
|
+
async listTools() {
|
|
27
|
+
if (!this.client)
|
|
28
|
+
throw new Error('MCP client not connected');
|
|
29
|
+
const result = await this.client.listTools();
|
|
30
|
+
return result.tools.map(t => ({
|
|
31
|
+
name: t.name,
|
|
32
|
+
description: t.description,
|
|
33
|
+
inputSchema: t.inputSchema,
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
async callTool(name, args) {
|
|
37
|
+
if (!this.client)
|
|
38
|
+
throw new Error('MCP client not connected');
|
|
39
|
+
return this.client.callTool({ name, arguments: args });
|
|
40
|
+
}
|
|
41
|
+
async disconnect() {
|
|
42
|
+
if (this.client) {
|
|
43
|
+
await this.client.close().catch(() => { });
|
|
44
|
+
this.client = null;
|
|
45
|
+
}
|
|
46
|
+
this.transport = null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=mcp-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-client.js","sourceRoot":"","sources":["../../src/mcp-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAQjD,MAAM,OAAO,SAAS;IACZ,MAAM,GAAkB,IAAI,CAAC;IAC7B,SAAS,GAAgC,IAAI,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,UAAyD,EAAE;QACvE,MAAM,IAAI,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAE5C,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC7B,CAAC;QAED,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC;QAED,IAAI,CAAC,SAAS,GAAG,IAAI,oBAAoB,CAAC;YACxC,OAAO,EAAE,KAAK;YACd,IAAI;YACJ,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CACtB,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,iBAAiB,EAAE,EAAE,EACrD,EAAE,YAAY,EAAE,EAAE,EAAE,CACrB,CAAC;QAEF,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,SAAS;QACb,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QAC7C,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC5B,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,WAAW,EAAE,CAAC,CAAC,WAAkD;SAClE,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,IAA6B;QACxD,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9D,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YAC1C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxB,CAAC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface McpTool {
|
|
2
|
+
name: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
inputSchema?: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
interface RegisterResponse {
|
|
7
|
+
deviceId: string;
|
|
8
|
+
name: string;
|
|
9
|
+
type: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class ServerClient {
|
|
12
|
+
private serverUrl;
|
|
13
|
+
private apiKey;
|
|
14
|
+
constructor(serverUrl: string, apiKey: string);
|
|
15
|
+
private request;
|
|
16
|
+
connect(params: {
|
|
17
|
+
name?: string;
|
|
18
|
+
type: 'chrome';
|
|
19
|
+
deviceId?: string;
|
|
20
|
+
tools?: McpTool[];
|
|
21
|
+
}): Promise<RegisterResponse>;
|
|
22
|
+
disconnect(deviceId: string): Promise<void>;
|
|
23
|
+
heartbeat(deviceId: string): Promise<void>;
|
|
24
|
+
pollCommand(deviceId: string, signal?: AbortSignal): Promise<Record<string, unknown> | null>;
|
|
25
|
+
sendResult(requestId: string, result: Record<string, unknown>): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { logger } from './logger.js';
|
|
2
|
+
export class ServerClient {
|
|
3
|
+
serverUrl;
|
|
4
|
+
apiKey;
|
|
5
|
+
constructor(serverUrl, apiKey) {
|
|
6
|
+
this.serverUrl = serverUrl;
|
|
7
|
+
this.apiKey = apiKey;
|
|
8
|
+
}
|
|
9
|
+
async request(path, options = {}) {
|
|
10
|
+
const url = `${this.serverUrl}${path}`;
|
|
11
|
+
const res = await fetch(url, {
|
|
12
|
+
...options,
|
|
13
|
+
headers: {
|
|
14
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
15
|
+
'Content-Type': 'application/json',
|
|
16
|
+
...options.headers,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
return res;
|
|
20
|
+
}
|
|
21
|
+
async connect(params) {
|
|
22
|
+
const res = await this.request('/api/dokos/connect', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
body: JSON.stringify(params),
|
|
25
|
+
});
|
|
26
|
+
if (!res.ok) {
|
|
27
|
+
const body = await res.json().catch(() => ({}));
|
|
28
|
+
throw new Error(`Registration failed (${res.status}): ${body.error || res.statusText}`);
|
|
29
|
+
}
|
|
30
|
+
return res.json();
|
|
31
|
+
}
|
|
32
|
+
async disconnect(deviceId) {
|
|
33
|
+
try {
|
|
34
|
+
await this.request('/api/dokos/disconnect', {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
body: JSON.stringify({ deviceId }),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
catch { /* best effort */ }
|
|
40
|
+
}
|
|
41
|
+
async heartbeat(deviceId) {
|
|
42
|
+
const res = await this.request('/api/devices/heartbeat', {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
body: JSON.stringify({ deviceId }),
|
|
45
|
+
});
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
logger.warn(`Heartbeat failed: ${res.status}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async pollCommand(deviceId, signal) {
|
|
51
|
+
const res = await this.request(`/api/commands/poll?deviceId=${encodeURIComponent(deviceId)}`, {
|
|
52
|
+
method: 'GET',
|
|
53
|
+
signal,
|
|
54
|
+
});
|
|
55
|
+
if (res.status === 204)
|
|
56
|
+
return null;
|
|
57
|
+
if (!res.ok) {
|
|
58
|
+
throw new Error(`Poll failed: ${res.status}`);
|
|
59
|
+
}
|
|
60
|
+
return res.json();
|
|
61
|
+
}
|
|
62
|
+
async sendResult(requestId, result) {
|
|
63
|
+
const maxRetries = 3;
|
|
64
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
65
|
+
try {
|
|
66
|
+
const res = await this.request(`/api/commands/${requestId}/result`, {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
body: JSON.stringify(result),
|
|
69
|
+
});
|
|
70
|
+
if (res.ok)
|
|
71
|
+
return;
|
|
72
|
+
logger.warn(`sendResult HTTP error: ${res.status}, attempt ${attempt + 1}`);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
logger.error(`sendResult failed, attempt ${attempt + 1}`, error);
|
|
76
|
+
}
|
|
77
|
+
if (attempt < maxRetries - 1) {
|
|
78
|
+
await new Promise(r => setTimeout(r, 300 * (attempt + 1)));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
logger.error(`sendResult failed after all retries for ${requestId}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=server-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-client.js","sourceRoot":"","sources":["../../src/server-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface UpdateInfo {
|
|
2
|
+
current: string;
|
|
3
|
+
latest: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function initUpdateCheck(): void;
|
|
6
|
+
export declare function getUpdateResult(): UpdateInfo | null;
|
|
7
|
+
export declare function checkForUpdateNow(): Promise<UpdateInfo | null>;
|
|
8
|
+
export declare function formatUpdateNotification(info: UpdateInfo): string;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as https from 'https';
|
|
5
|
+
import { getCurrentVersion, compareVersions } from './version.js';
|
|
6
|
+
const CACHE_DIR = path.join(os.homedir(), '.dokobot');
|
|
7
|
+
const CACHE_FILE = path.join(CACHE_DIR, 'update-check.json');
|
|
8
|
+
const CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
|
|
9
|
+
const FETCH_TIMEOUT = 5000; // 5 seconds
|
|
10
|
+
const REGISTRY_URL = 'https://registry.npmjs.org/@dokobot/cli/latest';
|
|
11
|
+
let pendingCheck = null;
|
|
12
|
+
let resolvedResult = null;
|
|
13
|
+
function readCache() {
|
|
14
|
+
try {
|
|
15
|
+
const data = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));
|
|
16
|
+
return data;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function writeCache(data) {
|
|
23
|
+
try {
|
|
24
|
+
if (!fs.existsSync(CACHE_DIR)) {
|
|
25
|
+
fs.mkdirSync(CACHE_DIR, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify(data));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// ignore write errors
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function fetchLatestVersion() {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const req = https.get(REGISTRY_URL, { timeout: FETCH_TIMEOUT }, (res) => {
|
|
36
|
+
let body = '';
|
|
37
|
+
res.on('data', (chunk) => { body += chunk.toString(); });
|
|
38
|
+
res.on('end', () => {
|
|
39
|
+
try {
|
|
40
|
+
const data = JSON.parse(body);
|
|
41
|
+
resolve(data.version);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
reject(new Error('Invalid JSON from registry'));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
req.on('error', reject);
|
|
49
|
+
req.on('timeout', () => {
|
|
50
|
+
req.destroy();
|
|
51
|
+
reject(new Error('Request timed out'));
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async function performCheck(skipCache = false) {
|
|
56
|
+
const current = getCurrentVersion();
|
|
57
|
+
if (!skipCache) {
|
|
58
|
+
const cache = readCache();
|
|
59
|
+
if (cache && Date.now() - cache.lastCheckedAt < CHECK_INTERVAL) {
|
|
60
|
+
if (compareVersions(cache.latestVersion, current) > 0) {
|
|
61
|
+
return { current, latest: cache.latestVersion };
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const latest = await fetchLatestVersion();
|
|
68
|
+
writeCache({ lastCheckedAt: Date.now(), latestVersion: latest });
|
|
69
|
+
if (compareVersions(latest, current) > 0) {
|
|
70
|
+
return { current, latest };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// silently ignore network errors
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
export function initUpdateCheck() {
|
|
79
|
+
pendingCheck = performCheck().then((result) => {
|
|
80
|
+
resolvedResult = result;
|
|
81
|
+
return result;
|
|
82
|
+
}).catch(() => null);
|
|
83
|
+
}
|
|
84
|
+
export function getUpdateResult() {
|
|
85
|
+
return resolvedResult;
|
|
86
|
+
}
|
|
87
|
+
export async function checkForUpdateNow() {
|
|
88
|
+
return performCheck(true);
|
|
89
|
+
}
|
|
90
|
+
export function formatUpdateNotification(info) {
|
|
91
|
+
const Y = '\x1b[33m';
|
|
92
|
+
const C = '\x1b[36m';
|
|
93
|
+
const B = '\x1b[1m';
|
|
94
|
+
const R = '\x1b[0m';
|
|
95
|
+
const D = '\x1b[2m';
|
|
96
|
+
const line = `${D}─${'─'.repeat(48)}${R}`;
|
|
97
|
+
return [
|
|
98
|
+
'',
|
|
99
|
+
line,
|
|
100
|
+
` ${Y}Update available!${R} ${D}${info.current}${R} → ${C}${B}${info.latest}${R}`,
|
|
101
|
+
` Run ${C}npm i -g @dokobot/cli${R} to update`,
|
|
102
|
+
line,
|
|
103
|
+
'',
|
|
104
|
+
].join('\n');
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=update-checker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-checker.js","sourceRoot":"","sources":["../../src/update-checker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
let cachedVersion;
|
|
5
|
+
export function getCurrentVersion() {
|
|
6
|
+
if (cachedVersion)
|
|
7
|
+
return cachedVersion;
|
|
8
|
+
try {
|
|
9
|
+
const thisDir = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const pkgPath = path.resolve(thisDir, '../../package.json');
|
|
11
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
12
|
+
cachedVersion = pkg.version;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
cachedVersion = '0.0.0';
|
|
16
|
+
}
|
|
17
|
+
return cachedVersion;
|
|
18
|
+
}
|
|
19
|
+
export function compareVersions(a, b) {
|
|
20
|
+
const pa = a.split('.').map(Number);
|
|
21
|
+
const pb = b.split('.').map(Number);
|
|
22
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
23
|
+
const diff = (pa[i] || 0) - (pb[i] || 0);
|
|
24
|
+
if (diff !== 0)
|
|
25
|
+
return diff > 0 ? 1 : -1;
|
|
26
|
+
}
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,IAAI,aAAiC,CAAC;AAEtC,MAAM,UAAU,iBAAiB;IAC/B,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QAC1D,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,aAAa,GAAG,OAAO,CAAC;IAC1B,CAAC;IACD,OAAO,aAAc,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,CAAS,EAAE,CAAS;IAClD,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACxD,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACzC,IAAI,IAAI,KAAK,CAAC;YAAE,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dokobot/cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Dokobot CLI - Connect Chrome browser as a doko via Chrome DevTools MCP",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dokobot": "./dist/bin/dokobot.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsx bin/dokobot.ts",
|
|
12
|
+
"clean": "rm -rf dist",
|
|
13
|
+
"prepublishOnly": "yarn build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": ["dokobot", "chrome", "devtools", "mcp", "browser", "ai-agent", "cli"],
|
|
16
|
+
"homepage": "https://dokobot.ai",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/dokobot/dokobot",
|
|
20
|
+
"directory": "cli"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
24
|
+
"commander": "^12.0.0",
|
|
25
|
+
"inquirer": "^12.0.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^24.3.0",
|
|
29
|
+
"tsx": "^4.0.0",
|
|
30
|
+
"typescript": "^5"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
}
|
|
39
|
+
}
|