@vforsh/argus 0.1.15 → 0.1.16

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 (54) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/argus.js +691 -285
  3. package/dist/cli/register/registerEval.d.ts.map +1 -1
  4. package/dist/cli/register/registerEval.js +6 -0
  5. package/dist/cli/register/registerEval.js.map +1 -1
  6. package/dist/cli/register/registerExtension.d.ts.map +1 -1
  7. package/dist/cli/register/registerExtension.js +24 -1
  8. package/dist/cli/register/registerExtension.js.map +1 -1
  9. package/dist/cli/register/registerWatcher.d.ts.map +1 -1
  10. package/dist/cli/register/registerWatcher.js +1 -0
  11. package/dist/cli/register/registerWatcher.js.map +1 -1
  12. package/dist/commands/eval.d.ts +2 -0
  13. package/dist/commands/eval.d.ts.map +1 -1
  14. package/dist/commands/eval.js.map +1 -1
  15. package/dist/commands/evalBundle.d.ts +17 -0
  16. package/dist/commands/evalBundle.d.ts.map +1 -0
  17. package/dist/commands/evalBundle.js +119 -0
  18. package/dist/commands/evalBundle.js.map +1 -0
  19. package/dist/commands/evalShared.d.ts +1 -0
  20. package/dist/commands/evalShared.d.ts.map +1 -1
  21. package/dist/commands/evalShared.js +21 -3
  22. package/dist/commands/evalShared.js.map +1 -1
  23. package/dist/commands/evalUntil.d.ts +2 -0
  24. package/dist/commands/evalUntil.d.ts.map +1 -1
  25. package/dist/commands/evalUntil.js.map +1 -1
  26. package/dist/commands/extension/attach.d.ts +12 -0
  27. package/dist/commands/extension/attach.d.ts.map +1 -0
  28. package/dist/commands/extension/attach.js +175 -0
  29. package/dist/commands/extension/attach.js.map +1 -0
  30. package/dist/commands/extension/info.d.ts.map +1 -1
  31. package/dist/commands/extension/info.js +13 -33
  32. package/dist/commands/extension/info.js.map +1 -1
  33. package/dist/commands/extension/nativeHost.d.ts +35 -5
  34. package/dist/commands/extension/nativeHost.d.ts.map +1 -1
  35. package/dist/commands/extension/nativeHost.js +95 -12
  36. package/dist/commands/extension/nativeHost.js.map +1 -1
  37. package/dist/commands/extension/remove.d.ts.map +1 -1
  38. package/dist/commands/extension/remove.js +18 -38
  39. package/dist/commands/extension/remove.js.map +1 -1
  40. package/dist/commands/extension/resolveExtensionWatcher.d.ts +1 -1
  41. package/dist/commands/extension/resolveExtensionWatcher.d.ts.map +1 -1
  42. package/dist/commands/extension/resolveExtensionWatcher.js +15 -29
  43. package/dist/commands/extension/resolveExtensionWatcher.js.map +1 -1
  44. package/dist/commands/extension/setup.d.ts.map +1 -1
  45. package/dist/commands/extension/setup.js +20 -35
  46. package/dist/commands/extension/setup.js.map +1 -1
  47. package/dist/commands/extension/status.d.ts.map +1 -1
  48. package/dist/commands/extension/status.js +12 -38
  49. package/dist/commands/extension/status.js.map +1 -1
  50. package/dist/commands/watcherNativeHost.d.ts +1 -0
  51. package/dist/commands/watcherNativeHost.d.ts.map +1 -1
  52. package/dist/commands/watcherNativeHost.js +8 -2
  53. package/dist/commands/watcherNativeHost.js.map +1 -1
  54. package/package.json +3 -2
package/dist/argus.js CHANGED
@@ -5,29 +5,15 @@ var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- function __accessProp(key) {
9
- return this[key];
10
- }
11
- var __toESMCache_node;
12
- var __toESMCache_esm;
13
8
  var __toESM = (mod, isNodeMode, target) => {
14
- var canCache = mod != null && typeof mod === "object";
15
- if (canCache) {
16
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
- var cached = cache.get(mod);
18
- if (cached)
19
- return cached;
20
- }
21
9
  target = mod != null ? __create(__getProtoOf(mod)) : {};
22
10
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
11
  for (let key of __getOwnPropNames(mod))
24
12
  if (!__hasOwnProp.call(to, key))
25
13
  __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
14
+ get: () => mod[key],
27
15
  enumerable: true
28
16
  });
29
- if (canCache)
30
- cache.set(mod, to);
31
17
  return to;
32
18
  };
33
19
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
@@ -2144,7 +2130,7 @@ var {
2144
2130
  // package.json
2145
2131
  var package_default = {
2146
2132
  name: "@vforsh/argus",
2147
- version: "0.1.15",
2133
+ version: "0.1.16",
2148
2134
  repository: {
2149
2135
  type: "git",
2150
2136
  url: "git+https://github.com/vforsh/argus.git",
@@ -2172,12 +2158,13 @@ var package_default = {
2172
2158
  dependencies: {
2173
2159
  "@vforsh/argus-plugin-api": "workspace:*",
2174
2160
  commander: "14.0.2",
2161
+ esbuild: "0.25.12",
2175
2162
  prettier: "3.7.4"
2176
2163
  },
2177
2164
  scripts: {
2178
2165
  clean: "node ../../scripts/clean-dist.mjs",
2179
2166
  build: "npm run clean && tsc -b && bun run bundle",
2180
- bundle: "bun build dist/bin.js --outfile dist/argus.js --target node",
2167
+ bundle: "bun build dist/bin.js --outfile dist/argus.js --target node --external esbuild",
2181
2168
  typecheck: "tsc -b --pretty false",
2182
2169
  "chrome:start": "bun src/bin.ts chrome start"
2183
2170
  }
@@ -11320,6 +11307,7 @@ var createVisibilityController = () => {
11320
11307
  // ../argus-watcher/dist/runtime/watcherSetup.js
11321
11308
  var normalizeWatcherSetup = (options, watcherId) => {
11322
11309
  const sourceMode = options.source ?? "cdp";
11310
+ const extensionRole = options.extensionRole ?? "tab";
11323
11311
  const host = options.host ?? "127.0.0.1";
11324
11312
  const port = options.port ?? 0;
11325
11313
  const chrome = options.chrome ?? { host: "127.0.0.1", port: 9222 };
@@ -11363,6 +11351,7 @@ var normalizeWatcherSetup = (options, watcherId) => {
11363
11351
  };
11364
11352
  return {
11365
11353
  sourceMode,
11354
+ extensionRole,
11366
11355
  host,
11367
11356
  port,
11368
11357
  chrome,
@@ -14068,6 +14057,57 @@ var createNativeMessaging = () => {
14068
14057
  };
14069
14058
  };
14070
14059
 
14060
+ // ../argus-watcher/dist/native-messaging/control-session-manager.js
14061
+ class ControlSessionManager {
14062
+ messaging;
14063
+ pendingTabsRequest = null;
14064
+ constructor(messaging) {
14065
+ this.messaging = messaging;
14066
+ this.messaging.onMessage((message) => {
14067
+ this.handleMessage(message);
14068
+ });
14069
+ }
14070
+ attachTabWatcher(tabId) {
14071
+ this.messaging.send({
14072
+ type: "attach_tab_watcher",
14073
+ tabId
14074
+ });
14075
+ }
14076
+ detachTabWatcher(tabId) {
14077
+ this.messaging.send({
14078
+ type: "detach_tab_watcher",
14079
+ tabId
14080
+ });
14081
+ }
14082
+ async listTabs(filter) {
14083
+ return new Promise((resolve2, reject) => {
14084
+ this.pendingTabsRequest = { resolve: resolve2, reject };
14085
+ this.messaging.send({
14086
+ type: "list_tabs",
14087
+ filter
14088
+ });
14089
+ setTimeout(() => {
14090
+ if (!this.pendingTabsRequest) {
14091
+ return;
14092
+ }
14093
+ this.pendingTabsRequest.reject(new Error("List tabs request timed out"));
14094
+ this.pendingTabsRequest = null;
14095
+ }, 5000);
14096
+ });
14097
+ }
14098
+ handleMessage(message) {
14099
+ switch (message.type) {
14100
+ case "list_tabs_response":
14101
+ this.handleListTabsResponse(message);
14102
+ return;
14103
+ }
14104
+ }
14105
+ handleListTabsResponse(message) {
14106
+ this.pendingTabsRequest?.resolve(message.tabs);
14107
+ this.pendingTabsRequest = null;
14108
+ }
14109
+ }
14110
+
14071
14111
  // ../argus-watcher/dist/native-messaging/session-manager.js
14072
14112
  var nextRequestId = 1;
14073
14113
 
@@ -14077,7 +14117,6 @@ class SessionManager {
14077
14117
  pendingRequests = new Map;
14078
14118
  eventHandlers = new Map;
14079
14119
  events;
14080
- pendingTabsRequest = null;
14081
14120
  constructor(messaging, events) {
14082
14121
  this.messaging = messaging;
14083
14122
  this.events = events;
@@ -14105,9 +14144,6 @@ class SessionManager {
14105
14144
  case "cookie_query_response":
14106
14145
  this.handleCookieQueryResponse(message);
14107
14146
  break;
14108
- case "list_tabs_response":
14109
- this.handleListTabsResponse(message);
14110
- break;
14111
14147
  case "target_selected":
14112
14148
  this.events.onTargetSelected(message.tabId, message.frameId ?? null);
14113
14149
  break;
@@ -14160,13 +14196,6 @@ class SessionManager {
14160
14196
  pending.resolve(result);
14161
14197
  }
14162
14198
  }
14163
- handleListTabsResponse(message) {
14164
- if (this.pendingTabsRequest) {
14165
- this.pendingTabsRequest.resolve(message.tabs);
14166
- this.pendingTabsRequest = null;
14167
- }
14168
- this.events.onTabsUpdated(message.tabs);
14169
- }
14170
14199
  createSession(message) {
14171
14200
  const { tabId, url, title, faviconUrl } = message;
14172
14201
  const enabledDomains = new Set;
@@ -14247,22 +14276,6 @@ class SessionManager {
14247
14276
  };
14248
14277
  this.messaging.send(message);
14249
14278
  }
14250
- async listTabs(filter) {
14251
- return new Promise((resolve2, reject) => {
14252
- this.pendingTabsRequest = { resolve: resolve2, reject };
14253
- const message = {
14254
- type: "list_tabs",
14255
- filter
14256
- };
14257
- this.messaging.send(message);
14258
- setTimeout(() => {
14259
- if (this.pendingTabsRequest) {
14260
- this.pendingTabsRequest.reject(new Error("List tabs request timed out"));
14261
- this.pendingTabsRequest = null;
14262
- }
14263
- }, 5000);
14264
- });
14265
- }
14266
14279
  getSession(tabId) {
14267
14280
  return this.sessions.get(tabId);
14268
14281
  }
@@ -14923,7 +14936,87 @@ var TARGET_RECOVERY_INTERVAL_MS = 500;
14923
14936
  var TARGET_RECOVERY_TIMEOUT_MS = 30000;
14924
14937
  var FRAME_COMMAND_READY_TIMEOUT_MS = 3000;
14925
14938
  var FRAME_COMMAND_READY_POLL_MS = 100;
14939
+ var createDetachedSession = (message) => ({
14940
+ isAttached: () => false,
14941
+ sendAndWait: async () => {
14942
+ const error = new Error(message);
14943
+ error.code = "cdp_not_attached";
14944
+ throw error;
14945
+ },
14946
+ onEvent: () => () => {}
14947
+ });
14948
+ var createControlExtensionSource = (options) => {
14949
+ const { events, watcherId, watcherHost, watcherPort } = options;
14950
+ const messaging = createNativeMessaging();
14951
+ const controlSession = new ControlSessionManager(messaging);
14952
+ const hostInfo = {
14953
+ watcherId: watcherId ?? "extension-control",
14954
+ watcherHost: watcherHost ?? "127.0.0.1",
14955
+ watcherPort: watcherPort ?? 0,
14956
+ watcherPid: process.pid
14957
+ };
14958
+ let stopping = false;
14959
+ messaging.start();
14960
+ messaging.send({ type: "host_ready" });
14961
+ sendHostInfo();
14962
+ events.onStatus({
14963
+ attached: false,
14964
+ target: null,
14965
+ reason: "control_watcher"
14966
+ });
14967
+ messaging.onDisconnect(() => {
14968
+ console.error("[ExtensionControlSource] Extension disconnected");
14969
+ if (!stopping) {
14970
+ events.onStatus({ attached: false, target: null, reason: "extension_disconnected" });
14971
+ events.onDetach?.("extension_disconnected");
14972
+ }
14973
+ });
14974
+ return {
14975
+ session: createDetachedSession("Extension control watcher does not expose a CDP tab session"),
14976
+ syncWatcherInfo: (info) => {
14977
+ hostInfo.watcherId = info.watcherId;
14978
+ hostInfo.watcherHost = info.watcherHost;
14979
+ hostInfo.watcherPort = info.watcherPort;
14980
+ hostInfo.watcherPid = info.watcherPid;
14981
+ sendHostInfo();
14982
+ },
14983
+ stop: async () => {
14984
+ stopping = true;
14985
+ messaging.stop();
14986
+ },
14987
+ listTargets: async () => [],
14988
+ listTabs: async (filter) => await controlSession.listTabs(filter),
14989
+ attachTarget: (targetId) => {
14990
+ controlSession.attachTabWatcher(parseControlTabTarget(targetId, "attach"));
14991
+ },
14992
+ detachTarget: (targetId) => {
14993
+ controlSession.detachTabWatcher(parseControlTabTarget(targetId, "detach"));
14994
+ }
14995
+ };
14996
+ function sendHostInfo() {
14997
+ messaging.send({
14998
+ type: "host_info",
14999
+ watcherId: hostInfo.watcherId,
15000
+ watcherHost: hostInfo.watcherHost,
15001
+ watcherPort: hostInfo.watcherPort,
15002
+ pid: hostInfo.watcherPid
15003
+ });
15004
+ }
15005
+ };
15006
+ var parseControlTabTarget = (targetId, action) => {
15007
+ const target = parseExtensionTargetId(targetId);
15008
+ if (!target.frameId) {
15009
+ return target.tabId;
15010
+ }
15011
+ if (action === "attach") {
15012
+ throw new Error(`Cannot attach iframe ${target.frameId} from extension-control before tab ${target.tabId} has a tab watcher`);
15013
+ }
15014
+ throw new Error(`Cannot detach iframe ${target.frameId} from extension-control. Detach tab ${target.tabId} instead.`);
15015
+ };
14926
15016
  var createExtensionSource = (options) => {
15017
+ if (options.role === "control") {
15018
+ return createControlExtensionSource(options);
15019
+ }
14927
15020
  const { events, ignoreMatcher, stripUrlPrefixes: stripUrlPrefixes2, watcherId, watcherHost, watcherPort } = options;
14928
15021
  const messaging = createNativeMessaging();
14929
15022
  const hostInfo = {
@@ -14962,7 +15055,6 @@ var createExtensionSource = (options) => {
14962
15055
  emitStatus(null, reason);
14963
15056
  events.onDetach?.(reason);
14964
15057
  },
14965
- onTabsUpdated: () => {},
14966
15058
  onTargetSelected: (tabId, frameId) => {
14967
15059
  const session = currentSession;
14968
15060
  if (!session || session.tabId !== tabId) {
@@ -15002,6 +15094,7 @@ var createExtensionSource = (options) => {
15002
15094
  });
15003
15095
  delegatingSessions.add(pageController);
15004
15096
  messaging.start();
15097
+ messaging.send({ type: "host_ready" });
15005
15098
  sendHostInfo();
15006
15099
  messaging.onDisconnect(() => {
15007
15100
  console.error("[ExtensionSource] Extension disconnected");
@@ -15034,13 +15127,26 @@ var createExtensionSource = (options) => {
15034
15127
  };
15035
15128
  const attachTarget = (targetId) => {
15036
15129
  const target = parseExtensionTargetId(targetId);
15037
- const session = requireOwnedSession(target.tabId);
15130
+ const session = currentSession;
15131
+ if (!session) {
15132
+ if (target.frameId) {
15133
+ throw new Error(`Cannot attach iframe ${target.frameId} before tab ${target.tabId} is attached`);
15134
+ }
15135
+ throw new Error(`Watcher ${hostInfo.watcherId} is a tab watcher. Use extension-control to attach tab ${target.tabId}.`);
15136
+ }
15137
+ if (session.tabId !== target.tabId) {
15138
+ throw new Error(`Watcher ${hostInfo.watcherId} is pinned to tab ${session.tabId}, not tab ${target.tabId}`);
15139
+ }
15038
15140
  requestTargetSelection(session, target.frameId);
15039
15141
  };
15040
15142
  const detachTarget = (targetId) => {
15041
15143
  const target = parseExtensionTargetId(targetId);
15042
- const session = requireOwnedSession(target.tabId);
15043
- sessionManager.detachTab(session.tabId);
15144
+ const session = currentSession;
15145
+ if (!session || session.tabId === target.tabId) {
15146
+ sessionManager.detachTab(target.tabId);
15147
+ return;
15148
+ }
15149
+ throw new Error(`Watcher ${hostInfo.watcherId} is pinned to tab ${session.tabId}, not tab ${target.tabId}`);
15044
15150
  };
15045
15151
  return {
15046
15152
  session: proxySession,
@@ -15084,9 +15190,6 @@ var createExtensionSource = (options) => {
15084
15190
  },
15085
15191
  stop,
15086
15192
  listTargets,
15087
- listTabs: async (filter) => {
15088
- return await sessionManager.listTabs(filter);
15089
- },
15090
15193
  attachTarget,
15091
15194
  detachTarget
15092
15195
  };
@@ -15099,16 +15202,6 @@ var createExtensionSource = (options) => {
15099
15202
  pid: hostInfo.watcherPid
15100
15203
  });
15101
15204
  }
15102
- function requireOwnedSession(tabId) {
15103
- const session = currentSession;
15104
- if (!session) {
15105
- throw createNotAttachedError();
15106
- }
15107
- if (session.tabId !== tabId) {
15108
- throw new Error(`Watcher ${hostInfo.watcherId} is pinned to tab ${session.tabId}, not tab ${tabId}`);
15109
- }
15110
- return session;
15111
- }
15112
15205
  function rebindDelegatingSessions() {
15113
15206
  for (const controller of delegatingSessions) {
15114
15207
  controller.rebind();
@@ -15360,7 +15453,7 @@ function delay4(ms) {
15360
15453
 
15361
15454
  // ../argus-watcher/dist/runtime/watcherServices.js
15362
15455
  var createWatcherRuntimeServices = (options, setup, callbacks) => {
15363
- const { sourceMode, chrome, artifactsBaseDir, netBuffer, realtimeNetBuffer, sessionHandle, ignoreMatcher, stripUrlPrefixes: stripUrlPrefixes2, watcherId, host, record } = setup;
15456
+ const { sourceMode, chrome, artifactsBaseDir, netBuffer, realtimeNetBuffer, sessionHandle, ignoreMatcher, stripUrlPrefixes: stripUrlPrefixes2, watcherId, host, record, extensionRole } = setup;
15364
15457
  if (sourceMode === "extension") {
15365
15458
  const sourceHandle2 = createExtensionSource({
15366
15459
  events: {
@@ -15378,6 +15471,7 @@ var createWatcherRuntimeServices = (options, setup, callbacks) => {
15378
15471
  watcherId,
15379
15472
  watcherHost: host,
15380
15473
  watcherPort: record.port,
15474
+ role: extensionRole,
15381
15475
  ignoreMatcher: ignoreMatcher ? (url) => ignoreMatcher.matches(url) : null,
15382
15476
  stripUrlPrefixes: stripUrlPrefixes2
15383
15477
  });
@@ -16968,21 +17062,27 @@ var checkWatcherReachable = async (watcher) => {
16968
17062
  // dist/commands/watcherNativeHost.js
16969
17063
  var runWatcherNativeHost = async (options) => {
16970
17064
  const watcherId = options.id?.trim() || "extension";
17065
+ const role = options.role?.trim() || "tab";
17066
+ if (role !== "tab" && role !== "control") {
17067
+ console.error(`Invalid native host role: ${role}. Expected "tab" or "control".`);
17068
+ process.exit(2);
17069
+ }
16971
17070
  let handle51;
16972
17071
  try {
16973
17072
  handle51 = await startWatcher({
16974
17073
  id: watcherId,
16975
17074
  source: "extension",
17075
+ extensionRole: role,
16976
17076
  host: "127.0.0.1",
16977
17077
  port: 0,
16978
17078
  net: { enabled: true },
16979
- pageIndicator: { enabled: true }
17079
+ pageIndicator: { enabled: role === "tab" }
16980
17080
  });
16981
17081
  } catch (error) {
16982
17082
  console.error(`Failed to start watcher: ${error instanceof Error ? error.message : error}`);
16983
17083
  process.exit(1);
16984
17084
  }
16985
- console.error(`[NativeHost] Watcher started: id=${handle51.watcher.id} port=${handle51.watcher.port}`);
17085
+ console.error(`[NativeHost] Watcher started: id=${handle51.watcher.id} role=${role} port=${handle51.watcher.port}`);
16986
17086
  const cleanup = async () => {
16987
17087
  try {
16988
17088
  await handle51.close();
@@ -17105,7 +17205,7 @@ Examples:
17105
17205
  `).action(async (options) => {
17106
17206
  await runWatcherPrune(options);
17107
17207
  });
17108
- watcher.command("native-host").description("[internal] Start as Native Messaging host for Chrome extension").option("--id <watcherId>", "Watcher id (default: extension)").option("--json", "Output JSON for automation").action(async (options) => {
17208
+ watcher.command("native-host").description("[internal] Start as Native Messaging host for Chrome extension").option("--id <watcherId>", "Watcher id (default: extension)").option("--role <role>", "Native host role: tab or control (default: tab)").option("--json", "Output JSON for automation").action(async (options) => {
17109
17209
  await runWatcherNativeHost(options);
17110
17210
  });
17111
17211
  }
@@ -18019,7 +18119,7 @@ var runNetClear = defineWatcherCommand({
18019
18119
 
18020
18120
  // dist/commands/netExport.js
18021
18121
  import fs9 from "node:fs/promises";
18022
- import path11 from "node:path";
18122
+ import path12 from "node:path";
18023
18123
 
18024
18124
  // dist/eval/evalClient.js
18025
18125
  var evalOnce = async (input) => {
@@ -18195,6 +18295,117 @@ var resolveDerivedNetFilters = (options) => {
18195
18295
 
18196
18296
  // dist/commands/evalShared.js
18197
18297
  import { readFile as readFile2 } from "node:fs/promises";
18298
+
18299
+ // dist/commands/evalBundle.js
18300
+ import { realpath } from "node:fs/promises";
18301
+ import path11 from "node:path";
18302
+ var esbuildModulePromise;
18303
+ var nodeModulesSegment = `${path11.sep}node_modules${path11.sep}`;
18304
+ var loadEsbuild = () => esbuildModulePromise ??= import("esbuild");
18305
+ var bundleEvalEntry = async (entryPath) => {
18306
+ let entryPoint;
18307
+ try {
18308
+ entryPoint = await realpath(path11.resolve(entryPath));
18309
+ } catch {
18310
+ return { ok: false, error: `Failed to read entry file: ${entryPath}` };
18311
+ }
18312
+ const bundleRoot = path11.dirname(entryPoint);
18313
+ try {
18314
+ const esbuild = await loadEsbuild();
18315
+ const result = await esbuild.build({
18316
+ absWorkingDir: bundleRoot,
18317
+ entryPoints: [entryPoint],
18318
+ bundle: true,
18319
+ write: false,
18320
+ splitting: false,
18321
+ format: "esm",
18322
+ platform: "browser",
18323
+ target: "es2022",
18324
+ sourcemap: false,
18325
+ logLevel: "silent",
18326
+ plugins: [createLocalFilesPlugin(bundleRoot)]
18327
+ });
18328
+ const output = result.outputFiles[0];
18329
+ if (output == null) {
18330
+ return { ok: false, error: "Bundle produced no output." };
18331
+ }
18332
+ const code = `${output.text.trimEnd()}
18333
+ //# sourceURL=argus-file://${entryPoint}
18334
+ `;
18335
+ return { ok: true, code };
18336
+ } catch (error) {
18337
+ return { ok: false, error: formatBundleFailure(error) };
18338
+ }
18339
+ };
18340
+ var createLocalFilesPlugin = (bundleRoot) => {
18341
+ const bundleRootPrefix = bundleRoot.endsWith(path11.sep) ? bundleRoot : `${bundleRoot}${path11.sep}`;
18342
+ return {
18343
+ name: "argus-eval-bundle",
18344
+ setup(build) {
18345
+ build.onResolve({ filter: /.*/ }, (args) => {
18346
+ if (args.kind === "entry-point") {
18347
+ return;
18348
+ }
18349
+ if (args.path.startsWith("node:")) {
18350
+ return bundleError(`Node built-in import ${JSON.stringify(args.path)} is not allowed in page eval.`);
18351
+ }
18352
+ if (!args.path.startsWith(".") && !path11.isAbsolute(args.path)) {
18353
+ return bundleError(`Import ${JSON.stringify(args.path)} is not allowed. Use relative local files under ${bundleRoot}.`);
18354
+ }
18355
+ return;
18356
+ });
18357
+ build.onLoad({ filter: /.*/ }, async (args) => {
18358
+ let resolvedPath;
18359
+ try {
18360
+ resolvedPath = await realpath(args.path);
18361
+ } catch {
18362
+ return bundleError(`Failed to read file: ${args.path}`);
18363
+ }
18364
+ if (resolvedPath !== bundleRoot && !resolvedPath.startsWith(bundleRootPrefix)) {
18365
+ return bundleError(`Import escapes the entry directory: ${args.path}`);
18366
+ }
18367
+ if (resolvedPath.includes(nodeModulesSegment)) {
18368
+ return bundleError(`Import from node_modules is not allowed: ${args.path}`);
18369
+ }
18370
+ return;
18371
+ });
18372
+ build.onEnd((result) => {
18373
+ if (result.errors.length > 0) {
18374
+ return;
18375
+ }
18376
+ const outputs = result.outputFiles ?? [];
18377
+ if (outputs.length !== 1) {
18378
+ return bundleError(`Bundle produced ${outputs.length} outputs; expected exactly one script.`);
18379
+ }
18380
+ if (/\bexport\b/.test(outputs[0]?.text ?? "")) {
18381
+ return bundleError("Bundled script contains top-level export statements, which page eval cannot run. Remove exports from the entry file (helpers may still export symbols for import).");
18382
+ }
18383
+ });
18384
+ }
18385
+ };
18386
+ };
18387
+ var bundleError = (text) => ({ errors: [{ text }] });
18388
+ var formatBundleFailure = (error) => {
18389
+ if (isEsbuildFailure(error)) {
18390
+ return error.errors.map(formatEsbuildMessage).join(`
18391
+ `);
18392
+ }
18393
+ return error instanceof Error ? error.message : String(error);
18394
+ };
18395
+ var isEsbuildFailure = (error) => error instanceof Error && ("errors" in error) && Array.isArray(error.errors) && error.errors.length > 0;
18396
+ var formatEsbuildMessage = (issue) => {
18397
+ const message = issue.text || "Bundle failed.";
18398
+ const { file, line, column } = issue.location ?? {};
18399
+ if (!file) {
18400
+ return message;
18401
+ }
18402
+ if (line == null || column == null) {
18403
+ return `${file}: ${message}`;
18404
+ }
18405
+ return `${file}:${line}:${column}: ${message}`;
18406
+ };
18407
+
18408
+ // dist/commands/evalShared.js
18198
18409
  var prepareEvalExpression = async (inline, options, output) => {
18199
18410
  const evalArgs = parseEvalArgs(options.arg);
18200
18411
  if (evalArgs.error) {
@@ -18223,7 +18434,8 @@ var prepareEvalExpression = async (inline, options, output) => {
18223
18434
  var resolveExpression = async (inline, options, output) => {
18224
18435
  const wantsStdin = options.stdin === true || inline === "-";
18225
18436
  const hasInline = inline != null && inline !== "-";
18226
- const hasFile = options.file != null;
18437
+ const filePath = options.file;
18438
+ const hasFile = filePath != null;
18227
18439
  if (hasInline && hasFile) {
18228
18440
  output.writeWarn("Cannot combine an inline expression with --file");
18229
18441
  return null;
@@ -18236,10 +18448,25 @@ var resolveExpression = async (inline, options, output) => {
18236
18448
  output.writeWarn("Cannot combine --file with stdin input");
18237
18449
  return null;
18238
18450
  }
18451
+ if (options.bundle && !hasFile) {
18452
+ output.writeWarn("--bundle requires --file");
18453
+ return null;
18454
+ }
18239
18455
  let expression;
18240
- if (hasFile) {
18456
+ if (hasFile && options.bundle) {
18457
+ const bundled = await bundleEvalEntry(filePath);
18458
+ if (!bundled.ok) {
18459
+ output.writeWarn(`Failed to bundle --file: ${bundled.error}`);
18460
+ return null;
18461
+ }
18462
+ if (!bundled.code.trim()) {
18463
+ output.writeWarn(`Bundled file is empty: ${options.file}`);
18464
+ return null;
18465
+ }
18466
+ expression = bundled.code;
18467
+ } else if (hasFile) {
18241
18468
  try {
18242
- const content = await readFile2(options.file ?? "", "utf8");
18469
+ const content = await readFile2(filePath, "utf8");
18243
18470
  if (!content.trim()) {
18244
18471
  output.writeWarn(`File is empty: ${options.file}`);
18245
18472
  return null;
@@ -18804,7 +19031,7 @@ var validateNetExportOptions = (options) => {
18804
19031
  return { value: { out, format: "har" } };
18805
19032
  };
18806
19033
  var writeHarFile = async (outPath, har) => {
18807
- await fs9.mkdir(path11.dirname(outPath), { recursive: true });
19034
+ await fs9.mkdir(path12.dirname(outPath), { recursive: true });
18808
19035
  await fs9.writeFile(outPath, `${JSON.stringify(har, null, 2)}
18809
19036
  `, "utf8");
18810
19037
  };
@@ -19151,10 +19378,10 @@ var fetchNetRequestBody = async (watcher, query, part, output, config = {}) => {
19151
19378
  bodyQuery.set("part", part);
19152
19379
  return await fetchNetRequestRoute(watcher, "/net/request/body", bodyQuery, NET_REQUEST_BODY_TIMEOUT_MS, output, config);
19153
19380
  };
19154
- var fetchNetRequestRoute = async (watcher, path12, query, timeoutMs, output, config = {}) => {
19381
+ var fetchNetRequestRoute = async (watcher, path13, query, timeoutMs, output, config = {}) => {
19155
19382
  try {
19156
19383
  const response = await fetchWatcherJson(watcher, {
19157
- path: path12,
19384
+ path: path13,
19158
19385
  query,
19159
19386
  timeoutMs,
19160
19387
  returnErrorResponse: true
@@ -20133,20 +20360,20 @@ var fetchAuthCookies = async (id, input, output) => {
20133
20360
  var normalizeCookieIdentityInput = (input, output) => {
20134
20361
  const name = input.name.trim();
20135
20362
  const domain = input.domain.trim();
20136
- const path12 = input.path.trim();
20363
+ const path13 = input.path.trim();
20137
20364
  if (!name) {
20138
20365
  return writeCookieInputError(output, "name is required");
20139
20366
  }
20140
20367
  if (!domain) {
20141
20368
  return writeCookieInputError(output, "domain is required");
20142
20369
  }
20143
- if (!path12) {
20370
+ if (!path13) {
20144
20371
  return writeCookieInputError(output, "path is required");
20145
20372
  }
20146
- if (!path12.startsWith("/")) {
20373
+ if (!path13.startsWith("/")) {
20147
20374
  return writeCookieInputError(output, 'path must start with "/"');
20148
20375
  }
20149
- return { name, domain, path: path12 };
20376
+ return { name, domain, path: path13 };
20150
20377
  };
20151
20378
  var parseCookieSetInput = (input, output) => {
20152
20379
  const identity = normalizeCookieIdentityInput(input, output);
@@ -20894,11 +21121,12 @@ ${script}})();
20894
21121
  // dist/cli/register/registerEval.js
20895
21122
  var collectValue = (value, previous = []) => [...previous, value];
20896
21123
  function registerEval(program2) {
20897
- const evalCmd = program2.command("eval").alias("js").argument("[id]", "Watcher id to query").argument("[expression]", "JS expression to evaluate (or use --file / --stdin)").description("Evaluate a JS expression in the connected page").option("--no-await", "Do not await promises").option("--timeout <ms>", "Eval timeout in milliseconds").option("--json", "Output JSON for automation").option("--no-return-by-value", "Disable returnByValue (use preview)").option("--no-fail-on-exception", "Do not exit with code 1 when the evaluation throws").option("--retry <n>", "Retry failed evaluations up to N times").option("-q, --silent", "Suppress success output; only emit output on error").option("--interval <ms|duration>", "Re-evaluate every interval (e.g. 500, 3s)").option("--count <n>", "Stop after N iterations (requires --interval)").option("--until <condition>", "Stop when local condition becomes truthy (requires --interval)").option("-f, --file <path>", "Read expression from a file").option("--stdin", "Read expression from stdin").option("--inject <path>", "Read setup code from a file and run it before the expression").option("--iframe <selector>", "Eval in iframe via postMessage (requires helper script)").option("--iframe-namespace <name>", "Message type prefix for iframe eval (default: argus)").option("--iframe-timeout <ms>", "Timeout for iframe postMessage response (default: 5000)").option("--arg <key=value>", "Argument exposed to eval scripts as args[key]", collectValue, []).addHelpText("after", `
21124
+ const evalCmd = program2.command("eval").alias("js").argument("[id]", "Watcher id to query").argument("[expression]", "JS expression to evaluate (or use --file / --stdin)").description("Evaluate a JS expression in the connected page").option("--no-await", "Do not await promises").option("--timeout <ms>", "Eval timeout in milliseconds").option("--json", "Output JSON for automation").option("--no-return-by-value", "Disable returnByValue (use preview)").option("--no-fail-on-exception", "Do not exit with code 1 when the evaluation throws").option("--retry <n>", "Retry failed evaluations up to N times").option("-q, --silent", "Suppress success output; only emit output on error").option("--interval <ms|duration>", "Re-evaluate every interval (e.g. 500, 3s)").option("--count <n>", "Stop after N iterations (requires --interval)").option("--until <condition>", "Stop when local condition becomes truthy (requires --interval)").option("-f, --file <path>", "Read expression from a file").option("--bundle", "Bundle local imports from --file into one script before eval").option("--stdin", "Read expression from stdin").option("--inject <path>", "Read setup code from a file and run it before the expression").option("--iframe <selector>", "Eval in iframe via postMessage (requires helper script)").option("--iframe-namespace <name>", "Message type prefix for iframe eval (default: argus)").option("--iframe-timeout <ms>", "Timeout for iframe postMessage response (default: 5000)").option("--arg <key=value>", "Argument exposed to eval scripts as args[key]", collectValue, []).addHelpText("after", `
20898
21125
  Examples:
20899
21126
  $ argus eval app "location.href"
20900
21127
  $ argus eval app "await fetch('/ping').then(r => r.status)"
20901
21128
  $ argus eval app --file ./script.js
21129
+ $ argus eval app --file ./script.js --bundle
20902
21130
  $ argus eval app "window.store.getState()" --inject ./debug-hooks.js
20903
21131
  $ argus eval app --file ./script.js --arg level=10 --arg mode=fast
20904
21132
  $ cat script.js | argus eval app --stdin
@@ -20923,6 +21151,7 @@ Examples:
20923
21151
  count: options.count,
20924
21152
  until: options.until,
20925
21153
  file: options.file,
21154
+ bundle: options.bundle,
20926
21155
  stdin: options.stdin,
20927
21156
  inject: options.inject,
20928
21157
  iframe: options.iframe,
@@ -20940,7 +21169,7 @@ Examples:
20940
21169
  `).action(async (options) => {
20941
21170
  await runIframeHelper(options);
20942
21171
  });
20943
- program2.command("eval-until").alias("wait").argument("[id]", "Watcher id to query").argument("[expression]", "JS expression to poll until truthy (or use --file / --stdin)").description("Poll a JS expression until it returns a truthy value").option("--no-await", "Do not await promises").option("--timeout <ms>", "Per-eval timeout in milliseconds").option("--json", "Output JSON for automation").option("--no-return-by-value", "Disable returnByValue (use preview)").option("--no-fail-on-exception", "Do not exit with code 1 when the evaluation throws").option("--retry <n>", "Retry failed evaluations up to N times").option("-q, --silent", "Suppress success output; only emit output on error").option("--interval <ms|duration>", "Polling interval (default: 250ms)").option("--count <n>", "Stop after N iterations").option("--total-timeout <duration>", "Max wall-clock time (e.g. 30s, 2m)").option("--verbose", "Print intermediate (falsy) results").option("-f, --file <path>", "Read expression from a file").option("--stdin", "Read expression from stdin").option("--inject <path>", "Read setup code from a file and run it before the expression").option("--iframe <selector>", "Eval in iframe via postMessage (requires helper script)").option("--iframe-namespace <name>", "Message type prefix for iframe eval (default: argus)").option("--iframe-timeout <ms>", "Timeout for iframe postMessage response (default: 5000)").option("--arg <key=value>", "Argument exposed to eval scripts as args[key]", collectValue, []).addHelpText("after", `
21172
+ program2.command("eval-until").alias("wait").argument("[id]", "Watcher id to query").argument("[expression]", "JS expression to poll until truthy (or use --file / --stdin)").description("Poll a JS expression until it returns a truthy value").option("--no-await", "Do not await promises").option("--timeout <ms>", "Per-eval timeout in milliseconds").option("--json", "Output JSON for automation").option("--no-return-by-value", "Disable returnByValue (use preview)").option("--no-fail-on-exception", "Do not exit with code 1 when the evaluation throws").option("--retry <n>", "Retry failed evaluations up to N times").option("-q, --silent", "Suppress success output; only emit output on error").option("--interval <ms|duration>", "Polling interval (default: 250ms)").option("--count <n>", "Stop after N iterations").option("--total-timeout <duration>", "Max wall-clock time (e.g. 30s, 2m)").option("--verbose", "Print intermediate (falsy) results").option("-f, --file <path>", "Read expression from a file").option("--bundle", "Bundle local imports from --file into one script before eval").option("--stdin", "Read expression from stdin").option("--inject <path>", "Read setup code from a file and run it before the expression").option("--iframe <selector>", "Eval in iframe via postMessage (requires helper script)").option("--iframe-namespace <name>", "Message type prefix for iframe eval (default: argus)").option("--iframe-timeout <ms>", "Timeout for iframe postMessage response (default: 5000)").option("--arg <key=value>", "Argument exposed to eval scripts as args[key]", collectValue, []).addHelpText("after", `
20944
21173
  Examples:
20945
21174
  $ argus eval-until app "document.querySelector('#loaded')"
20946
21175
  $ argus eval-until app "await window.appReadyPromise"
@@ -20949,6 +21178,7 @@ Examples:
20949
21178
  $ argus eval-until app "window.data" --verbose
20950
21179
  $ argus eval-until app "window.data" --count 20 --interval 1s
20951
21180
  $ argus eval-until app --file ./check.js --total-timeout 1m
21181
+ $ argus eval-until app --file ./check.js --bundle --total-timeout 1m
20952
21182
  $ argus wait app "window.appReady" --inject ./debug-hooks.js --total-timeout 20s
20953
21183
  $ argus wait app --file ./ready.js --arg level=10 --total-timeout 20s
20954
21184
  `).action(async (id, expression, options) => {
@@ -20965,6 +21195,7 @@ Examples:
20965
21195
  totalTimeout: options.totalTimeout,
20966
21196
  verbose: options.verbose,
20967
21197
  file: options.file,
21198
+ bundle: options.bundle,
20968
21199
  stdin: options.stdin,
20969
21200
  inject: options.inject,
20970
21201
  iframe: options.iframe,
@@ -22779,7 +23010,7 @@ function resolveActionOptions(options, command) {
22779
23010
 
22780
23011
  // dist/commands/configInit.js
22781
23012
  import fs12 from "node:fs/promises";
22782
- import path12 from "node:path";
23013
+ import path13 from "node:path";
22783
23014
  import { fileURLToPath, pathToFileURL } from "node:url";
22784
23015
  var DEFAULT_CONFIG_PATH = ".argus/config.json";
22785
23016
  var buildConfigTemplate = (schemaRef) => ({
@@ -22806,12 +23037,12 @@ var buildConfigTemplate = (schemaRef) => ({
22806
23037
  });
22807
23038
  var resolveConfigPath = (cwd, targetPath) => {
22808
23039
  if (!targetPath) {
22809
- return path12.resolve(cwd, DEFAULT_CONFIG_PATH);
23040
+ return path13.resolve(cwd, DEFAULT_CONFIG_PATH);
22810
23041
  }
22811
- return path12.isAbsolute(targetPath) ? targetPath : path12.resolve(cwd, targetPath);
23042
+ return path13.isAbsolute(targetPath) ? targetPath : path13.resolve(cwd, targetPath);
22812
23043
  };
22813
23044
  var ensureParentDir2 = async (filePath) => {
22814
- const dir = path12.dirname(filePath);
23045
+ const dir = path13.dirname(filePath);
22815
23046
  try {
22816
23047
  await fs12.mkdir(dir, { recursive: true });
22817
23048
  return true;
@@ -22890,33 +23121,33 @@ Examples:
22890
23121
  });
22891
23122
  }
22892
23123
 
22893
- // dist/commands/extension/setup.js
22894
- import fs14 from "node:fs";
22895
- import { execSync as execSync2 } from "node:child_process";
22896
-
22897
23124
  // dist/commands/extension/nativeHost.js
22898
23125
  import fs13 from "node:fs";
22899
- import path13 from "node:path";
23126
+ import path14 from "node:path";
22900
23127
  import os4 from "node:os";
22901
23128
  import { execSync } from "node:child_process";
22902
- var HOST_NAME = "com.vforsh.argus.bridge";
23129
+ var BRIDGE_HOST_NAME = "com.vforsh.argus.bridge";
23130
+ var CONTROL_HOST_NAME = "com.vforsh.argus.control";
23131
+ var CONTROL_WATCHER_ID = "extension-control";
23132
+ var HOST_NAMES = [BRIDGE_HOST_NAME, CONTROL_HOST_NAME];
22903
23133
  var getManifestDir = (platform) => {
22904
23134
  switch (platform) {
22905
23135
  case "darwin":
22906
- return path13.join(os4.homedir(), "Library/Application Support/Google/Chrome/NativeMessagingHosts");
23136
+ return path14.join(os4.homedir(), "Library/Application Support/Google/Chrome/NativeMessagingHosts");
22907
23137
  case "linux":
22908
- return path13.join(os4.homedir(), ".config/google-chrome/NativeMessagingHosts");
23138
+ return path14.join(os4.homedir(), ".config/google-chrome/NativeMessagingHosts");
22909
23139
  case "win32":
22910
- return path13.join(process.env.LOCALAPPDATA ?? os4.homedir(), "Google/Chrome/User Data/NativeMessagingHosts");
23140
+ return path14.join(process.env.LOCALAPPDATA ?? os4.homedir(), "Google/Chrome/User Data/NativeMessagingHosts");
22911
23141
  default:
22912
23142
  throw new Error(`Unsupported platform: ${platform}`);
22913
23143
  }
22914
23144
  };
22915
- var getManifestPath = (platform) => {
22916
- return path13.join(getManifestDir(platform), `${HOST_NAME}.json`);
23145
+ var getManifestPath = (platform, hostName = BRIDGE_HOST_NAME) => {
23146
+ return path14.join(getManifestDir(platform), `${hostName}.json`);
22917
23147
  };
22918
- var getWrapperPath = (platform) => {
22919
- return path13.join(getManifestDir(platform), "argus-native-host.sh");
23148
+ var getWrapperPath = (platform, hostName = BRIDGE_HOST_NAME) => {
23149
+ const filename = hostName === CONTROL_HOST_NAME ? "argus-native-control-host.sh" : "argus-native-host.sh";
23150
+ return path14.join(getManifestDir(platform), filename);
22920
23151
  };
22921
23152
  var getPlatform = () => {
22922
23153
  const platform = os4.platform();
@@ -22928,7 +23159,7 @@ var getPlatform = () => {
22928
23159
  var findArgusExecutable = () => {
22929
23160
  try {
22930
23161
  const npmGlobalPrefix = execSync("npm config get prefix", { encoding: "utf8" }).trim();
22931
- const globalBinPath = path13.join(npmGlobalPrefix, "bin", "argus");
23162
+ const globalBinPath = path14.join(npmGlobalPrefix, "bin", "argus");
22932
23163
  if (fs13.existsSync(globalBinPath)) {
22933
23164
  return globalBinPath;
22934
23165
  }
@@ -22944,25 +23175,71 @@ var findArgusExecutable = () => {
22944
23175
  var findNodePath = () => {
22945
23176
  return fs13.realpathSync(process.execPath);
22946
23177
  };
22947
- var createManifest = (extensionId, executablePath) => {
23178
+ var createManifest = (extensionId, executablePath, hostName = BRIDGE_HOST_NAME) => {
22948
23179
  return {
22949
- name: HOST_NAME,
22950
- description: "Argus Watcher Native Messaging Host",
23180
+ name: hostName,
23181
+ description: hostName === CONTROL_HOST_NAME ? "Argus Extension Control Native Messaging Host" : "Argus Watcher Native Messaging Host",
22951
23182
  path: executablePath,
22952
23183
  type: "stdio",
22953
23184
  allowed_origins: [`chrome-extension://${extensionId}/`]
22954
23185
  };
22955
23186
  };
22956
- var createWrapperScript = (platform, executablePath) => {
22957
- const wrapperDir = getManifestDir(platform);
22958
- const wrapperPath = path13.join(wrapperDir, "argus-native-host.sh");
23187
+ var createWrapperScript = (platform, executablePath, hostName = BRIDGE_HOST_NAME) => {
23188
+ const wrapperPath = getWrapperPath(platform, hostName);
22959
23189
  const nodePath = findNodePath();
23190
+ const args = hostName === CONTROL_HOST_NAME ? `watcher native-host --role control --id ${CONTROL_WATCHER_ID}` : "watcher native-host --role tab";
22960
23191
  const script = `#!/bin/bash
22961
- exec "${nodePath}" "${executablePath}" watcher native-host
23192
+ exec "${nodePath}" "${executablePath}" ${args}
22962
23193
  `;
22963
23194
  fs13.writeFileSync(wrapperPath, script, { mode: 493 });
22964
23195
  return wrapperPath;
22965
23196
  };
23197
+ var installNativeHosts = (platform, extensionId, executablePath) => {
23198
+ fs13.mkdirSync(getManifestDir(platform), { recursive: true });
23199
+ return HOST_NAMES.map((hostName) => {
23200
+ const wrapperPath = createWrapperScript(platform, executablePath, hostName);
23201
+ const manifestPath = getManifestPath(platform, hostName);
23202
+ const manifest = createManifest(extensionId, wrapperPath, hostName);
23203
+ fs13.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
23204
+ registerWindowsNativeHost(platform, hostName, manifestPath);
23205
+ return { hostName, manifestPath, wrapperPath };
23206
+ });
23207
+ };
23208
+ var inspectNativeHosts = (platform) => {
23209
+ return HOST_NAMES.map((hostName) => inspectNativeHost(platform, hostName));
23210
+ };
23211
+ var removeNativeHosts = (platform) => {
23212
+ return HOST_NAMES.map((hostName) => {
23213
+ const host = inspectNativeHost(platform, hostName);
23214
+ const manifestRemoved = removeFileIfExists(host.manifestPath);
23215
+ const wrapperRemoved = removeFileIfExists(host.wrapperPath);
23216
+ unregisterWindowsNativeHost(platform, hostName);
23217
+ return { ...host, manifestRemoved, wrapperRemoved };
23218
+ });
23219
+ };
23220
+ var inspectNativeHost = (platform, hostName) => {
23221
+ const manifestPath = getManifestPath(platform, hostName);
23222
+ const wrapperPath = getWrapperPath(platform, hostName);
23223
+ const manifestExists = fs13.existsSync(manifestPath);
23224
+ const wrapperExists = fs13.existsSync(wrapperPath);
23225
+ const wrapperExecutable = wrapperExists && isWrapperExecutable(wrapperPath);
23226
+ const manifest = manifestExists ? readManifest(manifestPath) : null;
23227
+ const manifestValid = Boolean(manifest && manifest.name === hostName && manifest.type === "stdio" && Array.isArray(manifest.allowed_origins));
23228
+ const extensionId = extractExtensionId(manifest?.allowed_origins?.[0]);
23229
+ return {
23230
+ hostName,
23231
+ manifestPath,
23232
+ wrapperPath,
23233
+ manifestExists,
23234
+ manifestValid,
23235
+ wrapperExists,
23236
+ wrapperExecutable,
23237
+ configured: manifestExists && manifestValid && wrapperExists && wrapperExecutable,
23238
+ installed: manifestExists && wrapperExists,
23239
+ extensionId,
23240
+ argusPath: manifest?.path ?? null
23241
+ };
23242
+ };
22966
23243
  var readManifest = (manifestPath) => {
22967
23244
  try {
22968
23245
  const content = fs13.readFileSync(manifestPath, "utf8");
@@ -22986,6 +23263,36 @@ var shortenPath = (filePath) => {
22986
23263
  }
22987
23264
  return filePath;
22988
23265
  };
23266
+ var extractExtensionId = (origin) => {
23267
+ if (!origin) {
23268
+ return null;
23269
+ }
23270
+ const match = origin.match(/^chrome-extension:\/\/([^/]+)\/$/);
23271
+ return match?.[1] ?? null;
23272
+ };
23273
+ var removeFileIfExists = (filePath) => {
23274
+ if (!fs13.existsSync(filePath)) {
23275
+ return false;
23276
+ }
23277
+ fs13.unlinkSync(filePath);
23278
+ return true;
23279
+ };
23280
+ var registerWindowsNativeHost = (platform, hostName, manifestPath) => {
23281
+ if (platform !== "win32") {
23282
+ return;
23283
+ }
23284
+ const regKey = `HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${hostName}`;
23285
+ execSync(`reg add "${regKey}" /ve /t REG_SZ /d "${manifestPath}" /f`, { stdio: "pipe" });
23286
+ };
23287
+ var unregisterWindowsNativeHost = (platform, hostName) => {
23288
+ if (platform !== "win32") {
23289
+ return;
23290
+ }
23291
+ const regKey = `HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${hostName}`;
23292
+ try {
23293
+ execSync(`reg delete "${regKey}" /f`, { stdio: "pipe" });
23294
+ } catch {}
23295
+ };
22989
23296
 
22990
23297
  // dist/commands/extension/setup.js
22991
23298
  var runExtensionSetup = async (options) => {
@@ -23003,10 +23310,6 @@ var runExtensionSetup = async (options) => {
23003
23310
  process.exitCode = 1;
23004
23311
  return;
23005
23312
  }
23006
- const manifestDir = getManifestDir(platform);
23007
- const manifestPath = getManifestPath(platform);
23008
- const wrapperPath = getWrapperPath(platform);
23009
- fs14.mkdirSync(manifestDir, { recursive: true });
23010
23313
  let executablePath;
23011
23314
  try {
23012
23315
  executablePath = findArgusExecutable();
@@ -23019,51 +23322,44 @@ var runExtensionSetup = async (options) => {
23019
23322
  process.exitCode = 1;
23020
23323
  return;
23021
23324
  }
23022
- createWrapperScript(platform, executablePath);
23023
- const manifest = createManifest(extensionId, wrapperPath);
23024
- fs14.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
23025
- if (platform === "win32") {
23026
- const regKey = `HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${HOST_NAME}`;
23027
- try {
23028
- execSync2(`reg add "${regKey}" /ve /t REG_SZ /d "${manifestPath}" /f`, { stdio: "pipe" });
23029
- } catch (error) {
23030
- if (options.json) {
23031
- output.writeJson({ success: false, error: `Failed to add registry key: ${error.message}` });
23032
- } else {
23033
- console.error("Failed to add registry key:", error);
23034
- }
23035
- process.exitCode = 1;
23036
- return;
23325
+ let installedHosts;
23326
+ try {
23327
+ installedHosts = installNativeHosts(platform, extensionId, executablePath);
23328
+ } catch (error) {
23329
+ if (options.json) {
23330
+ output.writeJson({ success: false, error: `Failed to install native hosts: ${error.message}` });
23331
+ } else {
23332
+ console.error("Failed to install native hosts:", error);
23037
23333
  }
23334
+ process.exitCode = 1;
23335
+ return;
23038
23336
  }
23039
23337
  if (options.json) {
23040
23338
  output.writeJson({
23041
23339
  success: true,
23042
- hostName: HOST_NAME,
23043
23340
  extensionId,
23044
- manifestPath,
23045
- wrapperPath,
23341
+ hosts: installedHosts,
23046
23342
  argusPath: executablePath
23047
23343
  });
23048
23344
  } else {
23049
23345
  output.writeHuman("");
23050
- output.writeHuman("Native messaging host installed");
23346
+ output.writeHuman("Native messaging hosts installed");
23051
23347
  output.writeHuman("");
23052
- output.writeHuman(` Host name: ${HOST_NAME}`);
23053
23348
  output.writeHuman(` Extension ID: ${extensionId}`);
23054
- output.writeHuman(` Manifest: ${shortenPath(manifestPath)}`);
23055
- output.writeHuman(` Wrapper: ${shortenPath(wrapperPath)}`);
23349
+ for (const host of installedHosts) {
23350
+ output.writeHuman(` Host name: ${host.hostName}`);
23351
+ output.writeHuman(` Manifest: ${shortenPath(host.manifestPath)}`);
23352
+ output.writeHuman(` Wrapper: ${shortenPath(host.wrapperPath)}`);
23353
+ }
23056
23354
  output.writeHuman("");
23057
23355
  output.writeHuman("Next steps:");
23058
23356
  output.writeHuman(" 1. Reload the extension in chrome://extensions");
23059
- output.writeHuman(" 2. Open the extension popup and attach a tab");
23357
+ output.writeHuman(" 2. Run `argus ext tabs` or open the extension popup and attach a tab");
23060
23358
  output.writeHuman("");
23061
23359
  }
23062
23360
  };
23063
23361
 
23064
23362
  // dist/commands/extension/remove.js
23065
- import fs15 from "node:fs";
23066
- import { execSync as execSync3 } from "node:child_process";
23067
23363
  var runExtensionRemove = async (options) => {
23068
23364
  const output = createOutput(options);
23069
23365
  let platform;
@@ -23078,55 +23374,42 @@ var runExtensionRemove = async (options) => {
23078
23374
  process.exitCode = 1;
23079
23375
  return;
23080
23376
  }
23081
- const manifestPath = getManifestPath(platform);
23082
- const wrapperPath = getWrapperPath(platform);
23083
- let manifestRemoved = false;
23084
- let wrapperRemoved = false;
23085
- if (fs15.existsSync(manifestPath)) {
23086
- fs15.unlinkSync(manifestPath);
23087
- manifestRemoved = true;
23088
- }
23089
- if (fs15.existsSync(wrapperPath)) {
23090
- fs15.unlinkSync(wrapperPath);
23091
- wrapperRemoved = true;
23092
- }
23093
- if (platform === "win32") {
23094
- const regKey = `HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${HOST_NAME}`;
23095
- try {
23096
- execSync3(`reg delete "${regKey}" /f`, { stdio: "pipe" });
23097
- } catch {}
23098
- }
23377
+ const removedHosts = removeNativeHosts(platform);
23378
+ const manifestRemoved = removedHosts.some((host) => host.manifestRemoved);
23379
+ const wrapperRemoved = removedHosts.some((host) => host.wrapperRemoved);
23099
23380
  if (options.json) {
23100
23381
  output.writeJson({
23101
23382
  success: true,
23102
23383
  manifestRemoved,
23103
23384
  wrapperRemoved,
23104
- manifestPath,
23105
- wrapperPath
23385
+ hosts: removedHosts
23106
23386
  });
23107
23387
  } else {
23108
23388
  output.writeHuman("");
23109
23389
  if (manifestRemoved || wrapperRemoved) {
23110
- output.writeHuman("Native messaging host removed");
23390
+ output.writeHuman("Native messaging hosts removed");
23111
23391
  output.writeHuman("");
23112
- if (manifestRemoved) {
23113
- output.writeHuman(` Removed manifest: ${shortenPath(manifestPath)}`);
23114
- }
23115
- if (wrapperRemoved) {
23116
- output.writeHuman(` Removed wrapper: ${shortenPath(wrapperPath)}`);
23392
+ for (const host of removedHosts) {
23393
+ if (host.manifestRemoved) {
23394
+ output.writeHuman(` Removed manifest: ${shortenPath(host.manifestPath)}`);
23395
+ }
23396
+ if (host.wrapperRemoved) {
23397
+ output.writeHuman(` Removed wrapper: ${shortenPath(host.wrapperPath)}`);
23398
+ }
23117
23399
  }
23118
23400
  } else {
23119
- output.writeHuman("Native messaging host was not installed");
23401
+ output.writeHuman("Native messaging hosts were not installed");
23120
23402
  output.writeHuman("");
23121
- output.writeHuman(` Manifest not found: ${shortenPath(manifestPath)}`);
23122
- output.writeHuman(` Wrapper not found: ${shortenPath(wrapperPath)}`);
23403
+ for (const host of removedHosts) {
23404
+ output.writeHuman(` Manifest not found: ${shortenPath(host.manifestPath)}`);
23405
+ output.writeHuman(` Wrapper not found: ${shortenPath(host.wrapperPath)}`);
23406
+ }
23123
23407
  }
23124
23408
  output.writeHuman("");
23125
23409
  }
23126
23410
  };
23127
23411
 
23128
23412
  // dist/commands/extension/status.js
23129
- import fs16 from "node:fs";
23130
23413
  var runExtensionStatus = async (options) => {
23131
23414
  const output = createOutput(options);
23132
23415
  let platform;
@@ -23141,40 +23424,14 @@ var runExtensionStatus = async (options) => {
23141
23424
  process.exitCode = 1;
23142
23425
  return;
23143
23426
  }
23144
- const manifestPath = getManifestPath(platform);
23145
- const wrapperPath = getWrapperPath(platform);
23146
- const manifestExists = fs16.existsSync(manifestPath);
23147
- const wrapperExists = fs16.existsSync(wrapperPath);
23148
- const wrapperExecutable = wrapperExists && isWrapperExecutable(wrapperPath);
23149
- let manifest = null;
23150
- let manifestValid = false;
23151
- let extensionId = null;
23152
- if (manifestExists) {
23153
- manifest = readManifest(manifestPath);
23154
- if (manifest && manifest.name === HOST_NAME && manifest.type === "stdio" && Array.isArray(manifest.allowed_origins)) {
23155
- manifestValid = true;
23156
- const origin = manifest.allowed_origins[0];
23157
- if (origin) {
23158
- const match = origin.match(/^chrome-extension:\/\/([^/]+)\/$/);
23159
- if (match) {
23160
- extensionId = match[1];
23161
- }
23162
- }
23163
- }
23164
- }
23165
- const configured = manifestExists && manifestValid && wrapperExists && wrapperExecutable;
23427
+ const hosts = inspectNativeHosts(platform);
23428
+ const configured = hosts.every((host) => host.configured);
23429
+ const extensionId = hosts.find((host) => host.extensionId)?.extensionId ?? null;
23166
23430
  if (options.json) {
23167
23431
  output.writeJson({
23168
23432
  configured,
23169
- hostName: HOST_NAME,
23170
- manifestPath,
23171
- wrapperPath,
23172
- manifestExists,
23173
- manifestValid,
23174
- wrapperExists,
23175
- wrapperExecutable,
23176
- extensionId,
23177
- argusPath: manifest?.path ?? null
23433
+ hosts,
23434
+ extensionId
23178
23435
  });
23179
23436
  if (!configured) {
23180
23437
  process.exitCode = 1;
@@ -23183,16 +23440,17 @@ var runExtensionStatus = async (options) => {
23183
23440
  }
23184
23441
  output.writeHuman("");
23185
23442
  if (configured) {
23186
- output.writeHuman("Native messaging host is configured");
23443
+ output.writeHuman("Native messaging hosts are configured");
23187
23444
  output.writeHuman("");
23188
- output.writeHuman(` Manifest: exists`);
23189
- output.writeHuman(` Wrapper: exists, executable`);
23190
23445
  output.writeHuman(` Extension ID: ${extensionId}`);
23191
23446
  } else {
23192
- output.writeHuman("Native messaging host not configured");
23447
+ output.writeHuman("Native messaging hosts not configured");
23193
23448
  output.writeHuman("");
23194
- output.writeHuman(` Manifest: ${manifestExists ? manifestValid ? "exists" : "exists, invalid" : "not found"}`);
23195
- output.writeHuman(` Wrapper: ${wrapperExists ? wrapperExecutable ? "exists, executable" : "exists, not executable" : "not found"}`);
23449
+ for (const host of hosts) {
23450
+ output.writeHuman(` ${host.hostName}`);
23451
+ output.writeHuman(` Manifest: ${host.manifestExists ? host.manifestValid ? "exists" : "exists, invalid" : "not found"}`);
23452
+ output.writeHuman(` Wrapper: ${host.wrapperExists ? host.wrapperExecutable ? "exists, executable" : "exists, not executable" : "not found"}`);
23453
+ }
23196
23454
  if (extensionId) {
23197
23455
  output.writeHuman(` Extension ID: ${extensionId}`);
23198
23456
  }
@@ -23204,7 +23462,6 @@ var runExtensionStatus = async (options) => {
23204
23462
  };
23205
23463
 
23206
23464
  // dist/commands/extension/info.js
23207
- import fs17 from "node:fs";
23208
23465
  var runExtensionInfo = async (options) => {
23209
23466
  const output = createOutput(options);
23210
23467
  let platform;
@@ -23219,51 +23476,32 @@ var runExtensionInfo = async (options) => {
23219
23476
  process.exitCode = 1;
23220
23477
  return;
23221
23478
  }
23222
- const manifestPath = getManifestPath(platform);
23223
- const wrapperPath = getWrapperPath(platform);
23224
- const manifestExists = fs17.existsSync(manifestPath);
23225
- const wrapperExists = fs17.existsSync(wrapperPath);
23226
- let extensionId = null;
23227
- let argusPath = null;
23228
- if (manifestExists) {
23229
- const manifest = readManifest(manifestPath);
23230
- if (manifest) {
23231
- argusPath = manifest.path;
23232
- const origin = manifest.allowed_origins?.[0];
23233
- if (origin) {
23234
- const match = origin.match(/^chrome-extension:\/\/([^/]+)\/$/);
23235
- if (match) {
23236
- extensionId = match[1];
23237
- }
23238
- }
23239
- }
23240
- }
23241
- const installed = manifestExists && wrapperExists;
23479
+ const hosts = inspectNativeHosts(platform);
23480
+ const installed = hosts.every((host) => host.installed);
23242
23481
  if (options.json) {
23243
23482
  output.writeJson({
23244
- hostName: HOST_NAME,
23245
23483
  platform,
23246
- manifestPath,
23247
- wrapperPath,
23248
23484
  installed,
23249
- extensionId,
23250
- argusPath
23485
+ hosts
23251
23486
  });
23252
23487
  return;
23253
23488
  }
23254
23489
  output.writeHuman("");
23255
23490
  output.writeHuman("Native Messaging Host Info");
23256
23491
  output.writeHuman("");
23257
- output.writeHuman(` Host name: ${HOST_NAME}`);
23258
23492
  output.writeHuman(` Platform: ${platform}`);
23259
- output.writeHuman(` Manifest path: ${shortenPath(manifestPath)}`);
23260
- output.writeHuman(` Wrapper path: ${shortenPath(wrapperPath)}`);
23493
+ for (const host of hosts) {
23494
+ output.writeHuman(` Host name: ${host.hostName}`);
23495
+ output.writeHuman(` Manifest path: ${shortenPath(host.manifestPath)}`);
23496
+ output.writeHuman(` Wrapper path: ${shortenPath(host.wrapperPath)}`);
23497
+ }
23261
23498
  output.writeHuman("");
23262
23499
  output.writeHuman("Current configuration:");
23263
23500
  output.writeHuman(` Installed: ${installed ? "yes" : "no"}`);
23264
- if (installed) {
23265
- output.writeHuman(` Extension ID: ${extensionId ?? "(unknown)"}`);
23266
- output.writeHuman(` Argus path: ${argusPath ?? "(unknown)"}`);
23501
+ for (const host of hosts.filter((entry) => entry.installed)) {
23502
+ output.writeHuman(` ${host.hostName}`);
23503
+ output.writeHuman(` Extension ID: ${host.extensionId ?? "(unknown)"}`);
23504
+ output.writeHuman(` Argus path: ${host.argusPath ?? "(unknown)"}`);
23267
23505
  }
23268
23506
  output.writeHuman("");
23269
23507
  };
@@ -23291,38 +23529,23 @@ var resolveExtensionWatcher = async (input) => {
23291
23529
  if (extensionWatchers.length === 0) {
23292
23530
  return {
23293
23531
  ok: false,
23294
- error: "No extension-backed watchers found. Attach a tab in the extension popup first.",
23532
+ error: "No extension-backed watchers found. Reload the extension after `argus extension setup`, or attach a tab in the extension popup.",
23295
23533
  exitCode: 2
23296
23534
  };
23297
23535
  }
23298
- const reachability = await Promise.all(extensionWatchers.map(async (watcher) => ({
23299
- watcher,
23300
- status: await checkWatcherStatus3(watcher)
23301
- })));
23302
- const attached = reachability.filter((entry) => entry.status.ok && entry.status.status.attached);
23303
- if (attached.length === 1) {
23304
- return { ok: true, watcher: attached[0].watcher, registry: registry2 };
23305
- }
23306
- const cwd = process.cwd();
23307
- const cwdMatches = extensionWatchers.filter((watcher) => watcher.cwd === cwd);
23308
- const attachedCwdMatches = attached.filter((entry) => entry.watcher.cwd === cwd);
23309
- if (attachedCwdMatches.length === 1) {
23310
- return { ok: true, watcher: attachedCwdMatches[0].watcher, registry: registry2 };
23311
- }
23312
- if (attached.length > 1) {
23313
- return { ok: false, error: "Watcher id required.", exitCode: 2, candidates: attached.map((entry) => entry.watcher) };
23314
- }
23315
- if (cwdMatches.length === 1) {
23316
- return { ok: true, watcher: cwdMatches[0], registry: registry2 };
23317
- }
23318
- if (cwdMatches.length > 1) {
23319
- return { ok: false, error: "Watcher id required.", exitCode: 2, candidates: cwdMatches };
23320
- }
23321
- const reachable = reachability.filter((entry) => entry.status.ok);
23322
- if (reachable.length === 1) {
23323
- return { ok: true, watcher: reachable[0].watcher, registry: registry2 };
23536
+ const controlWatcher = registry2.watchers[CONTROL_WATCHER_ID];
23537
+ if (controlWatcher?.source === "extension") {
23538
+ const status = await checkWatcherStatus3(controlWatcher);
23539
+ if (status.ok) {
23540
+ return { ok: true, watcher: controlWatcher, registry: registry2 };
23541
+ }
23324
23542
  }
23325
- return { ok: false, error: "Watcher id required.", exitCode: 2, candidates: extensionWatchers };
23543
+ return {
23544
+ ok: false,
23545
+ error: "extension-control watcher is unavailable. Reload the extension after `argus extension setup`.",
23546
+ exitCode: 2,
23547
+ candidates: extensionWatchers
23548
+ };
23326
23549
  };
23327
23550
  var getExtensionWatchers = (watchers) => watchers.filter((watcher) => watcher.source === "extension");
23328
23551
  var checkWatcherStatus3 = async (watcher) => {
@@ -23415,6 +23638,173 @@ var writeCommandError = (output, options, humanMessage, jsonPayload) => {
23415
23638
  };
23416
23639
  var formatError4 = (error) => error instanceof Error ? error.message : String(error);
23417
23640
 
23641
+ // dist/commands/extension/attach.js
23642
+ var runExtensionAttach = async (options) => {
23643
+ await runExtensionTabAction("attach", options);
23644
+ };
23645
+ var runExtensionDetach = async (options) => {
23646
+ await runExtensionTabAction("detach", options);
23647
+ };
23648
+ var runExtensionTabAction = async (action, options) => {
23649
+ const output = createOutput(options);
23650
+ const resolved = await resolveExtensionWatcher({});
23651
+ if (!resolved.ok) {
23652
+ writeResolveFailure2(output, options, resolved);
23653
+ return;
23654
+ }
23655
+ const selector = parseTabSelector(options);
23656
+ if (!selector.ok) {
23657
+ writeTabResolutionFailure(output, options, selector);
23658
+ return;
23659
+ }
23660
+ const tabs = await fetchTabs(resolved.watcher, selector.selector, output, options);
23661
+ if (!tabs) {
23662
+ return;
23663
+ }
23664
+ const tab = resolveTab(tabs, selector.selector);
23665
+ if (!tab.ok) {
23666
+ writeTabResolutionFailure(output, options, tab);
23667
+ return;
23668
+ }
23669
+ let response;
23670
+ try {
23671
+ response = await fetchWatcherJson(resolved.watcher, {
23672
+ path: action === "attach" ? "/attach" : "/detach",
23673
+ method: "POST",
23674
+ body: { tabId: tab.tab.tabId },
23675
+ timeoutMs: 5000,
23676
+ returnErrorResponse: true
23677
+ });
23678
+ } catch (error) {
23679
+ const message = error instanceof Error ? error.message : String(error);
23680
+ writeCommandError2(output, options, `${resolved.watcher.id}: failed to ${action} tab (${message})`, message);
23681
+ return;
23682
+ }
23683
+ if (!response.ok) {
23684
+ writeCommandError2(output, options, `Error: ${response.error.message}`, response);
23685
+ return;
23686
+ }
23687
+ if (options.json) {
23688
+ output.writeJson({
23689
+ ok: true,
23690
+ tab: tab.tab,
23691
+ watcherId: resolved.watcher.id
23692
+ });
23693
+ return;
23694
+ }
23695
+ const verb = action === "attach" ? "Attach" : "Detach";
23696
+ output.writeHuman(`${verb} request sent via ${resolved.watcher.id}`);
23697
+ output.writeHuman(` ${formatExtensionTabLine2(tab.tab)}`);
23698
+ };
23699
+ var fetchTabs = async (watcher, selector, output, options) => {
23700
+ const query = buildTabsQuery2(selector);
23701
+ try {
23702
+ const response = await fetchWatcherJson(watcher, {
23703
+ path: "/tabs",
23704
+ query,
23705
+ timeoutMs: 5000,
23706
+ returnErrorResponse: true
23707
+ });
23708
+ if (!response.ok) {
23709
+ writeCommandError2(output, options, `Error: ${response.error.message}`, response);
23710
+ return null;
23711
+ }
23712
+ return response.tabs;
23713
+ } catch (error) {
23714
+ const message = error instanceof Error ? error.message : String(error);
23715
+ writeCommandError2(output, options, `${watcher.id}: failed to list extension tabs (${message})`, message);
23716
+ return null;
23717
+ }
23718
+ };
23719
+ var parseTabSelector = (options) => {
23720
+ const tabId = parseTabId(options.tab);
23721
+ const url = options.url?.trim();
23722
+ const title = options.title?.trim();
23723
+ if (tabId !== null && (url || title)) {
23724
+ return { ok: false, reason: "Use --tab by itself, or resolve by --url/--title.", exitCode: 2 };
23725
+ }
23726
+ if (tabId === null && !url && !title) {
23727
+ return { ok: false, reason: "Specify --tab <tabId>, --url <substring>, or --title <substring>.", exitCode: 2 };
23728
+ }
23729
+ if (options.tab != null && tabId === null) {
23730
+ return { ok: false, reason: `Invalid --tab value: ${options.tab}`, exitCode: 2 };
23731
+ }
23732
+ if (tabId !== null) {
23733
+ return { ok: true, selector: { kind: "tab", tabId } };
23734
+ }
23735
+ return { ok: true, selector: { kind: "query", url, title } };
23736
+ };
23737
+ var resolveTab = (tabs, selector) => {
23738
+ const matches = selector.kind === "tab" ? tabs.filter((tab) => tab.tabId === selector.tabId) : tabs;
23739
+ if (matches.length === 0) {
23740
+ return { ok: false, reason: "No extension tab matched.", exitCode: 2 };
23741
+ }
23742
+ if (matches.length > 1) {
23743
+ return { ok: false, reason: "Multiple extension tabs matched. Use --tab to pick one.", exitCode: 2, matches };
23744
+ }
23745
+ return { ok: true, tab: matches[0] };
23746
+ };
23747
+ var parseTabId = (value) => {
23748
+ if (value === undefined) {
23749
+ return null;
23750
+ }
23751
+ const tabId = typeof value === "number" ? value : Number(value);
23752
+ return Number.isInteger(tabId) ? tabId : null;
23753
+ };
23754
+ var buildTabsQuery2 = (selector) => {
23755
+ const query = new URLSearchParams;
23756
+ if (selector.kind === "tab") {
23757
+ return query;
23758
+ }
23759
+ if (selector.url) {
23760
+ query.set("url", selector.url);
23761
+ }
23762
+ if (selector.title) {
23763
+ query.set("title", selector.title);
23764
+ }
23765
+ return query;
23766
+ };
23767
+ var writeTabResolutionFailure = (output, options, result) => {
23768
+ const matches = "matches" in result ? result.matches : undefined;
23769
+ if (options.json) {
23770
+ output.writeJson({ ok: false, error: result.reason, matches: matches ?? [] });
23771
+ } else {
23772
+ output.writeWarn(result.reason);
23773
+ if (matches) {
23774
+ for (const tab of matches) {
23775
+ output.writeWarn(` ${formatExtensionTabLine2(tab)}`);
23776
+ }
23777
+ }
23778
+ }
23779
+ process.exitCode = result.exitCode;
23780
+ };
23781
+ var writeResolveFailure2 = (output, options, resolved) => {
23782
+ if (options.json) {
23783
+ output.writeJson({ ok: false, error: resolved.error, candidates: resolved.candidates?.map((watcher) => watcher.id) ?? [] });
23784
+ } else {
23785
+ output.writeWarn(resolved.error);
23786
+ if (resolved.candidates && resolved.candidates.length > 0) {
23787
+ for (const watcher of resolved.candidates) {
23788
+ output.writeWarn(formatWatcherLine(watcher));
23789
+ }
23790
+ }
23791
+ }
23792
+ process.exitCode = resolved.exitCode;
23793
+ };
23794
+ var writeCommandError2 = (output, options, humanMessage, jsonPayload) => {
23795
+ if (options.json) {
23796
+ output.writeJson(typeof jsonPayload === "string" ? { ok: false, error: jsonPayload } : jsonPayload);
23797
+ } else {
23798
+ output.writeWarn(humanMessage);
23799
+ }
23800
+ process.exitCode = 1;
23801
+ };
23802
+ var formatExtensionTabLine2 = (tab) => {
23803
+ const state2 = tab.attached ? "attached" : "available";
23804
+ const label = tab.title || "(untitled)";
23805
+ return `${tab.tabId} [${state2}] ${label} - ${tab.url}`;
23806
+ };
23807
+
23418
23808
  // dist/cli/register/registerExtension.js
23419
23809
  function registerExtension(program2) {
23420
23810
  const extension = program2.command("extension").alias("ext").description("Browser extension management");
@@ -23436,7 +23826,7 @@ To get your extension ID:
23436
23826
  extension.command("info").description("Show native messaging host paths and configuration").option("--json", "Output JSON for automation").action(async (options) => {
23437
23827
  await runExtensionInfo(options);
23438
23828
  });
23439
- extension.command("tabs").description("List browser tabs visible to the extension transport").option("--id <watcherId>", "Extension-backed watcher id to use as the transport").option("--url <substring>", "Filter tabs by URL substring").option("--title <substring>", "Filter tabs by title substring").option("--json", "Output JSON for automation").addHelpText("after", `
23829
+ extension.command("tabs").description("List browser tabs visible to the extension transport").option("--id <watcherId>", "Extension-backed watcher id to use as the transport (default: extension-control)").option("--url <substring>", "Filter tabs by URL substring").option("--title <substring>", "Filter tabs by title substring").option("--json", "Output JSON for automation").addHelpText("after", `
23440
23830
  Examples:
23441
23831
  $ argus ext tabs
23442
23832
  $ argus ext tabs --url localhost
@@ -23445,6 +23835,22 @@ Examples:
23445
23835
  `).action(async (options) => {
23446
23836
  await runExtensionTabs(options);
23447
23837
  });
23838
+ extension.command("attach").description("Ask the extension control watcher to attach a browser tab").option("--tab <tabId>", "Browser tab id to attach").option("--url <substring>", "Resolve tab by URL substring").option("--title <substring>", "Resolve tab by title substring").option("--json", "Output JSON for automation").addHelpText("after", `
23839
+ Examples:
23840
+ $ argus ext attach --tab 123
23841
+ $ argus ext attach --url localhost
23842
+ $ argus ext attach --title Docs --json
23843
+ `).action(async (options) => {
23844
+ await runExtensionAttach(options);
23845
+ });
23846
+ extension.command("detach").description("Ask the extension control watcher to detach a browser tab").option("--tab <tabId>", "Browser tab id to detach").option("--url <substring>", "Resolve tab by URL substring").option("--title <substring>", "Resolve tab by title substring").option("--json", "Output JSON for automation").addHelpText("after", `
23847
+ Examples:
23848
+ $ argus ext detach --tab 123
23849
+ $ argus ext detach --url localhost
23850
+ $ argus ext detach --title Docs --json
23851
+ `).action(async (options) => {
23852
+ await runExtensionDetach(options);
23853
+ });
23448
23854
  }
23449
23855
 
23450
23856
  // dist/output/code.js
@@ -33707,7 +34113,7 @@ function hr2(a2, t, e) {
33707
34113
  }
33708
34114
  return t;
33709
34115
  }
33710
- var fs18 = hr2;
34116
+ var fs14 = hr2;
33711
34117
  function cr2(a2, t) {
33712
34118
  return t === false ? false : a2.charAt(t) === "/" && a2.charAt(t + 1) === "/" ? ls(a2, t) : t;
33713
34119
  }
@@ -33715,7 +34121,7 @@ var ds = cr2;
33715
34121
  function lr2(a2, t) {
33716
34122
  let e = null, s = t;
33717
34123
  for (;s !== e; )
33718
- e = s, s = cs(a2, s), s = ps(a2, s), s = ds(a2, s), s = fs18(a2, s);
34124
+ e = s, s = cs(a2, s), s = ps(a2, s), s = ds(a2, s), s = fs14(a2, s);
33719
34125
  return s;
33720
34126
  }
33721
34127
  var ms = lr2;
@@ -35715,13 +36121,13 @@ function kc({ node: e, parent: t }) {
35715
36121
  let r = /^[fx]?(?:describe|it|test)$/u;
35716
36122
  return t.type === "TaggedTemplateExpression" && t.quasi === e && t.tag.type === "MemberExpression" && t.tag.property.type === "Identifier" && t.tag.property.name === "each" && (t.tag.object.type === "Identifier" && r.test(t.tag.object.name) || t.tag.object.type === "MemberExpression" && t.tag.object.property.type === "Identifier" && (t.tag.object.property.name === "only" || t.tag.object.property.name === "skip") && t.tag.object.object.type === "Identifier" && r.test(t.tag.object.object.name));
35717
36123
  }
35718
- var fs19 = [(e, t) => e.type === "ObjectExpression" && t === "properties", (e, t) => e.type === "CallExpression" && e.callee.type === "Identifier" && e.callee.name === "Component" && t === "arguments", (e, t) => e.type === "Decorator" && t === "expression"];
36124
+ var fs15 = [(e, t) => e.type === "ObjectExpression" && t === "properties", (e, t) => e.type === "CallExpression" && e.callee.type === "Identifier" && e.callee.name === "Component" && t === "arguments", (e, t) => e.type === "Decorator" && t === "expression"];
35719
36125
  function oo2(e) {
35720
36126
  let t = (n) => n.type === "TemplateLiteral", r = (n, s) => Oe3(n) && !n.computed && n.key.type === "Identifier" && n.key.name === "styles" && s === "value";
35721
- return e.match(t, (n, s) => q3(n) && s === "elements", r, ...fs19) || e.match(t, r, ...fs19);
36127
+ return e.match(t, (n, s) => q3(n) && s === "elements", r, ...fs15) || e.match(t, r, ...fs15);
35722
36128
  }
35723
36129
  function ys2(e) {
35724
- return e.match((t) => t.type === "TemplateLiteral", (t, r) => Oe3(t) && !t.computed && t.key.type === "Identifier" && t.key.name === "template" && r === "value", ...fs19);
36130
+ return e.match((t) => t.type === "TemplateLiteral", (t, r) => Oe3(t) && !t.computed && t.key.type === "Identifier" && t.key.name === "template" && r === "value", ...fs15);
35725
36131
  }
35726
36132
  function Ds2(e, t) {
35727
36133
  return T3(e, x2.Block | x2.Leading, ({ value: r }) => r === ` ${t} `);
@@ -39628,7 +40034,7 @@ var ft4 = w4((gx, Mi3) => {
39628
40034
  ct4.default = ct4;
39629
40035
  });
39630
40036
  var fe3 = w4((wx, zi3) => {
39631
- var Bi3 = Me4(), Ui3 = ft4(), Fc2 = ut4(), { isClean: Fi3, my: $i3 } = Ht4(), fs20, Wi3, Gi3, ps3;
40037
+ var Bi3 = Me4(), Ui3 = ft4(), Fc2 = ut4(), { isClean: Fi3, my: $i3 } = Ht4(), fs16, Wi3, Gi3, ps3;
39632
40038
  function Yi3(t) {
39633
40039
  return t.map((e) => (e.nodes && (e.nodes = Yi3(e.nodes)), delete e.source, e));
39634
40040
  }
@@ -39727,7 +40133,7 @@ var fe3 = w4((wx, zi3) => {
39727
40133
  } else if (e.selector || e.selectors)
39728
40134
  e = [new ps3(e)];
39729
40135
  else if (e.name)
39730
- e = [new fs20(e)];
40136
+ e = [new fs16(e)];
39731
40137
  else if (e.text)
39732
40138
  e = [new Bi3(e)];
39733
40139
  else
@@ -39829,7 +40235,7 @@ var fe3 = w4((wx, zi3) => {
39829
40235
  ps3 = t;
39830
40236
  };
39831
40237
  z3.registerAtRule = (t) => {
39832
- fs20 = t;
40238
+ fs16 = t;
39833
40239
  };
39834
40240
  z3.registerRoot = (t) => {
39835
40241
  Gi3 = t;
@@ -39837,7 +40243,7 @@ var fe3 = w4((wx, zi3) => {
39837
40243
  zi3.exports = z3;
39838
40244
  z3.default = z3;
39839
40245
  z3.rebuild = (t) => {
39840
- t.type === "atrule" ? Object.setPrototypeOf(t, fs20.prototype) : t.type === "rule" ? Object.setPrototypeOf(t, ps3.prototype) : t.type === "decl" ? Object.setPrototypeOf(t, Ui3.prototype) : t.type === "comment" ? Object.setPrototypeOf(t, Bi3.prototype) : t.type === "root" && Object.setPrototypeOf(t, Gi3.prototype), t[$i3] = true, t.nodes && t.nodes.forEach((e) => {
40246
+ t.type === "atrule" ? Object.setPrototypeOf(t, fs16.prototype) : t.type === "rule" ? Object.setPrototypeOf(t, ps3.prototype) : t.type === "decl" ? Object.setPrototypeOf(t, Ui3.prototype) : t.type === "comment" ? Object.setPrototypeOf(t, Bi3.prototype) : t.type === "root" && Object.setPrototypeOf(t, Gi3.prototype), t[$i3] = true, t.nodes && t.nodes.forEach((e) => {
39841
40247
  z3.rebuild(e);
39842
40248
  });
39843
40249
  };
@@ -45242,9 +45648,9 @@ async function resolveEditSource(id, url, options, output) {
45242
45648
  }
45243
45649
  return readStdinSource(output);
45244
45650
  }
45245
- async function readFileSource(path14, output) {
45651
+ async function readFileSource(path15, output) {
45246
45652
  try {
45247
- return { text: await readFile6(path14, "utf8") };
45653
+ return { text: await readFile6(path15, "utf8") };
45248
45654
  } catch (error) {
45249
45655
  output.writeWarn(`Failed to read file: ${error instanceof Error ? error.message : error}`);
45250
45656
  process.exitCode = 1;
@@ -45453,7 +45859,7 @@ var runLocateLabel = async (id, label, options) => {
45453
45859
  exact: options.exact
45454
45860
  }, options);
45455
45861
  };
45456
- var runLocateRequest = async (id, path14, body, options) => {
45862
+ var runLocateRequest = async (id, path15, body, options) => {
45457
45863
  const output = createOutput(options);
45458
45864
  const action = normalizeLocateAction(options.action, output);
45459
45865
  if (action == null && options.action) {
@@ -45471,7 +45877,7 @@ var runLocateRequest = async (id, path14, body, options) => {
45471
45877
  }
45472
45878
  const result = await requestWatcherCommandAction({
45473
45879
  id,
45474
- path: path14,
45880
+ path: path15,
45475
45881
  method: "POST",
45476
45882
  body,
45477
45883
  timeoutMs: 30000
@@ -45559,7 +45965,7 @@ Examples:
45559
45965
  }
45560
45966
 
45561
45967
  // dist/cli/plugins/registerPlugins.js
45562
- import path14 from "node:path";
45968
+ import path15 from "node:path";
45563
45969
  import { existsSync as existsSync3 } from "node:fs";
45564
45970
  import { pathToFileURL as pathToFileURL2 } from "node:url";
45565
45971
 
@@ -45579,9 +45985,9 @@ var resolvePluginAlias = (spec, aliases) => {
45579
45985
 
45580
45986
  // dist/cli/plugins/pluginHost.js
45581
45987
  var DEFAULT_TIMEOUT_MS2 = 30000;
45582
- var postWatcherJson = (id, path14, body, timeoutMs = DEFAULT_TIMEOUT_MS2) => requestWatcherJson({
45988
+ var postWatcherJson = (id, path15, body, timeoutMs = DEFAULT_TIMEOUT_MS2) => requestWatcherJson({
45583
45989
  id,
45584
- path: path14,
45990
+ path: path15,
45585
45991
  method: "POST",
45586
45992
  body,
45587
45993
  timeoutMs
@@ -45663,7 +46069,7 @@ var resolvePluginModuleUrl = (specifier, baseDirs) => {
45663
46069
  if (isPathLikeSpecifier(trimmed)) {
45664
46070
  for (const baseDir of baseDirs) {
45665
46071
  try {
45666
- const resolvedPath = path14.resolve(baseDir, trimmed);
46072
+ const resolvedPath = path15.resolve(baseDir, trimmed);
45667
46073
  if (existsSync3(resolvedPath)) {
45668
46074
  return { ok: true, url: pathToFileURL2(resolvedPath).href };
45669
46075
  }
@@ -45685,7 +46091,7 @@ ${errors.map((e) => `- ${e}`).join(`
45685
46091
  }
45686
46092
  for (const baseDir of baseDirs) {
45687
46093
  try {
45688
- const baseUrl = pathToFileURL2(path14.join(baseDir, "noop.js")).href;
46094
+ const baseUrl = pathToFileURL2(path15.join(baseDir, "noop.js")).href;
45689
46095
  return { ok: true, url: resolveWithImportMeta(trimmed, baseUrl) };
45690
46096
  } catch (error) {
45691
46097
  const msg = error instanceof Error ? error.message : String(error);
@@ -45836,14 +46242,14 @@ var runPluginList = (options) => {
45836
46242
  };
45837
46243
 
45838
46244
  // dist/commands/pluginConfig.js
45839
- import fs20 from "node:fs/promises";
46245
+ import fs16 from "node:fs/promises";
45840
46246
  import { existsSync as existsSync4 } from "node:fs";
45841
- import path15 from "node:path";
46247
+ import path16 from "node:path";
45842
46248
  var DEFAULT_CONFIG_PATH2 = ".argus/config.json";
45843
46249
  var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
45844
46250
  var resolveTargetConfigPath = (cwd, cliPath) => {
45845
46251
  if (cliPath) {
45846
- const resolved = path15.isAbsolute(cliPath) ? cliPath : path15.resolve(cwd, cliPath);
46252
+ const resolved = path16.isAbsolute(cliPath) ? cliPath : path16.resolve(cwd, cliPath);
45847
46253
  return { path: resolved, exists: existsSync4(resolved) };
45848
46254
  }
45849
46255
  const discovered = resolveArgusConfigPath({ cwd });
@@ -45851,14 +46257,14 @@ var resolveTargetConfigPath = (cwd, cliPath) => {
45851
46257
  return { path: discovered, exists: true };
45852
46258
  if (process.exitCode)
45853
46259
  return null;
45854
- return { path: path15.resolve(cwd, DEFAULT_CONFIG_PATH2), exists: false };
46260
+ return { path: path16.resolve(cwd, DEFAULT_CONFIG_PATH2), exists: false };
45855
46261
  };
45856
46262
  var readConfigObject = async (configPath, exists) => {
45857
46263
  if (!exists)
45858
46264
  return {};
45859
46265
  let raw;
45860
46266
  try {
45861
- raw = await fs20.readFile(configPath, "utf8");
46267
+ raw = await fs16.readFile(configPath, "utf8");
45862
46268
  } catch (error) {
45863
46269
  console.error(`Failed to read Argus config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
45864
46270
  process.exitCode = 2;
@@ -45910,8 +46316,8 @@ var getPluginAliases = (config, configPath) => {
45910
46316
  };
45911
46317
  var writeConfigObject = async (configPath, config) => {
45912
46318
  try {
45913
- await fs20.mkdir(path15.dirname(configPath), { recursive: true });
45914
- await fs20.writeFile(configPath, `${JSON.stringify(config, null, "\t")}
46319
+ await fs16.mkdir(path16.dirname(configPath), { recursive: true });
46320
+ await fs16.writeFile(configPath, `${JSON.stringify(config, null, "\t")}
45915
46321
  `, "utf8");
45916
46322
  return true;
45917
46323
  } catch (error) {