@everydaydevopsio/pilot 0.5.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.
Files changed (108) hide show
  1. package/.ai-agent-bridge.yaml +10 -0
  2. package/.dockerignore +9 -0
  3. package/CLAUDE.md +61 -0
  4. package/LICENSE +21 -0
  5. package/README.md +252 -0
  6. package/dist/browser.d.ts +100 -0
  7. package/dist/browser.d.ts.map +1 -0
  8. package/dist/browser.js +654 -0
  9. package/dist/browser.js.map +1 -0
  10. package/dist/cli/init.d.ts +4 -0
  11. package/dist/cli/init.d.ts.map +1 -0
  12. package/dist/cli/init.js +56 -0
  13. package/dist/cli/init.js.map +1 -0
  14. package/dist/cli/skill-template.d.ts +2 -0
  15. package/dist/cli/skill-template.d.ts.map +1 -0
  16. package/dist/cli/skill-template.js +114 -0
  17. package/dist/cli/skill-template.js.map +1 -0
  18. package/dist/commands/click.d.ts +21 -0
  19. package/dist/commands/click.d.ts.map +1 -0
  20. package/dist/commands/click.js +64 -0
  21. package/dist/commands/click.js.map +1 -0
  22. package/dist/commands/evaluate.d.ts +14 -0
  23. package/dist/commands/evaluate.d.ts.map +1 -0
  24. package/dist/commands/evaluate.js +28 -0
  25. package/dist/commands/evaluate.js.map +1 -0
  26. package/dist/commands/navigate.d.ts +19 -0
  27. package/dist/commands/navigate.d.ts.map +1 -0
  28. package/dist/commands/navigate.js +78 -0
  29. package/dist/commands/navigate.js.map +1 -0
  30. package/dist/commands/page_info.d.ts +8 -0
  31. package/dist/commands/page_info.d.ts.map +1 -0
  32. package/dist/commands/page_info.js +13 -0
  33. package/dist/commands/page_info.js.map +1 -0
  34. package/dist/commands/screenshot.d.ts +18 -0
  35. package/dist/commands/screenshot.d.ts.map +1 -0
  36. package/dist/commands/screenshot.js +37 -0
  37. package/dist/commands/screenshot.js.map +1 -0
  38. package/dist/commands/tabs.d.ts +10 -0
  39. package/dist/commands/tabs.d.ts.map +1 -0
  40. package/dist/commands/tabs.js +15 -0
  41. package/dist/commands/tabs.js.map +1 -0
  42. package/dist/commands/type.d.ts +14 -0
  43. package/dist/commands/type.d.ts.map +1 -0
  44. package/dist/commands/type.js +61 -0
  45. package/dist/commands/type.js.map +1 -0
  46. package/dist/commands/wait.d.ts +17 -0
  47. package/dist/commands/wait.d.ts.map +1 -0
  48. package/dist/commands/wait.js +61 -0
  49. package/dist/commands/wait.js.map +1 -0
  50. package/dist/mcp/console-buffer.d.ts +28 -0
  51. package/dist/mcp/console-buffer.d.ts.map +1 -0
  52. package/dist/mcp/console-buffer.js +68 -0
  53. package/dist/mcp/console-buffer.js.map +1 -0
  54. package/dist/mcp/index.d.ts +3 -0
  55. package/dist/mcp/index.d.ts.map +1 -0
  56. package/dist/mcp/index.js +74 -0
  57. package/dist/mcp/index.js.map +1 -0
  58. package/dist/mcp/server.d.ts +18 -0
  59. package/dist/mcp/server.d.ts.map +1 -0
  60. package/dist/mcp/server.js +46 -0
  61. package/dist/mcp/server.js.map +1 -0
  62. package/dist/mcp/tools/browser.d.ts +5 -0
  63. package/dist/mcp/tools/browser.d.ts.map +1 -0
  64. package/dist/mcp/tools/browser.js +435 -0
  65. package/dist/mcp/tools/browser.js.map +1 -0
  66. package/dist/mcp/tools/errors.d.ts +4 -0
  67. package/dist/mcp/tools/errors.d.ts.map +1 -0
  68. package/dist/mcp/tools/errors.js +117 -0
  69. package/dist/mcp/tools/errors.js.map +1 -0
  70. package/dist/mcp/tools/names.d.ts +7 -0
  71. package/dist/mcp/tools/names.d.ts.map +1 -0
  72. package/dist/mcp/tools/names.js +24 -0
  73. package/dist/mcp/tools/names.js.map +1 -0
  74. package/dist/util/config.d.ts +34 -0
  75. package/dist/util/config.d.ts.map +1 -0
  76. package/dist/util/config.js +53 -0
  77. package/dist/util/config.js.map +1 -0
  78. package/dist/util/logger.d.ts +4 -0
  79. package/dist/util/logger.d.ts.map +1 -0
  80. package/dist/util/logger.js +16 -0
  81. package/dist/util/logger.js.map +1 -0
  82. package/dist/viewport.d.ts +105 -0
  83. package/dist/viewport.d.ts.map +1 -0
  84. package/dist/viewport.js +145 -0
  85. package/dist/viewport.js.map +1 -0
  86. package/package.json +74 -0
  87. package/smoke/mcp-smoke.ts +161 -0
  88. package/src/browser.ts +798 -0
  89. package/src/cli/init.ts +62 -0
  90. package/src/cli/skill-template.ts +113 -0
  91. package/src/commands/click.ts +93 -0
  92. package/src/commands/evaluate.ts +45 -0
  93. package/src/commands/navigate.ts +105 -0
  94. package/src/commands/page_info.ts +21 -0
  95. package/src/commands/screenshot.ts +72 -0
  96. package/src/commands/tabs.ts +35 -0
  97. package/src/commands/type.ts +77 -0
  98. package/src/commands/wait.ts +90 -0
  99. package/src/mcp/console-buffer.ts +94 -0
  100. package/src/mcp/index.ts +107 -0
  101. package/src/mcp/server.ts +67 -0
  102. package/src/mcp/tools/browser.ts +605 -0
  103. package/src/mcp/tools/errors.ts +158 -0
  104. package/src/mcp/tools/names.ts +25 -0
  105. package/src/util/config.ts +71 -0
  106. package/src/util/logger.ts +18 -0
  107. package/src/viewport.ts +204 -0
  108. package/tsconfig.json +24 -0
@@ -0,0 +1,90 @@
1
+ import { z } from 'zod';
2
+ import type { Client } from 'chrome-remote-interface';
3
+ import type { BrowserManager } from '../browser.js';
4
+
5
+ export const WaitParamsSchema = z
6
+ .object({
7
+ selector: z.string().optional(),
8
+ selectorVisible: z.string().optional(),
9
+ networkIdle: z.boolean().optional(),
10
+ ms: z.number().int().min(0).optional(),
11
+ timeoutMs: z.number().int().positive().default(10000)
12
+ })
13
+ .refine(
14
+ (v) =>
15
+ v.selector !== undefined ||
16
+ v.selectorVisible !== undefined ||
17
+ v.networkIdle !== undefined ||
18
+ v.ms !== undefined,
19
+ { message: 'At least one wait condition is required' }
20
+ );
21
+
22
+ export type WaitParams = z.infer<typeof WaitParamsSchema>;
23
+
24
+ export interface WaitResult {
25
+ ok: boolean;
26
+ elapsed: number;
27
+ }
28
+
29
+ async function waitForSelectorPresent(
30
+ client: Client,
31
+ selector: string,
32
+ timeoutMs: number
33
+ ): Promise<void> {
34
+ const start = Date.now();
35
+ while (Date.now() - start < timeoutMs) {
36
+ const result = await client.Runtime.evaluate({
37
+ expression: `!!document.querySelector(${JSON.stringify(selector)})`,
38
+ returnByValue: true
39
+ });
40
+ if (result.result.value === true) return;
41
+ await new Promise((r) => setTimeout(r, 100));
42
+ }
43
+ throw new Error(`timeout waiting for selector: ${selector}`);
44
+ }
45
+
46
+ async function waitForSelectorVisible(
47
+ client: Client,
48
+ selector: string,
49
+ timeoutMs: number
50
+ ): Promise<void> {
51
+ const start = Date.now();
52
+ while (Date.now() - start < timeoutMs) {
53
+ const result = await client.Runtime.evaluate({
54
+ expression: `(function() {
55
+ const el = document.querySelector(${JSON.stringify(selector)});
56
+ if (!el) return false;
57
+ const rect = el.getBoundingClientRect();
58
+ return rect.width > 0 && rect.height > 0;
59
+ })()`,
60
+ returnByValue: true
61
+ });
62
+ if (result.result.value === true) return;
63
+ await new Promise((r) => setTimeout(r, 100));
64
+ }
65
+ throw new Error(`timeout waiting for visible selector: ${selector}`);
66
+ }
67
+
68
+ export async function executeWait(
69
+ client: Client,
70
+ manager: BrowserManager,
71
+ params: WaitParams
72
+ ): Promise<WaitResult> {
73
+ const start = Date.now();
74
+
75
+ if (params.ms !== undefined) {
76
+ await new Promise((r) => setTimeout(r, params.ms));
77
+ } else if (params.selector !== undefined) {
78
+ await waitForSelectorPresent(client, params.selector, params.timeoutMs);
79
+ } else if (params.selectorVisible !== undefined) {
80
+ await waitForSelectorVisible(
81
+ client,
82
+ params.selectorVisible,
83
+ params.timeoutMs
84
+ );
85
+ } else if (params.networkIdle) {
86
+ await manager.waitForNetworkIdle(params.timeoutMs);
87
+ }
88
+
89
+ return { ok: true, elapsed: Date.now() - start };
90
+ }
@@ -0,0 +1,94 @@
1
+ export type ConsoleLevel = 'log' | 'warn' | 'error' | 'info' | 'debug';
2
+
3
+ export interface ConsoleMessage {
4
+ level: ConsoleLevel;
5
+ text: string;
6
+ url: string;
7
+ lineNumber: number;
8
+ timestamp: number;
9
+ }
10
+
11
+ export interface ConsoleFilter {
12
+ level?: ConsoleLevel | ConsoleLevel[];
13
+ sinceMs?: number;
14
+ limit?: number;
15
+ }
16
+
17
+ export class ConsoleBuffer {
18
+ private buffer: ConsoleMessage[] = [];
19
+ private head = 0;
20
+ private count = 0;
21
+
22
+ constructor(private readonly maxSize: number = 1000) {}
23
+
24
+ push(msg: ConsoleMessage): void {
25
+ if (this.count < this.maxSize) {
26
+ this.buffer.push(msg);
27
+ this.count++;
28
+ } else {
29
+ this.buffer[this.head] = msg;
30
+ this.head = (this.head + 1) % this.maxSize;
31
+ }
32
+ }
33
+
34
+ getAll(filter?: ConsoleFilter): ConsoleMessage[] {
35
+ const messages = this.toArray();
36
+ return this.applyFilter(messages, filter);
37
+ }
38
+
39
+ getErrors(sinceMs?: number): ConsoleMessage[] {
40
+ return this.getAll({
41
+ level: 'error',
42
+ sinceMs
43
+ });
44
+ }
45
+
46
+ clear(): number {
47
+ const cleared = this.count;
48
+ this.buffer = [];
49
+ this.head = 0;
50
+ this.count = 0;
51
+ return cleared;
52
+ }
53
+
54
+ size(): number {
55
+ return this.count;
56
+ }
57
+
58
+ private toArray(): ConsoleMessage[] {
59
+ if (this.count < this.maxSize) {
60
+ return [...this.buffer];
61
+ }
62
+ return [
63
+ ...this.buffer.slice(this.head),
64
+ ...this.buffer.slice(0, this.head)
65
+ ];
66
+ }
67
+
68
+ private applyFilter(
69
+ messages: ConsoleMessage[],
70
+ filter?: ConsoleFilter
71
+ ): ConsoleMessage[] {
72
+ if (!filter) return messages;
73
+
74
+ let result = messages;
75
+
76
+ if (filter.level) {
77
+ const levels = Array.isArray(filter.level)
78
+ ? filter.level
79
+ : [filter.level];
80
+ result = result.filter((m) => levels.includes(m.level));
81
+ }
82
+
83
+ if (filter.sinceMs !== undefined) {
84
+ const cutoff = Date.now() - filter.sinceMs;
85
+ result = result.filter((m) => m.timestamp >= cutoff);
86
+ }
87
+
88
+ if (filter.limit !== undefined && result.length > filter.limit) {
89
+ result = result.slice(-filter.limit);
90
+ }
91
+
92
+ return result;
93
+ }
94
+ }
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync } from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
+ import { createMcpServer } from './server.js';
8
+ import { loadConfig } from '../util/config.js';
9
+ import { createLogger } from '../util/logger.js';
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const pkg = JSON.parse(
13
+ readFileSync(join(__dirname, '../../package.json'), 'utf-8')
14
+ ) as { version: string; description: string; name: string };
15
+
16
+ const args = process.argv.slice(2);
17
+
18
+ async function run(): Promise<void> {
19
+ if (args.includes('--version') || args.includes('-v')) {
20
+ console.log(pkg.version);
21
+ return;
22
+ }
23
+
24
+ if (args.includes('--help') || args.includes('-h')) {
25
+ console.log(`${pkg.name} v${pkg.version}`);
26
+ console.log(pkg.description);
27
+ console.log('');
28
+ console.log('Usage: pilot [command] [options]');
29
+ console.log('');
30
+ console.log('Commands:');
31
+ console.log(
32
+ ' init [--force] Install the Claude Code debug-browser skill'
33
+ );
34
+ console.log(' (default) Start the MCP server on stdio');
35
+ console.log('');
36
+ console.log('Options:');
37
+ console.log(' --version, -v Show version number');
38
+ console.log(' --help, -h Show this help message');
39
+ console.log('');
40
+ console.log('Environment variables:');
41
+ console.log(
42
+ ' PILOT_CDP_PORT Chrome DevTools Protocol port (default: 9222)'
43
+ );
44
+ console.log(
45
+ ' PILOT_CDP_HOST Chrome DevTools Protocol host (default: 127.0.0.1)'
46
+ );
47
+ console.log(
48
+ ' PILOT_MCP_BUFFER_SIZE Console event buffer size (default: 1000)'
49
+ );
50
+ console.log(
51
+ ' PILOT_LOG_LEVEL Log level: trace|debug|info|warn|error|fatal (default: info)'
52
+ );
53
+ console.log(
54
+ ' PILOT_PROFILE_NAME Browser profile name (default: profile1)'
55
+ );
56
+ console.log(
57
+ ' PILOT_CHROME_NO_SANDBOX Override Chrome sandbox (true/false)'
58
+ );
59
+ console.log(' PILOT_VIEWPORT Viewport preset (default: desktop)');
60
+ console.log(
61
+ ' Presets: desktop, desktop-small, tablet,'
62
+ );
63
+ console.log(
64
+ ' tablet-landscape, mobile, mobile-landscape, mobile-small'
65
+ );
66
+ return;
67
+ }
68
+
69
+ const subcommand = args[0] && !args[0].startsWith('-') ? args[0] : null;
70
+
71
+ const baseConfig = loadConfig();
72
+ createLogger(baseConfig.logLevel);
73
+
74
+ if (subcommand === 'init') {
75
+ const { runInit } = await import('../cli/init.js');
76
+ const force = args.includes('--force');
77
+ await runInit({ force });
78
+ return;
79
+ }
80
+
81
+ const config = {
82
+ bufferSize: parseInt(process.env.PILOT_MCP_BUFFER_SIZE ?? '1000', 10),
83
+ cdpPort: process.env.PILOT_CDP_PORT
84
+ ? parseInt(process.env.PILOT_CDP_PORT, 10)
85
+ : undefined,
86
+ cdpHost: process.env.PILOT_CDP_HOST
87
+ };
88
+
89
+ const { server, cleanup } = await createMcpServer(config);
90
+ const transport = new StdioServerTransport();
91
+
92
+ async function shutdown(): Promise<void> {
93
+ await cleanup();
94
+ process.exit(0);
95
+ }
96
+
97
+ process.on('SIGINT', () => void shutdown());
98
+ process.on('SIGTERM', () => void shutdown());
99
+
100
+ await server.connect(transport);
101
+ console.error('[pilot] MCP server running on stdio');
102
+ }
103
+
104
+ run().catch((err) => {
105
+ console.error('[pilot] Failed to start:', err);
106
+ process.exit(1);
107
+ });
@@ -0,0 +1,67 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { BrowserManager } from '../browser.js';
3
+ import { loadConfig } from '../util/config.js';
4
+ import { ConsoleBuffer, type ConsoleMessage } from './console-buffer.js';
5
+ import { registerBrowserTools } from './tools/browser.js';
6
+ import { registerErrorTools } from './tools/errors.js';
7
+
8
+ export interface McpConfig {
9
+ bufferSize: number;
10
+ cdpPort?: number;
11
+ cdpHost?: string;
12
+ }
13
+
14
+ export interface BrowserContext {
15
+ manager: BrowserManager | null;
16
+ consoleBuffer: ConsoleBuffer;
17
+ baseConfig: McpConfig;
18
+ }
19
+
20
+ export async function createMcpServer(config: McpConfig): Promise<{
21
+ server: McpServer;
22
+ cleanup: () => Promise<void>;
23
+ }> {
24
+ const server = new McpServer({
25
+ name: 'pilot',
26
+ version: '0.1.0'
27
+ });
28
+
29
+ const consoleBuffer = new ConsoleBuffer(config.bufferSize);
30
+
31
+ const context: BrowserContext = {
32
+ manager: null,
33
+ consoleBuffer,
34
+ baseConfig: config
35
+ };
36
+
37
+ function attachConsoleBuffer(manager: BrowserManager): void {
38
+ manager.setEventCallback((event, data) => {
39
+ if (event === 'console_message') {
40
+ consoleBuffer.push(data as ConsoleMessage);
41
+ }
42
+ });
43
+ }
44
+
45
+ function makeBrowserManager(): BrowserManager {
46
+ const browserConfig = loadConfig({
47
+ cdpPort: config.cdpPort,
48
+ cdpHost: config.cdpHost
49
+ });
50
+ const manager = new BrowserManager(browserConfig);
51
+ attachConsoleBuffer(manager);
52
+ return manager;
53
+ }
54
+
55
+ registerBrowserTools(server, context, makeBrowserManager);
56
+ registerErrorTools(server, consoleBuffer);
57
+
58
+ return {
59
+ server,
60
+ cleanup: async () => {
61
+ if (context.manager) {
62
+ await context.manager.destroy();
63
+ context.manager = null;
64
+ }
65
+ }
66
+ };
67
+ }