opendevbrowser 0.0.11 → 0.0.15

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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +289 -28
  3. package/dist/chunk-JVBMT2O5.js +7173 -0
  4. package/dist/chunk-JVBMT2O5.js.map +1 -0
  5. package/dist/cli/index.js +3690 -275
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/index.js +1080 -2857
  8. package/dist/index.js.map +1 -1
  9. package/dist/opendevbrowser.js +1080 -2857
  10. package/dist/opendevbrowser.js.map +1 -1
  11. package/extension/dist/annotate-content.css +237 -0
  12. package/extension/dist/annotate-content.js +934 -0
  13. package/extension/dist/background.js +1291 -8
  14. package/extension/dist/logging.js +50 -0
  15. package/extension/dist/ops/dom-bridge.js +355 -0
  16. package/extension/dist/ops/ops-runtime.js +1249 -0
  17. package/extension/dist/ops/ops-session-store.js +189 -0
  18. package/extension/dist/ops/redaction.js +52 -0
  19. package/extension/dist/ops/snapshot-builder.js +4 -0
  20. package/extension/dist/ops/snapshot-shared.js +220 -0
  21. package/extension/dist/popup.js +398 -21
  22. package/extension/dist/relay-settings.js +3 -1
  23. package/extension/dist/services/CDPRouter.js +501 -103
  24. package/extension/dist/services/ConnectionManager.js +464 -57
  25. package/extension/dist/services/NativePortManager.js +182 -0
  26. package/extension/dist/services/RelayClient.js +227 -26
  27. package/extension/dist/services/TabManager.js +81 -0
  28. package/extension/dist/services/TargetSessionMap.js +146 -0
  29. package/extension/dist/services/cdp-router-commands.js +203 -0
  30. package/extension/dist/services/url-restrictions.js +41 -0
  31. package/extension/dist/types.js +3 -1
  32. package/extension/icons/icon128.png +0 -0
  33. package/extension/icons/icon16.png +0 -0
  34. package/extension/icons/icon32.png +0 -0
  35. package/extension/icons/icon48.png +0 -0
  36. package/extension/manifest.json +17 -3
  37. package/extension/popup.html +469 -65
  38. package/package.json +2 -2
  39. package/skills/AGENTS.md +34 -61
  40. package/skills/data-extraction/SKILL.md +95 -103
  41. package/skills/form-testing/SKILL.md +75 -82
  42. package/skills/login-automation/SKILL.md +76 -66
  43. package/skills/opendevbrowser-best-practices/SKILL.md +90 -49
  44. package/skills/opendevbrowser-continuity-ledger/SKILL.md +57 -23
  45. package/dist/chunk-R5VUZEUU.js +0 -128
  46. package/dist/chunk-R5VUZEUU.js.map +0 -1
  47. package/extension/dist/popup.jsx +0 -150
package/dist/cli/index.js CHANGED
@@ -1,8 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ DaemonClient,
4
+ EXIT_DISCONNECTED,
5
+ EXIT_EXECUTION,
6
+ EXIT_USAGE,
7
+ buildAnnotateResult,
8
+ callDaemon,
9
+ createOpenDevBrowserCore,
10
+ createUsageError,
3
11
  extractExtension,
4
- generateSecureToken
5
- } from "../chunk-R5VUZEUU.js";
12
+ fetchDaemonStatusFromMetadata,
13
+ fetchWithTimeout,
14
+ formatErrorPayload,
15
+ generateSecureToken,
16
+ getExtensionPath,
17
+ loadGlobalConfig,
18
+ readDaemonMetadata,
19
+ resolveExitCode,
20
+ startDaemon,
21
+ toCliError,
22
+ writeFileAtomic
23
+ } from "../chunk-JVBMT2O5.js";
6
24
 
7
25
  // src/cli/args.ts
8
26
  var SHORT_FLAGS = {
@@ -17,38 +35,142 @@ function expandShortFlags(args) {
17
35
  return args.map((arg) => SHORT_FLAGS[arg] ?? arg);
18
36
  }
19
37
  function parseSkillsMode(args) {
38
+ const hasLocal = args.includes("--skills-local");
39
+ const hasGlobal = args.includes("--skills-global");
40
+ if (hasLocal && hasGlobal) {
41
+ throw createUsageError("Choose either --skills-local or --skills-global.");
42
+ }
20
43
  if (args.includes("--no-skills")) {
21
44
  return "none";
22
45
  }
23
- if (args.includes("--skills-local")) {
46
+ if (hasLocal) {
24
47
  return "local";
25
48
  }
26
- if (args.includes("--skills-global")) {
49
+ if (hasGlobal) {
27
50
  return "global";
28
51
  }
29
52
  return "global";
30
53
  }
54
+ function parseOutputFormat(args) {
55
+ const outputFlag = args.find((arg) => arg.startsWith("--output-format"));
56
+ if (!outputFlag) {
57
+ return "text";
58
+ }
59
+ let value;
60
+ if (outputFlag.includes("=")) {
61
+ value = outputFlag.split("=", 2)[1];
62
+ } else {
63
+ const index = args.indexOf(outputFlag);
64
+ value = index >= 0 ? args[index + 1] : void 0;
65
+ }
66
+ if (value === "text" || value === "json" || value === "stream-json") {
67
+ return value;
68
+ }
69
+ throw createUsageError(`Invalid --output-format: ${value ?? "missing"}`);
70
+ }
71
+ function parseTransport(args) {
72
+ const transportFlag = args.find((arg) => arg.startsWith("--transport"));
73
+ if (!transportFlag) {
74
+ return "relay";
75
+ }
76
+ let value;
77
+ if (transportFlag.includes("=")) {
78
+ value = transportFlag.split("=", 2)[1];
79
+ } else {
80
+ const index = args.indexOf(transportFlag);
81
+ value = index >= 0 ? args[index + 1] : void 0;
82
+ }
83
+ if (value === "relay" || value === "native") {
84
+ return value;
85
+ }
86
+ throw createUsageError(`Invalid --transport: ${value ?? "missing"}`);
87
+ }
31
88
  function parseArgs(argv) {
32
- const args = expandShortFlags(argv.slice(2));
89
+ let args = expandShortFlags(argv.slice(2));
90
+ let commandOverride = null;
91
+ if (args[0] && !args[0].startsWith("-")) {
92
+ const candidate = args[0];
93
+ if (candidate === "install" || candidate === "update" || candidate === "uninstall" || candidate === "help" || candidate === "version" || candidate === "serve" || candidate === "daemon" || candidate === "native" || candidate === "run" || candidate === "launch" || candidate === "connect" || candidate === "disconnect" || candidate === "status" || candidate === "goto" || candidate === "wait" || candidate === "snapshot" || candidate === "click" || candidate === "hover" || candidate === "press" || candidate === "check" || candidate === "uncheck" || candidate === "type" || candidate === "select" || candidate === "scroll" || candidate === "scroll-into-view" || candidate === "targets-list" || candidate === "target-use" || candidate === "target-new" || candidate === "target-close" || candidate === "page" || candidate === "pages" || candidate === "page-close" || candidate === "dom-html" || candidate === "dom-text" || candidate === "dom-attr" || candidate === "dom-value" || candidate === "dom-visible" || candidate === "dom-enabled" || candidate === "dom-checked" || candidate === "clone-page" || candidate === "clone-component" || candidate === "perf" || candidate === "screenshot" || candidate === "console-poll" || candidate === "network-poll" || candidate === "annotate") {
94
+ commandOverride = candidate;
95
+ args = args.slice(1);
96
+ } else {
97
+ throw createUsageError(`Unknown command: ${candidate}`);
98
+ }
99
+ }
100
+ const hasGlobal = args.includes("--global");
101
+ const hasLocal = args.includes("--local");
102
+ if (hasGlobal && hasLocal) {
103
+ throw createUsageError("Choose either --global or --local.");
104
+ }
33
105
  const skillsMode = parseSkillsMode(args);
34
106
  const fullInstall = args.includes("--full");
35
- if (args.includes("--help") || args.includes("-h")) {
36
- return { command: "help", withConfig: false, noPrompt: false, skillsMode, fullInstall };
107
+ const outputFormat = parseOutputFormat(args);
108
+ const transport = commandOverride === "annotate" ? "relay" : parseTransport(args);
109
+ if (commandOverride === "help" || args.includes("--help") || args.includes("-h")) {
110
+ return {
111
+ command: "help",
112
+ withConfig: false,
113
+ noPrompt: false,
114
+ noInteractive: false,
115
+ quiet: false,
116
+ outputFormat,
117
+ transport,
118
+ skillsMode,
119
+ fullInstall,
120
+ rawArgs: args
121
+ };
37
122
  }
38
- if (args.includes("--version") || args.includes("-v")) {
39
- return { command: "version", withConfig: false, noPrompt: false, skillsMode, fullInstall };
123
+ if (commandOverride === "version" || args.includes("--version") || args.includes("-v")) {
124
+ return {
125
+ command: "version",
126
+ withConfig: false,
127
+ noPrompt: false,
128
+ noInteractive: false,
129
+ quiet: false,
130
+ outputFormat,
131
+ transport,
132
+ skillsMode,
133
+ fullInstall,
134
+ rawArgs: args
135
+ };
40
136
  }
41
- if (args.includes("--update")) {
137
+ if (commandOverride === "update" || args.includes("--update")) {
42
138
  const mode2 = args.includes("--global") ? "global" : args.includes("--local") ? "local" : void 0;
43
- return { command: "update", mode: mode2, withConfig: false, noPrompt: false, skillsMode, fullInstall };
139
+ return {
140
+ command: "update",
141
+ mode: mode2,
142
+ withConfig: false,
143
+ noPrompt: false,
144
+ noInteractive: false,
145
+ quiet: false,
146
+ outputFormat,
147
+ transport,
148
+ skillsMode,
149
+ fullInstall,
150
+ rawArgs: args
151
+ };
44
152
  }
45
- if (args.includes("--uninstall")) {
153
+ if (commandOverride === "uninstall" || args.includes("--uninstall")) {
46
154
  const mode2 = args.includes("--global") ? "global" : args.includes("--local") ? "local" : void 0;
47
- const noPrompt2 = args.includes("--no-prompt");
48
- return { command: "uninstall", mode: mode2, withConfig: false, noPrompt: noPrompt2, skillsMode, fullInstall };
155
+ const noPrompt2 = args.includes("--no-prompt") || args.includes("--no-interactive");
156
+ return {
157
+ command: "uninstall",
158
+ mode: mode2,
159
+ withConfig: false,
160
+ noPrompt: noPrompt2,
161
+ noInteractive: noPrompt2,
162
+ quiet: args.includes("--quiet"),
163
+ outputFormat,
164
+ transport,
165
+ skillsMode,
166
+ fullInstall,
167
+ rawArgs: args
168
+ };
49
169
  }
50
170
  const withConfig = args.includes("--with-config") || fullInstall;
51
- const noPrompt = args.includes("--no-prompt");
171
+ const noPrompt = args.includes("--no-prompt") || args.includes("--no-interactive");
172
+ const noInteractive = args.includes("--no-interactive") || noPrompt;
173
+ const quiet = args.includes("--quiet");
52
174
  let mode;
53
175
  if (args.includes("--global")) {
54
176
  mode = "global";
@@ -66,34 +188,161 @@ function parseArgs(argv) {
66
188
  "--version",
67
189
  "--with-config",
68
190
  "--no-prompt",
191
+ "--no-interactive",
192
+ "--quiet",
193
+ "--output-format",
69
194
  "--full",
195
+ "--port",
196
+ "--token",
197
+ "--stop",
198
+ "--script",
199
+ "--headless",
200
+ "--profile",
201
+ "--persist-profile",
202
+ "--chrome-path",
203
+ "--start-url",
204
+ "--flag",
205
+ "--session-id",
206
+ "--close-browser",
207
+ "--ws-endpoint",
208
+ "--host",
209
+ "--cdp-port",
210
+ "--url",
211
+ "--wait-until",
212
+ "--timeout-ms",
213
+ "--ref",
214
+ "--state",
215
+ "--until",
216
+ "--mode",
217
+ "--max-chars",
218
+ "--cursor",
219
+ "--text",
220
+ "--clear",
221
+ "--submit",
222
+ "--values",
223
+ "--dy",
224
+ "--key",
225
+ "--attr",
226
+ "--name",
227
+ "--target-id",
228
+ "--tab-id",
229
+ "--include-urls",
230
+ "--path",
231
+ "--since-seq",
232
+ "--max",
233
+ "--daemon",
234
+ "--transport",
235
+ "--no-extension",
236
+ "--extension-only",
237
+ "--extension-legacy",
238
+ "--wait-for-extension",
239
+ "--wait-timeout-ms",
70
240
  "--skills-global",
71
241
  "--skills-local",
72
- "--no-skills"
242
+ "--no-skills",
243
+ "--screenshot-mode",
244
+ "--debug",
245
+ "--context"
246
+ ]);
247
+ const validEqualsFlags = /* @__PURE__ */ new Set([
248
+ "--output-format",
249
+ "--transport",
250
+ "--session-id",
251
+ "--url",
252
+ "--screenshot-mode",
253
+ "--context",
254
+ "--timeout-ms",
255
+ "--target-id",
256
+ "--tab-id"
73
257
  ]);
74
258
  for (const arg of args) {
75
259
  if (arg.startsWith("--") && !validFlags.has(arg)) {
76
- throw new Error(`Unknown flag: ${arg}`);
260
+ if (arg.includes("=")) {
261
+ const baseFlag = arg.split("=", 2)[0] ?? "";
262
+ if (validEqualsFlags.has(baseFlag)) {
263
+ continue;
264
+ }
265
+ }
266
+ throw createUsageError(`Unknown flag: ${arg}`);
77
267
  }
78
268
  if (arg.startsWith("-") && !arg.startsWith("--") && !SHORT_FLAGS[arg]) {
79
- throw new Error(`Unknown flag: ${arg}`);
269
+ throw createUsageError(`Unknown flag: ${arg}`);
80
270
  }
81
271
  }
82
- return { command: "install", mode, withConfig, noPrompt, skillsMode, fullInstall };
272
+ return {
273
+ command: commandOverride ?? "install",
274
+ mode,
275
+ withConfig,
276
+ noPrompt,
277
+ noInteractive,
278
+ quiet,
279
+ outputFormat,
280
+ transport,
281
+ skillsMode,
282
+ fullInstall,
283
+ rawArgs: args
284
+ };
83
285
  }
84
286
  function getHelpText() {
85
287
  return `
86
288
  OpenDevBrowser CLI - Install and manage the OpenDevBrowser plugin
87
289
 
88
290
  USAGE:
89
- npx opendevbrowser [options]
291
+ npx opendevbrowser [command] [options]
90
292
 
91
293
  COMMANDS:
92
- (default) Install the plugin (interactive if no mode specified)
93
- --update, -u Clear cached plugin to trigger reinstall
94
- --uninstall Remove plugin from config
95
- --help, -h Show this help message
96
- --version, -v Show version
294
+ install Install the plugin (default if no command specified)
295
+ update Clear cached plugin to trigger reinstall
296
+ uninstall Remove plugin from config
297
+ serve Start or stop the local daemon
298
+ daemon Install/uninstall/status daemon auto-start
299
+ native Install/uninstall/status native messaging host
300
+ run Execute a JSON script in a single process
301
+ launch Launch a managed browser session via daemon
302
+ connect Connect to an existing browser via daemon
303
+ disconnect Disconnect a daemon session
304
+ status Get daemon status (or session status with --session-id)
305
+ goto Navigate current session to a URL
306
+ wait Wait for load or a ref to appear
307
+ snapshot Capture a snapshot of the active page
308
+ click Click an element by ref
309
+ hover Hover an element by ref
310
+ press Press a keyboard key
311
+ check Check a checkbox by ref
312
+ uncheck Uncheck a checkbox by ref
313
+ type Type into an element by ref
314
+ select Select values in a select by ref
315
+ scroll Scroll the page or element by ref
316
+ scroll-into-view Scroll an element into view by ref
317
+ targets-list List page targets
318
+ target-use Focus a target by id
319
+ target-new Open a new target
320
+ target-close Close a target by id
321
+ page Open or focus a named page
322
+ pages List named pages
323
+ page-close Close a named page
324
+ dom-html Capture HTML for a ref
325
+ dom-text Capture text for a ref
326
+ dom-attr Capture attribute value for a ref
327
+ dom-value Capture input value for a ref
328
+ dom-visible Check visibility for a ref
329
+ dom-enabled Check enabled state for a ref
330
+ dom-checked Check checked state for a ref
331
+ clone-page Clone the active page to React
332
+ clone-component Clone a component by ref
333
+ perf Capture performance metrics
334
+ screenshot Capture a screenshot
335
+ console-poll Poll console events
336
+ network-poll Poll network events
337
+ annotate Request interactive annotations (direct or relay)
338
+ help Show this help message
339
+ version Show version
340
+
341
+ ALIASES:
342
+ --update, -u Same as update
343
+ --uninstall Same as uninstall
344
+ --help, -h Same as help
345
+ --version, -v Same as version
97
346
 
98
347
  INSTALL OPTIONS:
99
348
  --global, -g Install to ~/.config/opencode/opencode.json
@@ -101,6 +350,10 @@ INSTALL OPTIONS:
101
350
  --with-config Also create opendevbrowser.jsonc with defaults
102
351
  --full, -f Create config and pre-extract extension assets
103
352
  --no-prompt Skip prompts, use defaults (global install)
353
+ --no-interactive Alias of --no-prompt
354
+ --quiet Suppress non-error output
355
+ --output-format Output format: text (default), json, stream-json
356
+ --transport Transport: relay (default) or native
104
357
  --skills-global Install bundled skills to ~/.config/opencode/skill (default)
105
358
  --skills-local Install bundled skills to ./.opencode/skill
106
359
  --no-skills Skip installing bundled skills
@@ -115,68 +368,54 @@ EXAMPLES:
115
368
  npx opendevbrowser --no-skills # Skip skill installation
116
369
  npx opendevbrowser --update # Update plugin
117
370
  npx opendevbrowser --uninstall --global # Remove from global config
371
+ npx opendevbrowser native install <extension-id> # Install native host
118
372
  `.trim();
119
373
  }
374
+ function detectOutputFormat(argv) {
375
+ const args = expandShortFlags(argv.slice(2));
376
+ try {
377
+ return parseOutputFormat(args);
378
+ } catch {
379
+ return "text";
380
+ }
381
+ }
382
+
383
+ // src/cli/commands/registry.ts
384
+ var registry = /* @__PURE__ */ new Map();
385
+ function registerCommand(definition) {
386
+ registry.set(definition.name, definition);
387
+ }
388
+ function getCommand(name) {
389
+ return registry.get(name);
390
+ }
120
391
 
121
392
  // src/cli/installers/global.ts
122
- import * as fs4 from "fs";
393
+ import * as fs3 from "fs";
123
394
 
124
395
  // src/cli/utils/config.ts
125
- import * as fs2 from "fs";
126
- import * as path2 from "path";
127
- import * as os from "os";
128
- import { parse as parseJsonc, modify, applyEdits } from "jsonc-parser";
129
-
130
- // src/utils/fs.ts
131
396
  import * as fs from "fs";
132
397
  import * as path from "path";
133
- import * as crypto from "crypto";
134
- function writeFileAtomic(filePath, content, options = {}) {
135
- const { encoding = "utf-8", mode } = options;
136
- const dir = path.dirname(filePath);
137
- const hash = crypto.randomBytes(8).toString("hex");
138
- const tempPath = path.join(dir, `.${path.basename(filePath)}.${process.pid}.${hash}.tmp`);
139
- try {
140
- if (!fs.existsSync(dir)) {
141
- fs.mkdirSync(dir, { recursive: true });
142
- }
143
- const writeOptions = { encoding };
144
- if (mode !== void 0) {
145
- writeOptions.mode = mode;
146
- }
147
- fs.writeFileSync(tempPath, content, writeOptions);
148
- fs.renameSync(tempPath, filePath);
149
- } catch (error) {
150
- try {
151
- if (fs.existsSync(tempPath)) {
152
- fs.unlinkSync(tempPath);
153
- }
154
- } catch {
155
- }
156
- throw error;
157
- }
158
- }
159
-
160
- // src/cli/utils/config.ts
398
+ import * as os from "os";
399
+ import { parse as parseJsonc, modify, applyEdits } from "jsonc-parser";
161
400
  var PLUGIN_NAME = "opendevbrowser";
162
401
  var SCHEMA_URL = "https://opencode.ai/config.json";
163
402
  function getGlobalConfigPath() {
164
- const configDir = process.env.OPENCODE_CONFIG_DIR || path2.join(os.homedir(), ".config", "opencode");
165
- return path2.join(configDir, "opencode.json");
403
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path.join(os.homedir(), ".config", "opencode");
404
+ return path.join(configDir, "opencode.json");
166
405
  }
167
406
  function getLocalConfigPath() {
168
- return path2.join(process.cwd(), "opencode.json");
407
+ return path.join(process.cwd(), "opencode.json");
169
408
  }
170
409
  function ensureDir(dirPath) {
171
- if (!fs2.existsSync(dirPath)) {
172
- fs2.mkdirSync(dirPath, { recursive: true });
410
+ if (!fs.existsSync(dirPath)) {
411
+ fs.mkdirSync(dirPath, { recursive: true });
173
412
  }
174
413
  }
175
414
  function readConfig(configPath) {
176
- if (!fs2.existsSync(configPath)) {
415
+ if (!fs.existsSync(configPath)) {
177
416
  return { content: "", config: {} };
178
417
  }
179
- const content = fs2.readFileSync(configPath, "utf-8");
418
+ const content = fs.readFileSync(configPath, "utf-8");
180
419
  const errors = [];
181
420
  const parsed = parseJsonc(content, errors, { allowTrailingComma: true });
182
421
  if (errors.length > 0) {
@@ -228,10 +467,10 @@ function removePluginFromContent(content, pluginName = PLUGIN_NAME) {
228
467
  }
229
468
 
230
469
  // src/cli/templates/config.ts
231
- import * as fs3 from "fs";
232
- import * as path3 from "path";
470
+ import * as fs2 from "fs";
471
+ import * as path2 from "path";
233
472
  import * as os2 from "os";
234
- function buildConfigTemplate(token) {
473
+ function buildConfigTemplate(relayToken, daemonToken) {
235
474
  return `{
236
475
  // OpenDevBrowser Plugin Configuration
237
476
  // See: https://github.com/anthropics/opendevbrowser#configuration
@@ -295,7 +534,9 @@ function buildConfigTemplate(token) {
295
534
  },
296
535
 
297
536
  "relayPort": 8787,
298
- "relayToken": "${token}",
537
+ "relayToken": "${relayToken}",
538
+ "daemonPort": 8788,
539
+ "daemonToken": "${daemonToken}",
299
540
 
300
541
  "flags": [],
301
542
 
@@ -305,22 +546,23 @@ function buildConfigTemplate(token) {
305
546
  }
306
547
  function getPluginConfigPath(mode) {
307
548
  if (mode === "global") {
308
- const configDir = process.env.OPENCODE_CONFIG_DIR || path3.join(os2.homedir(), ".config", "opencode");
309
- return path3.join(configDir, "opendevbrowser.jsonc");
549
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path2.join(os2.homedir(), ".config", "opencode");
550
+ return path2.join(configDir, "opendevbrowser.jsonc");
310
551
  }
311
- return path3.join(process.cwd(), "opendevbrowser.jsonc");
552
+ return path2.join(process.cwd(), "opendevbrowser.jsonc");
312
553
  }
313
554
  function createPluginConfig(mode) {
314
555
  const configPath = getPluginConfigPath(mode);
315
- if (fs3.existsSync(configPath)) {
556
+ if (fs2.existsSync(configPath)) {
316
557
  return { created: false, path: configPath };
317
558
  }
318
- const dir = path3.dirname(configPath);
319
- if (!fs3.existsSync(dir)) {
320
- fs3.mkdirSync(dir, { recursive: true });
559
+ const dir = path2.dirname(configPath);
560
+ if (!fs2.existsSync(dir)) {
561
+ fs2.mkdirSync(dir, { recursive: true });
321
562
  }
322
- const token = generateSecureToken();
323
- writeFileAtomic(configPath, buildConfigTemplate(token));
563
+ const relayToken = generateSecureToken();
564
+ const daemonToken = generateSecureToken();
565
+ writeFileAtomic(configPath, buildConfigTemplate(relayToken, daemonToken));
324
566
  return { created: true, path: configPath };
325
567
  }
326
568
 
@@ -340,7 +582,7 @@ function installGlobal(withConfig = false) {
340
582
  }
341
583
  const newContent = updateConfigContent(content, "opendevbrowser");
342
584
  ensureDir(configPath.replace(/[/\\][^/\\]+$/, ""));
343
- fs4.writeFileSync(configPath, newContent, "utf-8");
585
+ fs3.writeFileSync(configPath, newContent, "utf-8");
344
586
  if (withConfig) {
345
587
  createPluginConfig("global");
346
588
  }
@@ -364,7 +606,7 @@ function installGlobal(withConfig = false) {
364
606
  }
365
607
 
366
608
  // src/cli/installers/local.ts
367
- import * as fs5 from "fs";
609
+ import * as fs4 from "fs";
368
610
  function installLocal(withConfig = false) {
369
611
  const configPath = getLocalConfigPath();
370
612
  try {
@@ -379,7 +621,7 @@ function installLocal(withConfig = false) {
379
621
  };
380
622
  }
381
623
  const newContent = updateConfigContent(content, "opendevbrowser");
382
- fs5.writeFileSync(configPath, newContent, "utf-8");
624
+ fs4.writeFileSync(configPath, newContent, "utf-8");
383
625
  if (withConfig) {
384
626
  createPluginConfig("local");
385
627
  }
@@ -403,12 +645,12 @@ function installLocal(withConfig = false) {
403
645
  }
404
646
 
405
647
  // src/cli/installers/skills.ts
406
- import * as fs7 from "fs";
407
- import * as path5 from "path";
408
-
409
- // src/cli/utils/skills.ts
410
648
  import * as fs6 from "fs";
411
649
  import * as path4 from "path";
650
+
651
+ // src/cli/utils/skills.ts
652
+ import * as fs5 from "fs";
653
+ import * as path3 from "path";
412
654
  import * as os3 from "os";
413
655
  import { fileURLToPath } from "url";
414
656
  var PACKAGE_NAME = "opendevbrowser";
@@ -417,17 +659,17 @@ var cachedPackageRoot = null;
417
659
  function findPackageRoot(startDir) {
418
660
  let current = startDir;
419
661
  while (true) {
420
- const pkgPath = path4.join(current, "package.json");
421
- if (fs6.existsSync(pkgPath)) {
662
+ const pkgPath = path3.join(current, "package.json");
663
+ if (fs5.existsSync(pkgPath)) {
422
664
  try {
423
- const parsed = JSON.parse(fs6.readFileSync(pkgPath, "utf-8"));
665
+ const parsed = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
424
666
  if (parsed.name === PACKAGE_NAME) {
425
667
  return current;
426
668
  }
427
669
  } catch {
428
670
  }
429
671
  }
430
- const parent = path4.dirname(current);
672
+ const parent = path3.dirname(current);
431
673
  if (parent === current) {
432
674
  break;
433
675
  }
@@ -437,23 +679,23 @@ function findPackageRoot(startDir) {
437
679
  }
438
680
  function getPackageRoot() {
439
681
  if (cachedPackageRoot) return cachedPackageRoot;
440
- const moduleDir = path4.dirname(fileURLToPath(import.meta.url));
682
+ const moduleDir = path3.dirname(fileURLToPath(import.meta.url));
441
683
  cachedPackageRoot = findPackageRoot(moduleDir);
442
684
  return cachedPackageRoot;
443
685
  }
444
686
  function getBundledSkillsDir() {
445
- const skillsDir = path4.join(getPackageRoot(), "skills");
446
- if (!fs6.existsSync(skillsDir)) {
687
+ const skillsDir = path3.join(getPackageRoot(), "skills");
688
+ if (!fs5.existsSync(skillsDir)) {
447
689
  throw new Error(`Bundled skills directory not found at ${skillsDir}`);
448
690
  }
449
691
  return skillsDir;
450
692
  }
451
693
  function getGlobalSkillDir() {
452
- const configDir = process.env.OPENCODE_CONFIG_DIR || path4.join(os3.homedir(), ".config", "opencode");
453
- return path4.join(configDir, SKILL_DIR_NAME);
694
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path3.join(os3.homedir(), ".config", "opencode");
695
+ return path3.join(configDir, SKILL_DIR_NAME);
454
696
  }
455
697
  function getLocalSkillDir() {
456
- return path4.join(process.cwd(), ".opencode", SKILL_DIR_NAME);
698
+ return path3.join(process.cwd(), ".opencode", SKILL_DIR_NAME);
457
699
  }
458
700
 
459
701
  // src/cli/installers/skills.ts
@@ -463,18 +705,18 @@ function installSkills(mode) {
463
705
  const skipped = [];
464
706
  try {
465
707
  const sourceDir = getBundledSkillsDir();
466
- const entries = fs7.readdirSync(sourceDir, { withFileTypes: true });
708
+ const entries = fs6.readdirSync(sourceDir, { withFileTypes: true });
467
709
  ensureDir(targetDir);
468
710
  for (const entry of entries) {
469
711
  if (!entry.isDirectory()) continue;
470
712
  const skillName = entry.name;
471
- const sourcePath = path5.join(sourceDir, skillName);
472
- const targetPath = path5.join(targetDir, skillName);
473
- if (fs7.existsSync(targetPath)) {
713
+ const sourcePath = path4.join(sourceDir, skillName);
714
+ const targetPath = path4.join(targetDir, skillName);
715
+ if (fs6.existsSync(targetPath)) {
474
716
  skipped.push(skillName);
475
717
  continue;
476
718
  }
477
- fs7.cpSync(sourcePath, targetPath, { recursive: true });
719
+ fs6.cpSync(sourcePath, targetPath, { recursive: true });
478
720
  installed.push(skillName);
479
721
  }
480
722
  const summary = `Skills ${mode} install: ${installed.length} installed${skipped.length ? `, ${skipped.length} skipped` : ""} (${targetDir})`;
@@ -498,31 +740,31 @@ function installSkills(mode) {
498
740
  }
499
741
 
500
742
  // src/cli/commands/update.ts
501
- import * as fs8 from "fs";
502
- import * as path6 from "path";
743
+ import * as fs7 from "fs";
744
+ import * as path5 from "path";
503
745
  import * as os4 from "os";
504
746
  var PLUGIN_NAME2 = "opendevbrowser";
505
747
  function getCacheDir() {
506
- return process.env.OPENCODE_CACHE_DIR || path6.join(os4.homedir(), ".cache", "opencode");
748
+ return process.env.OPENCODE_CACHE_DIR || path5.join(os4.homedir(), ".cache", "opencode");
507
749
  }
508
750
  function rmdir(dirPath) {
509
751
  const cacheDir = getCacheDir();
510
- const resolvedCache = path6.resolve(cacheDir);
511
- const resolvedPath = path6.resolve(dirPath);
512
- if (!resolvedPath.startsWith(resolvedCache + path6.sep) || resolvedPath === resolvedCache) {
752
+ const resolvedCache = path5.resolve(cacheDir);
753
+ const resolvedPath = path5.resolve(dirPath);
754
+ if (!resolvedPath.startsWith(resolvedCache + path5.sep) || resolvedPath === resolvedCache) {
513
755
  throw new Error(`Security: refusing to delete path outside cache directory: ${dirPath}`);
514
756
  }
515
- if (fs8.existsSync(dirPath)) {
516
- fs8.rmSync(dirPath, { recursive: true, force: true });
757
+ if (fs7.existsSync(dirPath)) {
758
+ fs7.rmSync(dirPath, { recursive: true, force: true });
517
759
  }
518
760
  }
519
761
  function runUpdate() {
520
762
  const cacheDir = getCacheDir();
521
- const nodeModulesDir = path6.join(cacheDir, "node_modules");
522
- const pluginCacheDir = path6.join(nodeModulesDir, PLUGIN_NAME2);
763
+ const nodeModulesDir = path5.join(cacheDir, "node_modules");
764
+ const pluginCacheDir = path5.join(nodeModulesDir, PLUGIN_NAME2);
523
765
  try {
524
- if (!fs8.existsSync(pluginCacheDir)) {
525
- if (fs8.existsSync(nodeModulesDir)) {
766
+ if (!fs7.existsSync(pluginCacheDir)) {
767
+ if (fs7.existsSync(nodeModulesDir)) {
526
768
  rmdir(nodeModulesDir);
527
769
  return {
528
770
  success: true,
@@ -553,20 +795,20 @@ function runUpdate() {
553
795
  }
554
796
 
555
797
  // src/cli/commands/uninstall.ts
556
- import * as fs9 from "fs";
557
- import * as path7 from "path";
798
+ import * as fs8 from "fs";
799
+ import * as path6 from "path";
558
800
  import * as os5 from "os";
559
801
  function getPluginConfigPath2(mode) {
560
802
  if (mode === "global") {
561
- const configDir = process.env.OPENCODE_CONFIG_DIR || path7.join(os5.homedir(), ".config", "opencode");
562
- return path7.join(configDir, "opendevbrowser.jsonc");
803
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path6.join(os5.homedir(), ".config", "opencode");
804
+ return path6.join(configDir, "opendevbrowser.jsonc");
563
805
  }
564
- return path7.join(process.cwd(), "opendevbrowser.jsonc");
806
+ return path6.join(process.cwd(), "opendevbrowser.jsonc");
565
807
  }
566
808
  function removePluginConfigFile(mode) {
567
809
  const configPath = getPluginConfigPath2(mode);
568
- if (fs9.existsSync(configPath)) {
569
- fs9.unlinkSync(configPath);
810
+ if (fs8.existsSync(configPath)) {
811
+ fs8.unlinkSync(configPath);
570
812
  return true;
571
813
  }
572
814
  return false;
@@ -585,7 +827,7 @@ function runUninstall(mode, deleteConfigFile = false) {
585
827
  };
586
828
  }
587
829
  const newContent = removePluginFromContent(content, "opendevbrowser");
588
- fs9.writeFileSync(configPath, newContent, "utf-8");
830
+ fs8.writeFileSync(configPath, newContent, "utf-8");
589
831
  let configFileDeleted = false;
590
832
  if (deleteConfigFile) {
591
833
  configFileDeleted = removePluginConfigFile(mode);
@@ -624,179 +866,3352 @@ function findInstalledConfigs() {
624
866
  return { global, local };
625
867
  }
626
868
 
627
- // src/cli/index.ts
628
- var VERSION = "0.1.0";
629
- async function promptInstallMode() {
630
- if (!process.stdin.isTTY) {
631
- console.log("Non-interactive mode detected. Using global install.");
632
- return "global";
869
+ // src/cli/utils/parse.ts
870
+ function parseNumberFlag(value, flag, options = {}) {
871
+ const parsed = Number(value);
872
+ if (!Number.isFinite(parsed)) {
873
+ throw createUsageError(`Invalid ${flag}: ${value}`);
633
874
  }
634
- return new Promise((resolve2) => {
635
- console.log("\nWhere would you like to install opendevbrowser?\n");
636
- console.log(" 1. Global (~/.config/opencode/opencode.json)");
637
- console.log(" 2. Local (./opencode.json in this project)\n");
638
- process.stdout.write("Enter choice [1]: ");
639
- process.stdin.setEncoding("utf8");
640
- let resolved = false;
641
- let timeoutId = null;
642
- const cleanup = () => {
643
- if (timeoutId !== null) {
644
- clearTimeout(timeoutId);
645
- timeoutId = null;
875
+ const requireInteger = options.integer ?? true;
876
+ if (requireInteger && !Number.isInteger(parsed)) {
877
+ throw createUsageError(`Invalid ${flag}: ${value}`);
878
+ }
879
+ if (typeof options.min === "number" && parsed < options.min) {
880
+ throw createUsageError(`Invalid ${flag}: ${value}`);
881
+ }
882
+ if (typeof options.max === "number" && parsed > options.max) {
883
+ throw createUsageError(`Invalid ${flag}: ${value}`);
884
+ }
885
+ return parsed;
886
+ }
887
+
888
+ // src/cli/commands/native.ts
889
+ import * as fs9 from "fs";
890
+ import * as path7 from "path";
891
+ import { homedir as homedir6 } from "os";
892
+ import { execFileSync } from "child_process";
893
+ import { fileURLToPath as fileURLToPath2 } from "url";
894
+ var EXTENSION_ID_RE = /^[a-p]{32}$/;
895
+ var EXTENSION_NAME = "OpenDevBrowser Relay";
896
+ var ANNOTATION_COMMAND_NAME = "toggle-annotation";
897
+ var normalizeExtensionId = (value) => {
898
+ if (!value) return null;
899
+ const trimmed = value.trim();
900
+ if (!trimmed) return null;
901
+ return EXTENSION_ID_RE.test(trimmed) ? trimmed : null;
902
+ };
903
+ var requireExtensionId = (value) => {
904
+ if (!value) {
905
+ throw createUsageError("Missing extension ID. Usage: opendevbrowser native install <extension-id>");
906
+ }
907
+ const normalized = normalizeExtensionId(value);
908
+ if (!normalized) {
909
+ throw createUsageError("Invalid extension ID format. Expected 32 characters (a-p).");
910
+ }
911
+ return normalized;
912
+ };
913
+ var parseNativeArgs = (rawArgs) => {
914
+ const subcommand = rawArgs[0];
915
+ if (subcommand !== "install" && subcommand !== "uninstall" && subcommand !== "status") {
916
+ throw createUsageError("Usage: opendevbrowser native <install|uninstall|status> [extension-id]");
917
+ }
918
+ if (subcommand === "install") {
919
+ const extensionId = requireExtensionId(rawArgs[1]);
920
+ return { subcommand, extensionId };
921
+ }
922
+ return { subcommand };
923
+ };
924
+ var getManifestDir = () => {
925
+ if (process.platform === "darwin") {
926
+ return path7.join(process.env.HOME || "", "Library", "Application Support", "Google", "Chrome", "NativeMessagingHosts");
927
+ }
928
+ if (process.platform === "linux") {
929
+ return path7.join(process.env.HOME || "", ".config", "google-chrome", "NativeMessagingHosts");
930
+ }
931
+ if (process.platform === "win32") {
932
+ const base = process.env.LOCALAPPDATA || (process.env.USERPROFILE ? path7.join(process.env.USERPROFILE, "AppData", "Local") : "");
933
+ if (!base) {
934
+ throw createUsageError("LOCALAPPDATA is not set. Unable to locate NativeMessagingHosts directory.");
935
+ }
936
+ return path7.join(base, "Google", "Chrome", "User Data", "NativeMessagingHosts");
937
+ }
938
+ throw createUsageError(`Native messaging is not supported on ${process.platform}.`);
939
+ };
940
+ var getScriptsDir = () => {
941
+ const __filename = fileURLToPath2(import.meta.url);
942
+ const startDir = path7.dirname(__filename);
943
+ const rootsToScan = [startDir, process.cwd()];
944
+ for (const root of rootsToScan) {
945
+ let current = path7.resolve(root);
946
+ while (true) {
947
+ const scriptsDir = path7.join(current, "scripts", "native");
948
+ const packageJsonPath = path7.join(current, "package.json");
949
+ if (fs9.existsSync(scriptsDir) && fs9.existsSync(packageJsonPath)) {
950
+ return scriptsDir;
646
951
  }
647
- };
648
- process.stdin.once("data", (data) => {
649
- cleanup();
650
- if (resolved) return;
651
- resolved = true;
652
- const input = data.toString().trim();
653
- if (input === "2") {
654
- resolve2("local");
655
- } else {
656
- resolve2("global");
952
+ const parent = path7.dirname(current);
953
+ if (parent === current) {
954
+ break;
657
955
  }
658
- });
659
- process.stdin.once("close", () => {
660
- cleanup();
661
- if (resolved) return;
662
- resolved = true;
663
- resolve2("global");
664
- });
665
- timeoutId = setTimeout(() => {
666
- timeoutId = null;
667
- if (resolved) return;
668
- resolved = true;
669
- console.log("\nTimeout - using global install.");
670
- resolve2("global");
671
- }, 3e4);
672
- });
673
- }
674
- async function promptUninstallMode() {
675
- const installed = findInstalledConfigs();
676
- if (!installed.global && !installed.local) {
677
- console.log("opendevbrowser is not installed in any config.");
678
- return null;
956
+ current = parent;
957
+ }
679
958
  }
680
- if (installed.global && !installed.local) {
681
- return "global";
959
+ throw createUsageError("Unable to locate scripts/native directory.");
960
+ };
961
+ var getHostScriptPath = () => {
962
+ return path7.join(getScriptsDir(), "host.cjs");
963
+ };
964
+ var getManifestPath = () => {
965
+ return path7.join(getManifestDir(), "com.opendevbrowser.native.json");
966
+ };
967
+ var getWrapperPath = () => {
968
+ const wrapperName = process.platform === "win32" ? "com.opendevbrowser.native.cmd" : "com.opendevbrowser.native.sh";
969
+ return path7.join(getManifestDir(), wrapperName);
970
+ };
971
+ var readManifest = (manifestPath) => {
972
+ try {
973
+ const raw = fs9.readFileSync(manifestPath, "utf8");
974
+ const data = JSON.parse(raw);
975
+ const origins = Array.isArray(data.allowed_origins) ? data.allowed_origins : [];
976
+ const match = origins.find((origin) => origin.startsWith("chrome-extension://"));
977
+ if (!match) return { extensionId: null };
978
+ const id = match.replace("chrome-extension://", "").replace("/", "");
979
+ return { extensionId: EXTENSION_ID_RE.test(id) ? id : null };
980
+ } catch {
981
+ return { extensionId: null };
682
982
  }
683
- if (!installed.global && installed.local) {
684
- return "local";
983
+ };
984
+ var runScript = (script, args) => {
985
+ if (process.platform === "win32") {
986
+ execFileSync("powershell", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", script, ...args], { stdio: "pipe" });
987
+ return;
685
988
  }
686
- if (!process.stdin.isTTY) {
687
- console.log("Plugin found in both global and local configs. Use --global or --local flag.");
989
+ execFileSync("bash", [script, ...args], { stdio: "pipe" });
990
+ };
991
+ var readRegistryPath = () => {
992
+ if (process.platform !== "win32") return null;
993
+ const key = "HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\com.opendevbrowser.native";
994
+ try {
995
+ const output = execFileSync("reg", ["query", key, "/ve"], { encoding: "utf8" });
996
+ const lines = output.split(/\r?\n/);
997
+ for (const line of lines) {
998
+ if (line.includes("REG_SZ")) {
999
+ const parts = line.trim().split(/\s{2,}/);
1000
+ return parts[parts.length - 1] || null;
1001
+ }
1002
+ }
1003
+ return null;
1004
+ } catch {
688
1005
  return null;
689
1006
  }
690
- return new Promise((resolve2) => {
691
- console.log("\nopendevbrowser is installed in multiple locations:\n");
692
- console.log(" 1. Global (~/.config/opencode/opencode.json)");
693
- console.log(" 2. Local (./opencode.json)");
694
- console.log(" 3. Cancel\n");
695
- process.stdout.write("Which to uninstall? [3]: ");
696
- process.stdin.setEncoding("utf8");
697
- process.stdin.once("data", (data) => {
698
- const input = data.toString().trim();
699
- if (input === "1") {
700
- resolve2("global");
701
- } else if (input === "2") {
702
- resolve2("local");
703
- } else {
704
- resolve2(null);
705
- }
706
- });
707
- process.stdin.once("close", () => {
708
- resolve2(null);
709
- });
710
- });
711
- }
712
- async function main() {
1007
+ };
1008
+ var normalizePath = (value) => {
713
1009
  try {
714
- const args = parseArgs(process.argv);
715
- switch (args.command) {
716
- case "help":
717
- console.log(getHelpText());
718
- process.exit(0);
719
- break;
720
- case "version":
721
- console.log(`opendevbrowser v${VERSION}`);
722
- process.exit(0);
723
- break;
724
- case "update": {
725
- const result = runUpdate();
726
- console.log(result.message);
727
- process.exit(result.success ? 0 : 1);
728
- break;
1010
+ return fs9.realpathSync(value);
1011
+ } catch {
1012
+ return path7.resolve(value);
1013
+ }
1014
+ };
1015
+ var getChromeUserDataRoots = () => {
1016
+ if (process.platform === "darwin") {
1017
+ return [
1018
+ path7.join(homedir6(), "Library", "Application Support", "Google", "Chrome"),
1019
+ path7.join(homedir6(), "Library", "Application Support", "Chromium"),
1020
+ path7.join(homedir6(), "Library", "Application Support", "BraveSoftware", "Brave-Browser")
1021
+ ];
1022
+ }
1023
+ if (process.platform === "linux") {
1024
+ return [
1025
+ path7.join(homedir6(), ".config", "google-chrome"),
1026
+ path7.join(homedir6(), ".config", "chromium"),
1027
+ path7.join(homedir6(), ".config", "BraveSoftware", "Brave-Browser")
1028
+ ];
1029
+ }
1030
+ if (process.platform === "win32") {
1031
+ const base = process.env.LOCALAPPDATA || (process.env.USERPROFILE ? path7.join(process.env.USERPROFILE, "AppData", "Local") : "");
1032
+ if (!base) return [];
1033
+ return [
1034
+ path7.join(base, "Google", "Chrome", "User Data"),
1035
+ path7.join(base, "Chromium", "User Data"),
1036
+ path7.join(base, "BraveSoftware", "Brave-Browser", "User Data")
1037
+ ];
1038
+ }
1039
+ return [];
1040
+ };
1041
+ var PROFILE_PREFERENCES_FILES = ["Preferences", "Secure Preferences"];
1042
+ var getProfileDirs = (root) => {
1043
+ try {
1044
+ const entries = fs9.readdirSync(root, { withFileTypes: true });
1045
+ return entries.filter((entry) => entry.isDirectory() && (entry.name === "Default" || entry.name.startsWith("Profile "))).map((entry) => path7.join(root, entry.name)).filter((dir) => PROFILE_PREFERENCES_FILES.some((filename) => fs9.existsSync(path7.join(dir, filename))));
1046
+ } catch {
1047
+ return [];
1048
+ }
1049
+ };
1050
+ var readProfilePreferences = (profileDir) => {
1051
+ const records = [];
1052
+ for (const filename of PROFILE_PREFERENCES_FILES) {
1053
+ try {
1054
+ const raw = fs9.readFileSync(path7.join(profileDir, filename), "utf8");
1055
+ records.push(JSON.parse(raw));
1056
+ } catch {
1057
+ }
1058
+ }
1059
+ return records;
1060
+ };
1061
+ var findExtensionIdInCommands = (preferences) => {
1062
+ const extensionCommands = preferences.extensions;
1063
+ const commandMaps = [
1064
+ extensionCommands?.commands,
1065
+ preferences.account_values?.extensions?.commands
1066
+ ];
1067
+ for (const commandMap of commandMaps) {
1068
+ if (!commandMap) {
1069
+ continue;
1070
+ }
1071
+ for (const value of Object.values(commandMap)) {
1072
+ if (typeof value !== "object" || value === null) {
1073
+ continue;
729
1074
  }
730
- case "uninstall": {
731
- let mode = args.mode;
732
- if (!mode && !args.noPrompt) {
733
- mode = await promptUninstallMode() ?? void 0;
734
- if (!mode) {
735
- console.log("Uninstall cancelled.");
736
- process.exit(0);
737
- }
738
- }
739
- if (!mode) {
740
- console.error("Error: Please specify --global or --local for uninstall.");
741
- process.exit(1);
742
- }
743
- const result = runUninstall(mode);
744
- console.log(result.message);
745
- process.exit(result.success ? 0 : 1);
746
- break;
1075
+ const entry = value;
1076
+ const commandName = typeof entry.command_name === "string" ? entry.command_name : null;
1077
+ const extensionId = typeof entry.extension === "string" ? entry.extension : null;
1078
+ if (commandName === ANNOTATION_COMMAND_NAME && extensionId && EXTENSION_ID_RE.test(extensionId)) {
1079
+ return extensionId;
747
1080
  }
748
- case "install":
749
- default: {
750
- let mode = args.mode;
751
- if (!mode) {
752
- mode = await promptInstallMode();
753
- }
754
- const result = mode === "global" ? installGlobal(args.withConfig) : installLocal(args.withConfig);
755
- console.log(result.message);
756
- if (args.skillsMode === "none") {
757
- console.log("Skill installation skipped (--no-skills).");
758
- } else if (result.success) {
759
- const skillsResult = installSkills(args.skillsMode);
760
- if (skillsResult.success) {
761
- console.log(skillsResult.message);
762
- } else {
763
- console.warn(skillsResult.message);
1081
+ }
1082
+ }
1083
+ return null;
1084
+ };
1085
+ var findExtensionIdInPreferences = (preferences, extensionPath) => {
1086
+ const extensions = preferences.extensions;
1087
+ const settings = extensions?.settings;
1088
+ if (!settings) return null;
1089
+ const normalizedTargetPath = extensionPath ? normalizePath(extensionPath) : null;
1090
+ let nameMatch = null;
1091
+ for (const [id, entry] of Object.entries(settings)) {
1092
+ if (!EXTENSION_ID_RE.test(id) || typeof entry !== "object" || entry === null) {
1093
+ continue;
1094
+ }
1095
+ const record = entry;
1096
+ const recordPath = typeof record.path === "string" ? record.path : null;
1097
+ if (recordPath && normalizedTargetPath) {
1098
+ if (normalizePath(recordPath) === normalizedTargetPath) {
1099
+ return { id, matchedBy: "path" };
1100
+ }
1101
+ }
1102
+ const manifest = record.manifest;
1103
+ const name = typeof manifest?.name === "string" ? manifest.name : null;
1104
+ if (!nameMatch && name === EXTENSION_NAME) {
1105
+ nameMatch = id;
1106
+ }
1107
+ }
1108
+ if (nameMatch) {
1109
+ return { id: nameMatch, matchedBy: "name" };
1110
+ }
1111
+ return null;
1112
+ };
1113
+ var getExtensionPathCandidates = () => {
1114
+ const candidates = /* @__PURE__ */ new Set();
1115
+ const primary = getExtensionPath();
1116
+ if (primary) {
1117
+ candidates.add(normalizePath(primary));
1118
+ }
1119
+ const cwdExtension = path7.join(process.cwd(), "extension");
1120
+ if (fs9.existsSync(path7.join(cwdExtension, "manifest.json"))) {
1121
+ candidates.add(normalizePath(cwdExtension));
1122
+ }
1123
+ if (candidates.size === 0) {
1124
+ return [null];
1125
+ }
1126
+ return [...candidates];
1127
+ };
1128
+ var getNativeStatusSnapshot = () => {
1129
+ const hostScript = getHostScriptPath();
1130
+ const manifestPath = getManifestPath();
1131
+ const wrapperPath = getWrapperPath();
1132
+ const registryPath = readRegistryPath();
1133
+ let installed = false;
1134
+ let manifestExists = false;
1135
+ let wrapperExists = false;
1136
+ let extensionIdValue = null;
1137
+ if (fs9.existsSync(manifestPath)) {
1138
+ manifestExists = true;
1139
+ installed = true;
1140
+ const manifest = readManifest(manifestPath);
1141
+ extensionIdValue = manifest.extensionId;
1142
+ }
1143
+ if (fs9.existsSync(wrapperPath)) {
1144
+ wrapperExists = true;
1145
+ }
1146
+ if (!manifestExists || !wrapperExists) {
1147
+ installed = false;
1148
+ }
1149
+ if (process.platform === "win32" && !registryPath) {
1150
+ installed = false;
1151
+ }
1152
+ return {
1153
+ installed,
1154
+ manifestPath: manifestExists ? manifestPath : null,
1155
+ wrapperPath: wrapperExists ? wrapperPath : null,
1156
+ hostScriptPath: hostScript,
1157
+ extensionId: extensionIdValue,
1158
+ registryPath
1159
+ };
1160
+ };
1161
+ function discoverExtensionId() {
1162
+ const extensionPaths = getExtensionPathCandidates();
1163
+ const roots = getChromeUserDataRoots();
1164
+ for (const root of roots) {
1165
+ for (const profileDir of getProfileDirs(root)) {
1166
+ let nameFallback = null;
1167
+ let commandFallback = null;
1168
+ for (const preferences of readProfilePreferences(profileDir)) {
1169
+ for (const extensionPath of extensionPaths) {
1170
+ const match = findExtensionIdInPreferences(preferences, extensionPath);
1171
+ if (!match) {
1172
+ continue;
764
1173
  }
765
- } else {
766
- console.warn("Skill installation skipped because plugin install failed.");
767
- }
768
- if (args.fullInstall && result.success) {
769
- try {
770
- const extensionPath = extractExtension();
771
- if (extensionPath) {
772
- console.log(`Extension assets extracted to ${extensionPath}`);
773
- } else {
774
- console.warn("Extension assets not found; skipping extraction.");
775
- }
776
- } catch (error) {
777
- const message = error instanceof Error ? error.message : String(error);
778
- console.warn(`Extension pre-extraction failed: ${message}`);
1174
+ if (match.matchedBy === "path") {
1175
+ return { extensionId: match.id, matchedBy: match.matchedBy };
1176
+ }
1177
+ if (!nameFallback) {
1178
+ nameFallback = match;
779
1179
  }
780
1180
  }
781
- if (result.success && !result.alreadyInstalled) {
782
- console.log("\nNext steps:");
783
- console.log(" 1. Start or restart OpenCode");
784
- console.log(" 2. Use opendevbrowser_status to verify the plugin is loaded");
785
- console.log("\nFor help: npx opendevbrowser --help");
1181
+ if (!commandFallback) {
1182
+ commandFallback = findExtensionIdInCommands(preferences);
786
1183
  }
787
- process.exit(result.success ? 0 : 1);
788
- break;
789
1184
  }
790
- }
1185
+ if (nameFallback) {
1186
+ return { extensionId: nameFallback.id, matchedBy: nameFallback.matchedBy };
1187
+ }
1188
+ if (commandFallback) {
1189
+ return { extensionId: commandFallback, matchedBy: "command" };
1190
+ }
1191
+ }
1192
+ }
1193
+ return { extensionId: null };
1194
+ }
1195
+ function installNativeHost(extensionId) {
1196
+ const normalized = normalizeExtensionId(extensionId);
1197
+ if (!normalized) {
1198
+ return {
1199
+ success: false,
1200
+ message: "Invalid extension ID format. Expected 32 characters (a-p).",
1201
+ exitCode: EXIT_EXECUTION
1202
+ };
1203
+ }
1204
+ const hostScript = getHostScriptPath();
1205
+ if (!fs9.existsSync(hostScript)) {
1206
+ return {
1207
+ success: false,
1208
+ message: `Native host not found at ${hostScript}.`,
1209
+ exitCode: EXIT_EXECUTION
1210
+ };
1211
+ }
1212
+ const scriptsDir = getScriptsDir();
1213
+ const manifestPath = getManifestPath();
1214
+ const installScript = process.platform === "win32" ? path7.join(scriptsDir, "install.ps1") : path7.join(scriptsDir, "install.sh");
1215
+ try {
1216
+ runScript(installScript, [normalized]);
1217
+ return {
1218
+ success: true,
1219
+ message: `Native host installed for extension ${normalized}.`,
1220
+ data: { manifestPath }
1221
+ };
1222
+ } catch (error) {
1223
+ const message = error instanceof Error ? error.message : String(error);
1224
+ return {
1225
+ success: false,
1226
+ message: `Native install failed: ${message}`,
1227
+ exitCode: EXIT_EXECUTION
1228
+ };
1229
+ }
1230
+ }
1231
+ async function runNativeCommand(args) {
1232
+ const { subcommand, extensionId } = parseNativeArgs(args.rawArgs);
1233
+ const scriptsDir = getScriptsDir();
1234
+ const uninstallScript = process.platform === "win32" ? path7.join(scriptsDir, "uninstall.ps1") : path7.join(scriptsDir, "uninstall.sh");
1235
+ if (subcommand === "install") {
1236
+ return installNativeHost(extensionId);
1237
+ }
1238
+ if (subcommand === "uninstall") {
1239
+ try {
1240
+ runScript(uninstallScript, []);
1241
+ return { success: true, message: "Native host uninstalled." };
1242
+ } catch (error) {
1243
+ const message2 = error instanceof Error ? error.message : String(error);
1244
+ return { success: false, message: `Native uninstall failed: ${message2}`, exitCode: EXIT_EXECUTION };
1245
+ }
1246
+ }
1247
+ const data = getNativeStatusSnapshot();
1248
+ if (!data.installed) {
1249
+ return {
1250
+ success: false,
1251
+ message: "Native host not installed.",
1252
+ data,
1253
+ exitCode: EXIT_DISCONNECTED
1254
+ };
1255
+ }
1256
+ const message = data.extensionId ? `Native host installed for extension ${data.extensionId}.` : "Native host installed (extension id missing).";
1257
+ return { success: true, message, data };
1258
+ }
1259
+
1260
+ // src/cli/commands/serve.ts
1261
+ var daemonHandle = null;
1262
+ function parseServeArgs(rawArgs) {
1263
+ const parsed = { stop: false };
1264
+ for (let i = 0; i < rawArgs.length; i += 1) {
1265
+ const arg = rawArgs[i];
1266
+ if (arg === "--stop") {
1267
+ parsed.stop = true;
1268
+ continue;
1269
+ }
1270
+ if (arg === "--port") {
1271
+ const value = rawArgs[i + 1];
1272
+ if (!value) {
1273
+ throw createUsageError("Missing value for --port");
1274
+ }
1275
+ parsed.port = parseNumberFlag(value, "--port", { min: 1, max: 65535 });
1276
+ i += 1;
1277
+ continue;
1278
+ }
1279
+ if (arg?.startsWith("--port=")) {
1280
+ const value = arg.split("=", 2)[1];
1281
+ if (!value) {
1282
+ throw createUsageError("Missing value for --port");
1283
+ }
1284
+ parsed.port = parseNumberFlag(value, "--port", { min: 1, max: 65535 });
1285
+ continue;
1286
+ }
1287
+ if (arg === "--token") {
1288
+ const value = rawArgs[i + 1];
1289
+ if (!value) {
1290
+ throw createUsageError("Missing value for --token");
1291
+ }
1292
+ parsed.token = value;
1293
+ i += 1;
1294
+ continue;
1295
+ }
1296
+ if (arg?.startsWith("--token=")) {
1297
+ const value = arg.split("=", 2)[1];
1298
+ if (!value) {
1299
+ throw createUsageError("Missing value for --token");
1300
+ }
1301
+ parsed.token = value;
1302
+ continue;
1303
+ }
1304
+ }
1305
+ return parsed;
1306
+ }
1307
+ async function runServe(args) {
1308
+ const serveArgs = parseServeArgs(args.rawArgs);
1309
+ if (serveArgs.stop) {
1310
+ const metadata = readDaemonMetadata();
1311
+ if (!metadata) {
1312
+ if (daemonHandle) {
1313
+ await daemonHandle.stop();
1314
+ daemonHandle = null;
1315
+ return { success: true, message: "Daemon stopped." };
1316
+ }
1317
+ return { success: false, message: "Daemon not running.", exitCode: EXIT_DISCONNECTED };
1318
+ }
1319
+ try {
1320
+ const response = await fetchWithTimeout(`http://127.0.0.1:${metadata.port}/stop`, {
1321
+ method: "POST",
1322
+ headers: { Authorization: `Bearer ${metadata.token}` }
1323
+ });
1324
+ if (!response.ok) {
1325
+ throw new Error(`Stop failed (${response.status})`);
1326
+ }
1327
+ return { success: true, message: "Daemon stopped." };
1328
+ } catch (error) {
1329
+ const message2 = error instanceof Error ? error.message : String(error);
1330
+ return { success: false, message: `Failed to stop daemon: ${message2}`, exitCode: EXIT_EXECUTION };
1331
+ }
1332
+ }
1333
+ const config = loadGlobalConfig();
1334
+ let nativeStatus = getNativeStatusSnapshot();
1335
+ let nativeMessage = null;
1336
+ if (!nativeStatus.installed) {
1337
+ const discovered = discoverExtensionId();
1338
+ const extensionId = config.nativeExtensionId ?? discovered.extensionId ?? null;
1339
+ const usedDiscovery = !config.nativeExtensionId && Boolean(discovered.extensionId);
1340
+ if (extensionId) {
1341
+ const installResult = installNativeHost(extensionId);
1342
+ if (installResult.success) {
1343
+ const suffix = usedDiscovery && discovered.matchedBy ? ` (auto-detected by ${discovered.matchedBy})` : "";
1344
+ nativeMessage = `${installResult.message ?? "Native host installed."}${suffix}`;
1345
+ nativeStatus = getNativeStatusSnapshot();
1346
+ } else {
1347
+ nativeMessage = `Native host install skipped: ${installResult.message ?? "unknown error"}`;
1348
+ }
1349
+ } else {
1350
+ nativeMessage = "Native host not installed. Set nativeExtensionId in opendevbrowser.jsonc to auto-install.";
1351
+ }
1352
+ }
1353
+ const handle = await startDaemon({
1354
+ port: serveArgs.port,
1355
+ token: serveArgs.token,
1356
+ config
1357
+ });
1358
+ daemonHandle = handle;
1359
+ const { state } = handle;
1360
+ const baseMessage = `Daemon running on 127.0.0.1:${state.port} (relay ${state.relayPort})`;
1361
+ const message = nativeMessage ? `${baseMessage}
1362
+ ${nativeMessage}` : baseMessage;
1363
+ return {
1364
+ success: true,
1365
+ message,
1366
+ data: { port: state.port, pid: state.pid, relayPort: state.relayPort, native: nativeStatus },
1367
+ exitCode: null
1368
+ };
1369
+ }
1370
+
1371
+ // src/cli/daemon-autostart.ts
1372
+ import { execFileSync as execFileSync2 } from "child_process";
1373
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
1374
+ import { homedir as homedir7 } from "os";
1375
+ import { dirname as dirname5, join as join8, resolve as resolve3 } from "path";
1376
+ import { fileURLToPath as fileURLToPath3 } from "url";
1377
+ var MAC_LABEL = "com.opendevbrowser.daemon";
1378
+ var WIN_TASK_NAME = "OpenDevBrowser Daemon";
1379
+ var defaultDeps = () => ({
1380
+ platform: process.platform,
1381
+ argv1: process.argv[1] ?? "",
1382
+ moduleUrl: import.meta.url,
1383
+ uid: typeof process.getuid === "function" ? process.getuid() : 0,
1384
+ homedir: homedir7,
1385
+ existsSync: existsSync8,
1386
+ mkdirSync: mkdirSync3,
1387
+ writeFileSync: writeFileSync4,
1388
+ unlinkSync: unlinkSync2,
1389
+ execFileSync: execFileSync2
1390
+ });
1391
+ var resolveCliPathFromModule = (moduleUrl, exists) => {
1392
+ const modulePath = fileURLToPath3(moduleUrl);
1393
+ const candidate = resolve3(dirname5(modulePath), "..", "index.js");
1394
+ if (!exists(candidate)) {
1395
+ throw new Error(`CLI entrypoint not found at ${candidate}`);
1396
+ }
1397
+ return candidate;
1398
+ };
1399
+ var resolveCliEntrypoint = (deps = {}) => {
1400
+ const resolved = { ...defaultDeps(), ...deps };
1401
+ const exists = resolved.existsSync;
1402
+ let cliPath = null;
1403
+ if (resolved.argv1) {
1404
+ const candidate = resolve3(resolved.argv1);
1405
+ if (exists(candidate)) {
1406
+ cliPath = candidate;
1407
+ }
1408
+ }
1409
+ if (!cliPath) {
1410
+ cliPath = resolveCliPathFromModule(resolved.moduleUrl, exists);
1411
+ }
1412
+ const nodePath = process.execPath;
1413
+ const args = [cliPath, "serve"];
1414
+ const command = `"${nodePath}" "${cliPath}" serve`;
1415
+ return { nodePath, cliPath, args, command };
1416
+ };
1417
+ var getLaunchAgentPath = (home = homedir7()) => {
1418
+ return join8(home, "Library", "LaunchAgents", `${MAC_LABEL}.plist`);
1419
+ };
1420
+ var buildLaunchAgentPlist = (entrypoint, options = {}) => {
1421
+ const label = options.label ?? MAC_LABEL;
1422
+ const stdoutPath = options.stdoutPath ?? join8(homedir7(), "Library", "Logs", "opendevbrowser-daemon.log");
1423
+ const stderrPath = options.stderrPath ?? join8(homedir7(), "Library", "Logs", "opendevbrowser-daemon.err.log");
1424
+ const programArgs = [entrypoint.nodePath, ...entrypoint.args].map((value) => ` <string>${value}</string>`).join("\n");
1425
+ return [
1426
+ '<?xml version="1.0" encoding="UTF-8"?>',
1427
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
1428
+ '<plist version="1.0">',
1429
+ "<dict>",
1430
+ ` <key>Label</key>`,
1431
+ ` <string>${label}</string>`,
1432
+ " <key>ProgramArguments</key>",
1433
+ " <array>",
1434
+ programArgs,
1435
+ " </array>",
1436
+ " <key>RunAtLoad</key>",
1437
+ " <true/>",
1438
+ " <key>KeepAlive</key>",
1439
+ " <true/>",
1440
+ " <key>StandardOutPath</key>",
1441
+ ` <string>${stdoutPath}</string>`,
1442
+ " <key>StandardErrorPath</key>",
1443
+ ` <string>${stderrPath}</string>`,
1444
+ "</dict>",
1445
+ "</plist>",
1446
+ ""
1447
+ ].join("\n");
1448
+ };
1449
+ var buildWindowsTaskArgs = (entrypoint, taskName = WIN_TASK_NAME) => {
1450
+ const command = `"${entrypoint.nodePath}" "${entrypoint.cliPath}" serve`;
1451
+ const args = [
1452
+ "/Create",
1453
+ "/TN",
1454
+ taskName,
1455
+ "/TR",
1456
+ command,
1457
+ "/SC",
1458
+ "ONLOGON",
1459
+ "/RL",
1460
+ "LIMITED",
1461
+ "/F"
1462
+ ];
1463
+ return { taskName, command, args };
1464
+ };
1465
+ var runCommand = (exec, command, args, ignoreFailure = false) => {
1466
+ try {
1467
+ exec(command, args, { stdio: "ignore" });
791
1468
  } catch (error) {
1469
+ if (ignoreFailure) return;
792
1470
  const message = error instanceof Error ? error.message : String(error);
793
- console.error(`Error: ${message}`);
794
- console.error("\nFor help: npx opendevbrowser --help");
795
- process.exit(1);
1471
+ throw new Error(`${command} ${args.join(" ")} failed: ${message}`);
1472
+ }
1473
+ };
1474
+ var installMacAutostart = (deps = {}) => {
1475
+ const resolved = { ...defaultDeps(), ...deps };
1476
+ const entrypoint = resolveCliEntrypoint(resolved);
1477
+ const plistPath = getLaunchAgentPath(resolved.homedir());
1478
+ resolved.mkdirSync(dirname5(plistPath), { recursive: true });
1479
+ resolved.writeFileSync(plistPath, buildLaunchAgentPlist(entrypoint), { encoding: "utf-8" });
1480
+ const uid = resolved.uid;
1481
+ runCommand(resolved.execFileSync, "launchctl", ["bootout", `gui/${uid}`, plistPath], true);
1482
+ runCommand(resolved.execFileSync, "launchctl", ["bootstrap", `gui/${uid}`, plistPath]);
1483
+ runCommand(resolved.execFileSync, "launchctl", ["enable", `gui/${uid}/${MAC_LABEL}`], true);
1484
+ runCommand(resolved.execFileSync, "launchctl", ["kickstart", "-k", `gui/${uid}/${MAC_LABEL}`], true);
1485
+ return {
1486
+ platform: "darwin",
1487
+ supported: true,
1488
+ installed: true,
1489
+ location: plistPath,
1490
+ label: MAC_LABEL,
1491
+ command: entrypoint.command
1492
+ };
1493
+ };
1494
+ var uninstallMacAutostart = (deps = {}) => {
1495
+ const resolved = { ...defaultDeps(), ...deps };
1496
+ const plistPath = getLaunchAgentPath(resolved.homedir());
1497
+ const uid = resolved.uid;
1498
+ runCommand(resolved.execFileSync, "launchctl", ["bootout", `gui/${uid}`, plistPath], true);
1499
+ if (resolved.existsSync(plistPath)) {
1500
+ resolved.unlinkSync(plistPath);
1501
+ }
1502
+ return {
1503
+ platform: "darwin",
1504
+ supported: true,
1505
+ installed: false,
1506
+ location: plistPath,
1507
+ label: MAC_LABEL
1508
+ };
1509
+ };
1510
+ var isWindowsTaskInstalled = (deps = {}) => {
1511
+ const resolved = { ...defaultDeps(), ...deps };
1512
+ try {
1513
+ resolved.execFileSync("schtasks", ["/Query", "/TN", WIN_TASK_NAME], { stdio: "ignore" });
1514
+ return true;
1515
+ } catch {
1516
+ return false;
1517
+ }
1518
+ };
1519
+ var installWindowsAutostart = (deps = {}) => {
1520
+ const resolved = { ...defaultDeps(), ...deps };
1521
+ const entrypoint = resolveCliEntrypoint(resolved);
1522
+ const { args } = buildWindowsTaskArgs(entrypoint, WIN_TASK_NAME);
1523
+ runCommand(resolved.execFileSync, "schtasks", args);
1524
+ return {
1525
+ platform: "win32",
1526
+ supported: true,
1527
+ installed: true,
1528
+ taskName: WIN_TASK_NAME,
1529
+ command: entrypoint.command
1530
+ };
1531
+ };
1532
+ var uninstallWindowsAutostart = (deps = {}) => {
1533
+ const resolved = { ...defaultDeps(), ...deps };
1534
+ runCommand(resolved.execFileSync, "schtasks", ["/Delete", "/TN", WIN_TASK_NAME, "/F"], true);
1535
+ return {
1536
+ platform: "win32",
1537
+ supported: true,
1538
+ installed: false,
1539
+ taskName: WIN_TASK_NAME
1540
+ };
1541
+ };
1542
+ var getAutostartStatus = (deps = {}) => {
1543
+ const resolved = { ...defaultDeps(), ...deps };
1544
+ const platform = resolved.platform;
1545
+ if (platform === "darwin") {
1546
+ const location = getLaunchAgentPath(resolved.homedir());
1547
+ return {
1548
+ platform,
1549
+ supported: true,
1550
+ installed: resolved.existsSync(location),
1551
+ location,
1552
+ label: MAC_LABEL
1553
+ };
1554
+ }
1555
+ if (platform === "win32") {
1556
+ return {
1557
+ platform,
1558
+ supported: true,
1559
+ installed: isWindowsTaskInstalled(resolved),
1560
+ taskName: WIN_TASK_NAME
1561
+ };
1562
+ }
1563
+ return {
1564
+ platform,
1565
+ supported: false,
1566
+ installed: false
1567
+ };
1568
+ };
1569
+ var installAutostart = (deps = {}) => {
1570
+ const platform = deps.platform ?? process.platform;
1571
+ if (platform === "darwin") {
1572
+ return installMacAutostart(deps);
1573
+ }
1574
+ if (platform === "win32") {
1575
+ return installWindowsAutostart(deps);
1576
+ }
1577
+ return {
1578
+ platform,
1579
+ supported: false,
1580
+ installed: false
1581
+ };
1582
+ };
1583
+ var uninstallAutostart = (deps = {}) => {
1584
+ const platform = deps.platform ?? process.platform;
1585
+ if (platform === "darwin") {
1586
+ return uninstallMacAutostart(deps);
1587
+ }
1588
+ if (platform === "win32") {
1589
+ return uninstallWindowsAutostart(deps);
1590
+ }
1591
+ return {
1592
+ platform,
1593
+ supported: false,
1594
+ installed: false
1595
+ };
1596
+ };
1597
+
1598
+ // src/cli/commands/daemon.ts
1599
+ var parseDaemonArgs = (rawArgs) => {
1600
+ const subcommand = rawArgs[0];
1601
+ if (subcommand === "install" || subcommand === "uninstall" || subcommand === "status") {
1602
+ return { subcommand };
1603
+ }
1604
+ throw createUsageError("Usage: opendevbrowser daemon <install|uninstall|status>");
1605
+ };
1606
+ var stopDaemonIfRunning = async () => {
1607
+ const metadata = readDaemonMetadata();
1608
+ if (!metadata) {
1609
+ return false;
1610
+ }
1611
+ try {
1612
+ const response = await fetchWithTimeout(`http://127.0.0.1:${metadata.port}/stop`, {
1613
+ method: "POST",
1614
+ headers: { Authorization: `Bearer ${metadata.token}` }
1615
+ });
1616
+ return response.ok;
1617
+ } catch {
1618
+ return false;
1619
+ }
1620
+ };
1621
+ var buildStatusMessage = (autostart, running) => {
1622
+ if (!autostart.supported) {
1623
+ return `Daemon autostart is not supported on ${autostart.platform}.`;
1624
+ }
1625
+ const installed = autostart.installed ? "installed" : "not installed";
1626
+ const runningText = running ? "running" : "not running";
1627
+ const location = autostart.location ? ` at ${autostart.location}` : "";
1628
+ const task = autostart.taskName ? ` (${autostart.taskName})` : "";
1629
+ return `Autostart ${installed}${location}${task}. Daemon is ${runningText}.`;
1630
+ };
1631
+ async function runDaemonCommand(args) {
1632
+ const { subcommand } = parseDaemonArgs(args.rawArgs);
1633
+ if (subcommand === "install") {
1634
+ const result = installAutostart();
1635
+ if (!result.supported) {
1636
+ return {
1637
+ success: false,
1638
+ message: `Daemon autostart is not supported on ${result.platform}.`,
1639
+ data: result,
1640
+ exitCode: EXIT_EXECUTION
1641
+ };
1642
+ }
1643
+ return {
1644
+ success: true,
1645
+ message: `Daemon autostart installed (${result.platform}).`,
1646
+ data: result
1647
+ };
1648
+ }
1649
+ if (subcommand === "uninstall") {
1650
+ const result = uninstallAutostart();
1651
+ if (!result.supported) {
1652
+ return {
1653
+ success: false,
1654
+ message: `Daemon autostart is not supported on ${result.platform}.`,
1655
+ data: result,
1656
+ exitCode: EXIT_EXECUTION
1657
+ };
1658
+ }
1659
+ await stopDaemonIfRunning();
1660
+ return {
1661
+ success: true,
1662
+ message: `Daemon autostart removed (${result.platform}).`,
1663
+ data: result
1664
+ };
1665
+ }
1666
+ const autostart = getAutostartStatus();
1667
+ const daemonStatus = await fetchDaemonStatusFromMetadata();
1668
+ const running = Boolean(daemonStatus);
1669
+ const message = buildStatusMessage(autostart, running);
1670
+ const data = {
1671
+ installed: autostart.installed,
1672
+ running,
1673
+ autostart: autostart.supported ? autostart : void 0
1674
+ };
1675
+ if (!running) {
1676
+ return {
1677
+ success: false,
1678
+ message,
1679
+ data,
1680
+ exitCode: EXIT_DISCONNECTED
1681
+ };
1682
+ }
1683
+ return {
1684
+ success: true,
1685
+ message,
1686
+ data: { ...data, status: daemonStatus }
1687
+ };
1688
+ }
1689
+
1690
+ // src/cli/commands/run.ts
1691
+ import { readFileSync as readFileSync4 } from "fs";
1692
+
1693
+ // src/cli/output.ts
1694
+ function writeOutput(payload, options) {
1695
+ if (options.quiet) {
1696
+ return;
1697
+ }
1698
+ if (options.format === "text") {
1699
+ if (typeof payload === "string") {
1700
+ console.log(payload);
1701
+ } else {
1702
+ console.log(JSON.stringify(payload, null, 2));
1703
+ }
1704
+ return;
1705
+ }
1706
+ if (options.format === "stream-json") {
1707
+ if (Array.isArray(payload)) {
1708
+ for (const entry of payload) {
1709
+ console.log(JSON.stringify(entry));
1710
+ }
1711
+ return;
1712
+ }
1713
+ }
1714
+ console.log(JSON.stringify(payload));
1715
+ }
1716
+
1717
+ // src/cli/commands/run.ts
1718
+ function parseRunArgs(rawArgs) {
1719
+ const parsed = { flags: [] };
1720
+ for (let i = 0; i < rawArgs.length; i += 1) {
1721
+ const arg = rawArgs[i];
1722
+ if (arg === "--script") {
1723
+ const value = rawArgs[i + 1];
1724
+ if (!value) throw createUsageError("Missing value for --script");
1725
+ parsed.scriptPath = value;
1726
+ i += 1;
1727
+ continue;
1728
+ }
1729
+ if (arg?.startsWith("--script=")) {
1730
+ parsed.scriptPath = arg.split("=", 2)[1];
1731
+ continue;
1732
+ }
1733
+ if (arg === "--headless") {
1734
+ parsed.headless = true;
1735
+ continue;
1736
+ }
1737
+ if (arg === "--profile") {
1738
+ const value = rawArgs[i + 1];
1739
+ if (!value) throw createUsageError("Missing value for --profile");
1740
+ parsed.profile = value;
1741
+ i += 1;
1742
+ continue;
1743
+ }
1744
+ if (arg?.startsWith("--profile=")) {
1745
+ parsed.profile = arg.split("=", 2)[1];
1746
+ continue;
1747
+ }
1748
+ if (arg === "--persist-profile") {
1749
+ parsed.persistProfile = true;
1750
+ continue;
1751
+ }
1752
+ if (arg === "--chrome-path") {
1753
+ const value = rawArgs[i + 1];
1754
+ if (!value) throw createUsageError("Missing value for --chrome-path");
1755
+ parsed.chromePath = value;
1756
+ i += 1;
1757
+ continue;
1758
+ }
1759
+ if (arg?.startsWith("--chrome-path=")) {
1760
+ parsed.chromePath = arg.split("=", 2)[1];
1761
+ continue;
1762
+ }
1763
+ if (arg === "--start-url") {
1764
+ const value = rawArgs[i + 1];
1765
+ if (!value) throw createUsageError("Missing value for --start-url");
1766
+ parsed.startUrl = value;
1767
+ i += 1;
1768
+ continue;
1769
+ }
1770
+ if (arg?.startsWith("--start-url=")) {
1771
+ const value = arg.split("=", 2)[1];
1772
+ if (!value) throw createUsageError("Missing value for --start-url");
1773
+ parsed.startUrl = value;
1774
+ continue;
1775
+ }
1776
+ if (arg === "--flag") {
1777
+ const value = rawArgs[i + 1];
1778
+ if (!value) throw createUsageError("Missing value for --flag");
1779
+ parsed.flags.push(value);
1780
+ i += 1;
1781
+ continue;
1782
+ }
1783
+ if (arg?.startsWith("--flag=")) {
1784
+ const value = arg.split("=", 2)[1];
1785
+ if (!value) throw createUsageError("Missing value for --flag");
1786
+ parsed.flags.push(value);
1787
+ continue;
1788
+ }
1789
+ }
1790
+ return parsed;
1791
+ }
1792
+ function readScriptFromStdin() {
1793
+ return new Promise((resolve4, reject) => {
1794
+ let data = "";
1795
+ process.stdin.setEncoding("utf8");
1796
+ process.stdin.on("data", (chunk) => {
1797
+ data += chunk;
1798
+ });
1799
+ process.stdin.on("end", () => resolve4(data));
1800
+ process.stdin.on("error", reject);
1801
+ });
1802
+ }
1803
+ async function runScriptCommand(args) {
1804
+ const runArgs = parseRunArgs(args.rawArgs);
1805
+ const outputOptions = { format: args.outputFormat, quiet: args.quiet };
1806
+ let scriptRaw = "";
1807
+ if (runArgs.scriptPath) {
1808
+ scriptRaw = readFileSync4(runArgs.scriptPath, "utf-8");
1809
+ } else if (!process.stdin.isTTY) {
1810
+ scriptRaw = await readScriptFromStdin();
1811
+ } else {
1812
+ throw createUsageError("Provide --script <path> or pipe JSON to stdin.");
1813
+ }
1814
+ let steps = [];
1815
+ try {
1816
+ const parsed = JSON.parse(scriptRaw);
1817
+ if (Array.isArray(parsed)) {
1818
+ steps = parsed;
1819
+ } else if (parsed && typeof parsed === "object" && Array.isArray(parsed.steps)) {
1820
+ steps = parsed.steps;
1821
+ } else {
1822
+ throw new Error("Script must be a JSON array or an object with steps.");
1823
+ }
1824
+ } catch (error) {
1825
+ const message = error instanceof Error ? error.message : "Invalid JSON script.";
1826
+ writeOutput({ success: false, error: message, exitCode: EXIT_USAGE }, outputOptions);
1827
+ return { success: false, message, exitCode: EXIT_USAGE, data: { suppressOutput: true } };
1828
+ }
1829
+ const core = createOpenDevBrowserCore({ directory: process.cwd() });
1830
+ const launchResult = await core.manager.launch({
1831
+ profile: runArgs.profile,
1832
+ headless: runArgs.headless,
1833
+ startUrl: runArgs.startUrl,
1834
+ chromePath: runArgs.chromePath,
1835
+ flags: runArgs.flags.length ? runArgs.flags : void 0,
1836
+ persistProfile: runArgs.persistProfile
1837
+ });
1838
+ try {
1839
+ const result = await core.runner.run(launchResult.sessionId, steps, true);
1840
+ writeOutput({
1841
+ success: true,
1842
+ sessionId: launchResult.sessionId,
1843
+ warnings: launchResult.warnings.length ? launchResult.warnings : void 0,
1844
+ ...result
1845
+ }, outputOptions);
1846
+ return { success: true, data: { suppressOutput: true } };
1847
+ } finally {
1848
+ await core.manager.disconnect(launchResult.sessionId, true);
1849
+ core.cleanup();
1850
+ }
1851
+ }
1852
+
1853
+ // src/cli/commands/session/launch.ts
1854
+ function parseLaunchArgs(rawArgs) {
1855
+ const parsed = { flags: [] };
1856
+ for (let i = 0; i < rawArgs.length; i += 1) {
1857
+ const arg = rawArgs[i];
1858
+ if (arg === "--headless") {
1859
+ parsed.headless = true;
1860
+ continue;
1861
+ }
1862
+ if (arg === "--profile") {
1863
+ const value = rawArgs[i + 1];
1864
+ if (!value) throw createUsageError("Missing value for --profile");
1865
+ parsed.profile = value;
1866
+ i += 1;
1867
+ continue;
1868
+ }
1869
+ if (arg?.startsWith("--profile=")) {
1870
+ parsed.profile = arg.split("=", 2)[1];
1871
+ continue;
1872
+ }
1873
+ if (arg === "--start-url") {
1874
+ const value = rawArgs[i + 1];
1875
+ if (!value) throw createUsageError("Missing value for --start-url");
1876
+ parsed.startUrl = value;
1877
+ i += 1;
1878
+ continue;
1879
+ }
1880
+ if (arg?.startsWith("--start-url=")) {
1881
+ parsed.startUrl = arg.split("=", 2)[1];
1882
+ continue;
1883
+ }
1884
+ if (arg === "--chrome-path") {
1885
+ const value = rawArgs[i + 1];
1886
+ if (!value) throw createUsageError("Missing value for --chrome-path");
1887
+ parsed.chromePath = value;
1888
+ i += 1;
1889
+ continue;
1890
+ }
1891
+ if (arg?.startsWith("--chrome-path=")) {
1892
+ parsed.chromePath = arg.split("=", 2)[1];
1893
+ continue;
1894
+ }
1895
+ if (arg === "--persist-profile") {
1896
+ parsed.persistProfile = true;
1897
+ continue;
1898
+ }
1899
+ if (arg === "--no-extension") {
1900
+ parsed.noExtension = true;
1901
+ continue;
1902
+ }
1903
+ if (arg === "--extension-only") {
1904
+ parsed.extensionOnly = true;
1905
+ continue;
1906
+ }
1907
+ if (arg === "--extension-legacy") {
1908
+ parsed.extensionLegacy = true;
1909
+ continue;
1910
+ }
1911
+ if (arg === "--wait-for-extension") {
1912
+ parsed.waitForExtension = true;
1913
+ continue;
1914
+ }
1915
+ if (arg === "--wait-timeout-ms") {
1916
+ const value = rawArgs[i + 1];
1917
+ if (!value) throw createUsageError("Missing value for --wait-timeout-ms");
1918
+ parsed.waitTimeoutMs = parseNumberFlag(value, "--wait-timeout-ms", { min: 1 });
1919
+ i += 1;
1920
+ continue;
1921
+ }
1922
+ if (arg?.startsWith("--wait-timeout-ms=")) {
1923
+ const value = arg.split("=", 2)[1];
1924
+ if (!value) throw createUsageError("Missing value for --wait-timeout-ms");
1925
+ parsed.waitTimeoutMs = parseNumberFlag(value, "--wait-timeout-ms", { min: 1 });
1926
+ continue;
1927
+ }
1928
+ if (arg === "--flag") {
1929
+ const value = rawArgs[i + 1];
1930
+ if (!value) throw createUsageError("Missing value for --flag");
1931
+ parsed.flags.push(value);
1932
+ i += 1;
1933
+ continue;
1934
+ }
1935
+ if (arg?.startsWith("--flag=")) {
1936
+ const value = arg.split("=", 2)[1];
1937
+ if (!value) throw createUsageError("Missing value for --flag");
1938
+ parsed.flags.push(value);
1939
+ continue;
1940
+ }
1941
+ }
1942
+ return parsed;
1943
+ }
1944
+ async function runSessionLaunch(args) {
1945
+ const launchArgs = parseLaunchArgs(args.rawArgs);
1946
+ try {
1947
+ const result = await callDaemon("session.launch", launchArgs);
1948
+ return {
1949
+ success: true,
1950
+ message: `Session launched: ${result.sessionId}`,
1951
+ data: result
1952
+ };
1953
+ } catch (error) {
1954
+ if (args.noInteractive) {
1955
+ throw error;
1956
+ }
1957
+ const message = error instanceof Error ? error.message : "";
1958
+ const lower = message.toLowerCase();
1959
+ const isExtensionFailure = message.includes("Extension not connected") || message.includes("Extension relay connection failed") || lower.includes("unauthorized");
1960
+ if (!isExtensionFailure) {
1961
+ throw error;
1962
+ }
1963
+ const retry = await promptYesNo(
1964
+ lower.includes("unauthorized") ? "Relay token mismatch detected. Open the extension popup and click Connect to refresh pairing, then retry now?" : "Extension not connected. Open the extension popup and click Connect, then retry now?",
1965
+ false
1966
+ );
1967
+ if (retry) {
1968
+ try {
1969
+ const result = await callDaemon("session.launch", { ...launchArgs, waitForExtension: true });
1970
+ return {
1971
+ success: true,
1972
+ message: `Session launched: ${result.sessionId}`,
1973
+ data: result
1974
+ };
1975
+ } catch (retryError) {
1976
+ error = retryError;
1977
+ }
1978
+ }
1979
+ const proceedManaged = await promptYesNo("Proceed with a managed session (headed)?", false);
1980
+ if (proceedManaged) {
1981
+ const useHeadless = await promptYesNo("Run headless instead?", false);
1982
+ const result = await callDaemon("session.launch", {
1983
+ ...launchArgs,
1984
+ noExtension: true,
1985
+ headless: useHeadless ? true : false
1986
+ });
1987
+ return {
1988
+ success: true,
1989
+ message: `Session launched: ${result.sessionId}`,
1990
+ data: result
1991
+ };
1992
+ }
1993
+ const proceedCdp = await promptYesNo("Proceed with CDPConnect (requires Chrome --remote-debugging-port=9222)?", false);
1994
+ if (proceedCdp) {
1995
+ const result = await callDaemon("session.connect", {});
1996
+ return {
1997
+ success: true,
1998
+ message: `Session connected: ${result.sessionId}`,
1999
+ data: result
2000
+ };
2001
+ }
2002
+ throw error;
2003
+ }
2004
+ }
2005
+ function promptYesNo(question, defaultYes) {
2006
+ if (!process.stdin.isTTY) {
2007
+ return Promise.resolve(false);
2008
+ }
2009
+ const suffix = defaultYes ? " [Y/n] " : " [y/N] ";
2010
+ return new Promise((resolve4) => {
2011
+ process.stdout.write(`${question}${suffix}`);
2012
+ process.stdin.setEncoding("utf8");
2013
+ process.stdin.once("data", (data) => {
2014
+ const input = data.toString().trim().toLowerCase();
2015
+ if (!input) {
2016
+ resolve4(defaultYes);
2017
+ return;
2018
+ }
2019
+ resolve4(input === "y" || input === "yes");
2020
+ });
2021
+ });
2022
+ }
2023
+
2024
+ // src/cli/commands/session/connect.ts
2025
+ function parseConnectArgs(rawArgs) {
2026
+ const parsed = {};
2027
+ for (let i = 0; i < rawArgs.length; i += 1) {
2028
+ const arg = rawArgs[i];
2029
+ if (arg === "--ws-endpoint") {
2030
+ const value = rawArgs[i + 1];
2031
+ if (!value) throw createUsageError("Missing value for --ws-endpoint");
2032
+ parsed.wsEndpoint = value;
2033
+ i += 1;
2034
+ continue;
2035
+ }
2036
+ if (arg?.startsWith("--ws-endpoint=")) {
2037
+ parsed.wsEndpoint = arg.split("=", 2)[1];
2038
+ continue;
2039
+ }
2040
+ if (arg === "--host") {
2041
+ const value = rawArgs[i + 1];
2042
+ if (!value) throw createUsageError("Missing value for --host");
2043
+ parsed.host = value;
2044
+ i += 1;
2045
+ continue;
2046
+ }
2047
+ if (arg?.startsWith("--host=")) {
2048
+ const value = arg.split("=", 2)[1];
2049
+ if (!value) throw createUsageError("Missing value for --host");
2050
+ parsed.host = value;
2051
+ continue;
2052
+ }
2053
+ if (arg === "--cdp-port") {
2054
+ const value = rawArgs[i + 1];
2055
+ if (!value) throw createUsageError("Missing value for --cdp-port");
2056
+ parsed.port = parseNumberFlag(value, "--cdp-port", { min: 1, max: 65535 });
2057
+ i += 1;
2058
+ continue;
2059
+ }
2060
+ if (arg?.startsWith("--cdp-port=")) {
2061
+ const value = arg.split("=", 2)[1];
2062
+ if (!value) throw createUsageError("Missing value for --cdp-port");
2063
+ parsed.port = parseNumberFlag(value, "--cdp-port", { min: 1, max: 65535 });
2064
+ continue;
2065
+ }
2066
+ if (arg === "--extension-legacy") {
2067
+ parsed.extensionLegacy = true;
2068
+ continue;
2069
+ }
2070
+ }
2071
+ return parsed;
2072
+ }
2073
+ async function runSessionConnect(args) {
2074
+ const connectArgs = parseConnectArgs(args.rawArgs);
2075
+ const result = await callDaemon("session.connect", connectArgs);
2076
+ return {
2077
+ success: true,
2078
+ message: `Session connected: ${result.sessionId}`,
2079
+ data: result
2080
+ };
2081
+ }
2082
+
2083
+ // src/cli/commands/session/disconnect.ts
2084
+ function parseDisconnectArgs(rawArgs) {
2085
+ const parsed = {};
2086
+ for (let i = 0; i < rawArgs.length; i += 1) {
2087
+ const arg = rawArgs[i];
2088
+ if (arg === "--session-id") {
2089
+ const value = rawArgs[i + 1];
2090
+ if (!value) throw createUsageError("Missing value for --session-id");
2091
+ parsed.sessionId = value;
2092
+ i += 1;
2093
+ continue;
2094
+ }
2095
+ if (arg?.startsWith("--session-id=")) {
2096
+ parsed.sessionId = arg.split("=", 2)[1];
2097
+ continue;
2098
+ }
2099
+ if (arg === "--close-browser") {
2100
+ parsed.closeBrowser = true;
2101
+ continue;
2102
+ }
2103
+ }
2104
+ return parsed;
2105
+ }
2106
+ async function runSessionDisconnect(args) {
2107
+ const { sessionId, closeBrowser } = parseDisconnectArgs(args.rawArgs);
2108
+ if (!sessionId) {
2109
+ throw createUsageError("Missing --session-id");
2110
+ }
2111
+ await callDaemon("session.disconnect", { sessionId, closeBrowser }, { timeoutMs: 2e4 });
2112
+ return { success: true, message: `Session disconnected: ${sessionId}` };
2113
+ }
2114
+
2115
+ // src/cli/commands/session/status.ts
2116
+ function parseStatusArgs(rawArgs) {
2117
+ const parsed = {};
2118
+ for (let i = 0; i < rawArgs.length; i += 1) {
2119
+ const arg = rawArgs[i];
2120
+ if (arg === "--session-id") {
2121
+ const value = rawArgs[i + 1];
2122
+ if (!value) throw createUsageError("Missing value for --session-id");
2123
+ parsed.sessionId = value;
2124
+ i += 1;
2125
+ continue;
2126
+ }
2127
+ if (arg?.startsWith("--session-id=")) {
2128
+ parsed.sessionId = arg.split("=", 2)[1];
2129
+ continue;
2130
+ }
2131
+ }
2132
+ return parsed;
2133
+ }
2134
+ async function runSessionStatus(args) {
2135
+ const { sessionId } = parseStatusArgs(args.rawArgs);
2136
+ if (!sessionId) {
2137
+ throw createUsageError("Missing --session-id");
2138
+ }
2139
+ const result = await callDaemon("session.status", { sessionId });
2140
+ return { success: true, message: `Session status: ${sessionId}`, data: result };
2141
+ }
2142
+
2143
+ // src/cli/commands/status.ts
2144
+ var parseStatusArgs2 = (rawArgs) => {
2145
+ const parsed = { daemon: false };
2146
+ for (let i = 0; i < rawArgs.length; i += 1) {
2147
+ const arg = rawArgs[i];
2148
+ if (arg === "--daemon") {
2149
+ parsed.daemon = true;
2150
+ continue;
2151
+ }
2152
+ if (arg === "--session-id") {
2153
+ const value = rawArgs[i + 1];
2154
+ if (!value) throw createUsageError("Missing value for --session-id");
2155
+ parsed.sessionId = value;
2156
+ i += 1;
2157
+ continue;
2158
+ }
2159
+ if (arg?.startsWith("--session-id=")) {
2160
+ parsed.sessionId = arg.split("=", 2)[1];
2161
+ continue;
2162
+ }
2163
+ }
2164
+ return parsed;
2165
+ };
2166
+ async function runStatus(args) {
2167
+ const { sessionId, daemon } = parseStatusArgs2(args.rawArgs);
2168
+ if (sessionId && daemon) {
2169
+ throw createUsageError("Use --session-id or --daemon, not both.");
2170
+ }
2171
+ if (sessionId) {
2172
+ return runSessionStatus(args);
2173
+ }
2174
+ if (!daemon && args.transport === "native") {
2175
+ const nativeStatus2 = getNativeStatusSnapshot();
2176
+ if (!nativeStatus2.installed) {
2177
+ return {
2178
+ success: false,
2179
+ message: "Native host not installed.",
2180
+ data: nativeStatus2,
2181
+ exitCode: EXIT_DISCONNECTED
2182
+ };
2183
+ }
2184
+ return {
2185
+ success: true,
2186
+ message: nativeStatus2.extensionId ? `Native host installed for extension ${nativeStatus2.extensionId}.` : "Native host installed.",
2187
+ data: nativeStatus2
2188
+ };
2189
+ }
2190
+ const daemonStatus = await fetchDaemonStatusFromMetadata();
2191
+ if (!daemonStatus) {
2192
+ throw createUsageError("Daemon not running. Start with `opendevbrowser serve`.");
2193
+ }
2194
+ const nativeStatus = getNativeStatusSnapshot();
2195
+ const baseMessage = [
2196
+ `Daemon OK (pid=${daemonStatus.pid})`,
2197
+ `Relay: port=${daemonStatus.relay.port ?? "n/a"} ext=${daemonStatus.relay.extensionConnected ? "on" : "off"} handshake=${daemonStatus.relay.extensionHandshakeComplete ? "on" : "off"} cdp=${daemonStatus.relay.cdpConnected ? "on" : "off"} annotate=${daemonStatus.relay.annotationConnected ? "on" : "off"} ops=${daemonStatus.relay.opsConnected ? "on" : "off"} pairing=${daemonStatus.relay.pairingRequired ? "on" : "off"} health=${daemonStatus.relay.health?.reason ?? "n/a"}`,
2198
+ `Native: ${nativeStatus.installed ? "installed" : "not installed"}${nativeStatus.extensionId ? ` (${nativeStatus.extensionId})` : ""}`,
2199
+ daemonStatus.relay.lastHandshakeError ? `Relay last handshake error: ${daemonStatus.relay.lastHandshakeError.code} (${daemonStatus.relay.lastHandshakeError.message})` : "Relay last handshake error: none",
2200
+ "Legend: ext=extension websocket, handshake=extension handshake, cdp=active /cdp client, annotate=annotation channel, ops=ops clients, pairing=token required, health=relay status"
2201
+ ].join("\n");
2202
+ const message = daemon || args.outputFormat !== "text" ? baseMessage : [
2203
+ "Warning: `status` defaults to daemon status. Use --daemon explicitly or --session-id for session status.",
2204
+ baseMessage
2205
+ ].join("\n");
2206
+ return {
2207
+ success: true,
2208
+ message,
2209
+ data: { ...daemonStatus, native: nativeStatus }
2210
+ };
2211
+ }
2212
+
2213
+ // src/cli/commands/nav/goto.ts
2214
+ function parseGotoArgs(rawArgs) {
2215
+ const parsed = {};
2216
+ for (let i = 0; i < rawArgs.length; i += 1) {
2217
+ const arg = rawArgs[i];
2218
+ if (arg === "--session-id") {
2219
+ const value = rawArgs[i + 1];
2220
+ if (!value) throw createUsageError("Missing value for --session-id");
2221
+ parsed.sessionId = value;
2222
+ i += 1;
2223
+ continue;
2224
+ }
2225
+ if (arg?.startsWith("--session-id=")) {
2226
+ parsed.sessionId = arg.split("=", 2)[1];
2227
+ continue;
2228
+ }
2229
+ if (arg === "--url") {
2230
+ const value = rawArgs[i + 1];
2231
+ if (!value) throw createUsageError("Missing value for --url");
2232
+ parsed.url = value;
2233
+ i += 1;
2234
+ continue;
2235
+ }
2236
+ if (arg?.startsWith("--url=")) {
2237
+ parsed.url = arg.split("=", 2)[1];
2238
+ continue;
2239
+ }
2240
+ if (arg === "--wait-until") {
2241
+ const value = rawArgs[i + 1];
2242
+ if (!value) throw createUsageError("Missing value for --wait-until");
2243
+ parsed.waitUntil = value;
2244
+ i += 1;
2245
+ continue;
2246
+ }
2247
+ if (arg?.startsWith("--wait-until=")) {
2248
+ parsed.waitUntil = arg.split("=", 2)[1];
2249
+ continue;
2250
+ }
2251
+ if (arg === "--timeout-ms") {
2252
+ const value = rawArgs[i + 1];
2253
+ if (!value) throw createUsageError("Missing value for --timeout-ms");
2254
+ parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
2255
+ i += 1;
2256
+ continue;
2257
+ }
2258
+ if (arg?.startsWith("--timeout-ms=")) {
2259
+ const value = arg.split("=", 2)[1];
2260
+ if (!value) throw createUsageError("Missing value for --timeout-ms");
2261
+ parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
2262
+ continue;
2263
+ }
2264
+ }
2265
+ return parsed;
2266
+ }
2267
+ async function runGoto(args) {
2268
+ const { sessionId, url, waitUntil, timeoutMs } = parseGotoArgs(args.rawArgs);
2269
+ if (!sessionId) throw createUsageError("Missing --session-id");
2270
+ if (!url) throw createUsageError("Missing --url");
2271
+ const result = await callDaemon("nav.goto", { sessionId, url, waitUntil, timeoutMs });
2272
+ return { success: true, message: `Navigated: ${url}`, data: result };
2273
+ }
2274
+
2275
+ // src/cli/commands/nav/wait.ts
2276
+ function parseWaitArgs(rawArgs) {
2277
+ const parsed = {};
2278
+ for (let i = 0; i < rawArgs.length; i += 1) {
2279
+ const arg = rawArgs[i];
2280
+ if (arg === "--session-id") {
2281
+ const value = rawArgs[i + 1];
2282
+ if (!value) throw createUsageError("Missing value for --session-id");
2283
+ parsed.sessionId = value;
2284
+ i += 1;
2285
+ continue;
2286
+ }
2287
+ if (arg?.startsWith("--session-id=")) {
2288
+ parsed.sessionId = arg.split("=", 2)[1];
2289
+ continue;
2290
+ }
2291
+ if (arg === "--ref") {
2292
+ const value = rawArgs[i + 1];
2293
+ if (!value) throw createUsageError("Missing value for --ref");
2294
+ parsed.ref = value;
2295
+ i += 1;
2296
+ continue;
2297
+ }
2298
+ if (arg?.startsWith("--ref=")) {
2299
+ parsed.ref = arg.split("=", 2)[1];
2300
+ continue;
2301
+ }
2302
+ if (arg === "--state") {
2303
+ const value = rawArgs[i + 1];
2304
+ if (!value) throw createUsageError("Missing value for --state");
2305
+ parsed.state = value;
2306
+ i += 1;
2307
+ continue;
2308
+ }
2309
+ if (arg?.startsWith("--state=")) {
2310
+ parsed.state = arg.split("=", 2)[1];
2311
+ continue;
2312
+ }
2313
+ if (arg === "--until") {
2314
+ const value = rawArgs[i + 1];
2315
+ if (!value) throw createUsageError("Missing value for --until");
2316
+ parsed.until = value;
2317
+ i += 1;
2318
+ continue;
2319
+ }
2320
+ if (arg?.startsWith("--until=")) {
2321
+ parsed.until = arg.split("=", 2)[1];
2322
+ continue;
2323
+ }
2324
+ if (arg === "--timeout-ms") {
2325
+ const value = rawArgs[i + 1];
2326
+ if (!value) throw createUsageError("Missing value for --timeout-ms");
2327
+ parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
2328
+ i += 1;
2329
+ continue;
2330
+ }
2331
+ if (arg?.startsWith("--timeout-ms=")) {
2332
+ const value = arg.split("=", 2)[1];
2333
+ if (!value) throw createUsageError("Missing value for --timeout-ms");
2334
+ parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
2335
+ continue;
2336
+ }
2337
+ }
2338
+ return parsed;
2339
+ }
2340
+ async function runWait(args) {
2341
+ const { sessionId, ref, state, until, timeoutMs } = parseWaitArgs(args.rawArgs);
2342
+ if (!sessionId) throw createUsageError("Missing --session-id");
2343
+ const result = await callDaemon("nav.wait", { sessionId, ref, state, until, timeoutMs });
2344
+ return { success: true, message: "Wait complete.", data: result };
2345
+ }
2346
+
2347
+ // src/cli/commands/nav/snapshot.ts
2348
+ function parseSnapshotArgs(rawArgs) {
2349
+ const parsed = {};
2350
+ for (let i = 0; i < rawArgs.length; i += 1) {
2351
+ const arg = rawArgs[i];
2352
+ if (arg === "--session-id") {
2353
+ const value = rawArgs[i + 1];
2354
+ if (!value) throw createUsageError("Missing value for --session-id");
2355
+ parsed.sessionId = value;
2356
+ i += 1;
2357
+ continue;
2358
+ }
2359
+ if (arg?.startsWith("--session-id=")) {
2360
+ parsed.sessionId = arg.split("=", 2)[1];
2361
+ continue;
2362
+ }
2363
+ if (arg === "--mode") {
2364
+ const value = rawArgs[i + 1];
2365
+ if (!value) throw createUsageError("Missing value for --mode");
2366
+ parsed.mode = value;
2367
+ i += 1;
2368
+ continue;
2369
+ }
2370
+ if (arg?.startsWith("--mode=")) {
2371
+ parsed.mode = arg.split("=", 2)[1];
2372
+ continue;
2373
+ }
2374
+ if (arg === "--max-chars") {
2375
+ const value = rawArgs[i + 1];
2376
+ if (!value) throw createUsageError("Missing value for --max-chars");
2377
+ parsed.maxChars = Number(value);
2378
+ i += 1;
2379
+ continue;
2380
+ }
2381
+ if (arg?.startsWith("--max-chars=")) {
2382
+ parsed.maxChars = Number(arg.split("=", 2)[1]);
2383
+ continue;
2384
+ }
2385
+ if (arg === "--cursor") {
2386
+ const value = rawArgs[i + 1];
2387
+ if (!value) throw createUsageError("Missing value for --cursor");
2388
+ parsed.cursor = value;
2389
+ i += 1;
2390
+ continue;
2391
+ }
2392
+ if (arg?.startsWith("--cursor=")) {
2393
+ parsed.cursor = arg.split("=", 2)[1];
2394
+ continue;
2395
+ }
2396
+ }
2397
+ return parsed;
2398
+ }
2399
+ async function runSnapshot(args) {
2400
+ const { sessionId, mode, maxChars, cursor } = parseSnapshotArgs(args.rawArgs);
2401
+ if (!sessionId) throw createUsageError("Missing --session-id");
2402
+ const result = await callDaemon("nav.snapshot", { sessionId, mode, maxChars, cursor });
2403
+ return { success: true, message: "Snapshot captured.", data: result };
2404
+ }
2405
+
2406
+ // src/cli/commands/annotate.ts
2407
+ var requireValue = (value, flag) => {
2408
+ if (!value) throw createUsageError(`Missing value for ${flag}`);
2409
+ return value;
2410
+ };
2411
+ var requireScreenshotMode = (value) => {
2412
+ if (value === "visible" || value === "full" || value === "none") {
2413
+ return value;
2414
+ }
2415
+ throw createUsageError(`Invalid --screenshot-mode: ${value}`);
2416
+ };
2417
+ var requireTransport = (value) => {
2418
+ if (value === "auto" || value === "direct" || value === "relay") {
2419
+ return value;
2420
+ }
2421
+ throw createUsageError(`Invalid --transport: ${value}`);
2422
+ };
2423
+ var parseAnnotateArgs = (rawArgs) => {
2424
+ const parsed = {};
2425
+ for (let i = 0; i < rawArgs.length; i += 1) {
2426
+ const arg = rawArgs[i];
2427
+ if (arg === "--session-id") {
2428
+ const value = requireValue(rawArgs[i + 1], "--session-id");
2429
+ parsed.sessionId = value;
2430
+ i += 1;
2431
+ continue;
2432
+ }
2433
+ if (arg?.startsWith("--session-id=")) {
2434
+ const value = requireValue(arg.split("=", 2)[1], "--session-id");
2435
+ parsed.sessionId = value;
2436
+ continue;
2437
+ }
2438
+ if (arg === "--url") {
2439
+ const value = requireValue(rawArgs[i + 1], "--url");
2440
+ parsed.url = value;
2441
+ i += 1;
2442
+ continue;
2443
+ }
2444
+ if (arg?.startsWith("--url=")) {
2445
+ const value = requireValue(arg.split("=", 2)[1], "--url");
2446
+ parsed.url = value;
2447
+ continue;
2448
+ }
2449
+ if (arg === "--screenshot-mode") {
2450
+ const value = requireValue(rawArgs[i + 1], "--screenshot-mode");
2451
+ parsed.screenshotMode = requireScreenshotMode(value);
2452
+ i += 1;
2453
+ continue;
2454
+ }
2455
+ if (arg?.startsWith("--screenshot-mode=")) {
2456
+ const value = requireValue(arg.split("=", 2)[1], "--screenshot-mode");
2457
+ parsed.screenshotMode = requireScreenshotMode(value);
2458
+ continue;
2459
+ }
2460
+ if (arg === "--transport") {
2461
+ const value = requireValue(rawArgs[i + 1], "--transport");
2462
+ parsed.transport = requireTransport(value);
2463
+ i += 1;
2464
+ continue;
2465
+ }
2466
+ if (arg?.startsWith("--transport=")) {
2467
+ const value = requireValue(arg.split("=", 2)[1], "--transport");
2468
+ parsed.transport = requireTransport(value);
2469
+ continue;
2470
+ }
2471
+ if (arg === "--target-id") {
2472
+ const value = requireValue(rawArgs[i + 1], "--target-id");
2473
+ parsed.targetId = value;
2474
+ i += 1;
2475
+ continue;
2476
+ }
2477
+ if (arg?.startsWith("--target-id=")) {
2478
+ const value = requireValue(arg.split("=", 2)[1], "--target-id");
2479
+ parsed.targetId = value;
2480
+ continue;
2481
+ }
2482
+ if (arg === "--tab-id") {
2483
+ const value = requireValue(rawArgs[i + 1], "--tab-id");
2484
+ parsed.tabId = parseNumberFlag(value, "--tab-id", { min: 1 });
2485
+ i += 1;
2486
+ continue;
2487
+ }
2488
+ if (arg?.startsWith("--tab-id=")) {
2489
+ const value = requireValue(arg.split("=", 2)[1], "--tab-id");
2490
+ parsed.tabId = parseNumberFlag(value, "--tab-id", { min: 1 });
2491
+ continue;
2492
+ }
2493
+ if (arg === "--debug") {
2494
+ parsed.debug = true;
2495
+ continue;
2496
+ }
2497
+ if (arg === "--context") {
2498
+ const value = requireValue(rawArgs[i + 1], "--context");
2499
+ parsed.context = value;
2500
+ i += 1;
2501
+ continue;
2502
+ }
2503
+ if (arg?.startsWith("--context=")) {
2504
+ const value = requireValue(arg.split("=", 2)[1], "--context");
2505
+ parsed.context = value;
2506
+ continue;
2507
+ }
2508
+ if (arg === "--timeout-ms") {
2509
+ const value = requireValue(rawArgs[i + 1], "--timeout-ms");
2510
+ parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
2511
+ i += 1;
2512
+ continue;
2513
+ }
2514
+ if (arg?.startsWith("--timeout-ms=")) {
2515
+ const value = requireValue(arg.split("=", 2)[1], "--timeout-ms");
2516
+ parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
2517
+ continue;
2518
+ }
2519
+ }
2520
+ return parsed;
2521
+ };
2522
+ async function runAnnotate(args) {
2523
+ const { sessionId, url, screenshotMode, debug, context, timeoutMs, transport, targetId, tabId } = parseAnnotateArgs(args.rawArgs);
2524
+ if (!sessionId) throw createUsageError("Missing --session-id");
2525
+ const client = new DaemonClient({ autoRenew: true });
2526
+ const callTimeoutMs = typeof timeoutMs === "number" ? timeoutMs + 1e4 : void 0;
2527
+ try {
2528
+ const response = await client.call("annotate", {
2529
+ sessionId,
2530
+ transport,
2531
+ targetId,
2532
+ tabId,
2533
+ url,
2534
+ screenshotMode,
2535
+ debug,
2536
+ context,
2537
+ timeoutMs
2538
+ }, { timeoutMs: callTimeoutMs });
2539
+ if (response.status !== "ok" || !response.payload) {
2540
+ const message2 = response.error?.message ?? "Annotation failed.";
2541
+ throw new Error(message2);
2542
+ }
2543
+ const { message, details, screenshots } = await buildAnnotateResult(response.payload);
2544
+ return { success: true, message, data: { details, screenshots } };
2545
+ } finally {
2546
+ await client.releaseBinding().catch(() => {
2547
+ });
2548
+ }
2549
+ }
2550
+
2551
+ // src/cli/commands/interact/click.ts
2552
+ function parseClickArgs(rawArgs) {
2553
+ const parsed = {};
2554
+ for (let i = 0; i < rawArgs.length; i += 1) {
2555
+ const arg = rawArgs[i];
2556
+ if (arg === "--session-id") {
2557
+ const value = rawArgs[i + 1];
2558
+ if (!value) throw createUsageError("Missing value for --session-id");
2559
+ parsed.sessionId = value;
2560
+ i += 1;
2561
+ continue;
2562
+ }
2563
+ if (arg?.startsWith("--session-id=")) {
2564
+ parsed.sessionId = arg.split("=", 2)[1];
2565
+ continue;
2566
+ }
2567
+ if (arg === "--ref") {
2568
+ const value = rawArgs[i + 1];
2569
+ if (!value) throw createUsageError("Missing value for --ref");
2570
+ parsed.ref = value;
2571
+ i += 1;
2572
+ continue;
2573
+ }
2574
+ if (arg?.startsWith("--ref=")) {
2575
+ parsed.ref = arg.split("=", 2)[1];
2576
+ continue;
2577
+ }
2578
+ }
2579
+ return parsed;
2580
+ }
2581
+ async function runClick(args) {
2582
+ const { sessionId, ref } = parseClickArgs(args.rawArgs);
2583
+ if (!sessionId) throw createUsageError("Missing --session-id");
2584
+ if (!ref) throw createUsageError("Missing --ref");
2585
+ const result = await callDaemon("interact.click", { sessionId, ref });
2586
+ return { success: true, message: "Click complete.", data: result };
2587
+ }
2588
+
2589
+ // src/cli/commands/interact/hover.ts
2590
+ function parseHoverArgs(rawArgs) {
2591
+ const parsed = {};
2592
+ for (let i = 0; i < rawArgs.length; i += 1) {
2593
+ const arg = rawArgs[i];
2594
+ if (arg === "--session-id") {
2595
+ const value = rawArgs[i + 1];
2596
+ if (!value) throw createUsageError("Missing value for --session-id");
2597
+ parsed.sessionId = value;
2598
+ i += 1;
2599
+ continue;
2600
+ }
2601
+ if (arg?.startsWith("--session-id=")) {
2602
+ parsed.sessionId = arg.split("=", 2)[1];
2603
+ continue;
2604
+ }
2605
+ if (arg === "--ref") {
2606
+ const value = rawArgs[i + 1];
2607
+ if (!value) throw createUsageError("Missing value for --ref");
2608
+ parsed.ref = value;
2609
+ i += 1;
2610
+ continue;
2611
+ }
2612
+ if (arg?.startsWith("--ref=")) {
2613
+ parsed.ref = arg.split("=", 2)[1];
2614
+ continue;
2615
+ }
2616
+ }
2617
+ return parsed;
2618
+ }
2619
+ async function runHover(args) {
2620
+ const { sessionId, ref } = parseHoverArgs(args.rawArgs);
2621
+ if (!sessionId) throw createUsageError("Missing --session-id");
2622
+ if (!ref) throw createUsageError("Missing --ref");
2623
+ const result = await callDaemon("interact.hover", { sessionId, ref });
2624
+ return { success: true, message: "Hover complete.", data: result };
2625
+ }
2626
+
2627
+ // src/cli/commands/interact/press.ts
2628
+ function parsePressArgs(rawArgs) {
2629
+ const parsed = {};
2630
+ for (let i = 0; i < rawArgs.length; i += 1) {
2631
+ const arg = rawArgs[i];
2632
+ if (arg === "--session-id") {
2633
+ const value = rawArgs[i + 1];
2634
+ if (!value) throw createUsageError("Missing value for --session-id");
2635
+ parsed.sessionId = value;
2636
+ i += 1;
2637
+ continue;
2638
+ }
2639
+ if (arg?.startsWith("--session-id=")) {
2640
+ parsed.sessionId = arg.split("=", 2)[1];
2641
+ continue;
2642
+ }
2643
+ if (arg === "--key") {
2644
+ const value = rawArgs[i + 1];
2645
+ if (!value) throw createUsageError("Missing value for --key");
2646
+ parsed.key = value;
2647
+ i += 1;
2648
+ continue;
2649
+ }
2650
+ if (arg?.startsWith("--key=")) {
2651
+ parsed.key = arg.split("=", 2)[1];
2652
+ continue;
2653
+ }
2654
+ if (arg === "--ref") {
2655
+ const value = rawArgs[i + 1];
2656
+ if (!value) throw createUsageError("Missing value for --ref");
2657
+ parsed.ref = value;
2658
+ i += 1;
2659
+ continue;
2660
+ }
2661
+ if (arg?.startsWith("--ref=")) {
2662
+ parsed.ref = arg.split("=", 2)[1];
2663
+ continue;
2664
+ }
2665
+ }
2666
+ return parsed;
2667
+ }
2668
+ async function runPress(args) {
2669
+ const { sessionId, key, ref } = parsePressArgs(args.rawArgs);
2670
+ if (!sessionId) throw createUsageError("Missing --session-id");
2671
+ if (!key) throw createUsageError("Missing --key");
2672
+ const result = await callDaemon("interact.press", { sessionId, key, ref });
2673
+ return { success: true, message: "Key press complete.", data: result };
2674
+ }
2675
+
2676
+ // src/cli/commands/interact/check.ts
2677
+ function parseCheckArgs(rawArgs) {
2678
+ const parsed = {};
2679
+ for (let i = 0; i < rawArgs.length; i += 1) {
2680
+ const arg = rawArgs[i];
2681
+ if (arg === "--session-id") {
2682
+ const value = rawArgs[i + 1];
2683
+ if (!value) throw createUsageError("Missing value for --session-id");
2684
+ parsed.sessionId = value;
2685
+ i += 1;
2686
+ continue;
2687
+ }
2688
+ if (arg?.startsWith("--session-id=")) {
2689
+ parsed.sessionId = arg.split("=", 2)[1];
2690
+ continue;
2691
+ }
2692
+ if (arg === "--ref") {
2693
+ const value = rawArgs[i + 1];
2694
+ if (!value) throw createUsageError("Missing value for --ref");
2695
+ parsed.ref = value;
2696
+ i += 1;
2697
+ continue;
2698
+ }
2699
+ if (arg?.startsWith("--ref=")) {
2700
+ parsed.ref = arg.split("=", 2)[1];
2701
+ continue;
2702
+ }
2703
+ }
2704
+ return parsed;
2705
+ }
2706
+ async function runCheck(args) {
2707
+ const { sessionId, ref } = parseCheckArgs(args.rawArgs);
2708
+ if (!sessionId) throw createUsageError("Missing --session-id");
2709
+ if (!ref) throw createUsageError("Missing --ref");
2710
+ const result = await callDaemon("interact.check", { sessionId, ref });
2711
+ return { success: true, message: "Check complete.", data: result };
2712
+ }
2713
+
2714
+ // src/cli/commands/interact/uncheck.ts
2715
+ function parseUncheckArgs(rawArgs) {
2716
+ const parsed = {};
2717
+ for (let i = 0; i < rawArgs.length; i += 1) {
2718
+ const arg = rawArgs[i];
2719
+ if (arg === "--session-id") {
2720
+ const value = rawArgs[i + 1];
2721
+ if (!value) throw createUsageError("Missing value for --session-id");
2722
+ parsed.sessionId = value;
2723
+ i += 1;
2724
+ continue;
2725
+ }
2726
+ if (arg?.startsWith("--session-id=")) {
2727
+ parsed.sessionId = arg.split("=", 2)[1];
2728
+ continue;
2729
+ }
2730
+ if (arg === "--ref") {
2731
+ const value = rawArgs[i + 1];
2732
+ if (!value) throw createUsageError("Missing value for --ref");
2733
+ parsed.ref = value;
2734
+ i += 1;
2735
+ continue;
2736
+ }
2737
+ if (arg?.startsWith("--ref=")) {
2738
+ parsed.ref = arg.split("=", 2)[1];
2739
+ continue;
2740
+ }
2741
+ }
2742
+ return parsed;
2743
+ }
2744
+ async function runUncheck(args) {
2745
+ const { sessionId, ref } = parseUncheckArgs(args.rawArgs);
2746
+ if (!sessionId) throw createUsageError("Missing --session-id");
2747
+ if (!ref) throw createUsageError("Missing --ref");
2748
+ const result = await callDaemon("interact.uncheck", { sessionId, ref });
2749
+ return { success: true, message: "Uncheck complete.", data: result };
2750
+ }
2751
+
2752
+ // src/cli/commands/interact/type.ts
2753
+ function parseTypeArgs(rawArgs) {
2754
+ const parsed = {};
2755
+ for (let i = 0; i < rawArgs.length; i += 1) {
2756
+ const arg = rawArgs[i];
2757
+ if (arg === "--session-id") {
2758
+ const value = rawArgs[i + 1];
2759
+ if (!value) throw createUsageError("Missing value for --session-id");
2760
+ parsed.sessionId = value;
2761
+ i += 1;
2762
+ continue;
2763
+ }
2764
+ if (arg?.startsWith("--session-id=")) {
2765
+ parsed.sessionId = arg.split("=", 2)[1];
2766
+ continue;
2767
+ }
2768
+ if (arg === "--ref") {
2769
+ const value = rawArgs[i + 1];
2770
+ if (!value) throw createUsageError("Missing value for --ref");
2771
+ parsed.ref = value;
2772
+ i += 1;
2773
+ continue;
2774
+ }
2775
+ if (arg?.startsWith("--ref=")) {
2776
+ parsed.ref = arg.split("=", 2)[1];
2777
+ continue;
2778
+ }
2779
+ if (arg === "--text") {
2780
+ const value = rawArgs[i + 1];
2781
+ if (!value) throw createUsageError("Missing value for --text");
2782
+ parsed.text = value;
2783
+ i += 1;
2784
+ continue;
2785
+ }
2786
+ if (arg?.startsWith("--text=")) {
2787
+ parsed.text = arg.split("=", 2)[1];
2788
+ continue;
2789
+ }
2790
+ if (arg === "--clear") {
2791
+ parsed.clear = true;
2792
+ continue;
2793
+ }
2794
+ if (arg === "--submit") {
2795
+ parsed.submit = true;
2796
+ continue;
2797
+ }
2798
+ }
2799
+ return parsed;
2800
+ }
2801
+ async function runType(args) {
2802
+ const { sessionId, ref, text, clear, submit } = parseTypeArgs(args.rawArgs);
2803
+ if (!sessionId) throw createUsageError("Missing --session-id");
2804
+ if (!ref) throw createUsageError("Missing --ref");
2805
+ if (!text) throw createUsageError("Missing --text");
2806
+ const result = await callDaemon("interact.type", { sessionId, ref, text, clear, submit });
2807
+ return { success: true, message: "Type complete.", data: result };
2808
+ }
2809
+
2810
+ // src/cli/commands/interact/select.ts
2811
+ function parseSelectArgs(rawArgs) {
2812
+ const parsed = {};
2813
+ for (let i = 0; i < rawArgs.length; i += 1) {
2814
+ const arg = rawArgs[i];
2815
+ if (arg === "--session-id") {
2816
+ const value = rawArgs[i + 1];
2817
+ if (!value) throw createUsageError("Missing value for --session-id");
2818
+ parsed.sessionId = value;
2819
+ i += 1;
2820
+ continue;
2821
+ }
2822
+ if (arg?.startsWith("--session-id=")) {
2823
+ parsed.sessionId = arg.split("=", 2)[1];
2824
+ continue;
2825
+ }
2826
+ if (arg === "--ref") {
2827
+ const value = rawArgs[i + 1];
2828
+ if (!value) throw createUsageError("Missing value for --ref");
2829
+ parsed.ref = value;
2830
+ i += 1;
2831
+ continue;
2832
+ }
2833
+ if (arg?.startsWith("--ref=")) {
2834
+ parsed.ref = arg.split("=", 2)[1];
2835
+ continue;
2836
+ }
2837
+ if (arg === "--values") {
2838
+ const value = rawArgs[i + 1];
2839
+ if (!value) throw createUsageError("Missing value for --values");
2840
+ parsed.values = value.split(",").map((entry) => entry.trim()).filter(Boolean);
2841
+ i += 1;
2842
+ continue;
2843
+ }
2844
+ if (arg?.startsWith("--values=")) {
2845
+ const value = arg.split("=", 2)[1];
2846
+ if (!value) throw createUsageError("Missing value for --values");
2847
+ parsed.values = value.split(",").map((entry) => entry.trim()).filter(Boolean);
2848
+ continue;
2849
+ }
2850
+ }
2851
+ return parsed;
2852
+ }
2853
+ async function runSelect(args) {
2854
+ const { sessionId, ref, values } = parseSelectArgs(args.rawArgs);
2855
+ if (!sessionId) throw createUsageError("Missing --session-id");
2856
+ if (!ref) throw createUsageError("Missing --ref");
2857
+ if (!values || values.length === 0) throw createUsageError("Missing --values");
2858
+ const result = await callDaemon("interact.select", { sessionId, ref, values });
2859
+ return { success: true, message: "Select complete.", data: result };
2860
+ }
2861
+
2862
+ // src/cli/commands/interact/scroll.ts
2863
+ function parseScrollArgs(rawArgs) {
2864
+ const parsed = {};
2865
+ for (let i = 0; i < rawArgs.length; i += 1) {
2866
+ const arg = rawArgs[i];
2867
+ if (arg === "--session-id") {
2868
+ const value = rawArgs[i + 1];
2869
+ if (!value) throw createUsageError("Missing value for --session-id");
2870
+ parsed.sessionId = value;
2871
+ i += 1;
2872
+ continue;
2873
+ }
2874
+ if (arg?.startsWith("--session-id=")) {
2875
+ parsed.sessionId = arg.split("=", 2)[1];
2876
+ continue;
2877
+ }
2878
+ if (arg === "--ref") {
2879
+ const value = rawArgs[i + 1];
2880
+ if (!value) throw createUsageError("Missing value for --ref");
2881
+ parsed.ref = value;
2882
+ i += 1;
2883
+ continue;
2884
+ }
2885
+ if (arg?.startsWith("--ref=")) {
2886
+ parsed.ref = arg.split("=", 2)[1];
2887
+ continue;
2888
+ }
2889
+ if (arg === "--dy") {
2890
+ const value = rawArgs[i + 1];
2891
+ if (!value) throw createUsageError("Missing value for --dy");
2892
+ parsed.dy = Number(value);
2893
+ i += 1;
2894
+ continue;
2895
+ }
2896
+ if (arg?.startsWith("--dy=")) {
2897
+ parsed.dy = Number(arg.split("=", 2)[1]);
2898
+ continue;
2899
+ }
2900
+ }
2901
+ return parsed;
2902
+ }
2903
+ async function runScroll(args) {
2904
+ const { sessionId, ref, dy } = parseScrollArgs(args.rawArgs);
2905
+ if (!sessionId) throw createUsageError("Missing --session-id");
2906
+ if (typeof dy !== "number" || Number.isNaN(dy)) throw createUsageError("Missing --dy");
2907
+ const result = await callDaemon("interact.scroll", { sessionId, ref, dy });
2908
+ return { success: true, message: "Scroll complete.", data: result };
2909
+ }
2910
+
2911
+ // src/cli/commands/interact/scroll-into-view.ts
2912
+ function parseScrollIntoViewArgs(rawArgs) {
2913
+ const parsed = {};
2914
+ for (let i = 0; i < rawArgs.length; i += 1) {
2915
+ const arg = rawArgs[i];
2916
+ if (arg === "--session-id") {
2917
+ const value = rawArgs[i + 1];
2918
+ if (!value) throw createUsageError("Missing value for --session-id");
2919
+ parsed.sessionId = value;
2920
+ i += 1;
2921
+ continue;
2922
+ }
2923
+ if (arg?.startsWith("--session-id=")) {
2924
+ parsed.sessionId = arg.split("=", 2)[1];
2925
+ continue;
2926
+ }
2927
+ if (arg === "--ref") {
2928
+ const value = rawArgs[i + 1];
2929
+ if (!value) throw createUsageError("Missing value for --ref");
2930
+ parsed.ref = value;
2931
+ i += 1;
2932
+ continue;
2933
+ }
2934
+ if (arg?.startsWith("--ref=")) {
2935
+ parsed.ref = arg.split("=", 2)[1];
2936
+ continue;
2937
+ }
2938
+ }
2939
+ return parsed;
2940
+ }
2941
+ async function runScrollIntoView(args) {
2942
+ const { sessionId, ref } = parseScrollIntoViewArgs(args.rawArgs);
2943
+ if (!sessionId) throw createUsageError("Missing --session-id");
2944
+ if (!ref) throw createUsageError("Missing --ref");
2945
+ const result = await callDaemon("interact.scrollIntoView", { sessionId, ref });
2946
+ return { success: true, message: "Scroll into view complete.", data: result };
2947
+ }
2948
+
2949
+ // src/cli/commands/targets/list.ts
2950
+ function parseTargetsListArgs(rawArgs) {
2951
+ const parsed = {};
2952
+ for (let i = 0; i < rawArgs.length; i += 1) {
2953
+ const arg = rawArgs[i];
2954
+ if (arg === "--session-id") {
2955
+ const value = rawArgs[i + 1];
2956
+ if (!value) throw createUsageError("Missing value for --session-id");
2957
+ parsed.sessionId = value;
2958
+ i += 1;
2959
+ continue;
2960
+ }
2961
+ if (arg?.startsWith("--session-id=")) {
2962
+ parsed.sessionId = arg.split("=", 2)[1];
2963
+ continue;
2964
+ }
2965
+ if (arg === "--include-urls") {
2966
+ parsed.includeUrls = true;
2967
+ }
2968
+ }
2969
+ return parsed;
2970
+ }
2971
+ async function runTargetsList(args) {
2972
+ const { sessionId, includeUrls } = parseTargetsListArgs(args.rawArgs);
2973
+ if (!sessionId) throw createUsageError("Missing --session-id");
2974
+ const result = await callDaemon("targets.list", { sessionId, includeUrls });
2975
+ return { success: true, message: `Targets listed for session: ${sessionId}`, data: result };
2976
+ }
2977
+
2978
+ // src/cli/commands/targets/use.ts
2979
+ function parseTargetUseArgs(rawArgs) {
2980
+ const parsed = {};
2981
+ for (let i = 0; i < rawArgs.length; i += 1) {
2982
+ const arg = rawArgs[i];
2983
+ if (arg === "--session-id") {
2984
+ const value = rawArgs[i + 1];
2985
+ if (!value) throw createUsageError("Missing value for --session-id");
2986
+ parsed.sessionId = value;
2987
+ i += 1;
2988
+ continue;
2989
+ }
2990
+ if (arg?.startsWith("--session-id=")) {
2991
+ parsed.sessionId = arg.split("=", 2)[1];
2992
+ continue;
2993
+ }
2994
+ if (arg === "--target-id") {
2995
+ const value = rawArgs[i + 1];
2996
+ if (!value) throw createUsageError("Missing value for --target-id");
2997
+ parsed.targetId = value;
2998
+ i += 1;
2999
+ continue;
3000
+ }
3001
+ if (arg?.startsWith("--target-id=")) {
3002
+ parsed.targetId = arg.split("=", 2)[1];
3003
+ continue;
3004
+ }
3005
+ }
3006
+ return parsed;
3007
+ }
3008
+ async function runTargetUse(args) {
3009
+ const { sessionId, targetId } = parseTargetUseArgs(args.rawArgs);
3010
+ if (!sessionId) throw createUsageError("Missing --session-id");
3011
+ if (!targetId) throw createUsageError("Missing --target-id");
3012
+ const result = await callDaemon("targets.use", { sessionId, targetId });
3013
+ return { success: true, message: `Target selected: ${targetId}`, data: result };
3014
+ }
3015
+
3016
+ // src/cli/commands/targets/new.ts
3017
+ function parseTargetNewArgs(rawArgs) {
3018
+ const parsed = {};
3019
+ for (let i = 0; i < rawArgs.length; i += 1) {
3020
+ const arg = rawArgs[i];
3021
+ if (arg === "--session-id") {
3022
+ const value = rawArgs[i + 1];
3023
+ if (!value) throw createUsageError("Missing value for --session-id");
3024
+ parsed.sessionId = value;
3025
+ i += 1;
3026
+ continue;
3027
+ }
3028
+ if (arg?.startsWith("--session-id=")) {
3029
+ parsed.sessionId = arg.split("=", 2)[1];
3030
+ continue;
3031
+ }
3032
+ if (arg === "--url") {
3033
+ const value = rawArgs[i + 1];
3034
+ if (!value) throw createUsageError("Missing value for --url");
3035
+ parsed.url = value;
3036
+ i += 1;
3037
+ continue;
3038
+ }
3039
+ if (arg?.startsWith("--url=")) {
3040
+ parsed.url = arg.split("=", 2)[1];
3041
+ continue;
3042
+ }
3043
+ }
3044
+ return parsed;
3045
+ }
3046
+ async function runTargetNew(args) {
3047
+ const { sessionId, url } = parseTargetNewArgs(args.rawArgs);
3048
+ if (!sessionId) throw createUsageError("Missing --session-id");
3049
+ const result = await callDaemon("targets.new", { sessionId, url });
3050
+ return { success: true, message: "Target created.", data: result };
3051
+ }
3052
+
3053
+ // src/cli/commands/targets/close.ts
3054
+ function parseTargetCloseArgs(rawArgs) {
3055
+ const parsed = {};
3056
+ for (let i = 0; i < rawArgs.length; i += 1) {
3057
+ const arg = rawArgs[i];
3058
+ if (arg === "--session-id") {
3059
+ const value = rawArgs[i + 1];
3060
+ if (!value) throw createUsageError("Missing value for --session-id");
3061
+ parsed.sessionId = value;
3062
+ i += 1;
3063
+ continue;
3064
+ }
3065
+ if (arg?.startsWith("--session-id=")) {
3066
+ parsed.sessionId = arg.split("=", 2)[1];
3067
+ continue;
3068
+ }
3069
+ if (arg === "--target-id") {
3070
+ const value = rawArgs[i + 1];
3071
+ if (!value) throw createUsageError("Missing value for --target-id");
3072
+ parsed.targetId = value;
3073
+ i += 1;
3074
+ continue;
3075
+ }
3076
+ if (arg?.startsWith("--target-id=")) {
3077
+ parsed.targetId = arg.split("=", 2)[1];
3078
+ continue;
3079
+ }
3080
+ }
3081
+ return parsed;
3082
+ }
3083
+ async function runTargetClose(args) {
3084
+ const { sessionId, targetId } = parseTargetCloseArgs(args.rawArgs);
3085
+ if (!sessionId) throw createUsageError("Missing --session-id");
3086
+ if (!targetId) throw createUsageError("Missing --target-id");
3087
+ await callDaemon("targets.close", { sessionId, targetId });
3088
+ return { success: true, message: `Target closed: ${targetId}` };
3089
+ }
3090
+
3091
+ // src/cli/commands/pages/open.ts
3092
+ function parsePageOpenArgs(rawArgs) {
3093
+ const parsed = {};
3094
+ for (let i = 0; i < rawArgs.length; i += 1) {
3095
+ const arg = rawArgs[i];
3096
+ if (arg === "--session-id") {
3097
+ const value = rawArgs[i + 1];
3098
+ if (!value) throw createUsageError("Missing value for --session-id");
3099
+ parsed.sessionId = value;
3100
+ i += 1;
3101
+ continue;
3102
+ }
3103
+ if (arg?.startsWith("--session-id=")) {
3104
+ parsed.sessionId = arg.split("=", 2)[1];
3105
+ continue;
3106
+ }
3107
+ if (arg === "--name") {
3108
+ const value = rawArgs[i + 1];
3109
+ if (!value) throw createUsageError("Missing value for --name");
3110
+ parsed.name = value;
3111
+ i += 1;
3112
+ continue;
3113
+ }
3114
+ if (arg?.startsWith("--name=")) {
3115
+ parsed.name = arg.split("=", 2)[1];
3116
+ continue;
3117
+ }
3118
+ if (arg === "--url") {
3119
+ const value = rawArgs[i + 1];
3120
+ if (!value) throw createUsageError("Missing value for --url");
3121
+ parsed.url = value;
3122
+ i += 1;
3123
+ continue;
3124
+ }
3125
+ if (arg?.startsWith("--url=")) {
3126
+ parsed.url = arg.split("=", 2)[1];
3127
+ continue;
3128
+ }
3129
+ }
3130
+ return parsed;
3131
+ }
3132
+ async function runPageOpen(args) {
3133
+ const { sessionId, name, url } = parsePageOpenArgs(args.rawArgs);
3134
+ if (!sessionId) throw createUsageError("Missing --session-id");
3135
+ if (!name) throw createUsageError("Missing --name");
3136
+ const result = await callDaemon("page.open", { sessionId, name, url });
3137
+ return { success: true, message: `Page ready: ${name}`, data: result };
3138
+ }
3139
+
3140
+ // src/cli/commands/pages/list.ts
3141
+ function parsePagesListArgs(rawArgs) {
3142
+ const parsed = {};
3143
+ for (let i = 0; i < rawArgs.length; i += 1) {
3144
+ const arg = rawArgs[i];
3145
+ if (arg === "--session-id") {
3146
+ const value = rawArgs[i + 1];
3147
+ if (!value) throw createUsageError("Missing value for --session-id");
3148
+ parsed.sessionId = value;
3149
+ i += 1;
3150
+ continue;
3151
+ }
3152
+ if (arg?.startsWith("--session-id=")) {
3153
+ parsed.sessionId = arg.split("=", 2)[1];
3154
+ continue;
3155
+ }
3156
+ }
3157
+ return parsed;
3158
+ }
3159
+ async function runPagesList(args) {
3160
+ const { sessionId } = parsePagesListArgs(args.rawArgs);
3161
+ if (!sessionId) throw createUsageError("Missing --session-id");
3162
+ const result = await callDaemon("page.list", { sessionId });
3163
+ return { success: true, message: `Pages listed for session: ${sessionId}`, data: result };
3164
+ }
3165
+
3166
+ // src/cli/commands/pages/close.ts
3167
+ function parsePageCloseArgs(rawArgs) {
3168
+ const parsed = {};
3169
+ for (let i = 0; i < rawArgs.length; i += 1) {
3170
+ const arg = rawArgs[i];
3171
+ if (arg === "--session-id") {
3172
+ const value = rawArgs[i + 1];
3173
+ if (!value) throw createUsageError("Missing value for --session-id");
3174
+ parsed.sessionId = value;
3175
+ i += 1;
3176
+ continue;
3177
+ }
3178
+ if (arg?.startsWith("--session-id=")) {
3179
+ parsed.sessionId = arg.split("=", 2)[1];
3180
+ continue;
3181
+ }
3182
+ if (arg === "--name") {
3183
+ const value = rawArgs[i + 1];
3184
+ if (!value) throw createUsageError("Missing value for --name");
3185
+ parsed.name = value;
3186
+ i += 1;
3187
+ continue;
3188
+ }
3189
+ if (arg?.startsWith("--name=")) {
3190
+ parsed.name = arg.split("=", 2)[1];
3191
+ continue;
3192
+ }
3193
+ }
3194
+ return parsed;
3195
+ }
3196
+ async function runPageClose(args) {
3197
+ const { sessionId, name } = parsePageCloseArgs(args.rawArgs);
3198
+ if (!sessionId) throw createUsageError("Missing --session-id");
3199
+ if (!name) throw createUsageError("Missing --name");
3200
+ await callDaemon("page.close", { sessionId, name });
3201
+ return { success: true, message: `Page closed: ${name}` };
3202
+ }
3203
+
3204
+ // src/cli/commands/dom/html.ts
3205
+ function parseDomHtmlArgs(rawArgs) {
3206
+ const parsed = {};
3207
+ for (let i = 0; i < rawArgs.length; i += 1) {
3208
+ const arg = rawArgs[i];
3209
+ if (arg === "--session-id") {
3210
+ const value = rawArgs[i + 1];
3211
+ if (!value) throw createUsageError("Missing value for --session-id");
3212
+ parsed.sessionId = value;
3213
+ i += 1;
3214
+ continue;
3215
+ }
3216
+ if (arg?.startsWith("--session-id=")) {
3217
+ parsed.sessionId = arg.split("=", 2)[1];
3218
+ continue;
3219
+ }
3220
+ if (arg === "--ref") {
3221
+ const value = rawArgs[i + 1];
3222
+ if (!value) throw createUsageError("Missing value for --ref");
3223
+ parsed.ref = value;
3224
+ i += 1;
3225
+ continue;
3226
+ }
3227
+ if (arg?.startsWith("--ref=")) {
3228
+ parsed.ref = arg.split("=", 2)[1];
3229
+ continue;
3230
+ }
3231
+ if (arg === "--max-chars") {
3232
+ const value = rawArgs[i + 1];
3233
+ if (!value) throw createUsageError("Missing value for --max-chars");
3234
+ parsed.maxChars = Number(value);
3235
+ i += 1;
3236
+ continue;
3237
+ }
3238
+ if (arg?.startsWith("--max-chars=")) {
3239
+ parsed.maxChars = Number(arg.split("=", 2)[1]);
3240
+ continue;
3241
+ }
3242
+ }
3243
+ return parsed;
3244
+ }
3245
+ async function runDomHtml(args) {
3246
+ const { sessionId, ref, maxChars } = parseDomHtmlArgs(args.rawArgs);
3247
+ if (!sessionId) throw createUsageError("Missing --session-id");
3248
+ if (!ref) throw createUsageError("Missing --ref");
3249
+ const result = await callDaemon("dom.getHtml", { sessionId, ref, maxChars });
3250
+ return { success: true, message: "DOM HTML captured.", data: result };
3251
+ }
3252
+
3253
+ // src/cli/commands/dom/text.ts
3254
+ function parseDomTextArgs(rawArgs) {
3255
+ const parsed = {};
3256
+ for (let i = 0; i < rawArgs.length; i += 1) {
3257
+ const arg = rawArgs[i];
3258
+ if (arg === "--session-id") {
3259
+ const value = rawArgs[i + 1];
3260
+ if (!value) throw createUsageError("Missing value for --session-id");
3261
+ parsed.sessionId = value;
3262
+ i += 1;
3263
+ continue;
3264
+ }
3265
+ if (arg?.startsWith("--session-id=")) {
3266
+ parsed.sessionId = arg.split("=", 2)[1];
3267
+ continue;
3268
+ }
3269
+ if (arg === "--ref") {
3270
+ const value = rawArgs[i + 1];
3271
+ if (!value) throw createUsageError("Missing value for --ref");
3272
+ parsed.ref = value;
3273
+ i += 1;
3274
+ continue;
3275
+ }
3276
+ if (arg?.startsWith("--ref=")) {
3277
+ parsed.ref = arg.split("=", 2)[1];
3278
+ continue;
3279
+ }
3280
+ if (arg === "--max-chars") {
3281
+ const value = rawArgs[i + 1];
3282
+ if (!value) throw createUsageError("Missing value for --max-chars");
3283
+ parsed.maxChars = Number(value);
3284
+ i += 1;
3285
+ continue;
3286
+ }
3287
+ if (arg?.startsWith("--max-chars=")) {
3288
+ parsed.maxChars = Number(arg.split("=", 2)[1]);
3289
+ continue;
3290
+ }
3291
+ }
3292
+ return parsed;
3293
+ }
3294
+ async function runDomText(args) {
3295
+ const { sessionId, ref, maxChars } = parseDomTextArgs(args.rawArgs);
3296
+ if (!sessionId) throw createUsageError("Missing --session-id");
3297
+ if (!ref) throw createUsageError("Missing --ref");
3298
+ const result = await callDaemon("dom.getText", { sessionId, ref, maxChars });
3299
+ return { success: true, message: "DOM text captured.", data: result };
3300
+ }
3301
+
3302
+ // src/cli/commands/dom/attr.ts
3303
+ function parseDomAttrArgs(rawArgs) {
3304
+ const parsed = {};
3305
+ for (let i = 0; i < rawArgs.length; i += 1) {
3306
+ const arg = rawArgs[i];
3307
+ if (arg === "--session-id") {
3308
+ const value = rawArgs[i + 1];
3309
+ if (!value) throw createUsageError("Missing value for --session-id");
3310
+ parsed.sessionId = value;
3311
+ i += 1;
3312
+ continue;
3313
+ }
3314
+ if (arg?.startsWith("--session-id=")) {
3315
+ parsed.sessionId = arg.split("=", 2)[1];
3316
+ continue;
3317
+ }
3318
+ if (arg === "--ref") {
3319
+ const value = rawArgs[i + 1];
3320
+ if (!value) throw createUsageError("Missing value for --ref");
3321
+ parsed.ref = value;
3322
+ i += 1;
3323
+ continue;
3324
+ }
3325
+ if (arg?.startsWith("--ref=")) {
3326
+ parsed.ref = arg.split("=", 2)[1];
3327
+ continue;
3328
+ }
3329
+ if (arg === "--attr") {
3330
+ const value = rawArgs[i + 1];
3331
+ if (!value) throw createUsageError("Missing value for --attr");
3332
+ parsed.attr = value;
3333
+ i += 1;
3334
+ continue;
3335
+ }
3336
+ if (arg?.startsWith("--attr=")) {
3337
+ parsed.attr = arg.split("=", 2)[1];
3338
+ continue;
3339
+ }
3340
+ }
3341
+ return parsed;
3342
+ }
3343
+ async function runDomAttr(args) {
3344
+ const { sessionId, ref, attr } = parseDomAttrArgs(args.rawArgs);
3345
+ if (!sessionId) throw createUsageError("Missing --session-id");
3346
+ if (!ref) throw createUsageError("Missing --ref");
3347
+ if (!attr) throw createUsageError("Missing --attr");
3348
+ const result = await callDaemon("dom.getAttr", { sessionId, ref, name: attr });
3349
+ return { success: true, message: "DOM attribute captured.", data: result };
3350
+ }
3351
+
3352
+ // src/cli/commands/dom/value.ts
3353
+ function parseDomValueArgs(rawArgs) {
3354
+ const parsed = {};
3355
+ for (let i = 0; i < rawArgs.length; i += 1) {
3356
+ const arg = rawArgs[i];
3357
+ if (arg === "--session-id") {
3358
+ const value = rawArgs[i + 1];
3359
+ if (!value) throw createUsageError("Missing value for --session-id");
3360
+ parsed.sessionId = value;
3361
+ i += 1;
3362
+ continue;
3363
+ }
3364
+ if (arg?.startsWith("--session-id=")) {
3365
+ parsed.sessionId = arg.split("=", 2)[1];
3366
+ continue;
3367
+ }
3368
+ if (arg === "--ref") {
3369
+ const value = rawArgs[i + 1];
3370
+ if (!value) throw createUsageError("Missing value for --ref");
3371
+ parsed.ref = value;
3372
+ i += 1;
3373
+ continue;
3374
+ }
3375
+ if (arg?.startsWith("--ref=")) {
3376
+ parsed.ref = arg.split("=", 2)[1];
3377
+ continue;
3378
+ }
3379
+ }
3380
+ return parsed;
3381
+ }
3382
+ async function runDomValue(args) {
3383
+ const { sessionId, ref } = parseDomValueArgs(args.rawArgs);
3384
+ if (!sessionId) throw createUsageError("Missing --session-id");
3385
+ if (!ref) throw createUsageError("Missing --ref");
3386
+ const result = await callDaemon("dom.getValue", { sessionId, ref });
3387
+ return { success: true, message: "DOM value captured.", data: result };
3388
+ }
3389
+
3390
+ // src/cli/commands/dom/visible.ts
3391
+ function parseDomVisibleArgs(rawArgs) {
3392
+ const parsed = {};
3393
+ for (let i = 0; i < rawArgs.length; i += 1) {
3394
+ const arg = rawArgs[i];
3395
+ if (arg === "--session-id") {
3396
+ const value = rawArgs[i + 1];
3397
+ if (!value) throw createUsageError("Missing value for --session-id");
3398
+ parsed.sessionId = value;
3399
+ i += 1;
3400
+ continue;
3401
+ }
3402
+ if (arg?.startsWith("--session-id=")) {
3403
+ parsed.sessionId = arg.split("=", 2)[1];
3404
+ continue;
3405
+ }
3406
+ if (arg === "--ref") {
3407
+ const value = rawArgs[i + 1];
3408
+ if (!value) throw createUsageError("Missing value for --ref");
3409
+ parsed.ref = value;
3410
+ i += 1;
3411
+ continue;
3412
+ }
3413
+ if (arg?.startsWith("--ref=")) {
3414
+ parsed.ref = arg.split("=", 2)[1];
3415
+ continue;
3416
+ }
3417
+ }
3418
+ return parsed;
3419
+ }
3420
+ async function runDomVisible(args) {
3421
+ const { sessionId, ref } = parseDomVisibleArgs(args.rawArgs);
3422
+ if (!sessionId) throw createUsageError("Missing --session-id");
3423
+ if (!ref) throw createUsageError("Missing --ref");
3424
+ const result = await callDaemon("dom.isVisible", { sessionId, ref });
3425
+ return { success: true, message: "Visibility checked.", data: result };
3426
+ }
3427
+
3428
+ // src/cli/commands/dom/enabled.ts
3429
+ function parseDomEnabledArgs(rawArgs) {
3430
+ const parsed = {};
3431
+ for (let i = 0; i < rawArgs.length; i += 1) {
3432
+ const arg = rawArgs[i];
3433
+ if (arg === "--session-id") {
3434
+ const value = rawArgs[i + 1];
3435
+ if (!value) throw createUsageError("Missing value for --session-id");
3436
+ parsed.sessionId = value;
3437
+ i += 1;
3438
+ continue;
3439
+ }
3440
+ if (arg?.startsWith("--session-id=")) {
3441
+ parsed.sessionId = arg.split("=", 2)[1];
3442
+ continue;
3443
+ }
3444
+ if (arg === "--ref") {
3445
+ const value = rawArgs[i + 1];
3446
+ if (!value) throw createUsageError("Missing value for --ref");
3447
+ parsed.ref = value;
3448
+ i += 1;
3449
+ continue;
3450
+ }
3451
+ if (arg?.startsWith("--ref=")) {
3452
+ parsed.ref = arg.split("=", 2)[1];
3453
+ continue;
3454
+ }
3455
+ }
3456
+ return parsed;
3457
+ }
3458
+ async function runDomEnabled(args) {
3459
+ const { sessionId, ref } = parseDomEnabledArgs(args.rawArgs);
3460
+ if (!sessionId) throw createUsageError("Missing --session-id");
3461
+ if (!ref) throw createUsageError("Missing --ref");
3462
+ const result = await callDaemon("dom.isEnabled", { sessionId, ref });
3463
+ return { success: true, message: "Enabled state checked.", data: result };
3464
+ }
3465
+
3466
+ // src/cli/commands/dom/checked.ts
3467
+ function parseDomCheckedArgs(rawArgs) {
3468
+ const parsed = {};
3469
+ for (let i = 0; i < rawArgs.length; i += 1) {
3470
+ const arg = rawArgs[i];
3471
+ if (arg === "--session-id") {
3472
+ const value = rawArgs[i + 1];
3473
+ if (!value) throw createUsageError("Missing value for --session-id");
3474
+ parsed.sessionId = value;
3475
+ i += 1;
3476
+ continue;
3477
+ }
3478
+ if (arg?.startsWith("--session-id=")) {
3479
+ parsed.sessionId = arg.split("=", 2)[1];
3480
+ continue;
3481
+ }
3482
+ if (arg === "--ref") {
3483
+ const value = rawArgs[i + 1];
3484
+ if (!value) throw createUsageError("Missing value for --ref");
3485
+ parsed.ref = value;
3486
+ i += 1;
3487
+ continue;
3488
+ }
3489
+ if (arg?.startsWith("--ref=")) {
3490
+ parsed.ref = arg.split("=", 2)[1];
3491
+ continue;
3492
+ }
3493
+ }
3494
+ return parsed;
3495
+ }
3496
+ async function runDomChecked(args) {
3497
+ const { sessionId, ref } = parseDomCheckedArgs(args.rawArgs);
3498
+ if (!sessionId) throw createUsageError("Missing --session-id");
3499
+ if (!ref) throw createUsageError("Missing --ref");
3500
+ const result = await callDaemon("dom.isChecked", { sessionId, ref });
3501
+ return { success: true, message: "Checked state reported.", data: result };
3502
+ }
3503
+
3504
+ // src/cli/commands/export/clone-page.ts
3505
+ function parseClonePageArgs(rawArgs) {
3506
+ const parsed = {};
3507
+ for (let i = 0; i < rawArgs.length; i += 1) {
3508
+ const arg = rawArgs[i];
3509
+ if (arg === "--session-id") {
3510
+ const value = rawArgs[i + 1];
3511
+ if (!value) throw createUsageError("Missing value for --session-id");
3512
+ parsed.sessionId = value;
3513
+ i += 1;
3514
+ continue;
3515
+ }
3516
+ if (arg?.startsWith("--session-id=")) {
3517
+ parsed.sessionId = arg.split("=", 2)[1];
3518
+ continue;
3519
+ }
3520
+ }
3521
+ return parsed;
3522
+ }
3523
+ async function runClonePage(args) {
3524
+ const { sessionId } = parseClonePageArgs(args.rawArgs);
3525
+ if (!sessionId) throw createUsageError("Missing --session-id");
3526
+ const result = await callDaemon("export.clonePage", { sessionId });
3527
+ return { success: true, message: "Page cloned.", data: result };
3528
+ }
3529
+
3530
+ // src/cli/commands/export/clone-component.ts
3531
+ function parseCloneComponentArgs(rawArgs) {
3532
+ const parsed = {};
3533
+ for (let i = 0; i < rawArgs.length; i += 1) {
3534
+ const arg = rawArgs[i];
3535
+ if (arg === "--session-id") {
3536
+ const value = rawArgs[i + 1];
3537
+ if (!value) throw createUsageError("Missing value for --session-id");
3538
+ parsed.sessionId = value;
3539
+ i += 1;
3540
+ continue;
3541
+ }
3542
+ if (arg?.startsWith("--session-id=")) {
3543
+ parsed.sessionId = arg.split("=", 2)[1];
3544
+ continue;
3545
+ }
3546
+ if (arg === "--ref") {
3547
+ const value = rawArgs[i + 1];
3548
+ if (!value) throw createUsageError("Missing value for --ref");
3549
+ parsed.ref = value;
3550
+ i += 1;
3551
+ continue;
3552
+ }
3553
+ if (arg?.startsWith("--ref=")) {
3554
+ parsed.ref = arg.split("=", 2)[1];
3555
+ continue;
3556
+ }
3557
+ }
3558
+ return parsed;
3559
+ }
3560
+ async function runCloneComponent(args) {
3561
+ const { sessionId, ref } = parseCloneComponentArgs(args.rawArgs);
3562
+ if (!sessionId) throw createUsageError("Missing --session-id");
3563
+ if (!ref) throw createUsageError("Missing --ref");
3564
+ const result = await callDaemon("export.cloneComponent", { sessionId, ref });
3565
+ return { success: true, message: "Component cloned.", data: result };
3566
+ }
3567
+
3568
+ // src/cli/commands/devtools/perf.ts
3569
+ function parsePerfArgs(rawArgs) {
3570
+ const parsed = {};
3571
+ for (let i = 0; i < rawArgs.length; i += 1) {
3572
+ const arg = rawArgs[i];
3573
+ if (arg === "--session-id") {
3574
+ const value = rawArgs[i + 1];
3575
+ if (!value) throw createUsageError("Missing value for --session-id");
3576
+ parsed.sessionId = value;
3577
+ i += 1;
3578
+ continue;
3579
+ }
3580
+ if (arg?.startsWith("--session-id=")) {
3581
+ parsed.sessionId = arg.split("=", 2)[1];
3582
+ continue;
3583
+ }
3584
+ }
3585
+ return parsed;
3586
+ }
3587
+ async function runPerf(args) {
3588
+ const { sessionId } = parsePerfArgs(args.rawArgs);
3589
+ if (!sessionId) throw createUsageError("Missing --session-id");
3590
+ const result = await callDaemon("devtools.perf", { sessionId });
3591
+ return { success: true, message: "Performance metrics captured.", data: result };
3592
+ }
3593
+
3594
+ // src/cli/commands/devtools/screenshot.ts
3595
+ function parseScreenshotArgs(rawArgs) {
3596
+ const parsed = {};
3597
+ for (let i = 0; i < rawArgs.length; i += 1) {
3598
+ const arg = rawArgs[i];
3599
+ if (arg === "--session-id") {
3600
+ const value = rawArgs[i + 1];
3601
+ if (!value) throw createUsageError("Missing value for --session-id");
3602
+ parsed.sessionId = value;
3603
+ i += 1;
3604
+ continue;
3605
+ }
3606
+ if (arg?.startsWith("--session-id=")) {
3607
+ parsed.sessionId = arg.split("=", 2)[1];
3608
+ continue;
3609
+ }
3610
+ if (arg === "--path") {
3611
+ const value = rawArgs[i + 1];
3612
+ if (!value) throw createUsageError("Missing value for --path");
3613
+ parsed.path = value;
3614
+ i += 1;
3615
+ continue;
3616
+ }
3617
+ if (arg?.startsWith("--path=")) {
3618
+ parsed.path = arg.split("=", 2)[1];
3619
+ continue;
3620
+ }
3621
+ }
3622
+ return parsed;
3623
+ }
3624
+ async function runScreenshot(args) {
3625
+ const { sessionId, path: path8 } = parseScreenshotArgs(args.rawArgs);
3626
+ if (!sessionId) throw createUsageError("Missing --session-id");
3627
+ const result = await callDaemon("page.screenshot", { sessionId, path: path8 });
3628
+ return { success: true, message: "Screenshot captured.", data: result };
3629
+ }
3630
+
3631
+ // src/cli/commands/devtools/console-poll.ts
3632
+ function parseConsolePollArgs(rawArgs) {
3633
+ const parsed = {};
3634
+ for (let i = 0; i < rawArgs.length; i += 1) {
3635
+ const arg = rawArgs[i];
3636
+ if (arg === "--session-id") {
3637
+ const value = rawArgs[i + 1];
3638
+ if (!value) throw createUsageError("Missing value for --session-id");
3639
+ parsed.sessionId = value;
3640
+ i += 1;
3641
+ continue;
3642
+ }
3643
+ if (arg?.startsWith("--session-id=")) {
3644
+ parsed.sessionId = arg.split("=", 2)[1];
3645
+ continue;
3646
+ }
3647
+ if (arg === "--since-seq") {
3648
+ const value = rawArgs[i + 1];
3649
+ if (!value) throw createUsageError("Missing value for --since-seq");
3650
+ parsed.sinceSeq = Number(value);
3651
+ i += 1;
3652
+ continue;
3653
+ }
3654
+ if (arg?.startsWith("--since-seq=")) {
3655
+ parsed.sinceSeq = Number(arg.split("=", 2)[1]);
3656
+ continue;
3657
+ }
3658
+ if (arg === "--max") {
3659
+ const value = rawArgs[i + 1];
3660
+ if (!value) throw createUsageError("Missing value for --max");
3661
+ parsed.max = Number(value);
3662
+ i += 1;
3663
+ continue;
3664
+ }
3665
+ if (arg?.startsWith("--max=")) {
3666
+ parsed.max = Number(arg.split("=", 2)[1]);
3667
+ continue;
3668
+ }
3669
+ }
3670
+ return parsed;
3671
+ }
3672
+ async function runConsolePoll(args) {
3673
+ const { sessionId, sinceSeq, max } = parseConsolePollArgs(args.rawArgs);
3674
+ if (!sessionId) throw createUsageError("Missing --session-id");
3675
+ const result = await callDaemon("devtools.consolePoll", { sessionId, sinceSeq, max });
3676
+ return { success: true, message: "Console events polled.", data: result };
3677
+ }
3678
+
3679
+ // src/cli/commands/devtools/network-poll.ts
3680
+ function parseNetworkPollArgs(rawArgs) {
3681
+ const parsed = {};
3682
+ for (let i = 0; i < rawArgs.length; i += 1) {
3683
+ const arg = rawArgs[i];
3684
+ if (arg === "--session-id") {
3685
+ const value = rawArgs[i + 1];
3686
+ if (!value) throw createUsageError("Missing value for --session-id");
3687
+ parsed.sessionId = value;
3688
+ i += 1;
3689
+ continue;
3690
+ }
3691
+ if (arg?.startsWith("--session-id=")) {
3692
+ parsed.sessionId = arg.split("=", 2)[1];
3693
+ continue;
3694
+ }
3695
+ if (arg === "--since-seq") {
3696
+ const value = rawArgs[i + 1];
3697
+ if (!value) throw createUsageError("Missing value for --since-seq");
3698
+ parsed.sinceSeq = Number(value);
3699
+ i += 1;
3700
+ continue;
3701
+ }
3702
+ if (arg?.startsWith("--since-seq=")) {
3703
+ parsed.sinceSeq = Number(arg.split("=", 2)[1]);
3704
+ continue;
3705
+ }
3706
+ if (arg === "--max") {
3707
+ const value = rawArgs[i + 1];
3708
+ if (!value) throw createUsageError("Missing value for --max");
3709
+ parsed.max = Number(value);
3710
+ i += 1;
3711
+ continue;
3712
+ }
3713
+ if (arg?.startsWith("--max=")) {
3714
+ parsed.max = Number(arg.split("=", 2)[1]);
3715
+ continue;
3716
+ }
3717
+ }
3718
+ return parsed;
3719
+ }
3720
+ async function runNetworkPoll(args) {
3721
+ const { sessionId, sinceSeq, max } = parseNetworkPollArgs(args.rawArgs);
3722
+ if (!sessionId) throw createUsageError("Missing --session-id");
3723
+ const result = await callDaemon("devtools.networkPoll", { sessionId, sinceSeq, max });
3724
+ return { success: true, message: "Network events polled.", data: result };
3725
+ }
3726
+
3727
+ // src/cli/index.ts
3728
+ var VERSION = "0.1.0";
3729
+ async function promptInstallMode() {
3730
+ if (!process.stdin.isTTY) {
3731
+ console.log("Non-interactive mode detected. Using global install.");
3732
+ return "global";
3733
+ }
3734
+ return new Promise((resolve4) => {
3735
+ console.log("\nWhere would you like to install opendevbrowser?\n");
3736
+ console.log(" 1. Global (~/.config/opencode/opencode.json)");
3737
+ console.log(" 2. Local (./opencode.json in this project)\n");
3738
+ process.stdout.write("Enter choice [1]: ");
3739
+ process.stdin.setEncoding("utf8");
3740
+ let resolved = false;
3741
+ let timeoutId = null;
3742
+ const cleanup = () => {
3743
+ if (timeoutId !== null) {
3744
+ clearTimeout(timeoutId);
3745
+ timeoutId = null;
3746
+ }
3747
+ };
3748
+ process.stdin.once("data", (data) => {
3749
+ cleanup();
3750
+ if (resolved) return;
3751
+ resolved = true;
3752
+ const input = data.toString().trim();
3753
+ if (input === "2") {
3754
+ resolve4("local");
3755
+ } else {
3756
+ resolve4("global");
3757
+ }
3758
+ });
3759
+ process.stdin.once("close", () => {
3760
+ cleanup();
3761
+ if (resolved) return;
3762
+ resolved = true;
3763
+ resolve4("global");
3764
+ });
3765
+ timeoutId = setTimeout(() => {
3766
+ timeoutId = null;
3767
+ if (resolved) return;
3768
+ resolved = true;
3769
+ console.log("\nTimeout - using global install.");
3770
+ resolve4("global");
3771
+ }, 3e4);
3772
+ });
3773
+ }
3774
+ async function promptUninstallMode() {
3775
+ const installed = findInstalledConfigs();
3776
+ if (!installed.global && !installed.local) {
3777
+ console.log("opendevbrowser is not installed in any config.");
3778
+ return null;
3779
+ }
3780
+ if (installed.global && !installed.local) {
3781
+ return "global";
3782
+ }
3783
+ if (!installed.global && installed.local) {
3784
+ return "local";
3785
+ }
3786
+ if (!process.stdin.isTTY) {
3787
+ console.log("Plugin found in both global and local configs. Use --global or --local flag.");
3788
+ return null;
3789
+ }
3790
+ return new Promise((resolve4) => {
3791
+ console.log("\nopendevbrowser is installed in multiple locations:\n");
3792
+ console.log(" 1. Global (~/.config/opencode/opencode.json)");
3793
+ console.log(" 2. Local (./opencode.json)");
3794
+ console.log(" 3. Cancel\n");
3795
+ process.stdout.write("Which to uninstall? [3]: ");
3796
+ process.stdin.setEncoding("utf8");
3797
+ process.stdin.once("data", (data) => {
3798
+ const input = data.toString().trim();
3799
+ if (input === "1") {
3800
+ resolve4("global");
3801
+ } else if (input === "2") {
3802
+ resolve4("local");
3803
+ } else {
3804
+ resolve4(null);
3805
+ }
3806
+ });
3807
+ process.stdin.once("close", () => {
3808
+ resolve4(null);
3809
+ });
3810
+ });
3811
+ }
3812
+ function emitFatalError(error, outputFormat) {
3813
+ if (outputFormat === "text") {
3814
+ console.error(`Error: ${error.message}`);
3815
+ if (error.exitCode === EXIT_USAGE) {
3816
+ console.error("\nFor help: npx opendevbrowser --help");
3817
+ }
3818
+ return;
3819
+ }
3820
+ writeOutput(formatErrorPayload(error), { format: outputFormat });
3821
+ }
3822
+ async function main() {
3823
+ let outputFormat = null;
3824
+ let parseSucceeded = false;
3825
+ try {
3826
+ const args = parseArgs(process.argv);
3827
+ parseSucceeded = true;
3828
+ outputFormat = args.outputFormat;
3829
+ const outputOptions = { format: args.outputFormat, quiet: args.quiet };
3830
+ const emitResult = (result2, payload) => {
3831
+ const suppressOutput = Boolean(
3832
+ result2.data && typeof result2.data === "object" && "suppressOutput" in result2.data && result2.data.suppressOutput
3833
+ );
3834
+ if (suppressOutput) {
3835
+ return;
3836
+ }
3837
+ if (args.outputFormat === "text") {
3838
+ if (result2.message) {
3839
+ writeOutput(result2.message, outputOptions);
3840
+ }
3841
+ } else {
3842
+ const exitCode2 = resolveExitCode(result2);
3843
+ writeOutput({
3844
+ success: result2.success,
3845
+ message: result2.message,
3846
+ ...result2.success || !result2.message ? {} : { error: result2.message },
3847
+ ...result2.success || exitCode2 === null ? {} : { exitCode: exitCode2 },
3848
+ ...payload
3849
+ }, outputOptions);
3850
+ }
3851
+ };
3852
+ registerCommand({
3853
+ name: "help",
3854
+ description: "Show help",
3855
+ run: () => ({ success: true, message: getHelpText() })
3856
+ });
3857
+ registerCommand({
3858
+ name: "version",
3859
+ description: "Show version",
3860
+ run: () => ({ success: true, message: `opendevbrowser v${VERSION}` })
3861
+ });
3862
+ registerCommand({
3863
+ name: "update",
3864
+ description: "Clear cached plugin to trigger reinstall",
3865
+ run: () => {
3866
+ const result2 = runUpdate();
3867
+ return { success: result2.success, message: result2.message };
3868
+ }
3869
+ });
3870
+ registerCommand({
3871
+ name: "uninstall",
3872
+ description: "Remove plugin from config",
3873
+ run: async () => {
3874
+ let mode = args.mode;
3875
+ if (!mode && !args.noPrompt) {
3876
+ mode = await promptUninstallMode() ?? void 0;
3877
+ if (!mode) {
3878
+ return { success: true, message: "Uninstall cancelled." };
3879
+ }
3880
+ }
3881
+ if (!mode) {
3882
+ return { success: false, message: "Error: Please specify --global or --local for uninstall.", exitCode: EXIT_USAGE };
3883
+ }
3884
+ const result2 = runUninstall(mode);
3885
+ return { success: result2.success, message: result2.message };
3886
+ }
3887
+ });
3888
+ registerCommand({
3889
+ name: "install",
3890
+ description: "Install the plugin",
3891
+ run: async () => {
3892
+ const log = (...values) => {
3893
+ if (args.quiet) return;
3894
+ console.log(...values);
3895
+ };
3896
+ const warn = (...values) => {
3897
+ if (args.quiet) return;
3898
+ console.warn(...values);
3899
+ };
3900
+ let mode = args.mode;
3901
+ if (!mode) {
3902
+ mode = await promptInstallMode();
3903
+ }
3904
+ const result2 = mode === "global" ? installGlobal(args.withConfig) : installLocal(args.withConfig);
3905
+ const maybeInstallAutostart = () => {
3906
+ const status = getAutostartStatus();
3907
+ if (!status.supported) {
3908
+ return { status, installed: false, message: `Autostart not supported on ${status.platform}.` };
3909
+ }
3910
+ if (status.installed) {
3911
+ return { status, installed: true, message: "Autostart already installed." };
3912
+ }
3913
+ try {
3914
+ const result3 = installAutostart();
3915
+ return { status: result3, installed: result3.installed, message: `Autostart installed (${result3.platform}).` };
3916
+ } catch (error) {
3917
+ const message = error instanceof Error ? error.message : String(error);
3918
+ return { status, installed: false, message };
3919
+ }
3920
+ };
3921
+ if (args.outputFormat !== "text") {
3922
+ const payload = {
3923
+ alreadyInstalled: result2.alreadyInstalled
3924
+ };
3925
+ if (result2.success && args.skillsMode !== "none") {
3926
+ const skillsResult = installSkills(args.skillsMode);
3927
+ payload.skills = skillsResult;
3928
+ }
3929
+ if (args.fullInstall && result2.success) {
3930
+ try {
3931
+ const extensionPath = extractExtension();
3932
+ payload.extensionPath = extensionPath;
3933
+ } catch (error) {
3934
+ payload.extensionError = error instanceof Error ? error.message : String(error);
3935
+ }
3936
+ }
3937
+ if (result2.success && !result2.alreadyInstalled) {
3938
+ const autostart = maybeInstallAutostart();
3939
+ payload.autostart = autostart.status;
3940
+ if (!autostart.installed) {
3941
+ payload.autostartError = autostart.message;
3942
+ }
3943
+ }
3944
+ return { success: result2.success, message: result2.message, data: payload };
3945
+ }
3946
+ log(result2.message);
3947
+ if (args.skillsMode === "none") {
3948
+ log("Skill installation skipped (--no-skills).");
3949
+ } else if (result2.success) {
3950
+ const skillsResult = installSkills(args.skillsMode);
3951
+ if (skillsResult.success) {
3952
+ log(skillsResult.message);
3953
+ } else {
3954
+ warn(skillsResult.message);
3955
+ }
3956
+ } else {
3957
+ warn("Skill installation skipped because plugin install failed.");
3958
+ }
3959
+ if (args.fullInstall && result2.success) {
3960
+ try {
3961
+ const extensionPath = extractExtension();
3962
+ if (extensionPath) {
3963
+ log(`Extension assets extracted to ${extensionPath}`);
3964
+ } else {
3965
+ warn("Extension assets not found; skipping extraction.");
3966
+ }
3967
+ } catch (error) {
3968
+ const message = error instanceof Error ? error.message : String(error);
3969
+ warn(`Extension pre-extraction failed: ${message}`);
3970
+ }
3971
+ }
3972
+ if (result2.success && !result2.alreadyInstalled) {
3973
+ const autostart = maybeInstallAutostart();
3974
+ if (autostart.installed) {
3975
+ log(autostart.message);
3976
+ } else {
3977
+ warn(`Autostart install skipped: ${autostart.message}`);
3978
+ }
3979
+ }
3980
+ if (result2.success && !result2.alreadyInstalled) {
3981
+ log("\nNext steps:");
3982
+ log(" 1. Start or restart OpenCode");
3983
+ log(" 2. Use opendevbrowser_status to verify the plugin is loaded");
3984
+ log("\nFor help: npx opendevbrowser --help");
3985
+ }
3986
+ return { success: result2.success, message: result2.message };
3987
+ }
3988
+ });
3989
+ registerCommand({
3990
+ name: "serve",
3991
+ description: "Start or stop the local daemon",
3992
+ run: async () => runServe(args)
3993
+ });
3994
+ registerCommand({
3995
+ name: "daemon",
3996
+ description: "Install/uninstall/status daemon auto-start",
3997
+ run: async () => runDaemonCommand(args)
3998
+ });
3999
+ registerCommand({
4000
+ name: "native",
4001
+ description: "Install/uninstall/status native messaging host",
4002
+ run: async () => runNativeCommand(args)
4003
+ });
4004
+ registerCommand({
4005
+ name: "run",
4006
+ description: "Execute a JSON script in a single process",
4007
+ run: async () => runScriptCommand(args)
4008
+ });
4009
+ registerCommand({
4010
+ name: "launch",
4011
+ description: "Launch a managed browser session via daemon",
4012
+ run: async () => runSessionLaunch(args)
4013
+ });
4014
+ registerCommand({
4015
+ name: "connect",
4016
+ description: "Connect to an existing browser via daemon",
4017
+ run: async () => runSessionConnect(args)
4018
+ });
4019
+ registerCommand({
4020
+ name: "disconnect",
4021
+ description: "Disconnect a daemon session",
4022
+ run: async () => runSessionDisconnect(args)
4023
+ });
4024
+ registerCommand({
4025
+ name: "status",
4026
+ description: "Get daemon or session status",
4027
+ run: async () => runStatus(args)
4028
+ });
4029
+ registerCommand({
4030
+ name: "goto",
4031
+ description: "Navigate current session to a URL",
4032
+ run: async () => runGoto(args)
4033
+ });
4034
+ registerCommand({
4035
+ name: "wait",
4036
+ description: "Wait for load or a ref to appear",
4037
+ run: async () => runWait(args)
4038
+ });
4039
+ registerCommand({
4040
+ name: "snapshot",
4041
+ description: "Capture a snapshot of the active page",
4042
+ run: async () => runSnapshot(args)
4043
+ });
4044
+ registerCommand({
4045
+ name: "annotate",
4046
+ description: "Request interactive annotations (extension relay)",
4047
+ run: async () => runAnnotate(args)
4048
+ });
4049
+ registerCommand({
4050
+ name: "click",
4051
+ description: "Click an element by ref",
4052
+ run: async () => runClick(args)
4053
+ });
4054
+ registerCommand({
4055
+ name: "hover",
4056
+ description: "Hover an element by ref",
4057
+ run: async () => runHover(args)
4058
+ });
4059
+ registerCommand({
4060
+ name: "press",
4061
+ description: "Press a keyboard key",
4062
+ run: async () => runPress(args)
4063
+ });
4064
+ registerCommand({
4065
+ name: "check",
4066
+ description: "Check a checkbox by ref",
4067
+ run: async () => runCheck(args)
4068
+ });
4069
+ registerCommand({
4070
+ name: "uncheck",
4071
+ description: "Uncheck a checkbox by ref",
4072
+ run: async () => runUncheck(args)
4073
+ });
4074
+ registerCommand({
4075
+ name: "type",
4076
+ description: "Type into an element by ref",
4077
+ run: async () => runType(args)
4078
+ });
4079
+ registerCommand({
4080
+ name: "select",
4081
+ description: "Select values in a select by ref",
4082
+ run: async () => runSelect(args)
4083
+ });
4084
+ registerCommand({
4085
+ name: "scroll",
4086
+ description: "Scroll the page or element by ref",
4087
+ run: async () => runScroll(args)
4088
+ });
4089
+ registerCommand({
4090
+ name: "scroll-into-view",
4091
+ description: "Scroll an element into view by ref",
4092
+ run: async () => runScrollIntoView(args)
4093
+ });
4094
+ registerCommand({
4095
+ name: "targets-list",
4096
+ description: "List page targets",
4097
+ run: async () => runTargetsList(args)
4098
+ });
4099
+ registerCommand({
4100
+ name: "target-use",
4101
+ description: "Focus a target by id",
4102
+ run: async () => runTargetUse(args)
4103
+ });
4104
+ registerCommand({
4105
+ name: "target-new",
4106
+ description: "Open a new target",
4107
+ run: async () => runTargetNew(args)
4108
+ });
4109
+ registerCommand({
4110
+ name: "target-close",
4111
+ description: "Close a target by id",
4112
+ run: async () => runTargetClose(args)
4113
+ });
4114
+ registerCommand({
4115
+ name: "page",
4116
+ description: "Open or focus a named page",
4117
+ run: async () => runPageOpen(args)
4118
+ });
4119
+ registerCommand({
4120
+ name: "pages",
4121
+ description: "List named pages",
4122
+ run: async () => runPagesList(args)
4123
+ });
4124
+ registerCommand({
4125
+ name: "page-close",
4126
+ description: "Close a named page",
4127
+ run: async () => runPageClose(args)
4128
+ });
4129
+ registerCommand({
4130
+ name: "dom-html",
4131
+ description: "Capture HTML for a ref",
4132
+ run: async () => runDomHtml(args)
4133
+ });
4134
+ registerCommand({
4135
+ name: "dom-text",
4136
+ description: "Capture text for a ref",
4137
+ run: async () => runDomText(args)
4138
+ });
4139
+ registerCommand({
4140
+ name: "dom-attr",
4141
+ description: "Capture attribute value for a ref",
4142
+ run: async () => runDomAttr(args)
4143
+ });
4144
+ registerCommand({
4145
+ name: "dom-value",
4146
+ description: "Capture input value for a ref",
4147
+ run: async () => runDomValue(args)
4148
+ });
4149
+ registerCommand({
4150
+ name: "dom-visible",
4151
+ description: "Check visibility for a ref",
4152
+ run: async () => runDomVisible(args)
4153
+ });
4154
+ registerCommand({
4155
+ name: "dom-enabled",
4156
+ description: "Check enabled state for a ref",
4157
+ run: async () => runDomEnabled(args)
4158
+ });
4159
+ registerCommand({
4160
+ name: "dom-checked",
4161
+ description: "Check checked state for a ref",
4162
+ run: async () => runDomChecked(args)
4163
+ });
4164
+ registerCommand({
4165
+ name: "clone-page",
4166
+ description: "Clone the active page to React",
4167
+ run: async () => runClonePage(args)
4168
+ });
4169
+ registerCommand({
4170
+ name: "clone-component",
4171
+ description: "Clone a component by ref",
4172
+ run: async () => runCloneComponent(args)
4173
+ });
4174
+ registerCommand({
4175
+ name: "perf",
4176
+ description: "Capture performance metrics",
4177
+ run: async () => runPerf(args)
4178
+ });
4179
+ registerCommand({
4180
+ name: "screenshot",
4181
+ description: "Capture a screenshot",
4182
+ run: async () => runScreenshot(args)
4183
+ });
4184
+ registerCommand({
4185
+ name: "console-poll",
4186
+ description: "Poll console events",
4187
+ run: async () => runConsolePoll(args)
4188
+ });
4189
+ registerCommand({
4190
+ name: "network-poll",
4191
+ description: "Poll network events",
4192
+ run: async () => runNetworkPoll(args)
4193
+ });
4194
+ const command = getCommand(args.command);
4195
+ if (!command) {
4196
+ throw new Error(`Unknown command: ${args.command}`);
4197
+ }
4198
+ const result = await command.run(args);
4199
+ emitResult(result, result.data ? { data: result.data } : void 0);
4200
+ const exitCode = resolveExitCode(result);
4201
+ if (exitCode === null) {
4202
+ return;
4203
+ }
4204
+ process.exit(exitCode);
4205
+ } catch (error) {
4206
+ const format = outputFormat ?? detectOutputFormat(process.argv);
4207
+ const cliError = toCliError(error, parseSucceeded ? EXIT_EXECUTION : EXIT_USAGE);
4208
+ emitFatalError(cliError, format);
4209
+ process.exit(cliError.exitCode);
796
4210
  }
797
4211
  }
798
4212
  main().catch((error) => {
799
- console.error("Unexpected error:", error);
800
- process.exit(1);
4213
+ const cliError = toCliError(error, EXIT_EXECUTION);
4214
+ emitFatalError(cliError, detectOutputFormat(process.argv));
4215
+ process.exit(cliError.exitCode);
801
4216
  });
802
4217
  //# sourceMappingURL=index.js.map