@vforsh/argus 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/argus.js CHANGED
@@ -2130,7 +2130,7 @@ var {
2130
2130
  // package.json
2131
2131
  var package_default = {
2132
2132
  name: "@vforsh/argus",
2133
- version: "0.2.0",
2133
+ version: "0.3.1",
2134
2134
  repository: {
2135
2135
  type: "git",
2136
2136
  url: "git+https://github.com/vforsh/argus.git",
@@ -2156,9 +2156,10 @@ var package_default = {
2156
2156
  access: "public"
2157
2157
  },
2158
2158
  dependencies: {
2159
- "@vforsh/argus-core": "^0.2.0",
2160
- "@vforsh/argus-plugin-api": "^0.2.0",
2161
- "@vforsh/argus-watcher": "^0.2.0",
2159
+ "@vforsh/argus-client": "^0.3.0",
2160
+ "@vforsh/argus-core": "^0.3.0",
2161
+ "@vforsh/argus-plugin-api": "^0.3.0",
2162
+ "@vforsh/argus-watcher": "^0.3.1",
2162
2163
  commander: "14.0.2",
2163
2164
  esbuild: "0.25.12",
2164
2165
  prettier: "3.7.4"
@@ -3116,6 +3117,15 @@ var expectNullableWatcherSource = (value, source) => {
3116
3117
  throw new Error(`Invalid ${source}: expected "cdp", "extension", or null`);
3117
3118
  };
3118
3119
  // ../argus-core/dist/http/fetch.js
3120
+ class HttpResponseError extends Error {
3121
+ status;
3122
+ isHttpResponseError = true;
3123
+ constructor(message, status) {
3124
+ super(message);
3125
+ this.name = "HttpResponseError";
3126
+ this.status = status;
3127
+ }
3128
+ }
3119
3129
  var fetchJson = async (url, options = {}) => {
3120
3130
  const controller = new AbortController;
3121
3131
  const timeoutMs = options.timeoutMs ?? 5000;
@@ -3133,7 +3143,7 @@ var fetchJson = async (url, options = {}) => {
3133
3143
  return await response.json();
3134
3144
  }
3135
3145
  const errorMessage = await extractErrorMessage(response);
3136
- throw new Error(errorMessage ?? `Request failed (${response.status} ${response.statusText})`);
3146
+ throw new HttpResponseError(errorMessage ?? `Request failed (${response.status} ${response.statusText})`, response.status);
3137
3147
  }
3138
3148
  return await response.json();
3139
3149
  } catch (error) {
@@ -3158,7 +3168,7 @@ var fetchText = async (url, options = {}) => {
3158
3168
  headers: body ? { "Content-Type": "application/json" } : undefined
3159
3169
  });
3160
3170
  if (!response.ok) {
3161
- throw new Error(`Request failed (${response.status} ${response.statusText})`);
3171
+ throw new HttpResponseError(`Request failed (${response.status} ${response.statusText})`, response.status);
3162
3172
  }
3163
3173
  return await response.text();
3164
3174
  } catch (error) {
@@ -8192,9 +8202,6 @@ var formatError3 = (error) => error instanceof Error ? error.message : String(er
8192
8202
  // ../argus-watcher/dist/cdp/eval.js
8193
8203
  var evaluateExpression = async (session, options) => {
8194
8204
  const args = hasEvalArgs(options.args) ? options.args : undefined;
8195
- if (args) {
8196
- await installEvalArgs(session, args, options.timeoutMs);
8197
- }
8198
8205
  let scenarioBridge;
8199
8206
  try {
8200
8207
  if (options.scenario) {
@@ -8205,12 +8212,23 @@ var evaluateExpression = async (session, options) => {
8205
8212
  }
8206
8213
  const recordResult = await evaluateAndAwaitRecord(session, {
8207
8214
  ...options,
8208
- expression: scenarioBridge?.expression ?? options.expression,
8215
+ expression: wrapExpressionWithArgs(scenarioBridge?.expression ?? options.expression, args),
8209
8216
  replMode: scenarioBridge ? false : options.replMode
8210
8217
  });
8211
8218
  if (recordResult.exception)
8212
8219
  return recordResult.response;
8213
8220
  const record2 = recordResult.record;
8221
+ if (options.jsonValue) {
8222
+ const jsonResult = await materializeJsonValue(session, record2, options);
8223
+ if (!jsonResult.ok)
8224
+ return jsonResult.response;
8225
+ return {
8226
+ ok: true,
8227
+ result: jsonResult.json,
8228
+ type: record2?.type ?? null,
8229
+ exception: null
8230
+ };
8231
+ }
8214
8232
  const value = await materializeRemoteObject(session, record2, options);
8215
8233
  return {
8216
8234
  ok: true,
@@ -8220,12 +8238,16 @@ var evaluateExpression = async (session, options) => {
8220
8238
  };
8221
8239
  } finally {
8222
8240
  await scenarioBridge?.dispose();
8223
- if (args) {
8224
- await restoreEvalArgs(session, options.timeoutMs);
8225
- }
8226
8241
  }
8227
8242
  };
8228
8243
  var hasEvalArgs = (args) => args != null && Object.keys(args).length > 0;
8244
+ var wrapExpressionWithArgs = (expression, args) => {
8245
+ if (!args)
8246
+ return expression;
8247
+ return `{ const args = Object.freeze(${JSON.stringify(args)});
8248
+ ${expression}
8249
+ }`;
8250
+ };
8229
8251
  var evaluateAndAwaitRecord = async (session, options) => {
8230
8252
  const payload = await evaluateRawExpression(session, options);
8231
8253
  if (payload.exceptionDetails) {
@@ -8250,42 +8272,6 @@ var formatExceptionResponse = (payload) => ({
8250
8272
  details: payload.exceptionDetails?.exception ?? null
8251
8273
  }
8252
8274
  });
8253
- var EVAL_ARGS_STATE_KEY = "__argusEvalArgsPreviousDescriptor__";
8254
- var installEvalArgs = async (session, args, timeoutMs) => {
8255
- const payload = JSON.stringify(args);
8256
- await sendInternalEval(session, `(() => {
8257
- const previous = Object.getOwnPropertyDescriptor(globalThis, 'args');
8258
- Object.defineProperty(globalThis, ${JSON.stringify(EVAL_ARGS_STATE_KEY)}, { value: previous, configurable: true });
8259
- Object.defineProperty(globalThis, 'args', { value: Object.freeze(${payload}), configurable: true });
8260
- })()`, timeoutMs);
8261
- };
8262
- var restoreEvalArgs = async (session, timeoutMs) => {
8263
- await sendInternalEval(session, `(() => {
8264
- const stateKey = ${JSON.stringify(EVAL_ARGS_STATE_KEY)};
8265
- const previous = Object.getOwnPropertyDescriptor(globalThis, stateKey)?.value;
8266
- delete globalThis[stateKey];
8267
- delete globalThis.args;
8268
- if (previous) Object.defineProperty(globalThis, 'args', previous);
8269
- })()`, timeoutMs);
8270
- };
8271
- var sendInternalEval = async (session, expression, timeoutMs) => {
8272
- const payload = await session.sendAndWait("Runtime.evaluate", {
8273
- expression,
8274
- replMode: false,
8275
- awaitPromise: true,
8276
- returnByValue: true
8277
- }, { timeoutMs });
8278
- if (payload.exceptionDetails) {
8279
- throw new Error(formatInternalEvalError(payload.exceptionDetails));
8280
- }
8281
- };
8282
- var formatInternalEvalError = (exceptionDetails) => {
8283
- const exception = exceptionDetails.exception;
8284
- if (exception != null && typeof exception === "object" && "description" in exception && typeof exception.description === "string") {
8285
- return exception.description;
8286
- }
8287
- return exceptionDetails.text ?? "Failed to prepare eval args";
8288
- };
8289
8275
  var evaluateRawExpression = async (session, options) => await session.sendAndWait("Runtime.evaluate", {
8290
8276
  expression: options.expression,
8291
8277
  replMode: options.replMode ?? true,
@@ -8296,6 +8282,25 @@ var awaitPromiseResult = async (session, promiseObjectId, timeoutMs) => await se
8296
8282
  promiseObjectId,
8297
8283
  returnByValue: false
8298
8284
  }, { timeoutMs });
8285
+ var EMPTY_JSON_ENVELOPE = "{}";
8286
+ var materializeJsonValue = async (session, record2, options) => {
8287
+ if (!record2) {
8288
+ return { ok: true, json: EMPTY_JSON_ENVELOPE };
8289
+ }
8290
+ if (!record2.objectId) {
8291
+ return { ok: true, json: JSON.stringify({ v: record2.value }) ?? EMPTY_JSON_ENVELOPE };
8292
+ }
8293
+ const payload = await session.sendAndWait("Runtime.callFunctionOn", {
8294
+ objectId: record2.objectId,
8295
+ functionDeclaration: "function () { return JSON.stringify({ v: this }) }",
8296
+ returnByValue: true
8297
+ }, { timeoutMs: options.timeoutMs });
8298
+ if (payload.exceptionDetails) {
8299
+ return { ok: false, response: formatExceptionResponse(payload) };
8300
+ }
8301
+ const json = payload.result?.value;
8302
+ return { ok: true, json: typeof json === "string" ? json : EMPTY_JSON_ENVELOPE };
8303
+ };
8299
8304
  var materializeRemoteObject = async (session, record2, options) => {
8300
8305
  if (!record2) {
8301
8306
  return null;
@@ -8339,6 +8344,7 @@ var route18 = defineJsonRoute({
8339
8344
  awaitPromise: normalizeBoolean(payload.awaitPromise, true),
8340
8345
  replMode: normalizeBoolean(payload.replMode, true),
8341
8346
  returnByValue: normalizeBoolean(payload.returnByValue, true),
8347
+ jsonValue: normalizeBoolean(payload.jsonValue, false),
8342
8348
  timeoutMs: normalizeTimeout(payload.timeoutMs),
8343
8349
  scenario: normalizeBoolean(payload.scenario, false),
8344
8350
  scenarioServices: {
@@ -8539,7 +8545,7 @@ var filterNodesByText = async (session, nodeIds, text) => {
8539
8545
 
8540
8546
  // ../argus-watcher/dist/cdp/dom/selector.js
8541
8547
  var getDomRootId = async (session) => {
8542
- const targetContext = session.getTargetContext?.();
8548
+ const targetContext = session.getReadyTargetContext ? await session.getReadyTargetContext() : session.getTargetContext?.();
8543
8549
  if (targetContext?.kind === "frame") {
8544
8550
  return getFrameDomRootId(session, targetContext);
8545
8551
  }
@@ -8624,6 +8630,18 @@ var describeNodeByBackendId = async (session, backendNodeId) => {
8624
8630
  });
8625
8631
  return described.node ?? null;
8626
8632
  };
8633
+ var resolveNodeIdByBackendId = async (session, backendNodeId) => {
8634
+ const describedNode = await describeNodeByBackendId(session, backendNodeId);
8635
+ if (describedNode?.nodeId) {
8636
+ return describedNode.nodeId;
8637
+ }
8638
+ await getDomRootId(session);
8639
+ const pushed = await session.sendAndWait("DOM.pushNodesByBackendIdsToFrontend", {
8640
+ backendNodeIds: [backendNodeId]
8641
+ });
8642
+ const nodeId = pushed.nodeIds?.[0];
8643
+ return typeof nodeId === "number" && nodeId > 0 ? nodeId : null;
8644
+ };
8627
8645
  var toDomNodeDescriptor = (handle) => {
8628
8646
  if (handle.nodeId != null) {
8629
8647
  return { nodeId: handle.nodeId };
@@ -9024,7 +9042,39 @@ var locateByLabel = async (session, elementRefs, request) => {
9024
9042
  }
9025
9043
  return matchesText(element.name, request.label, request.exact ?? false);
9026
9044
  });
9027
- return buildLocateResponse(session, request.all ?? false, matches);
9045
+ return buildLocateResponse(session, request.all ?? false, await removeCompositeLabelWrappers(session, matches));
9046
+ };
9047
+ var NATIVE_LABELABLE_TAGS = new Set(["button", "input", "select", "textarea"]);
9048
+ var removeCompositeLabelWrappers = async (session, matches) => {
9049
+ if (matches.length < 2)
9050
+ return matches;
9051
+ const candidates = await Promise.all(matches.map(async (match) => {
9052
+ const [node, nodeId] = await Promise.all([
9053
+ describeNodeByBackendId(session, match.backendNodeId),
9054
+ resolveNodeIdByBackendId(session, match.backendNodeId)
9055
+ ]);
9056
+ const tag = node ? (node.localName ?? node.nodeName).toLowerCase() : "";
9057
+ return { match, nodeId, native: NATIVE_LABELABLE_TAGS.has(tag) };
9058
+ }));
9059
+ const nativeCandidates = candidates.filter((candidate) => candidate.native && candidate.nodeId != null);
9060
+ if (nativeCandidates.length === 0)
9061
+ return matches;
9062
+ const kept = [];
9063
+ for (const candidate of candidates) {
9064
+ if (candidate.native || candidate.nodeId == null) {
9065
+ kept.push(candidate.match);
9066
+ continue;
9067
+ }
9068
+ const descendants = await session.sendAndWait("DOM.querySelectorAll", {
9069
+ nodeId: candidate.nodeId,
9070
+ selector: "button,input,select,textarea"
9071
+ });
9072
+ const descendantIds = new Set(descendants.nodeIds ?? []);
9073
+ const wrapsMatchedNative = nativeCandidates.some((native) => native.nodeId != null && descendantIds.has(native.nodeId) && normalizeText(native.match.role) === normalizeText(candidate.match.role) && normalizeText(native.match.name) === normalizeText(candidate.match.name));
9074
+ if (!wrapsMatchedNative)
9075
+ kept.push(candidate.match);
9076
+ }
9077
+ return kept;
9028
9078
  };
9029
9079
  var buildLocateResponse = async (session, all, matches) => {
9030
9080
  const selected = all ? matches : matches.slice(0, 1);
@@ -9535,8 +9585,8 @@ var setFileOnResolvedNodes = async (session, nodeIds, files) => {
9535
9585
  // ../argus-watcher/dist/cdp/dom/fill.js
9536
9586
  var FILL_FUNCTION = `function(value) {
9537
9587
  var el = this;
9588
+ el.focus();
9538
9589
  if (el.isContentEditable) {
9539
- el.focus();
9540
9590
  el.textContent = value;
9541
9591
  el.dispatchEvent(new Event('input', { bubbles: true }));
9542
9592
  el.dispatchEvent(new Event('change', { bubbles: true }));
@@ -13842,7 +13892,7 @@ import fs7 from "node:fs/promises";
13842
13892
  // ../argus-watcher/dist/cdp/visualCapture.js
13843
13893
  var createVisualCapturePlan = async (session, pageSession, request) => {
13844
13894
  const subject = resolveVisualCaptureSubject(request);
13845
- const targetContext = session.getTargetContext?.();
13895
+ const targetContext = session.getReadyTargetContext ? await session.getReadyTargetContext() : session.getTargetContext?.();
13846
13896
  if (targetContext?.kind === "frame" && pageSession) {
13847
13897
  return createFrameCapturePlan({
13848
13898
  frameSession: session,
@@ -16244,7 +16294,6 @@ var createEmptyFrameState = () => ({
16244
16294
  topFrameId: null,
16245
16295
  requestedFrameId: null,
16246
16296
  requestedFrameHint: null,
16247
- requestedFrameDetached: false,
16248
16297
  activeFrameId: null,
16249
16298
  activeAttachedAt: null,
16250
16299
  frames: new Map,
@@ -16373,7 +16422,7 @@ var resolveRequestedTarget = (state2) => {
16373
16422
  if (nextFrameId) {
16374
16423
  return { kind: "frame", frameId: nextFrameId };
16375
16424
  }
16376
- return state2.requestedFrameDetached ? { kind: "page" } : { kind: "pending" };
16425
+ return { kind: "pending" };
16377
16426
  };
16378
16427
  var resolveSelectedFrameCommandState = (state2) => {
16379
16428
  if (state2.requestedFrameId == null) {
@@ -16414,7 +16463,7 @@ var buildFrameTargetContext = (state2, frameId) => ({
16414
16463
  var findSingleMatchingFrameId = (state2, predicate) => {
16415
16464
  let matchId = null;
16416
16465
  for (const frame of state2.frames.values()) {
16417
- if (!predicate(frame)) {
16466
+ if (frame.frameId === state2.topFrameId || !predicate(frame)) {
16418
16467
  continue;
16419
16468
  }
16420
16469
  if (matchId) {
@@ -16504,7 +16553,8 @@ var createDelegatingSession = (options) => {
16504
16553
  subscriptions.delete(subscription);
16505
16554
  };
16506
16555
  },
16507
- getTargetContext: options.getTargetContext
16556
+ getTargetContext: options.getTargetContext,
16557
+ getReadyTargetContext: options.getReadyTargetContext
16508
16558
  };
16509
16559
  controller.rebind();
16510
16560
  return { session, controller };
@@ -17034,31 +17084,54 @@ var seedExtensionFrameState = (session, state2) => {
17034
17084
  return state2;
17035
17085
  };
17036
17086
  var getSelectedExtensionTarget = (session, state2) => {
17037
- if (!state2?.activeFrameId) {
17087
+ const frameId = state2?.requestedFrameId ?? state2?.activeFrameId;
17088
+ if (!state2 || !frameId) {
17038
17089
  return buildPageTarget(session, { attached: true });
17039
17090
  }
17040
- const frame = state2.frames.get(state2.activeFrameId);
17091
+ const frame = state2.frames.get(frameId);
17041
17092
  if (frame) {
17042
17093
  return frameToTarget(session.tabId, frame, { attached: true, faviconUrl: session.faviconUrl, topFrameId: state2.topFrameId });
17043
17094
  }
17044
17095
  return buildSelectedIframeFallbackTarget(session, state2);
17045
17096
  };
17046
17097
  var setRequestedTargetSelection = (state2, frameId) => {
17098
+ if (state2.requestedFrameId !== frameId) {
17099
+ state2.activeAttachedAt = null;
17100
+ }
17101
+ const previousHint = state2.requestedFrameId === frameId ? state2.requestedFrameHint : null;
17047
17102
  state2.requestedFrameId = frameId;
17048
- state2.requestedFrameHint = frameId ? createRequestedFrameHint(state2.frames.get(frameId)) : null;
17049
- state2.requestedFrameDetached = false;
17103
+ state2.requestedFrameHint = frameId ? createRequestedFrameHint(state2.frames.get(frameId)) ?? previousHint : null;
17104
+ };
17105
+ var listExtensionTargets = (session, state2) => {
17106
+ if (!state2) {
17107
+ return [buildPageTarget(session, { attached: true })];
17108
+ }
17109
+ const selected = getSelectedExtensionTarget(session, state2);
17110
+ selected.targetReady = isSelectedTargetReady(state2);
17111
+ const targets = [buildPageTarget(session, { attached: false }), ...buildFrameTargets(state2, session.tabId, null, session.faviconUrl)];
17112
+ const selectedIndex = targets.findIndex((target) => target.id === selected.id);
17113
+ if (selectedIndex < 0) {
17114
+ targets.push(selected);
17115
+ } else {
17116
+ targets[selectedIndex] = selected;
17117
+ }
17118
+ return targets;
17050
17119
  };
17051
17120
  var reconcileExtensionTargetSelection = (session, state2, onTargetChanged) => {
17052
17121
  const resolution = resolveRequestedTarget(state2);
17053
17122
  if (resolution.kind === "page") {
17054
- state2.requestedFrameDetached = false;
17055
17123
  return activatePageTarget(state2, onTargetChanged);
17056
17124
  }
17057
17125
  if (resolution.kind === "pending") {
17058
- return false;
17126
+ if (state2.activeFrameId == null && state2.activeAttachedAt != null) {
17127
+ return false;
17128
+ }
17129
+ state2.activeFrameId = null;
17130
+ state2.activeAttachedAt = Date.now();
17131
+ onTargetChanged();
17132
+ return true;
17059
17133
  }
17060
17134
  syncRequestedFrameSelection(state2, resolution.frameId);
17061
- state2.requestedFrameDetached = false;
17062
17135
  if (state2.activeFrameId === resolution.frameId) {
17063
17136
  return false;
17064
17137
  }
@@ -17075,7 +17148,6 @@ var removeExtensionFrame = (state2, frameId) => {
17075
17148
  removeExtensionFrame(state2, childId);
17076
17149
  }
17077
17150
  if (state2.activeFrameId === frameId) {
17078
- state2.requestedFrameDetached = state2.requestedFrameId === frameId;
17079
17151
  state2.activeFrameId = null;
17080
17152
  state2.activeAttachedAt = null;
17081
17153
  }
@@ -17146,7 +17218,7 @@ var syncRequestedFrameSelection = (state2, frameId) => {
17146
17218
  };
17147
17219
  var buildSelectedIframeFallbackTarget = (session, state2) => {
17148
17220
  const hint = state2.requestedFrameHint;
17149
- const frameId = state2.activeFrameId;
17221
+ const frameId = state2.requestedFrameId ?? state2.activeFrameId;
17150
17222
  if (!frameId) {
17151
17223
  return buildPageTarget(session, { attached: true });
17152
17224
  }
@@ -17157,7 +17229,8 @@ var buildSelectedIframeFallbackTarget = (session, state2) => {
17157
17229
  type: "iframe",
17158
17230
  parentId: formatPageTargetId(session.tabId),
17159
17231
  faviconUrl: session.faviconUrl,
17160
- attached: true
17232
+ attached: true,
17233
+ targetReady: false
17161
17234
  };
17162
17235
  };
17163
17236
 
@@ -17223,6 +17296,10 @@ var createExtensionSource = (options) => {
17223
17296
  getCurrentSession: () => currentSession,
17224
17297
  requireCurrentSession: getCurrentExtensionSession,
17225
17298
  getTargetContext: () => getCurrentTargetContext() ?? { kind: "page" },
17299
+ getReadyTargetContext: async () => {
17300
+ const context = getCurrentTargetContext() ?? { kind: "page" };
17301
+ return context.kind === "frame" ? targetRecovery.waitForSelectedFrameCommandTarget() : context;
17302
+ },
17226
17303
  prepareCommand: async ({ method, params, targetContext }) => {
17227
17304
  if (targetContext.kind !== "frame") {
17228
17305
  return;
@@ -17268,16 +17345,7 @@ var createExtensionSource = (options) => {
17268
17345
  if (!session) {
17269
17346
  return [];
17270
17347
  }
17271
- const state2 = frameStateByTabId.get(session.tabId);
17272
- if (!state2 || state2.frames.size === 0) {
17273
- return [buildPageTarget(session, { attached: true })];
17274
- }
17275
- const activeFrameId = state2.activeFrameId;
17276
- const targets = [buildPageTarget(session, { attached: activeFrameId == null })];
17277
- for (const frame of buildFrameTargets(state2, session.tabId, activeFrameId, session.faviconUrl)) {
17278
- targets.push(frame);
17279
- }
17280
- return targets;
17348
+ return listExtensionTargets(session, frameStateByTabId.get(session.tabId));
17281
17349
  };
17282
17350
  const attachTarget = async (targetId) => {
17283
17351
  const target = parseExtensionTargetId(targetId);
@@ -17415,10 +17483,11 @@ var createExtensionSource = (options) => {
17415
17483
  return null;
17416
17484
  }
17417
17485
  const state2 = frameStateByTabId.get(session.tabId);
17418
- if (!state2?.activeFrameId) {
17486
+ const frameId = state2?.requestedFrameId ?? state2?.activeFrameId;
17487
+ if (!state2 || !frameId) {
17419
17488
  return { kind: "page" };
17420
17489
  }
17421
- return buildFrameTargetContext(state2, state2.activeFrameId);
17490
+ return buildFrameTargetContext(state2, frameId);
17422
17491
  }
17423
17492
  function getOrCreateFrameState(tabId) {
17424
17493
  const existing = frameStateByTabId.get(tabId);
@@ -21159,7 +21228,7 @@ var prepareEvalExpression = async (inline, options, output) => {
21159
21228
  return null;
21160
21229
  }
21161
21230
  return {
21162
- expression: wrapForIframeEval(wrapExpressionWithArgs(resolved.expression, args), {
21231
+ expression: wrapForIframeEval(wrapExpressionWithArgs2(resolved.expression, args), {
21163
21232
  selector: options.iframe,
21164
21233
  namespace: options.iframeNamespace ?? "argus",
21165
21234
  timeoutMs: iframeTimeoutMs.value ?? 5000
@@ -21331,16 +21400,13 @@ var parseCount = (value) => {
21331
21400
  }
21332
21401
  return { value: parsed };
21333
21402
  };
21334
- var wrapExpressionWithArgs = (source, args) => {
21403
+ var wrapExpressionWithArgs2 = (source, args) => {
21335
21404
  if (!hasEvalArgs2(args)) {
21336
21405
  return source;
21337
21406
  }
21338
21407
  return `const args = Object.freeze(${JSON.stringify(args)});
21339
21408
  ${source}`;
21340
21409
  };
21341
- var sleep5 = async (durationMs) => {
21342
- await new Promise((resolve2) => setTimeout(resolve2, durationMs));
21343
- };
21344
21410
  var formatException = (response) => {
21345
21411
  if (!response.exception) {
21346
21412
  return "";
@@ -24167,60 +24233,89 @@ var authCommands = [
24167
24233
  ]
24168
24234
  }
24169
24235
  ];
24170
-
24171
- // dist/commands/evalPolling.js
24236
+ // ../argus-client/dist/eval/pollEval.js
24172
24237
  var pollEval = async (input) => {
24173
- let running = true;
24174
- const stop = () => {
24175
- running = false;
24176
- };
24177
- process.on("SIGINT", stop);
24178
- process.on("SIGTERM", stop);
24179
- try {
24180
- const startTime = Date.now();
24181
- let iteration = 0;
24182
- while (running) {
24183
- if (input.totalTimeoutMs != null) {
24184
- const elapsed = Date.now() - startTime;
24185
- if (elapsed >= input.totalTimeoutMs) {
24186
- return { kind: "timeout", elapsedMs: elapsed };
24187
- }
24188
- }
24189
- iteration += 1;
24190
- const result = await evalWithRetries({
24191
- watcher: input.watcher,
24192
- expression: input.expression,
24193
- args: input.args,
24194
- awaitPromise: input.awaitPromise,
24195
- returnByValue: input.returnByValue,
24196
- timeoutMs: input.timeoutMs,
24197
- failOnException: input.failOnException,
24198
- retryCount: input.retryCount,
24199
- scenario: input.scenario
24200
- });
24201
- if (!result.ok) {
24202
- return { kind: "eval-error", failure: result };
24238
+ const startTime = Date.now();
24239
+ let iteration = 0;
24240
+ while (!input.signal?.aborted) {
24241
+ if (input.totalTimeoutMs != null) {
24242
+ const elapsedMs = Date.now() - startTime;
24243
+ if (elapsedMs >= input.totalTimeoutMs) {
24244
+ return { kind: "timeout", elapsedMs };
24203
24245
  }
24204
- const context = { response: result.response, iteration, attempt: result.attempt };
24205
- await input.onResult?.(result.response, context);
24206
- const stopResult = input.shouldStop?.(context);
24207
- if (stopResult) {
24208
- if (!stopResult.ok) {
24209
- return { kind: "condition-error", error: stopResult.error };
24210
- }
24211
- if (stopResult.matched) {
24212
- return { kind: "matched", response: result.response, iteration, attempt: result.attempt };
24213
- }
24246
+ }
24247
+ iteration += 1;
24248
+ const result = await input.runEval(iteration);
24249
+ if (!result.ok) {
24250
+ return { kind: "eval-error", failure: result.failure };
24251
+ }
24252
+ const context = { response: result.response, iteration, attempt: result.attempt };
24253
+ await input.onResult?.(result.response, context);
24254
+ const decision = input.shouldStop?.(context);
24255
+ if (decision) {
24256
+ if (!decision.ok) {
24257
+ return { kind: "condition-error", error: decision.error };
24214
24258
  }
24215
- if (input.count != null && iteration >= input.count) {
24216
- return { kind: "exhausted", iterations: iteration };
24259
+ if (decision.matched) {
24260
+ return { kind: "matched", response: result.response, iteration, attempt: result.attempt };
24217
24261
  }
24218
- await sleep5(input.intervalMs);
24219
24262
  }
24220
- return { kind: "interrupted" };
24263
+ if (input.count != null && iteration >= input.count) {
24264
+ return { kind: "exhausted", iterations: iteration };
24265
+ }
24266
+ await sleep5(input.intervalMs, input.signal);
24267
+ }
24268
+ return { kind: "interrupted" };
24269
+ };
24270
+ var sleep5 = async (durationMs, signal) => {
24271
+ if (signal?.aborted) {
24272
+ return;
24273
+ }
24274
+ await new Promise((resolve2) => {
24275
+ const finish = () => {
24276
+ clearTimeout(timer);
24277
+ signal?.removeEventListener("abort", finish);
24278
+ resolve2();
24279
+ };
24280
+ const timer = setTimeout(finish, durationMs);
24281
+ signal?.addEventListener("abort", finish, { once: true });
24282
+ });
24283
+ };
24284
+ // dist/commands/evalPolling.js
24285
+ var pollEval2 = async (input) => {
24286
+ const controller = new AbortController;
24287
+ const abort = () => controller.abort();
24288
+ process.on("SIGINT", abort);
24289
+ process.on("SIGTERM", abort);
24290
+ try {
24291
+ return await pollEval({
24292
+ intervalMs: input.intervalMs,
24293
+ count: input.count,
24294
+ totalTimeoutMs: input.totalTimeoutMs,
24295
+ signal: controller.signal,
24296
+ shouldStop: input.shouldStop,
24297
+ onResult: input.onResult,
24298
+ runEval: async () => {
24299
+ const result = await evalWithRetries({
24300
+ watcher: input.watcher,
24301
+ expression: input.expression,
24302
+ args: input.args,
24303
+ awaitPromise: input.awaitPromise,
24304
+ returnByValue: input.returnByValue,
24305
+ timeoutMs: input.timeoutMs,
24306
+ failOnException: input.failOnException,
24307
+ retryCount: input.retryCount,
24308
+ scenario: input.scenario
24309
+ });
24310
+ if (!result.ok) {
24311
+ return { ok: false, failure: result, attempt: result.attempt };
24312
+ }
24313
+ return { ok: true, response: result.response, attempt: result.attempt };
24314
+ }
24315
+ });
24221
24316
  } finally {
24222
- process.off("SIGINT", stop);
24223
- process.off("SIGTERM", stop);
24317
+ process.off("SIGINT", abort);
24318
+ process.off("SIGTERM", abort);
24224
24319
  }
24225
24320
  };
24226
24321
 
@@ -24300,7 +24395,7 @@ var runEval = async (id, rawExpression, options) => {
24300
24395
  await emitter.emitSuccess(singleResult.response, false);
24301
24396
  return;
24302
24397
  }
24303
- const pollResult = await pollEval({
24398
+ const pollResult = await pollEval2({
24304
24399
  watcher,
24305
24400
  expression: prepared.expression,
24306
24401
  args: prepared.args,
@@ -24414,7 +24509,7 @@ var runEvalUntil = async (id, rawExpression, options) => {
24414
24509
  if (!resolved)
24415
24510
  return;
24416
24511
  const { watcher } = resolved;
24417
- const pollResult = await pollEval({
24512
+ const pollResult = await pollEval2({
24418
24513
  watcher,
24419
24514
  expression: prepared.expression,
24420
24515
  args: prepared.args,
@@ -24537,6 +24632,7 @@ var sharedEvalHeadOptions = (timeoutDescription) => [
24537
24632
  { flags: "-q, --silent", description: "Suppress success output; only emit output on error" }
24538
24633
  ];
24539
24634
  var sharedEvalTailOptions = [
24635
+ { flags: "--expression <js>", description: "Expression to evaluate (alternative to the positional expression)" },
24540
24636
  { flags: "-f, --file <path>", description: "Read expression from a file" },
24541
24637
  { flags: "--bundle", description: "Bundle --file and its resolved imports into one script before eval" },
24542
24638
  { flags: "--no-bundle", description: "Read --file as-is (skip bundling and auto-bundle)" },
@@ -24589,7 +24685,12 @@ var evalCommands = [
24589
24685
  'argus eval app "document.title" --iframe "iframe" --iframe-timeout 10000'
24590
24686
  ],
24591
24687
  action: async (id, expression, options) => {
24592
- await runEval(id, expression, {
24688
+ if (expression != null && options.expression != null) {
24689
+ console.error("Provide either a positional expression or --expression, not both.");
24690
+ process.exitCode = 2;
24691
+ return;
24692
+ }
24693
+ await runEval(id, expression ?? options.expression, {
24593
24694
  json: options.json,
24594
24695
  await: options.await,
24595
24696
  timeout: options.timeout,
@@ -24667,7 +24768,12 @@ var evalCommands = [
24667
24768
  "argus wait app --file ./ready.js --args ./args.json --out ./ready.json"
24668
24769
  ],
24669
24770
  action: async (id, expression, options) => {
24670
- await runEvalUntil(id, expression, {
24771
+ if (expression != null && options.expression != null) {
24772
+ console.error("Provide either a positional expression or --expression, not both.");
24773
+ process.exitCode = 2;
24774
+ return;
24775
+ }
24776
+ await runEvalUntil(id, expression ?? options.expression, {
24671
24777
  json: options.json,
24672
24778
  await: options.await,
24673
24779
  timeout: options.timeout,
@@ -48325,6 +48431,7 @@ var fillCommand = {
48325
48431
  { flags: "--testid <id>", description: `Shorthand for --selector "[data-testid='<id>']"` },
48326
48432
  { flags: "--ref <elementRef>", description: "Stable element ref from snapshot/locate output" },
48327
48433
  { flags: "--name <attr>", description: 'Shorthand for --selector "[name=<attr>]"' },
48434
+ { flags: "--value <text>", description: "Value to fill (alternative to the positional value)" },
48328
48435
  { flags: "--value-file <path>", description: "Read value from a file" },
48329
48436
  { flags: "--value-stdin", description: 'Read value from stdin (also triggered by "-" as value arg)' },
48330
48437
  { flags: "--all", description: "Allow multiple matches (default: error if >1 match)" },
@@ -48337,6 +48444,7 @@ var fillCommand = {
48337
48444
  'argus fill app --testid "username" "Bob"',
48338
48445
  'argus fill app --ref e7 "Bob"',
48339
48446
  'argus fill app --name "title" "Hello"',
48447
+ 'argus fill app --selector "#username" --value "Bob"',
48340
48448
  'argus fill app --selector "textarea" "New content"',
48341
48449
  'argus fill app --selector "input[type=text]" --all "reset"',
48342
48450
  'argus fill app --selector "#desc" --value-file ./description.txt',
@@ -48344,6 +48452,11 @@ var fillCommand = {
48344
48452
  'argus fill app --selector "#input" - < value.txt'
48345
48453
  ],
48346
48454
  action: async (id, value, options) => {
48455
+ if (value != null && options.value != null) {
48456
+ console.error("Provide either a positional value or --value, not both.");
48457
+ process.exitCode = 2;
48458
+ return;
48459
+ }
48347
48460
  if (options.name && (options.testid || options.ref)) {
48348
48461
  console.error("Cannot use --name with --testid or --ref.");
48349
48462
  process.exitCode = 2;
@@ -48351,7 +48464,7 @@ var fillCommand = {
48351
48464
  }
48352
48465
  if (!resolveTestId(options))
48353
48466
  return;
48354
- await runDomFill(id, value, options);
48467
+ await runDomFill(id, value ?? options.value, options);
48355
48468
  }
48356
48469
  };
48357
48470
 
@@ -51244,7 +51357,7 @@ var resolveExtensionTarget = (targets, options) => {
51244
51357
  };
51245
51358
  var hasExtensionTargetSelector = (options) => Boolean(options.page === true || options.iframe || options.iframeUrl || options.iframeTitle);
51246
51359
  var formatExtensionTargetLine = (target) => {
51247
- const state2 = target.attached ? "attached" : "available";
51360
+ const state2 = target.targetReady === false ? "pending" : target.attached ? "attached" : "available";
51248
51361
  const type = target.type ?? "target";
51249
51362
  const title = target.title || "(untitled)";
51250
51363
  const parent = target.parentId ? ` [parent: ${shortenId(target.parentId)}]` : "";
@@ -51317,7 +51430,7 @@ var scoreIframeTarget = (target, page) => {
51317
51430
  var renderTargetNode = (target, prefix, isLast, childrenByParent, output, isRoot = false) => {
51318
51431
  const connector = targetTreeConnector(isRoot, isLast);
51319
51432
  const childPrefix = targetTreeChildPrefix(prefix, isRoot, isLast);
51320
- const state2 = target.attached ? " attached" : "";
51433
+ const state2 = target.targetReady === false ? " pending" : target.attached ? " attached" : "";
51321
51434
  output.writeHuman(`${prefix}${connector}${target.title || "(untitled)"} (${target.type ?? "target"}, ${shortenId(target.id)}${state2})`);
51322
51435
  output.writeHuman(`${childPrefix}${target.url}`);
51323
51436
  const children = childrenByParent.get(target.id) ?? [];
@@ -51574,6 +51687,14 @@ var inspectWatcher = async (id, diagnostics) => {
51574
51687
  }
51575
51688
  if (!bridge) {
51576
51689
  issues.push(`Watcher ${resolved.watcher.id} is not present in extension diagnostics.`);
51690
+ } else if (!bridge.connected) {
51691
+ issues.push(`Watcher ${resolved.watcher.id} native bridge is disconnected.`);
51692
+ }
51693
+ if (status.ok && !status.status.attached) {
51694
+ issues.push(`Watcher ${resolved.watcher.id} has no debugger-attached target.`);
51695
+ }
51696
+ if (status.ok && status.status.targetReady === false) {
51697
+ issues.push(`Watcher ${resolved.watcher.id} selected target is not ready for commands.`);
51577
51698
  }
51578
51699
  if (status.ok && status.status.attached && !selectedTarget) {
51579
51700
  issues.push(`Watcher ${resolved.watcher.id} is attached, but no selected target was reported by /targets.`);