@vforsh/argus 0.1.2 → 0.1.3

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 (249) hide show
  1. package/README.md +263 -16
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/bin.js +712 -9
  4. package/dist/bin.js.map +1 -1
  5. package/dist/cdp/resolveCdpEndpoint.d.ts +15 -0
  6. package/dist/cdp/resolveCdpEndpoint.d.ts.map +1 -0
  7. package/dist/cdp/resolveCdpEndpoint.js +56 -0
  8. package/dist/cdp/resolveCdpEndpoint.js.map +1 -0
  9. package/dist/cdp/selectTarget.d.ts +26 -0
  10. package/dist/cdp/selectTarget.d.ts.map +1 -0
  11. package/dist/cdp/selectTarget.js +87 -0
  12. package/dist/cdp/selectTarget.js.map +1 -0
  13. package/dist/cdp/types.d.ts +13 -0
  14. package/dist/cdp/types.d.ts.map +1 -0
  15. package/dist/cdp/types.js +2 -0
  16. package/dist/cdp/types.js.map +1 -0
  17. package/dist/commands/chrome.d.ts +41 -0
  18. package/dist/commands/chrome.d.ts.map +1 -0
  19. package/dist/commands/chrome.js +463 -0
  20. package/dist/commands/chrome.js.map +1 -0
  21. package/dist/commands/chromeStart.d.ts +9 -0
  22. package/dist/commands/chromeStart.d.ts.map +1 -0
  23. package/dist/commands/chromeStart.js +310 -0
  24. package/dist/commands/chromeStart.js.map +1 -0
  25. package/dist/commands/configInit.d.ts +6 -0
  26. package/dist/commands/configInit.d.ts.map +1 -0
  27. package/dist/commands/configInit.js +88 -0
  28. package/dist/commands/configInit.js.map +1 -0
  29. package/dist/commands/contexts.d.ts +7 -0
  30. package/dist/commands/contexts.d.ts.map +1 -0
  31. package/dist/commands/contexts.js +54 -0
  32. package/dist/commands/contexts.js.map +1 -0
  33. package/dist/commands/doctor.d.ts +5 -0
  34. package/dist/commands/doctor.d.ts.map +1 -0
  35. package/dist/commands/doctor.js +142 -0
  36. package/dist/commands/doctor.js.map +1 -0
  37. package/dist/commands/domAdd.d.ts +17 -0
  38. package/dist/commands/domAdd.d.ts.map +1 -0
  39. package/dist/commands/domAdd.js +203 -0
  40. package/dist/commands/domAdd.js.map +1 -0
  41. package/dist/commands/domClick.d.ts +9 -0
  42. package/dist/commands/domClick.d.ts.map +1 -0
  43. package/dist/commands/domClick.js +75 -0
  44. package/dist/commands/domClick.js.map +1 -0
  45. package/dist/commands/domDiff.d.ts +43 -0
  46. package/dist/commands/domDiff.d.ts.map +1 -0
  47. package/dist/commands/domDiff.js +279 -0
  48. package/dist/commands/domDiff.js.map +1 -0
  49. package/dist/commands/domHover.d.ts +9 -0
  50. package/dist/commands/domHover.d.ts.map +1 -0
  51. package/dist/commands/domHover.js +75 -0
  52. package/dist/commands/domHover.js.map +1 -0
  53. package/dist/commands/domInfo.d.ts +10 -0
  54. package/dist/commands/domInfo.d.ts.map +1 -0
  55. package/dist/commands/domInfo.js +93 -0
  56. package/dist/commands/domInfo.js.map +1 -0
  57. package/dist/commands/domModify.d.ts +35 -0
  58. package/dist/commands/domModify.d.ts.map +1 -0
  59. package/dist/commands/domModify.js +203 -0
  60. package/dist/commands/domModify.js.map +1 -0
  61. package/dist/commands/domRemove.d.ts +9 -0
  62. package/dist/commands/domRemove.d.ts.map +1 -0
  63. package/dist/commands/domRemove.js +75 -0
  64. package/dist/commands/domRemove.js.map +1 -0
  65. package/dist/commands/domTree.d.ts +11 -0
  66. package/dist/commands/domTree.d.ts.map +1 -0
  67. package/dist/commands/domTree.js +100 -0
  68. package/dist/commands/domTree.js.map +1 -0
  69. package/dist/commands/eval.d.ts +22 -0
  70. package/dist/commands/eval.d.ts.map +1 -0
  71. package/dist/commands/eval.js +292 -0
  72. package/dist/commands/eval.js.map +1 -0
  73. package/dist/commands/extension/attach.d.ts +8 -0
  74. package/dist/commands/extension/attach.d.ts.map +1 -0
  75. package/dist/commands/extension/attach.js +171 -0
  76. package/dist/commands/extension/attach.js.map +1 -0
  77. package/dist/commands/extension/detach.d.ts +6 -0
  78. package/dist/commands/extension/detach.d.ts.map +1 -0
  79. package/dist/commands/extension/detach.js +132 -0
  80. package/dist/commands/extension/detach.js.map +1 -0
  81. package/dist/commands/extension/info.d.ts +5 -0
  82. package/dist/commands/extension/info.d.ts.map +1 -0
  83. package/dist/commands/extension/info.js +68 -0
  84. package/dist/commands/extension/info.js.map +1 -0
  85. package/dist/commands/extension/nativeHost.d.ts +21 -0
  86. package/dist/commands/extension/nativeHost.d.ts.map +1 -0
  87. package/dist/commands/extension/nativeHost.js +106 -0
  88. package/dist/commands/extension/nativeHost.js.map +1 -0
  89. package/dist/commands/extension/remove.d.ts +5 -0
  90. package/dist/commands/extension/remove.d.ts.map +1 -0
  91. package/dist/commands/extension/remove.js +75 -0
  92. package/dist/commands/extension/remove.js.map +1 -0
  93. package/dist/commands/extension/setup.d.ts +6 -0
  94. package/dist/commands/extension/setup.d.ts.map +1 -0
  95. package/dist/commands/extension/setup.js +89 -0
  96. package/dist/commands/extension/setup.js.map +1 -0
  97. package/dist/commands/extension/status.d.ts +5 -0
  98. package/dist/commands/extension/status.d.ts.map +1 -0
  99. package/dist/commands/extension/status.js +82 -0
  100. package/dist/commands/extension/status.js.map +1 -0
  101. package/dist/commands/extension/targets.d.ts +13 -0
  102. package/dist/commands/extension/targets.d.ts.map +1 -0
  103. package/dist/commands/extension/targets.js +77 -0
  104. package/dist/commands/extension/targets.js.map +1 -0
  105. package/dist/commands/frames.d.ts +7 -0
  106. package/dist/commands/frames.d.ts.map +1 -0
  107. package/dist/commands/frames.js +90 -0
  108. package/dist/commands/frames.js.map +1 -0
  109. package/dist/commands/iframeHelper.d.ts +13 -0
  110. package/dist/commands/iframeHelper.d.ts.map +1 -0
  111. package/dist/commands/iframeHelper.js +63 -0
  112. package/dist/commands/iframeHelper.js.map +1 -0
  113. package/dist/commands/list.d.ts.map +1 -1
  114. package/dist/commands/list.js +8 -6
  115. package/dist/commands/list.js.map +1 -1
  116. package/dist/commands/logs.d.ts +6 -2
  117. package/dist/commands/logs.d.ts.map +1 -1
  118. package/dist/commands/logs.js +72 -16
  119. package/dist/commands/logs.js.map +1 -1
  120. package/dist/commands/net.d.ts +11 -0
  121. package/dist/commands/net.d.ts.map +1 -0
  122. package/dist/commands/net.js +90 -0
  123. package/dist/commands/net.js.map +1 -0
  124. package/dist/commands/netTail.d.ts +12 -0
  125. package/dist/commands/netTail.d.ts.map +1 -0
  126. package/dist/commands/netTail.js +112 -0
  127. package/dist/commands/netTail.js.map +1 -0
  128. package/dist/commands/page.d.ts +13 -0
  129. package/dist/commands/page.d.ts.map +1 -0
  130. package/dist/commands/page.js +340 -0
  131. package/dist/commands/page.js.map +1 -0
  132. package/dist/commands/reload.d.ts +8 -0
  133. package/dist/commands/reload.d.ts.map +1 -0
  134. package/dist/commands/reload.js +50 -0
  135. package/dist/commands/reload.js.map +1 -0
  136. package/dist/commands/screenshot.d.ts +9 -0
  137. package/dist/commands/screenshot.d.ts.map +1 -0
  138. package/dist/commands/screenshot.js +52 -0
  139. package/dist/commands/screenshot.js.map +1 -0
  140. package/dist/commands/storageLocal.d.ts +18 -0
  141. package/dist/commands/storageLocal.d.ts.map +1 -0
  142. package/dist/commands/storageLocal.js +119 -0
  143. package/dist/commands/storageLocal.js.map +1 -0
  144. package/dist/commands/tail.d.ts +6 -2
  145. package/dist/commands/tail.d.ts.map +1 -1
  146. package/dist/commands/tail.js +71 -15
  147. package/dist/commands/tail.js.map +1 -1
  148. package/dist/commands/trace.d.ts +27 -0
  149. package/dist/commands/trace.d.ts.map +1 -0
  150. package/dist/commands/trace.js +139 -0
  151. package/dist/commands/trace.js.map +1 -0
  152. package/dist/commands/watcherNativeHost.d.ts +13 -0
  153. package/dist/commands/watcherNativeHost.d.ts.map +1 -0
  154. package/dist/commands/watcherNativeHost.js +45 -0
  155. package/dist/commands/watcherNativeHost.js.map +1 -0
  156. package/dist/commands/watcherPrune.d.ts +9 -0
  157. package/dist/commands/watcherPrune.d.ts.map +1 -0
  158. package/dist/commands/watcherPrune.js +83 -0
  159. package/dist/commands/watcherPrune.js.map +1 -0
  160. package/dist/commands/watcherStart.d.ts +23 -0
  161. package/dist/commands/watcherStart.d.ts.map +1 -0
  162. package/dist/commands/watcherStart.js +182 -0
  163. package/dist/commands/watcherStart.js.map +1 -0
  164. package/dist/commands/watcherStatus.d.ts +7 -0
  165. package/dist/commands/watcherStatus.d.ts.map +1 -0
  166. package/dist/commands/watcherStatus.js +44 -0
  167. package/dist/commands/watcherStatus.js.map +1 -0
  168. package/dist/commands/watcherStop.d.ts +5 -0
  169. package/dist/commands/watcherStop.d.ts.map +1 -0
  170. package/dist/commands/watcherStop.js +103 -0
  171. package/dist/commands/watcherStop.js.map +1 -0
  172. package/dist/config/argusConfig.d.ts +67 -0
  173. package/dist/config/argusConfig.d.ts.map +1 -0
  174. package/dist/config/argusConfig.js +338 -0
  175. package/dist/config/argusConfig.js.map +1 -0
  176. package/dist/eval/evalClient.d.ts +35 -0
  177. package/dist/eval/evalClient.d.ts.map +1 -0
  178. package/dist/eval/evalClient.js +71 -0
  179. package/dist/eval/evalClient.js.map +1 -0
  180. package/dist/frame/frameSelector.d.ts +14 -0
  181. package/dist/frame/frameSelector.d.ts.map +1 -0
  182. package/dist/frame/frameSelector.js +31 -0
  183. package/dist/frame/frameSelector.js.map +1 -0
  184. package/dist/httpClient.d.ts +6 -1
  185. package/dist/httpClient.d.ts.map +1 -1
  186. package/dist/httpClient.js +54 -1
  187. package/dist/httpClient.js.map +1 -1
  188. package/dist/index.d.ts +1 -1
  189. package/dist/index.d.ts.map +1 -1
  190. package/dist/index.js +1 -1
  191. package/dist/index.js.map +1 -1
  192. package/dist/output/dom.d.ts +14 -0
  193. package/dist/output/dom.d.ts.map +1 -0
  194. package/dist/output/dom.js +92 -0
  195. package/dist/output/dom.js.map +1 -0
  196. package/dist/output/domDiff.d.ts +14 -0
  197. package/dist/output/domDiff.d.ts.map +1 -0
  198. package/dist/output/domDiff.js +151 -0
  199. package/dist/output/domDiff.js.map +1 -0
  200. package/dist/output/format.d.ts +3 -2
  201. package/dist/output/format.d.ts.map +1 -1
  202. package/dist/output/format.js +11 -3
  203. package/dist/output/format.js.map +1 -1
  204. package/dist/output/io.d.ts +14 -0
  205. package/dist/output/io.d.ts.map +1 -0
  206. package/dist/output/io.js +28 -0
  207. package/dist/output/io.js.map +1 -0
  208. package/dist/output/preview.d.ts +3 -0
  209. package/dist/output/preview.d.ts.map +1 -0
  210. package/dist/output/preview.js +6 -0
  211. package/dist/output/preview.js.map +1 -0
  212. package/dist/plugins/argusApi.d.ts +22 -0
  213. package/dist/plugins/argusApi.d.ts.map +1 -0
  214. package/dist/plugins/argusApi.js +92 -0
  215. package/dist/plugins/argusApi.js.map +1 -0
  216. package/dist/plugins/loader.d.ts +12 -0
  217. package/dist/plugins/loader.d.ts.map +1 -0
  218. package/dist/plugins/loader.js +92 -0
  219. package/dist/plugins/loader.js.map +1 -0
  220. package/dist/plugins/registry.d.ts +17 -0
  221. package/dist/plugins/registry.d.ts.map +1 -0
  222. package/dist/plugins/registry.js +44 -0
  223. package/dist/plugins/registry.js.map +1 -0
  224. package/dist/plugins/resolver.d.ts +6 -0
  225. package/dist/plugins/resolver.d.ts.map +1 -0
  226. package/dist/plugins/resolver.js +86 -0
  227. package/dist/plugins/resolver.js.map +1 -0
  228. package/dist/registry.d.ts +2 -0
  229. package/dist/registry.d.ts.map +1 -1
  230. package/dist/registry.js +12 -0
  231. package/dist/registry.js.map +1 -1
  232. package/dist/utils/chromeBin.d.ts +3 -0
  233. package/dist/utils/chromeBin.d.ts.map +1 -0
  234. package/dist/utils/chromeBin.js +46 -0
  235. package/dist/utils/chromeBin.js.map +1 -0
  236. package/dist/utils/ports.d.ts +3 -0
  237. package/dist/utils/ports.d.ts.map +1 -0
  238. package/dist/utils/ports.js +39 -0
  239. package/dist/utils/ports.js.map +1 -0
  240. package/dist/watchers/candidates.d.ts +4 -0
  241. package/dist/watchers/candidates.d.ts.map +1 -0
  242. package/dist/watchers/candidates.js +7 -0
  243. package/dist/watchers/candidates.js.map +1 -0
  244. package/dist/watchers/resolveWatcher.d.ts +16 -0
  245. package/dist/watchers/resolveWatcher.d.ts.map +1 -0
  246. package/dist/watchers/resolveWatcher.js +50 -0
  247. package/dist/watchers/resolveWatcher.js.map +1 -0
  248. package/package.json +7 -3
  249. package/schemas/argus.config.schema.json +115 -0
package/dist/bin.js CHANGED
@@ -3,6 +3,59 @@ import { Command } from 'commander';
3
3
  import { runList } from './commands/list.js';
4
4
  import { runLogs } from './commands/logs.js';
5
5
  import { runTail } from './commands/tail.js';
6
+ import { runNet } from './commands/net.js';
7
+ import { runNetTail } from './commands/netTail.js';
8
+ import { runEval } from './commands/eval.js';
9
+ import { runIframeHelper } from './commands/iframeHelper.js';
10
+ import { runTrace, runTraceStart, runTraceStop } from './commands/trace.js';
11
+ import { runScreenshot } from './commands/screenshot.js';
12
+ import { runReload } from './commands/reload.js';
13
+ import { runDomTree } from './commands/domTree.js';
14
+ import { runDomInfo } from './commands/domInfo.js';
15
+ import { runDomHover } from './commands/domHover.js';
16
+ import { runDomClick } from './commands/domClick.js';
17
+ import { runDomAdd } from './commands/domAdd.js';
18
+ import { runDomRemove } from './commands/domRemove.js';
19
+ import { runDomModifyAttr, runDomModifyClass, runDomModifyStyle, runDomModifyText, runDomModifyHtml } from './commands/domModify.js';
20
+ import { runChromeStart } from './commands/chromeStart.js';
21
+ import { runChromeVersion, runChromeStatus, runChromeTargets, runChromeOpen, runChromeActivate, runChromeClose, runChromeStop, } from './commands/chrome.js';
22
+ import { runPageReload } from './commands/page.js';
23
+ import { runDoctor } from './commands/doctor.js';
24
+ import { runWatcherStart } from './commands/watcherStart.js';
25
+ import { runWatcherStatus } from './commands/watcherStatus.js';
26
+ import { runWatcherStop } from './commands/watcherStop.js';
27
+ import { runWatcherPrune } from './commands/watcherPrune.js';
28
+ import { runWatcherNativeHost } from './commands/watcherNativeHost.js';
29
+ import { runStorageLocalGet, runStorageLocalSet, runStorageLocalRemove, runStorageLocalList, runStorageLocalClear } from './commands/storageLocal.js';
30
+ import { runConfigInit } from './commands/configInit.js';
31
+ import { runExtensionSetup } from './commands/extension/setup.js';
32
+ import { runExtensionRemove } from './commands/extension/remove.js';
33
+ import { runExtensionStatus } from './commands/extension/status.js';
34
+ import { runExtensionInfo } from './commands/extension/info.js';
35
+ import { loadArgusConfig, mergeChromeStartOptionsWithConfig, mergeWatcherStartOptionsWithConfig, resolveArgusConfigPath, } from './config/argusConfig.js';
36
+ import { PluginRegistry } from './plugins/registry.js';
37
+ const collectMatch = (value, previous) => [...previous, value];
38
+ const validateCaseFlags = (options) => {
39
+ if (options.ignoreCase && options.caseSensitive) {
40
+ console.error('Cannot combine --ignore-case with --case-sensitive.');
41
+ process.exitCode = 2;
42
+ return false;
43
+ }
44
+ return true;
45
+ };
46
+ const validateMatchOptions = (options) => {
47
+ if (!options.match || options.match.length === 0) {
48
+ return true;
49
+ }
50
+ const invalid = options.match.find((value) => value.trim().length === 0);
51
+ if (invalid != null) {
52
+ console.error('Invalid --match value: empty pattern.');
53
+ process.exitCode = 2;
54
+ return false;
55
+ }
56
+ options.match = options.match.map((value) => value.trim());
57
+ return true;
58
+ };
6
59
  const program = new Command();
7
60
  program
8
61
  .name('argus')
@@ -21,6 +74,7 @@ program
21
74
  });
22
75
  program
23
76
  .command('list')
77
+ .alias('ls')
24
78
  .description('List registered watchers')
25
79
  .option('--json', 'Output JSON for automation')
26
80
  .option('--by-cwd <substring>', 'Filter watchers by working directory substring')
@@ -28,33 +82,682 @@ program
28
82
  .action(async (options) => {
29
83
  await runList(options);
30
84
  });
85
+ program
86
+ .command('doctor')
87
+ .description('Run environment diagnostics for Argus')
88
+ .option('--json', 'Output JSON for automation')
89
+ .addHelpText('after', '\nExamples:\n $ argus doctor\n $ argus doctor --json\n')
90
+ .action(async (options) => {
91
+ await runDoctor(options);
92
+ });
93
+ const config = program.command('config').description('Manage Argus config files');
94
+ config
95
+ .command('init')
96
+ .description('Create an Argus config file')
97
+ .option('--path <file>', 'Path to write the config file (default: .argus/config.json)')
98
+ .option('--force', 'Overwrite existing config file')
99
+ .addHelpText('after', '\nExamples:\n $ argus config init\n $ argus config init --path argus.config.json\n')
100
+ .action(async (options) => {
101
+ await runConfigInit(options);
102
+ });
31
103
  program
32
104
  .command('logs')
33
- .argument('<id>', 'Watcher id to query')
105
+ .alias('log')
106
+ .argument('[id]', 'Watcher id to query')
34
107
  .description('Fetch recent logs from a watcher')
35
108
  .option('--levels <levels>', 'Comma-separated log levels')
36
- .option('--grep <text>', 'Filter by substring')
109
+ .option('--match <regex>', 'Filter by regex (repeatable)', collectMatch, [])
110
+ .option('--ignore-case', 'Use case-insensitive regex matching')
111
+ .option('--case-sensitive', 'Use case-sensitive regex matching')
112
+ .option('--source <pattern>', 'Filter by source substring')
37
113
  .option('--since <duration>', 'Filter by time window (e.g. 10m, 2h, 30s)')
38
114
  .option('--after <id>', 'Only return events after this id')
39
115
  .option('--limit <count>', 'Maximum number of events')
40
- .option('--json', 'Output JSON for automation')
41
- .addHelpText('after', '\nExamples:\n $ argus logs app\n $ argus logs app --since 10m --levels error,warning\n $ argus logs app --json\n')
116
+ .option('--json', 'Output bounded JSON preview for automation')
117
+ .option('--json-full', 'Output full JSON (can be very large)')
118
+ .addHelpText('after', '\nExamples:\n $ argus logs app\n $ argus logs app --since 10m --levels error,warning\n $ argus logs app --json\n $ argus logs app --json-full\n')
42
119
  .action(async (id, options) => {
120
+ if (options.json && options.jsonFull) {
121
+ console.error('Cannot combine --json with --json-full.');
122
+ process.exitCode = 2;
123
+ return;
124
+ }
125
+ if (!validateCaseFlags(options)) {
126
+ return;
127
+ }
128
+ if (!validateMatchOptions(options)) {
129
+ return;
130
+ }
43
131
  await runLogs(id, options);
44
132
  });
45
133
  program
46
134
  .command('tail')
47
- .argument('<id>', 'Watcher id to follow')
135
+ .argument('[id]', 'Watcher id to follow')
48
136
  .description('Tail logs from a watcher via long-polling')
49
137
  .option('--levels <levels>', 'Comma-separated log levels')
50
- .option('--grep <text>', 'Filter by substring')
138
+ .option('--match <regex>', 'Filter by regex (repeatable)', collectMatch, [])
139
+ .option('--ignore-case', 'Use case-insensitive regex matching')
140
+ .option('--case-sensitive', 'Use case-sensitive regex matching')
141
+ .option('--source <pattern>', 'Filter by source substring')
51
142
  .option('--after <id>', 'Start after this event id')
52
143
  .option('--limit <count>', 'Maximum number of events per poll')
53
144
  .option('--timeout <ms>', 'Long-poll timeout in milliseconds')
54
- .option('--json', 'Output newline-delimited JSON events')
55
- .addHelpText('after', '\nExamples:\n $ argus tail app\n $ argus tail app --levels error\n $ argus tail app --json\n')
145
+ .option('--json', 'Output bounded newline-delimited JSON events')
146
+ .option('--json-full', 'Output full newline-delimited JSON events (can be very large)')
147
+ .addHelpText('after', '\nExamples:\n $ argus tail app\n $ argus tail app --levels error\n $ argus tail app --json\n $ argus tail app --json-full\n')
56
148
  .action(async (id, options) => {
149
+ if (options.json && options.jsonFull) {
150
+ console.error('Cannot combine --json with --json-full.');
151
+ process.exitCode = 2;
152
+ return;
153
+ }
154
+ if (!validateCaseFlags(options)) {
155
+ return;
156
+ }
157
+ if (!validateMatchOptions(options)) {
158
+ return;
159
+ }
57
160
  await runTail(id, options);
58
161
  });
59
- program.parseAsync(process.argv);
162
+ const net = program
163
+ .command('net')
164
+ .alias('network')
165
+ .argument('[id]', 'Watcher id to query')
166
+ .description('Fetch recent network request summaries from a watcher')
167
+ .option('--after <id>', 'Only return requests after this id')
168
+ .option('--limit <count>', 'Maximum number of requests')
169
+ .option('--since <duration>', 'Filter by time window (e.g. 10m, 2h, 30s)')
170
+ .option('--grep <substring>', 'Substring match over redacted URLs')
171
+ .option('--json', 'Output JSON for automation')
172
+ .addHelpText('after', '\nExamples:\n $ argus net app --since 5m\n $ argus net app --grep api\n $ argus net app --json\n')
173
+ .action(async (id, options) => {
174
+ await runNet(id, options);
175
+ });
176
+ net.command('tail')
177
+ .argument('[id]', 'Watcher id to follow')
178
+ .description('Tail network request summaries via long-polling')
179
+ .option('--after <id>', 'Start after this request id')
180
+ .option('--limit <count>', 'Maximum number of requests per poll')
181
+ .option('--timeout <ms>', 'Long-poll timeout in milliseconds')
182
+ .option('--since <duration>', 'Filter by time window (e.g. 10m, 2h, 30s)')
183
+ .option('--grep <substring>', 'Substring match over redacted URLs')
184
+ .option('--json', 'Output newline-delimited JSON requests')
185
+ .addHelpText('after', '\nExamples:\n $ argus net tail app\n $ argus net tail app --grep api\n $ argus net tail app --json\n')
186
+ .action(async (id, options) => {
187
+ await runNetTail(id, options);
188
+ });
189
+ program
190
+ .command('eval')
191
+ .argument('[id]', 'Watcher id to query')
192
+ .argument('<expression>', 'JS expression to evaluate')
193
+ .description('Evaluate a JS expression in the connected page')
194
+ .option('--no-await', 'Do not await promises')
195
+ .option('--timeout <ms>', 'Eval timeout in milliseconds')
196
+ .option('--json', 'Output JSON for automation')
197
+ .option('--no-return-by-value', 'Disable returnByValue (use preview)')
198
+ .option('--no-fail-on-exception', 'Do not exit with code 1 when the evaluation throws')
199
+ .option('--retry <n>', 'Retry failed evaluations up to N times')
200
+ .option('-q, --silent', 'Suppress success output; only emit output on error')
201
+ .option('--interval <ms|duration>', 'Re-evaluate every interval (e.g. 500, 3s)')
202
+ .option('--count <n>', 'Stop after N iterations (requires --interval)')
203
+ .option('--until <condition>', 'Stop when local condition becomes truthy (requires --interval)')
204
+ .option('--iframe <selector>', 'Eval in iframe via postMessage (requires helper script)')
205
+ .option('--iframe-namespace <name>', 'Message type prefix for iframe eval (default: argus)')
206
+ .option('--iframe-timeout <ms>', 'Timeout for iframe postMessage response (default: 5000)')
207
+ .addHelpText('after', `
208
+ Examples:
209
+ $ argus eval app "location.href"
210
+ $ argus eval app "await fetch('/ping').then(r => r.status)"
211
+ $ argus eval app "document.title" --no-fail-on-exception
212
+ $ argus eval app "1+1" --retry 3
213
+ $ argus eval app "1+1" --silent
214
+ $ argus eval app "Date.now()" --interval 500 --count 10
215
+ $ argus eval app "document.title" --interval 250 --until 'result === "ready"'
216
+ $ argus eval app "window.gameState" --iframe "iframe#game"
217
+ $ argus eval app "document.title" --iframe "iframe" --iframe-timeout 10000
218
+ `)
219
+ .action(async (id, expression, options) => {
220
+ await runEval(id, expression, {
221
+ json: options.json,
222
+ await: options.await,
223
+ timeout: options.timeout,
224
+ returnByValue: options.returnByValue,
225
+ failOnException: options.failOnException,
226
+ retry: options.retry,
227
+ silent: options.silent,
228
+ interval: options.interval,
229
+ count: options.count,
230
+ until: options.until,
231
+ iframe: options.iframe,
232
+ iframeNamespace: options.iframeNamespace,
233
+ iframeTimeout: options.iframeTimeout,
234
+ });
235
+ });
236
+ program
237
+ .command('iframe-helper')
238
+ .description('Output helper script for cross-origin iframe eval via postMessage')
239
+ .option('--out <file>', 'Write script to file instead of stdout')
240
+ .option('--no-log', 'Omit console.log confirmation')
241
+ .option('--iife', 'Wrap in IIFE to avoid global scope')
242
+ .option('--namespace <name>', 'Message type prefix (default: argus)')
243
+ .addHelpText('after', `
244
+ Examples:
245
+ $ argus iframe-helper > helper.js
246
+ $ argus iframe-helper --out src/argus.js
247
+ $ argus iframe-helper --iife --no-log
248
+ $ argus iframe-helper --namespace myapp
249
+ `)
250
+ .action(async (options) => {
251
+ await runIframeHelper(options);
252
+ });
253
+ const trace = program
254
+ .command('trace')
255
+ .argument('[id]', 'Watcher id to query')
256
+ .description('Capture a Chrome trace to disk on the watcher')
257
+ .option('--duration <duration>', 'Capture for duration (e.g. 3s, 500ms)')
258
+ .option('--out <file>', 'Output trace file path (relative to artifacts base directory)')
259
+ .option('--categories <categories>', 'Comma-separated tracing categories')
260
+ .option('--options <options>', 'Tracing options string')
261
+ .option('--json', 'Output JSON for automation')
262
+ .addHelpText('after', '\nExamples:\n $ argus trace app --duration 3s --out trace.json\n')
263
+ .action(async (id, options) => {
264
+ await runTrace(id, options);
265
+ });
266
+ trace
267
+ .command('start')
268
+ .argument('[id]', 'Watcher id to query')
269
+ .description('Start Chrome tracing')
270
+ .option('--out <file>', 'Output trace file path (relative to artifacts base directory)')
271
+ .option('--categories <categories>', 'Comma-separated tracing categories')
272
+ .option('--options <options>', 'Tracing options string')
273
+ .option('--json', 'Output JSON for automation')
274
+ .addHelpText('after', '\nExamples:\n $ argus trace start app --out trace.json\n')
275
+ .action(async (id, options) => {
276
+ await runTraceStart(id, options);
277
+ });
278
+ trace
279
+ .command('stop')
280
+ .argument('[id]', 'Watcher id to query')
281
+ .description('Stop Chrome tracing')
282
+ .option('--trace-id <id>', 'Trace id returned from start')
283
+ .option('--json', 'Output JSON for automation')
284
+ .addHelpText('after', '\nExamples:\n $ argus trace stop app\n')
285
+ .action(async (id, options) => {
286
+ await runTraceStop(id, options);
287
+ });
288
+ program
289
+ .command('screenshot')
290
+ .argument('[id]', 'Watcher id to query')
291
+ .description('Capture a screenshot to disk on the watcher')
292
+ .option('--out <file>', 'Output screenshot file path (relative to artifacts base directory)')
293
+ .option('--selector <selector>', 'Optional CSS selector for element-only capture')
294
+ .option('--json', 'Output JSON for automation')
295
+ .addHelpText('after', '\nExamples:\n $ argus screenshot app --out shot.png\n $ argus screenshot app --selector "body" --out body.png\n')
296
+ .action(async (id, options) => {
297
+ await runScreenshot(id, options);
298
+ });
299
+ program
300
+ .command('reload')
301
+ .argument('[id]', 'Watcher id to reload')
302
+ .description('Reload the page attached to a watcher')
303
+ .option('--ignore-cache', 'Bypass browser cache')
304
+ .option('--json', 'Output JSON for automation')
305
+ .addHelpText('after', '\nExamples:\n $ argus reload app\n $ argus reload app --ignore-cache\n $ argus reload app --json\n')
306
+ .action(async (id, options) => {
307
+ await runReload(id, options);
308
+ });
309
+ const dom = program.command('dom').alias('html').description('Inspect DOM elements in the connected page');
310
+ dom.command('tree')
311
+ .argument('[id]', 'Watcher id to query')
312
+ .description('Fetch a DOM subtree rooted at element(s) matching a CSS selector')
313
+ .requiredOption('--selector <css>', 'CSS selector to match element(s)')
314
+ .option('--depth <n>', 'Max depth to traverse (default: 2)')
315
+ .option('--max-nodes <n>', 'Max total nodes to return (default: 5000)')
316
+ .option('--all', 'Allow multiple matches (default: error if >1 match)')
317
+ .option('--json', 'Output JSON for automation')
318
+ .addHelpText('after', '\nExamples:\n $ argus dom tree app --selector "body"\n $ argus dom tree app --selector "div" --all --depth 3\n $ argus dom tree app --selector "#root" --json\n')
319
+ .action(async (id, options) => {
320
+ await runDomTree(id, options);
321
+ });
322
+ dom.command('info')
323
+ .argument('[id]', 'Watcher id to query')
324
+ .description('Fetch detailed info for element(s) matching a CSS selector')
325
+ .requiredOption('--selector <css>', 'CSS selector to match element(s)')
326
+ .option('--all', 'Allow multiple matches (default: error if >1 match)')
327
+ .option('--outer-html-max <n>', 'Max characters for outerHTML (default: 50000)')
328
+ .option('--json', 'Output JSON for automation')
329
+ .addHelpText('after', '\nExamples:\n $ argus dom info app --selector "body"\n $ argus dom info app --selector "div" --all\n $ argus dom info app --selector "#root" --json\n')
330
+ .action(async (id, options) => {
331
+ await runDomInfo(id, options);
332
+ });
333
+ dom.command('hover')
334
+ .argument('[id]', 'Watcher id to query')
335
+ .description('Hover over element(s) matching a CSS selector')
336
+ .requiredOption('--selector <css>', 'CSS selector to match element(s)')
337
+ .option('--all', 'Allow multiple matches (default: error if >1 match)')
338
+ .option('--json', 'Output JSON for automation')
339
+ .addHelpText('after', '\nExamples:\n $ argus dom hover app --selector "#btn"\n $ argus dom hover app --selector ".item" --all\n $ argus dom hover app --selector "#btn" --json\n')
340
+ .action(async (id, options) => {
341
+ await runDomHover(id, options);
342
+ });
343
+ dom.command('click')
344
+ .argument('[id]', 'Watcher id to query')
345
+ .description('Click element(s) matching a CSS selector')
346
+ .requiredOption('--selector <css>', 'CSS selector to match element(s)')
347
+ .option('--all', 'Allow multiple matches (default: error if >1 match)')
348
+ .option('--json', 'Output JSON for automation')
349
+ .addHelpText('after', '\nExamples:\n $ argus dom click app --selector "#btn"\n $ argus dom click app --selector ".item" --all\n $ argus dom click app --selector "#btn" --json\n')
350
+ .action(async (id, options) => {
351
+ await runDomClick(id, options);
352
+ });
353
+ dom.command('add')
354
+ .argument('[id]', 'Watcher id to query')
355
+ .description('Insert HTML into the page relative to matched element(s)')
356
+ .requiredOption('--selector <css>', 'CSS selector for target element(s)')
357
+ .option('--html <string>', 'HTML to insert (use "-" for stdin)')
358
+ .option('--html-file <path>', 'Read HTML to insert from a file')
359
+ .option('--html-stdin', 'Read HTML to insert from stdin (same as --html -)')
360
+ .option('--position <pos>', 'Insert position: beforebegin, afterbegin, beforeend, afterend (aliases: before, after, prepend, append)', 'beforeend')
361
+ .option('--nth <index>', 'Insert at the zero-based match index')
362
+ .option('--first', 'Insert at the first match (same as --nth 0)')
363
+ .option('--expect <n>', 'Expect N matches before inserting')
364
+ .option('--text', 'Insert text content (uses insertAdjacentText)')
365
+ .option('--all', 'Insert at all matches (default: error if >1 match)')
366
+ .option('--json', 'Output JSON for automation')
367
+ .addHelpText('after', '\nExamples:\n $ argus dom add app --selector "#container" --html "<div>Hello</div>"\n $ argus dom add app --selector "body" --position append --html "<script src=\'debug.js\'></script>"\n $ argus dom add app --selector ".item" --all --position afterend --html "<hr>"\n $ argus dom add app --selector "#root" --html-file ./snippet.html\n $ cat snippet.html | argus dom add app --selector "#root" --html -\n $ argus dom add app --selector ".item" --nth 2 --html "<hr>"\n $ argus dom add app --selector "#banner" --text --html "Preview mode"\n')
368
+ .action(async (id, options) => {
369
+ await runDomAdd(id, options);
370
+ });
371
+ dom.command('remove')
372
+ .argument('[id]', 'Watcher id to query')
373
+ .description('Remove elements from the page')
374
+ .requiredOption('--selector <css>', 'CSS selector for elements to remove')
375
+ .option('--all', 'Remove all matches (default: error if >1 match)')
376
+ .option('--json', 'Output JSON for automation')
377
+ .addHelpText('after', '\nExamples:\n $ argus dom remove app --selector ".debug-overlay"\n $ argus dom remove app --selector "[data-testid=\'temp\']" --all\n')
378
+ .action(async (id, options) => {
379
+ await runDomRemove(id, options);
380
+ });
381
+ const domModify = dom.command('modify').description('Modify DOM element properties');
382
+ domModify
383
+ .command('attr')
384
+ .argument('[id]', 'Watcher id to query')
385
+ .argument('[attrs...]', 'Attributes: name (boolean) or name=value')
386
+ .requiredOption('--selector <css>', 'CSS selector for target element(s)')
387
+ .option('--remove <attrs...>', 'Attributes to remove')
388
+ .option('--all', 'Apply to all matches (default: error if >1 match)')
389
+ .option('--json', 'Output JSON for automation')
390
+ .addHelpText('after', '\nExamples:\n $ argus dom modify attr app --selector "#btn" disabled\n $ argus dom modify attr app --selector "#btn" data-loading=true aria-label="Submit"\n $ argus dom modify attr app --selector "#btn" --remove disabled data-temp\n')
391
+ .action(async (id, attrs, options) => {
392
+ await runDomModifyAttr(id, attrs, options);
393
+ });
394
+ domModify
395
+ .command('class')
396
+ .argument('[id]', 'Watcher id to query')
397
+ .argument('[classes...]', 'Shorthand: +add, -remove, ~toggle (or plain name to add)')
398
+ .requiredOption('--selector <css>', 'CSS selector for target element(s)')
399
+ .option('--add <classes...>', 'Classes to add')
400
+ .option('--remove <classes...>', 'Classes to remove')
401
+ .option('--toggle <classes...>', 'Classes to toggle')
402
+ .option('--all', 'Apply to all matches (default: error if >1 match)')
403
+ .option('--json', 'Output JSON for automation')
404
+ .addHelpText('after', '\nExamples:\n $ argus dom modify class app --selector "#btn" --add active highlighted\n $ argus dom modify class app --selector "#btn" --remove hidden disabled\n $ argus dom modify class app --selector "#btn" --toggle loading\n $ argus dom modify class app --selector "#btn" +active +primary -hidden ~loading\n')
405
+ .action(async (id, classes, options) => {
406
+ await runDomModifyClass(id, classes, options);
407
+ });
408
+ domModify
409
+ .command('style')
410
+ .argument('[id]', 'Watcher id to query')
411
+ .argument('[styles...]', 'Styles: property=value')
412
+ .requiredOption('--selector <css>', 'CSS selector for target element(s)')
413
+ .option('--remove <props...>', 'Style properties to remove')
414
+ .option('--all', 'Apply to all matches (default: error if >1 match)')
415
+ .option('--json', 'Output JSON for automation')
416
+ .addHelpText('after', '\nExamples:\n $ argus dom modify style app --selector "#btn" color=red font-size=14px\n $ argus dom modify style app --selector "#btn" --remove color font-size\n')
417
+ .action(async (id, styles, options) => {
418
+ await runDomModifyStyle(id, styles, options);
419
+ });
420
+ domModify
421
+ .command('text')
422
+ .argument('[id]', 'Watcher id to query')
423
+ .argument('<text>', 'Text content to set')
424
+ .requiredOption('--selector <css>', 'CSS selector for target element(s)')
425
+ .option('--all', 'Apply to all matches (default: error if >1 match)')
426
+ .option('--json', 'Output JSON for automation')
427
+ .addHelpText('after', '\nExamples:\n $ argus dom modify text app --selector "#msg" "Hello World"\n $ argus dom modify text app --selector ".counter" --all "0"\n')
428
+ .action(async (id, text, options) => {
429
+ await runDomModifyText(id, text, options);
430
+ });
431
+ domModify
432
+ .command('html')
433
+ .argument('[id]', 'Watcher id to query')
434
+ .argument('<html>', 'HTML content to set')
435
+ .requiredOption('--selector <css>', 'CSS selector for target element(s)')
436
+ .option('--all', 'Apply to all matches (default: error if >1 match)')
437
+ .option('--json', 'Output JSON for automation')
438
+ .addHelpText('after', '\nExamples:\n $ argus dom modify html app --selector "#container" "<p>New <strong>content</strong></p>"\n')
439
+ .action(async (id, html, options) => {
440
+ await runDomModifyHtml(id, html, options);
441
+ });
442
+ const chrome = program.command('chrome').alias('browser').description('Chrome/Chromium management commands');
443
+ chrome
444
+ .command('start')
445
+ .description('Launch Chrome with CDP enabled')
446
+ .option('--url <url>', 'URL to open in Chrome')
447
+ .option('--from-watcher <watcherId>', 'Use match.url from a registered watcher')
448
+ .option('--profile <type>', 'Profile mode: temp, default-full, default-medium, or default-lite (default: default-lite)')
449
+ .option('--dev-tools', 'Open DevTools for new tabs')
450
+ .option('--config <path>', 'Path to Argus config file')
451
+ .option('--json', 'Output JSON for automation')
452
+ .addHelpText('after', '\nExamples:\n $ argus chrome start\n $ argus chrome start --url http://localhost:3000\n $ argus chrome start --from-watcher app\n $ argus chrome start --profile default-full\n $ argus chrome start --profile default-medium\n $ argus chrome start --profile default-lite\n $ argus chrome start --profile temp\n $ argus chrome start --dev-tools\n $ argus chrome start --json\n')
453
+ .action(async (options, command) => {
454
+ const { config: configPath, ...cliOptions } = options;
455
+ const resolvedPath = resolveArgusConfigPath({ cliPath: configPath, cwd: process.cwd() });
456
+ if (!resolvedPath) {
457
+ if (configPath) {
458
+ return;
459
+ }
460
+ await runChromeStart(cliOptions);
461
+ return;
462
+ }
463
+ const configResult = loadArgusConfig(resolvedPath);
464
+ if (!configResult) {
465
+ return;
466
+ }
467
+ const merged = mergeChromeStartOptionsWithConfig(cliOptions, command, configResult);
468
+ if (!merged) {
469
+ return;
470
+ }
471
+ await runChromeStart(merged);
472
+ });
473
+ chrome
474
+ .command('version')
475
+ .description('Show Chrome version info from CDP endpoint')
476
+ .option('--cdp <host:port>', 'CDP host:port')
477
+ .option('--id <watcherId>', 'Use chrome config from a registered watcher')
478
+ .option('--json', 'Output JSON for automation')
479
+ .addHelpText('after', '\nExamples:\n $ argus chrome version\n $ argus chrome version --cdp 127.0.0.1:9222\n $ argus chrome version --id app\n $ argus chrome version --json\n')
480
+ .action(async (options) => {
481
+ await runChromeVersion(options);
482
+ });
483
+ chrome
484
+ .command('status')
485
+ .description('Check if Chrome CDP endpoint is reachable')
486
+ .option('--cdp <host:port>', 'CDP host:port')
487
+ .option('--id <watcherId>', 'Use chrome config from a registered watcher')
488
+ .option('--json', 'Output JSON for automation')
489
+ .addHelpText('after', '\nExamples:\n $ argus chrome status\n $ argus chrome status --cdp 127.0.0.1:9222\n $ argus chrome status --id app\n')
490
+ .action(async (options) => {
491
+ await runChromeStatus(options);
492
+ });
493
+ chrome
494
+ .command('stop')
495
+ .alias('quit')
496
+ .description('Close the Chrome instance via CDP')
497
+ .option('--cdp <host:port>', 'CDP host:port')
498
+ .option('--id <watcherId>', 'Use chrome config from a registered watcher')
499
+ .option('--json', 'Output JSON for automation')
500
+ .addHelpText('after', '\nExamples:\n $ argus chrome stop\n $ argus chrome stop --id app\n $ argus chrome stop --json\n')
501
+ .action(async (options) => {
502
+ await runChromeStop(options);
503
+ });
504
+ const collectParam = (value, previous) => [...previous, value];
505
+ const page = program.command('page').alias('tab').description('Page/tab management commands');
506
+ page.command('targets')
507
+ .aliases(['list', 'ls'])
508
+ .description('List Chrome targets (tabs, extensions, etc.)')
509
+ .option('--type <type>', 'Filter by target type (e.g. page, worker, iframe)')
510
+ .option('--tree', 'Show targets as a tree with parent-child relationships')
511
+ .option('--cdp <host:port>', 'CDP host:port')
512
+ .option('--id <watcherId>', 'Use chrome config from a registered watcher')
513
+ .option('--json', 'Output JSON for automation')
514
+ .addHelpText('after', '\nExamples:\n $ argus page targets\n $ argus page targets --type page\n $ argus page targets --type iframe\n $ argus page targets --tree\n $ argus page targets --json\n $ argus page targets --id app\n')
515
+ .action(async (options) => {
516
+ await runChromeTargets(options);
517
+ });
518
+ page.command('open')
519
+ .alias('new')
520
+ .description('Open a new tab in Chrome')
521
+ .requiredOption('--url <url>', 'URL to open')
522
+ .option('--cdp <host:port>', 'CDP host:port')
523
+ .option('--id <watcherId>', 'Use chrome config from a registered watcher')
524
+ .option('--json', 'Output JSON for automation')
525
+ .addHelpText('after', '\nExamples:\n $ argus page open --url http://localhost:3000\n $ argus page open --url localhost:3000\n $ argus page open --url http://example.com --json\n')
526
+ .action(async (options) => {
527
+ await runChromeOpen(options);
528
+ });
529
+ page.command('activate')
530
+ .description('Activate (focus) a Chrome target')
531
+ .argument('[targetId]', 'Target ID to activate')
532
+ .option('--title <substring>', 'Case-insensitive substring match against target title')
533
+ .option('--url <substring>', 'Case-insensitive substring match against target URL')
534
+ .option('--match <substring>', 'Case-insensitive substring match against title + URL')
535
+ .option('--cdp <host:port>', 'CDP host:port')
536
+ .option('--id <watcherId>', 'Use chrome config from a registered watcher')
537
+ .option('--json', 'Output JSON for automation')
538
+ .addHelpText('after', '\nExamples:\n $ argus page activate ABCD1234\n $ argus page activate --title "Docs"\n $ argus page activate --url localhost:3000\n $ argus page activate --match "Argus" --json\n')
539
+ .action(async (targetId, options) => {
540
+ await runChromeActivate({ ...options, targetId });
541
+ });
542
+ page.command('close')
543
+ .description('Close a Chrome target')
544
+ .argument('<targetId>', 'Target ID to close')
545
+ .option('--cdp <host:port>', 'CDP host:port')
546
+ .option('--id <watcherId>', 'Use chrome config from a registered watcher')
547
+ .option('--json', 'Output JSON for automation')
548
+ .addHelpText('after', '\nExamples:\n $ argus page close ABCD1234\n $ argus page close ABCD1234 --json\n')
549
+ .action(async (targetId, options) => {
550
+ await runChromeClose({ ...options, targetId });
551
+ });
552
+ page.command('reload')
553
+ .description('Reload a Chrome target')
554
+ .argument('[targetId]', 'Target ID to reload')
555
+ .option('--attached', 'Reload the attached page for a watcher (requires --id)')
556
+ .option('--cdp <host:port>', 'CDP host:port')
557
+ .option('--id <watcherId>', 'Use chrome config from a registered watcher')
558
+ .option('--param <key=value>', 'Update query param (repeatable, overwrite semantics)', collectParam, [])
559
+ .option('--params <a=b&c=d>', 'Update query params from string (overwrite semantics)')
560
+ .option('--json', 'Output JSON for automation')
561
+ .addHelpText('after', '\nExamples:\n $ argus page reload ABCD1234\n $ argus page reload --attached --id app\n $ argus page reload ABCD1234 --json\n $ argus page reload ABCD1234 --param foo=bar\n $ argus page reload ABCD1234 --param foo=bar --param baz=qux\n $ argus page reload ABCD1234 --params "a=1&b=2"\n')
562
+ .action(async (targetId, options) => {
563
+ await runPageReload({ ...options, targetId });
564
+ });
565
+ const watcher = program.command('watcher').alias('watchers').description('Watcher management commands');
566
+ watcher
567
+ .command('list')
568
+ .alias('ls')
569
+ .description('List registered watchers')
570
+ .option('--json', 'Output JSON for automation')
571
+ .option('--by-cwd <substring>', 'Filter watchers by working directory substring')
572
+ .addHelpText('after', '\nExamples:\n $ argus watcher list\n $ argus watcher list --json\n $ argus watcher list --by-cwd my-project\n')
573
+ .action(async (options) => {
574
+ await runList(options);
575
+ });
576
+ watcher
577
+ .command('status')
578
+ .alias('ping')
579
+ .argument('[id]', 'Watcher id to query')
580
+ .description('Check watcher status')
581
+ .option('--json', 'Output JSON for automation')
582
+ .addHelpText('after', '\nExamples:\n $ argus watcher status app\n $ argus watcher status app --json\n')
583
+ .action(async (id, options) => {
584
+ await runWatcherStatus(id, options);
585
+ });
586
+ watcher
587
+ .command('stop')
588
+ .alias('kill')
589
+ .argument('[id]', 'Watcher id to stop')
590
+ .description('Stop a watcher')
591
+ .addHelpText('after', '\nExamples:\n $ argus watcher stop app\n $ argus watcher kill app\n')
592
+ .action(async (id, options) => {
593
+ await runWatcherStop(id, options);
594
+ });
595
+ watcher
596
+ .command('start')
597
+ .description('Start an Argus watcher process')
598
+ .option('--id <watcherId>', 'Watcher id to announce in the registry')
599
+ .option('--source <mode>', 'Source mode: cdp (default) or extension')
600
+ .option('--url <url>', 'URL pattern to match for capturing logs (CDP mode only)')
601
+ .option('--type <type>', 'Filter by target type (e.g., page, iframe, worker) (CDP mode only)')
602
+ .option('--origin <origin>', 'Match against URL origin only (protocol + host + port) (CDP mode only)')
603
+ .option('--target <targetId>', 'Connect to a specific target by its Chrome target ID (CDP mode only)')
604
+ .option('--parent <pattern>', 'Filter by parent target URL pattern (CDP mode only)')
605
+ .option('--chrome-host <host>', 'Chrome CDP host (default: 127.0.0.1) (CDP mode only)')
606
+ .option('--chrome-port <port>', 'Chrome CDP port (default: 9222) (CDP mode only)')
607
+ .option('--artifacts <dir>', 'Artifacts base directory (default: <cwd>/argus-artifacts)')
608
+ .option('--no-page-indicator', 'Disable the in-page watcher indicator (CDP mode only)')
609
+ .option('--config <path>', 'Path to Argus config file')
610
+ .option('--json', 'Output JSON for automation')
611
+ .addHelpText('after', '\nExamples:\n $ argus watcher start --id app --url localhost:3000\n $ argus watcher start --id app --source extension\n $ argus watcher start --id game --type iframe --url localhost:3007\n $ argus watcher start --id game --origin https://localhost:3007\n $ argus watcher start --id game --target CC1135709D9AC3B9CC0446F8B58CC344\n $ argus watcher start --id game --type iframe --parent yandex.ru\n $ argus watcher start --id app --url localhost:3000 --no-page-indicator\n $ argus watcher start --id app --url localhost:3000 --json\n')
612
+ .action(async (options, command) => {
613
+ const { config: configPath, ...cliOptions } = options;
614
+ const resolvedPath = resolveArgusConfigPath({ cliPath: configPath, cwd: process.cwd() });
615
+ if (!resolvedPath) {
616
+ if (configPath) {
617
+ return;
618
+ }
619
+ await runWatcherStart(cliOptions);
620
+ return;
621
+ }
622
+ const configResult = loadArgusConfig(resolvedPath);
623
+ if (!configResult) {
624
+ return;
625
+ }
626
+ const merged = mergeWatcherStartOptionsWithConfig(cliOptions, command, configResult);
627
+ if (!merged) {
628
+ return;
629
+ }
630
+ await runWatcherStart(merged);
631
+ });
632
+ watcher
633
+ .command('prune')
634
+ .alias('clean')
635
+ .description('Remove unreachable watchers from the registry')
636
+ .option('--by-cwd <substring>', 'Filter watchers by working directory substring')
637
+ .option('--dry-run', 'Preview what would be removed without changing the registry')
638
+ .option('--json', 'Output JSON for automation')
639
+ .addHelpText('after', '\nExamples:\n $ argus watcher prune\n $ argus watcher prune --by-cwd my-project\n $ argus watcher prune --dry-run\n $ argus watcher prune --dry-run --json\n')
640
+ .action(async (options) => {
641
+ await runWatcherPrune(options);
642
+ });
643
+ watcher
644
+ .command('native-host')
645
+ .description('Start a watcher as a Native Messaging host (used by Chrome extension)')
646
+ .option('--id <watcherId>', 'Watcher id (default: extension)')
647
+ .option('--json', 'Output JSON for automation')
648
+ .action(async (options) => {
649
+ await runWatcherNativeHost(options);
650
+ });
651
+ const storage = program.command('storage').description('Interact with browser storage APIs');
652
+ const storageLocal = storage.command('local').description('Manage localStorage for the attached page');
653
+ storageLocal
654
+ .command('get')
655
+ .argument('[id]', 'Watcher id')
656
+ .argument('<key>', 'localStorage key to retrieve')
657
+ .option('--origin <origin>', 'Validate page origin matches this value')
658
+ .option('--json', 'Output JSON for automation')
659
+ .addHelpText('after', '\nExamples:\n $ argus storage local get app myKey\n $ argus storage local get app myKey --json\n')
660
+ .action(async (id, key, options) => {
661
+ await runStorageLocalGet(id, key, options);
662
+ });
663
+ storageLocal
664
+ .command('set')
665
+ .argument('[id]', 'Watcher id')
666
+ .argument('<key>', 'localStorage key to set')
667
+ .argument('<value>', 'Value to store')
668
+ .option('--origin <origin>', 'Validate page origin matches this value')
669
+ .option('--json', 'Output JSON for automation')
670
+ .addHelpText('after', '\nExamples:\n $ argus storage local set app myKey "myValue"\n $ argus storage local set app config \'{"debug":true}\'\n')
671
+ .action(async (id, key, value, options) => {
672
+ await runStorageLocalSet(id, key, value, options);
673
+ });
674
+ storageLocal
675
+ .command('remove')
676
+ .argument('[id]', 'Watcher id')
677
+ .argument('<key>', 'localStorage key to remove')
678
+ .option('--origin <origin>', 'Validate page origin matches this value')
679
+ .option('--json', 'Output JSON for automation')
680
+ .addHelpText('after', '\nExamples:\n $ argus storage local remove app myKey\n')
681
+ .action(async (id, key, options) => {
682
+ await runStorageLocalRemove(id, key, options);
683
+ });
684
+ storageLocal
685
+ .command('list')
686
+ .argument('[id]', 'Watcher id')
687
+ .option('--origin <origin>', 'Validate page origin matches this value')
688
+ .option('--json', 'Output JSON for automation')
689
+ .addHelpText('after', '\nExamples:\n $ argus storage local list app\n $ argus storage local list app --json\n')
690
+ .action(async (id, options) => {
691
+ await runStorageLocalList(id, options);
692
+ });
693
+ storageLocal
694
+ .command('clear')
695
+ .argument('[id]', 'Watcher id')
696
+ .option('--origin <origin>', 'Validate page origin matches this value')
697
+ .option('--json', 'Output JSON for automation')
698
+ .addHelpText('after', '\nExamples:\n $ argus storage local clear app\n')
699
+ .action(async (id, options) => {
700
+ await runStorageLocalClear(id, options);
701
+ });
702
+ const extension = program.command('extension').alias('ext').description('Browser extension management');
703
+ extension
704
+ .command('setup <extensionId>')
705
+ .description('Install native messaging host for the browser extension')
706
+ .option('--json', 'Output JSON for automation')
707
+ .addHelpText('after', '\nTo get your extension ID:\n 1. Open chrome://extensions\n 2. Enable Developer mode\n 3. Load argus-extension as unpacked\n 4. Copy the ID from the extension card\n')
708
+ .action(async (extensionId, options) => {
709
+ await runExtensionSetup({ extensionId, ...options });
710
+ });
711
+ extension
712
+ .command('remove')
713
+ .description('Uninstall native messaging host')
714
+ .option('--json', 'Output JSON for automation')
715
+ .action(async (options) => {
716
+ await runExtensionRemove(options);
717
+ });
718
+ extension
719
+ .command('status')
720
+ .description('Check native messaging host configuration')
721
+ .option('--json', 'Output JSON for automation')
722
+ .action(async (options) => {
723
+ await runExtensionStatus(options);
724
+ });
725
+ extension
726
+ .command('info')
727
+ .description('Show native messaging host paths and configuration')
728
+ .option('--json', 'Output JSON for automation')
729
+ .action(async (options) => {
730
+ await runExtensionInfo(options);
731
+ });
732
+ async function main() {
733
+ const cwd = process.cwd();
734
+ const configPath = resolveArgusConfigPath({ cwd });
735
+ const configResult = configPath ? loadArgusConfig(configPath) : null;
736
+ const config = configResult?.config ?? {};
737
+ const configDir = configResult?.configDir ?? cwd;
738
+ const registry = new PluginRegistry();
739
+ try {
740
+ await registry.loadFromConfig(config, {
741
+ cwd,
742
+ configDir,
743
+ argusConfig: config,
744
+ });
745
+ registry.registerWith(program);
746
+ }
747
+ catch (error) {
748
+ console.error('Plugin loading failed:', error);
749
+ process.exit(1);
750
+ }
751
+ const cleanup = async () => {
752
+ await registry.cleanup();
753
+ };
754
+ process.on('exit', () => void cleanup());
755
+ process.on('SIGINT', () => void cleanup());
756
+ process.on('SIGTERM', () => void cleanup());
757
+ await program.parseAsync(process.argv);
758
+ }
759
+ main().catch((error) => {
760
+ console.error(error);
761
+ process.exit(1);
762
+ });
60
763
  //# sourceMappingURL=bin.js.map