@nuanu-ai/agentbrowse 0.2.46 → 0.2.48

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 (190) hide show
  1. package/README.md +69 -10
  2. package/dist/agentpay-gateway.d.ts +9 -0
  3. package/dist/agentpay-gateway.d.ts.map +1 -1
  4. package/dist/agentpay-gateway.js +30 -0
  5. package/dist/agentpay-stagehand-llm.d.ts.map +1 -1
  6. package/dist/agentpay-stagehand-llm.js +9 -1
  7. package/dist/command-api-tracing.d.ts +19 -0
  8. package/dist/command-api-tracing.d.ts.map +1 -0
  9. package/dist/command-api-tracing.js +137 -0
  10. package/dist/commands/act.d.ts.map +1 -1
  11. package/dist/commands/act.js +822 -670
  12. package/dist/commands/act.test-harness.d.ts +6 -0
  13. package/dist/commands/act.test-harness.d.ts.map +1 -1
  14. package/dist/commands/act.test-harness.js +44 -1
  15. package/dist/commands/action-acceptance.d.ts.map +1 -1
  16. package/dist/commands/action-acceptance.js +115 -0
  17. package/dist/commands/captcha-solve.d.ts.map +1 -1
  18. package/dist/commands/captcha-solve.js +83 -16
  19. package/dist/commands/click-action-executor.d.ts +0 -1
  20. package/dist/commands/click-action-executor.d.ts.map +1 -1
  21. package/dist/commands/click-action-executor.js +31 -77
  22. package/dist/commands/close.d.ts +3 -3
  23. package/dist/commands/close.d.ts.map +1 -1
  24. package/dist/commands/close.js +178 -0
  25. package/dist/commands/descriptor-validation.d.ts.map +1 -1
  26. package/dist/commands/descriptor-validation.js +75 -57
  27. package/dist/commands/end-session.d.ts +25 -0
  28. package/dist/commands/end-session.d.ts.map +1 -0
  29. package/dist/commands/end-session.js +161 -0
  30. package/dist/commands/extract-stagehand-executor.js +1 -1
  31. package/dist/commands/extract.d.ts.map +1 -1
  32. package/dist/commands/extract.js +339 -202
  33. package/dist/commands/fill-secret.d.ts +3 -3
  34. package/dist/commands/fill-secret.d.ts.map +1 -1
  35. package/dist/commands/fill-secret.js +419 -234
  36. package/dist/commands/get-secrets-catalog.d.ts.map +1 -1
  37. package/dist/commands/get-secrets-catalog.js +66 -5
  38. package/dist/commands/interaction-kernel.d.ts +46 -0
  39. package/dist/commands/interaction-kernel.d.ts.map +1 -0
  40. package/dist/commands/interaction-kernel.js +215 -0
  41. package/dist/commands/launch.d.ts +0 -2
  42. package/dist/commands/launch.d.ts.map +1 -1
  43. package/dist/commands/launch.js +109 -17
  44. package/dist/commands/navigate.d.ts.map +1 -1
  45. package/dist/commands/navigate.js +188 -45
  46. package/dist/commands/observe-accessibility.d.ts.map +1 -1
  47. package/dist/commands/observe-accessibility.js +50 -39
  48. package/dist/commands/observe-dom-label-contract.d.ts.map +1 -1
  49. package/dist/commands/observe-dom-label-contract.js +5 -0
  50. package/dist/commands/observe-inventory.d.ts +13 -0
  51. package/dist/commands/observe-inventory.d.ts.map +1 -1
  52. package/dist/commands/observe-inventory.js +320 -65
  53. package/dist/commands/observe-persistence.d.ts.map +1 -1
  54. package/dist/commands/observe-persistence.js +3 -0
  55. package/dist/commands/observe-projection.d.ts +1 -0
  56. package/dist/commands/observe-projection.d.ts.map +1 -1
  57. package/dist/commands/observe-projection.js +7 -2
  58. package/dist/commands/observe-protected.d.ts +1 -0
  59. package/dist/commands/observe-protected.d.ts.map +1 -1
  60. package/dist/commands/observe-protected.js +9 -4
  61. package/dist/commands/observe-semantics.d.ts.map +1 -1
  62. package/dist/commands/observe-semantics.js +5 -2
  63. package/dist/commands/observe-stagehand.d.ts +1 -0
  64. package/dist/commands/observe-stagehand.d.ts.map +1 -1
  65. package/dist/commands/observe-stagehand.js +2 -0
  66. package/dist/commands/observe.d.ts +2 -0
  67. package/dist/commands/observe.d.ts.map +1 -1
  68. package/dist/commands/observe.js +387 -203
  69. package/dist/commands/observe.test-harness.d.ts +8 -0
  70. package/dist/commands/observe.test-harness.d.ts.map +1 -1
  71. package/dist/commands/observe.test-harness.js +48 -1
  72. package/dist/commands/poll-secret.d.ts +6 -0
  73. package/dist/commands/poll-secret.d.ts.map +1 -0
  74. package/dist/commands/poll-secret.js +159 -0
  75. package/dist/commands/request-secret.d.ts +6 -0
  76. package/dist/commands/request-secret.d.ts.map +1 -0
  77. package/dist/commands/request-secret.js +284 -0
  78. package/dist/commands/screenshot.d.ts.map +1 -1
  79. package/dist/commands/screenshot.js +172 -7
  80. package/dist/commands/select-action-executor.d.ts.map +1 -1
  81. package/dist/commands/semantic-observe.d.ts +4 -0
  82. package/dist/commands/semantic-observe.d.ts.map +1 -1
  83. package/dist/commands/semantic-observe.js +388 -17
  84. package/dist/commands/start-session.d.ts +31 -0
  85. package/dist/commands/start-session.d.ts.map +1 -0
  86. package/dist/commands/start-session.js +347 -0
  87. package/dist/commands/status.d.ts +2 -1
  88. package/dist/commands/status.d.ts.map +1 -1
  89. package/dist/commands/status.js +166 -144
  90. package/dist/control-semantics.d.ts +1 -0
  91. package/dist/control-semantics.d.ts.map +1 -1
  92. package/dist/control-semantics.js +51 -9
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +144 -45
  95. package/dist/otel-exporter.d.ts +58 -0
  96. package/dist/otel-exporter.d.ts.map +1 -0
  97. package/dist/otel-exporter.js +263 -0
  98. package/dist/otel-projector.d.ts +75 -0
  99. package/dist/otel-projector.d.ts.map +1 -0
  100. package/dist/otel-projector.js +409 -0
  101. package/dist/owned-browser.d.ts +1 -1
  102. package/dist/owned-browser.d.ts.map +1 -1
  103. package/dist/owned-browser.js +13 -1
  104. package/dist/owned-process.d.ts +2 -0
  105. package/dist/owned-process.d.ts.map +1 -1
  106. package/dist/owned-process.js +7 -3
  107. package/dist/playwright-runtime.d.ts +1 -1
  108. package/dist/playwright-runtime.d.ts.map +1 -1
  109. package/dist/playwright-runtime.js +8 -8
  110. package/dist/run-observability.d.ts +25 -0
  111. package/dist/run-observability.d.ts.map +1 -0
  112. package/dist/run-observability.js +115 -0
  113. package/dist/run-store.d.ts +274 -0
  114. package/dist/run-store.d.ts.map +1 -0
  115. package/dist/run-store.js +631 -0
  116. package/dist/runtime-metrics.d.ts +27 -0
  117. package/dist/runtime-metrics.d.ts.map +1 -0
  118. package/dist/runtime-metrics.js +66 -0
  119. package/dist/runtime-page-state.d.ts +11 -0
  120. package/dist/runtime-page-state.d.ts.map +1 -0
  121. package/dist/runtime-page-state.js +62 -0
  122. package/dist/runtime-protected-state.d.ts +16 -0
  123. package/dist/runtime-protected-state.d.ts.map +1 -0
  124. package/dist/runtime-protected-state.js +157 -0
  125. package/dist/runtime-state.d.ts +10 -44
  126. package/dist/runtime-state.d.ts.map +1 -1
  127. package/dist/runtime-state.js +57 -222
  128. package/dist/secrets/backend.d.ts +65 -16
  129. package/dist/secrets/backend.d.ts.map +1 -1
  130. package/dist/secrets/backend.js +135 -95
  131. package/dist/secrets/catalog-sync.d.ts.map +1 -1
  132. package/dist/secrets/catalog-sync.js +4 -1
  133. package/dist/secrets/form-matcher.d.ts +5 -5
  134. package/dist/secrets/form-matcher.d.ts.map +1 -1
  135. package/dist/secrets/form-matcher.js +292 -164
  136. package/dist/secrets/intent-output.d.ts +6 -10
  137. package/dist/secrets/intent-output.d.ts.map +1 -1
  138. package/dist/secrets/intent-output.js +4 -58
  139. package/dist/secrets/mock-agentpay-cabinet.d.ts +38 -27
  140. package/dist/secrets/mock-agentpay-cabinet.d.ts.map +1 -1
  141. package/dist/secrets/mock-agentpay-cabinet.js +177 -111
  142. package/dist/secrets/protected-artifact-guard.d.ts +2 -2
  143. package/dist/secrets/protected-artifact-guard.d.ts.map +1 -1
  144. package/dist/secrets/protected-artifact-guard.js +2 -2
  145. package/dist/secrets/protected-bindings.d.ts +1 -1
  146. package/dist/secrets/protected-bindings.d.ts.map +1 -1
  147. package/dist/secrets/protected-bindings.js +6 -0
  148. package/dist/secrets/protected-field-semantics.d.ts +9 -0
  149. package/dist/secrets/protected-field-semantics.d.ts.map +1 -0
  150. package/dist/secrets/protected-field-semantics.js +154 -0
  151. package/dist/secrets/protected-field-values.d.ts.map +1 -1
  152. package/dist/secrets/protected-field-values.js +3 -3
  153. package/dist/secrets/protected-fill.d.ts +1 -1
  154. package/dist/secrets/protected-fill.d.ts.map +1 -1
  155. package/dist/secrets/protected-fill.js +45 -149
  156. package/dist/secrets/protected-value-adapters.d.ts +2 -1
  157. package/dist/secrets/protected-value-adapters.d.ts.map +1 -1
  158. package/dist/secrets/protected-value-adapters.js +80 -1
  159. package/dist/secrets/request-output.d.ts +11 -0
  160. package/dist/secrets/request-output.d.ts.map +1 -0
  161. package/dist/secrets/request-output.js +75 -0
  162. package/dist/secrets/types.d.ts +15 -9
  163. package/dist/secrets/types.d.ts.map +1 -1
  164. package/dist/secrets/types.js +3 -0
  165. package/dist/session-event-exporter.d.ts +36 -0
  166. package/dist/session-event-exporter.d.ts.map +1 -0
  167. package/dist/session-event-exporter.js +428 -0
  168. package/dist/session.d.ts +16 -7
  169. package/dist/session.d.ts.map +1 -1
  170. package/dist/session.js +150 -23
  171. package/dist/sessions-backend.d.ts +354 -0
  172. package/dist/sessions-backend.d.ts.map +1 -0
  173. package/dist/sessions-backend.js +126 -0
  174. package/dist/solver/browser-launcher.d.ts +1 -1
  175. package/dist/solver/browser-launcher.d.ts.map +1 -1
  176. package/dist/solver/browser-launcher.js +39 -13
  177. package/dist/solver/captcha-solver.d.ts.map +1 -1
  178. package/dist/solver/captcha-solver.js +8 -1
  179. package/dist/solver/types.d.ts +1 -0
  180. package/dist/solver/types.d.ts.map +1 -1
  181. package/dist/workflow-session-completion.d.ts +33 -0
  182. package/dist/workflow-session-completion.d.ts.map +1 -0
  183. package/dist/workflow-session-completion.js +156 -0
  184. package/package.json +9 -1
  185. package/dist/commands/create-intent.d.ts +0 -6
  186. package/dist/commands/create-intent.d.ts.map +0 -1
  187. package/dist/commands/create-intent.js +0 -75
  188. package/dist/commands/poll-intent.d.ts +0 -6
  189. package/dist/commands/poll-intent.d.ts.map +0 -1
  190. package/dist/commands/poll-intent.js +0 -57
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { preflightAgentpayGateway } from './agentpay-gateway.js';
7
7
  import { browseCommand, browseCommandName } from './command-name.js';
8
8
  import { assertSemanticObserveRuntimeSupport } from './commands/semantic-observe-lexical.js';
9
9
  import { loadSession } from './session.js';
10
- import { outputError, outputJSON, fatal, info } from './output.js';
10
+ import { outputContractFailure, outputError, outputJSON, fatal, info } from './output.js';
11
11
  import { applyDemoProxyBootstrap } from './solver/config.js';
12
12
  function usageText() {
13
13
  return `Usage: ${browseCommandName()} <command> [args] [options]
@@ -15,14 +15,17 @@ function usageText() {
15
15
  Commands:
16
16
  init <apiKey> [--api-url <url>] Configure AgentPay gateway access for AgentBrowse
17
17
  launch [url] [options] Launch browser, optionally navigate to URL
18
+ start-session [name] [--merchant-name <name>]
19
+ Start workflow session and telemetry on the current browser
20
+ end-session Complete the active workflow session without closing the browser
18
21
  solve-captcha [--timeout <s>] Wait for captcha and solve it on current page
19
22
  navigate <url> Navigate current tab to URL
20
23
  get-secrets-catalog [url] Refresh stored-secret metadata for a URL or current page
21
- create-intent <fillRef> <storedSecretRef>
22
- Create or reuse an intent for one stored secret fill
23
- poll-intent <intentId> Poll stored-secret intent status
24
- fill-secret <fillRef> <intentId>
25
- Fill protected fields from the one-time delivered secret payload
24
+ request-secret <fillRef> <storedSecretRef> --merchant-name <name>
25
+ Create or reuse a session-bound secret request for one fill target
26
+ poll-secret <requestId> Poll one secret request state without blocking
27
+ fill-secret <fillRef> <requestId>
28
+ Claim an approved secret request and fill protected fields
26
29
  act <targetRef> <action> [value] Perform action on a previously observed target
27
30
  extract '<schema-json>' [scopeRef] Extract structured data from the page or a stored scope
28
31
  observe ["<goal>"] Discover available targets/elements
@@ -40,6 +43,7 @@ Options:
40
43
  --headless Launch browser in headless mode
41
44
  --path <file> Output path for screenshot
42
45
  --timeout <seconds> Timeout for solve-captcha command (default: 90)
46
+ --merchant-name <name> Merchant/vendor name for start-session or request-secret
43
47
  --help Show this help message
44
48
 
45
49
  Environment:
@@ -48,11 +52,13 @@ Environment:
48
52
  }
49
53
  const KNOWN_COMMANDS = new Set([
50
54
  'launch',
55
+ 'start-session',
56
+ 'end-session',
51
57
  'init',
52
58
  'navigate',
53
59
  'get-secrets-catalog',
54
- 'create-intent',
55
- 'poll-intent',
60
+ 'request-secret',
61
+ 'poll-secret',
56
62
  'fill-secret',
57
63
  'solve-captcha',
58
64
  'act',
@@ -201,19 +207,96 @@ function parseInitArgs(args) {
201
207
  apiUrl,
202
208
  };
203
209
  }
210
+ function parseStartSessionArgs(args) {
211
+ const nameParts = [];
212
+ let merchantName;
213
+ for (let i = 0; i < args.length; i++) {
214
+ const arg = args[i];
215
+ if (arg === '--merchant-name') {
216
+ const value = args[i + 1];
217
+ if (!value || value.startsWith('--')) {
218
+ outputError(`Usage: ${browseCommand('start-session', '[name]', '[--merchant-name <name>]')}`);
219
+ }
220
+ merchantName = value;
221
+ i += 1;
222
+ continue;
223
+ }
224
+ if (arg.startsWith('--')) {
225
+ outputError(`Usage: ${browseCommand('start-session', '[name]', '[--merchant-name <name>]')}`);
226
+ }
227
+ nameParts.push(arg);
228
+ }
229
+ return {
230
+ name: nameParts.join(' ').trim() || undefined,
231
+ merchantName,
232
+ };
233
+ }
234
+ function parseRequestSecretArgs(args) {
235
+ const positionals = [];
236
+ let merchantName;
237
+ for (let i = 0; i < args.length; i++) {
238
+ const arg = args[i];
239
+ if (arg === '--merchant-name') {
240
+ const value = args[i + 1];
241
+ if (!value || value.startsWith('--')) {
242
+ outputError(`Usage: ${browseCommand('request-secret', '<fillRef>', '<storedSecretRef>', '--merchant-name <name>')}`);
243
+ }
244
+ merchantName = value.trim();
245
+ i += 1;
246
+ continue;
247
+ }
248
+ if (arg.startsWith('--')) {
249
+ outputError(`Usage: ${browseCommand('request-secret', '<fillRef>', '<storedSecretRef>', '--merchant-name <name>')}`);
250
+ }
251
+ positionals.push(arg);
252
+ }
253
+ if (merchantName !== undefined && merchantName.length === 0) {
254
+ outputError('Invalid --merchant-name value. Expected a non-empty string.');
255
+ }
256
+ if (positionals.length !== 2) {
257
+ outputError(`Usage: ${browseCommand('request-secret', '<fillRef>', '<storedSecretRef>', '--merchant-name <name>')}`);
258
+ }
259
+ if (!merchantName) {
260
+ outputError(`The \`request-secret\` command requires \`--merchant-name <name>\` so the operator sees which vendor requested the secret.`);
261
+ }
262
+ return {
263
+ fillRef: positionals[0],
264
+ storedSecretRef: positionals[1],
265
+ merchantName,
266
+ };
267
+ }
204
268
  /** Require an active browser session. */
205
- function requireSessionRecord() {
269
+ function requireBrowserSessionRecord(command) {
206
270
  const session = loadSession();
207
- if (session)
271
+ if (session) {
208
272
  return session;
209
- // Fallback: try CDP on port 9222 directly
210
- // (Chrome may have been launched by agentpay-cli or externally)
211
- // Caller will handle connection errors. Runtime state starts empty.
212
- return {
213
- cdpUrl: 'ws://localhost:9222',
214
- pid: process.pid,
215
- launchedAt: new Date().toISOString(),
216
- };
273
+ }
274
+ return outputContractFailure({
275
+ error: 'workflow_session_required',
276
+ outcomeType: 'blocked',
277
+ message: `The \`${command}\` command requires an active product workflow session.`,
278
+ reason: `No persisted browser session was found. Run \`${browseCommandName()} launch [url]\` first, then \`${browseCommandName()} start-session\`.`,
279
+ });
280
+ }
281
+ function requireProductSessionRecord(command) {
282
+ const session = requireBrowserSessionRecord(command);
283
+ if (session.intentSessionId && session.activeRunId) {
284
+ return session;
285
+ }
286
+ if (!session.intentSessionId) {
287
+ return outputContractFailure({
288
+ error: 'workflow_session_required',
289
+ outcomeType: 'blocked',
290
+ message: `The \`${command}\` command requires an active product workflow session.`,
291
+ reason: `A browser session is loaded, but no product workflow session is active for it. Run \`${browseCommandName()} start-session\` first.`,
292
+ });
293
+ }
294
+ return outputContractFailure({
295
+ error: 'workflow_session_required',
296
+ outcomeType: 'blocked',
297
+ message: `The \`${command}\` command requires an active product workflow session.`,
298
+ reason: `The current product workflow session is missing its local run binding. Run \`${browseCommandName()} start-session\` again to re-bind the current browser session.`,
299
+ });
217
300
  }
218
301
  async function main(argv = process.argv) {
219
302
  applyDemoProxyBootstrap();
@@ -257,60 +340,74 @@ async function main(argv = process.argv) {
257
340
  proxy: launchArgs.proxy,
258
341
  noProxy: launchArgs.noProxy,
259
342
  });
260
- const updateNotice = await updateNoticePromise;
343
+ const updateNotice = await Promise.race([
344
+ updateNoticePromise,
345
+ new Promise((resolve) => {
346
+ queueMicrotask(() => resolve(null));
347
+ }),
348
+ ]);
261
349
  if (updateNotice) {
262
350
  info(updateNotice.message);
263
351
  }
264
352
  outputJSON(launchResult);
265
353
  break;
266
354
  }
355
+ case 'start-session': {
356
+ const startSessionArgs = parseStartSessionArgs(args);
357
+ const { startSession } = await import('./commands/start-session.js');
358
+ outputJSON(await startSession(startSessionArgs.name, {
359
+ merchantName: startSessionArgs.merchantName,
360
+ }));
361
+ break;
362
+ }
363
+ case 'end-session': {
364
+ const { endSession } = await import('./commands/end-session.js');
365
+ outputJSON(await endSession(requireProductSessionRecord(command)));
366
+ break;
367
+ }
267
368
  case 'navigate': {
268
369
  const url = getPositional(args);
269
370
  if (!url)
270
371
  outputError(`Usage: ${browseCommand('navigate', '<url>')}`);
271
372
  const { navigate } = await import('./commands/navigate.js');
272
- await navigate(requireSessionRecord(), url);
373
+ await navigate(requireProductSessionRecord(command), url);
273
374
  break;
274
375
  }
275
376
  case 'get-secrets-catalog': {
276
377
  const url = getPositional(args);
277
378
  const { getSecretsCatalog } = await import('./commands/get-secrets-catalog.js');
278
- await getSecretsCatalog(requireSessionRecord(), url);
379
+ await getSecretsCatalog(requireProductSessionRecord(command), url);
279
380
  break;
280
381
  }
281
- case 'create-intent': {
282
- const positionals = getPositionals(args);
283
- const [fillRef, storedSecretRef] = positionals;
284
- if (!fillRef || !storedSecretRef) {
285
- outputError(`Usage: ${browseCommand('create-intent', '<fillRef>', '<storedSecretRef>')}`);
286
- }
287
- const { createIntent } = await import('./commands/create-intent.js');
288
- await createIntent(requireSessionRecord(), fillRef, storedSecretRef);
382
+ case 'request-secret': {
383
+ const requestSecretArgs = parseRequestSecretArgs(args);
384
+ const { requestSecret } = await import('./commands/request-secret.js');
385
+ await requestSecret(requireProductSessionRecord(command), requestSecretArgs.fillRef, requestSecretArgs.storedSecretRef, requestSecretArgs.merchantName);
289
386
  break;
290
387
  }
291
- case 'poll-intent': {
292
- const intentId = getPositional(args);
293
- if (!intentId) {
294
- outputError(`Usage: ${browseCommand('poll-intent', '<intentId>')}`);
388
+ case 'poll-secret': {
389
+ const requestId = getPositional(args);
390
+ if (!requestId) {
391
+ outputError(`Usage: ${browseCommand('poll-secret', '<requestId>')}`);
295
392
  }
296
- const { pollIntent } = await import('./commands/poll-intent.js');
297
- await pollIntent(requireSessionRecord(), intentId);
393
+ const { pollSecret } = await import('./commands/poll-secret.js');
394
+ await pollSecret(requireProductSessionRecord(command), requestId);
298
395
  break;
299
396
  }
300
397
  case 'fill-secret': {
301
398
  const positionals = getPositionals(args);
302
- const [fillRef, intentId] = positionals;
303
- if (!fillRef || !intentId) {
304
- outputError(`Usage: ${browseCommand('fill-secret', '<fillRef>', '<intentId>')}`);
399
+ const [fillRef, requestId] = positionals;
400
+ if (!fillRef || !requestId) {
401
+ outputError(`Usage: ${browseCommand('fill-secret', '<fillRef>', '<requestId>')}`);
305
402
  }
306
403
  const { fillSecret } = await import('./commands/fill-secret.js');
307
- await fillSecret(requireSessionRecord(), fillRef, intentId);
404
+ await fillSecret(requireProductSessionRecord(command), fillRef, requestId);
308
405
  break;
309
406
  }
310
407
  case 'solve-captcha': {
311
408
  const timeout = parseCaptchaTimeout(args);
312
409
  const { captchaSolve } = await import('./commands/captcha-solve.js');
313
- await captchaSolve(loadSession(), timeout);
410
+ await captchaSolve(requireProductSessionRecord(command), timeout);
314
411
  break;
315
412
  }
316
413
  case 'act': {
@@ -323,7 +420,8 @@ async function main(argv = process.argv) {
323
420
  if (!isBrowseAction(action)) {
324
421
  outputError(`Unsupported act action: ${action}. Expected one of: click, fill, type, select, press.`);
325
422
  }
326
- await act(requireSessionRecord(), targetRef, action, valueParts.length > 0 ? valueParts.join(' ') : undefined);
423
+ const session = requireProductSessionRecord(command);
424
+ await act(session, targetRef, action, valueParts.length > 0 ? valueParts.join(' ') : undefined);
327
425
  break;
328
426
  }
329
427
  case 'extract': {
@@ -333,13 +431,13 @@ async function main(argv = process.argv) {
333
431
  outputError(`Usage: ${browseCommand('extract', "'<schema-json>'", '[scopeRef]')}`);
334
432
  }
335
433
  const { extract } = await import('./commands/extract.js');
336
- await extract(requireSessionRecord(), schemaJson, scopeRef);
434
+ await extract(requireProductSessionRecord(command), schemaJson, scopeRef);
337
435
  break;
338
436
  }
339
437
  case 'observe': {
340
438
  const instruction = getPositional(args);
341
439
  const { observe } = await import('./commands/observe.js');
342
- await observe(requireSessionRecord(), instruction);
440
+ await observe(requireProductSessionRecord(command), instruction);
343
441
  break;
344
442
  }
345
443
  case 'screenshot': {
@@ -349,12 +447,13 @@ async function main(argv = process.argv) {
349
447
  }
350
448
  const filePath = getFlag(args, '--path');
351
449
  const { screenshot } = await import('./commands/screenshot.js');
352
- await screenshot(requireSessionRecord(), filePath);
450
+ await screenshot(requireProductSessionRecord(command), filePath);
353
451
  break;
354
452
  }
355
453
  case 'status': {
454
+ requireProductSessionRecord(command);
356
455
  const { status } = await import('./commands/status.js');
357
- await status();
456
+ outputJSON(await status());
358
457
  break;
359
458
  }
360
459
  case 'close': {
@@ -0,0 +1,58 @@
1
+ import { type OtlpTraceRequest } from './otel-projector.js';
2
+ export type OtlpHttpJsonExporterConfig = {
3
+ tracesEndpoint: string;
4
+ headers: Record<string, string>;
5
+ timeoutMs: number;
6
+ protocol: 'http/json';
7
+ };
8
+ export type ExportStepTraceResult = {
9
+ attempted: false;
10
+ reason: 'config_missing' | 'run_missing' | 'step_missing' | 'unsupported_protocol';
11
+ } | {
12
+ attempted: true;
13
+ success: true;
14
+ status: number;
15
+ payload: OtlpTraceRequest;
16
+ } | {
17
+ attempted: true;
18
+ success: false;
19
+ status?: number;
20
+ reason: string;
21
+ payload: OtlpTraceRequest;
22
+ };
23
+ export type ExportRunRootTraceResult = {
24
+ attempted: false;
25
+ reason: 'config_missing' | 'run_missing';
26
+ } | {
27
+ attempted: true;
28
+ success: true;
29
+ status: number;
30
+ payload: OtlpTraceRequest;
31
+ } | {
32
+ attempted: true;
33
+ success: false;
34
+ status?: number;
35
+ reason: string;
36
+ payload: OtlpTraceRequest;
37
+ };
38
+ export declare function loadOtlpHttpJsonExporterConfig(env?: NodeJS.ProcessEnv): OtlpHttpJsonExporterConfig | null;
39
+ export declare function exportRunStepToOtlpHttpJson(params?: {
40
+ runId: string;
41
+ stepId: string;
42
+ config?: OtlpHttpJsonExporterConfig | null;
43
+ fetchImpl?: typeof fetch;
44
+ }): Promise<ExportStepTraceResult>;
45
+ export declare function buildRunStepOtlpTraceRequest(runId: string, stepId: string): OtlpTraceRequest | null;
46
+ export declare function exportRunRootToOtlpHttpJson(params?: {
47
+ runId: string;
48
+ config?: OtlpHttpJsonExporterConfig | null;
49
+ fetchImpl?: typeof fetch;
50
+ }): Promise<ExportRunRootTraceResult>;
51
+ export declare function buildRunRootOtlpTraceRequest(runId: string): OtlpTraceRequest | null;
52
+ export declare function exportRunStepToOtlpHttpJsonBestEffort(runId: string | undefined, stepId: string | undefined, options?: {
53
+ fetchImpl?: typeof fetch;
54
+ }): Promise<ExportStepTraceResult>;
55
+ export declare function exportRunRootToOtlpHttpJsonBestEffort(runId: string | undefined, options?: {
56
+ fetchImpl?: typeof fetch;
57
+ }): Promise<ExportRunRootTraceResult>;
58
+ //# sourceMappingURL=otel-exporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel-exporter.d.ts","sourceRoot":"","sources":["../src/otel-exporter.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAS7B,MAAM,MAAM,0BAA0B,GAAG;IACvC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,WAAW,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAC7B;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,gBAAgB,GAAG,aAAa,GAAG,cAAc,GAAG,sBAAsB,CAAC;CACpF,GACD;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,gBAAgB,CAAC;CAC3B,GACD;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,gBAAgB,CAAC;CAC3B,CAAC;AAEN,MAAM,MAAM,wBAAwB,GAChC;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,gBAAgB,GAAG,aAAa,CAAC;CAC1C,GACD;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,gBAAgB,CAAC;CAC3B,GACD;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,gBAAgB,CAAC;CAC3B,CAAC;AA4DN,wBAAgB,8BAA8B,CAC5C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,0BAA0B,GAAG,IAAI,CAsCnC;AAED,wBAAsB,2BAA2B,CAC/C,MAAM,GAAE;IACN,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,0BAA0B,GAAG,IAAI,CAAC;IAC3C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAI1B,GACA,OAAO,CAAC,qBAAqB,CAAC,CAgFhC;AAED,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,gBAAgB,GAAG,IAAI,CAyBzB;AAED,wBAAsB,2BAA2B,CAC/C,MAAM,GAAE;IACN,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,0BAA0B,GAAG,IAAI,CAAC;IAC3C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAG1B,GACA,OAAO,CAAC,wBAAwB,CAAC,CAkDnC;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAOnF;AAED,wBAAsB,qCAAqC,CACzD,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,GAAE;IACP,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACrB,GACL,OAAO,CAAC,qBAAqB,CAAC,CAUhC;AAED,wBAAsB,qCAAqC,CACzD,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,OAAO,GAAE;IACP,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACrB,GACL,OAAO,CAAC,wBAAwB,CAAC,CASnC"}
@@ -0,0 +1,263 @@
1
+ import { listRunEventRecordsForStep, loadArtifactManifest, loadBrowserStateSnapshot, loadRunRecord, loadStepRecord, setRunIngestState, } from './run-store.js';
2
+ import { projectRunRootToOtlpTraceRequest, projectStepToOtlpTraceRequest, } from './otel-projector.js';
3
+ import { exportRunStepViaSessionEvent } from './session-event-exporter.js';
4
+ import { buildAgentpayTelemetryTracesUrl, tryResolveAgentpayGatewayConfig, } from './agentpay-gateway.js';
5
+ const DEFAULT_HTTP_TIMEOUT_MS = 5_000;
6
+ function trimEnv(value) {
7
+ const trimmed = value?.trim();
8
+ return trimmed ? trimmed : undefined;
9
+ }
10
+ function parseHeaderList(value) {
11
+ if (!value) {
12
+ return {};
13
+ }
14
+ const headers = {};
15
+ for (const chunk of value.split(',')) {
16
+ const separatorIndex = chunk.indexOf('=');
17
+ if (separatorIndex === -1) {
18
+ continue;
19
+ }
20
+ const key = chunk.slice(0, separatorIndex).trim();
21
+ const headerValue = chunk.slice(separatorIndex + 1).trim();
22
+ if (!key || !headerValue) {
23
+ continue;
24
+ }
25
+ headers[key] = headerValue;
26
+ }
27
+ return headers;
28
+ }
29
+ function parseTimeoutMs(value) {
30
+ const parsed = value ? Number(value) : NaN;
31
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_HTTP_TIMEOUT_MS;
32
+ }
33
+ function buildTracesEndpoint(base) {
34
+ return `${base.replace(/\/+$/, '')}/v1/traces`;
35
+ }
36
+ async function postOtlpTraceRequest(params) {
37
+ const controller = new AbortController();
38
+ const timeout = setTimeout(() => controller.abort(), params.config.timeoutMs);
39
+ try {
40
+ return await params.fetchImpl(params.config.tracesEndpoint, {
41
+ method: 'POST',
42
+ headers: {
43
+ 'content-type': 'application/json',
44
+ ...params.config.headers,
45
+ },
46
+ body: JSON.stringify(params.payload),
47
+ signal: controller.signal,
48
+ });
49
+ }
50
+ finally {
51
+ clearTimeout(timeout);
52
+ }
53
+ }
54
+ export function loadOtlpHttpJsonExporterConfig(env = process.env) {
55
+ const protocol = trimEnv(env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL) ??
56
+ trimEnv(env.OTEL_EXPORTER_OTLP_PROTOCOL) ??
57
+ 'http/json';
58
+ if (protocol !== 'http/json') {
59
+ return null;
60
+ }
61
+ const tracesEndpoint = trimEnv(env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT);
62
+ const baseEndpoint = trimEnv(env.OTEL_EXPORTER_OTLP_ENDPOINT);
63
+ const gateway = tryResolveAgentpayGatewayConfig();
64
+ const resolvedEndpoint = tracesEndpoint ??
65
+ (baseEndpoint ? buildTracesEndpoint(baseEndpoint) : null) ??
66
+ (gateway ? buildAgentpayTelemetryTracesUrl(gateway.apiUrl) : null);
67
+ if (!resolvedEndpoint) {
68
+ return null;
69
+ }
70
+ const timeoutMs = parseTimeoutMs(trimEnv(env.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT) ??
71
+ trimEnv(env.OTEL_EXPORTER_OTLP_TIMEOUT));
72
+ const headers = {
73
+ ...parseHeaderList(trimEnv(env.OTEL_EXPORTER_OTLP_HEADERS)),
74
+ ...parseHeaderList(trimEnv(env.OTEL_EXPORTER_OTLP_TRACES_HEADERS)),
75
+ };
76
+ if (!headers.authorization && gateway) {
77
+ headers.authorization = `Bearer ${gateway.apiKey}`;
78
+ }
79
+ return {
80
+ tracesEndpoint: resolvedEndpoint,
81
+ headers,
82
+ timeoutMs,
83
+ protocol: 'http/json',
84
+ };
85
+ }
86
+ export async function exportRunStepToOtlpHttpJson(params = {
87
+ runId: '',
88
+ stepId: '',
89
+ }) {
90
+ const run = loadRunRecord(params.runId);
91
+ if (!run) {
92
+ return { attempted: false, reason: 'run_missing' };
93
+ }
94
+ const step = loadStepRecord(params.runId, params.stepId);
95
+ if (!step) {
96
+ return { attempted: false, reason: 'step_missing' };
97
+ }
98
+ const payload = buildRunStepOtlpTraceRequest(params.runId, params.stepId);
99
+ if (!payload) {
100
+ return { attempted: false, reason: 'step_missing' };
101
+ }
102
+ const fetchImpl = params.fetchImpl ?? fetch;
103
+ const sessionEventResult = await exportRunStepViaSessionEvent({
104
+ runId: params.runId,
105
+ stepId: params.stepId,
106
+ telemetryPayload: payload,
107
+ fetchImpl,
108
+ });
109
+ if (sessionEventResult.attempted) {
110
+ if (sessionEventResult.success) {
111
+ return {
112
+ attempted: true,
113
+ success: true,
114
+ status: sessionEventResult.status,
115
+ payload,
116
+ };
117
+ }
118
+ return {
119
+ attempted: true,
120
+ success: false,
121
+ ...(sessionEventResult.status !== undefined ? { status: sessionEventResult.status } : {}),
122
+ reason: sessionEventResult.reason,
123
+ payload,
124
+ };
125
+ }
126
+ const config = params.config ?? loadOtlpHttpJsonExporterConfig();
127
+ if (!config) {
128
+ return { attempted: false, reason: 'config_missing' };
129
+ }
130
+ try {
131
+ const response = await postOtlpTraceRequest({
132
+ payload,
133
+ config,
134
+ fetchImpl,
135
+ });
136
+ if (!response.ok) {
137
+ setRunIngestState(params.runId, 'export_failed');
138
+ return {
139
+ attempted: true,
140
+ success: false,
141
+ status: response.status,
142
+ reason: `otlp_http_status_${response.status}`,
143
+ payload,
144
+ };
145
+ }
146
+ setRunIngestState(params.runId, 'exported');
147
+ return {
148
+ attempted: true,
149
+ success: true,
150
+ status: response.status,
151
+ payload,
152
+ };
153
+ }
154
+ catch (error) {
155
+ setRunIngestState(params.runId, 'export_failed');
156
+ return {
157
+ attempted: true,
158
+ success: false,
159
+ reason: error instanceof Error ? error.message : String(error),
160
+ payload,
161
+ };
162
+ }
163
+ }
164
+ export function buildRunStepOtlpTraceRequest(runId, stepId) {
165
+ const run = loadRunRecord(runId);
166
+ if (!run) {
167
+ return null;
168
+ }
169
+ const step = loadStepRecord(runId, stepId);
170
+ if (!step) {
171
+ return null;
172
+ }
173
+ return projectStepToOtlpTraceRequest({
174
+ run,
175
+ step,
176
+ events: listRunEventRecordsForStep(runId, stepId),
177
+ beforeSnapshot: step.beforeSnapshotId
178
+ ? loadBrowserStateSnapshot(runId, step.beforeSnapshotId)
179
+ : null,
180
+ afterSnapshot: step.afterSnapshotId
181
+ ? loadBrowserStateSnapshot(runId, step.afterSnapshotId)
182
+ : null,
183
+ artifactManifest: step.artifactManifestId
184
+ ? loadArtifactManifest(runId, step.artifactManifestId)
185
+ : null,
186
+ });
187
+ }
188
+ export async function exportRunRootToOtlpHttpJson(params = {
189
+ runId: '',
190
+ }) {
191
+ const config = params.config ?? loadOtlpHttpJsonExporterConfig();
192
+ if (!config) {
193
+ return { attempted: false, reason: 'config_missing' };
194
+ }
195
+ const run = loadRunRecord(params.runId);
196
+ if (!run) {
197
+ return { attempted: false, reason: 'run_missing' };
198
+ }
199
+ const payload = buildRunRootOtlpTraceRequest(params.runId);
200
+ if (!payload) {
201
+ return { attempted: false, reason: 'run_missing' };
202
+ }
203
+ const fetchImpl = params.fetchImpl ?? fetch;
204
+ try {
205
+ const response = await postOtlpTraceRequest({
206
+ payload,
207
+ config,
208
+ fetchImpl,
209
+ });
210
+ if (!response.ok) {
211
+ setRunIngestState(params.runId, 'export_failed');
212
+ return {
213
+ attempted: true,
214
+ success: false,
215
+ status: response.status,
216
+ reason: `otlp_http_status_${response.status}`,
217
+ payload,
218
+ };
219
+ }
220
+ setRunIngestState(params.runId, 'exported');
221
+ return {
222
+ attempted: true,
223
+ success: true,
224
+ status: response.status,
225
+ payload,
226
+ };
227
+ }
228
+ catch (error) {
229
+ setRunIngestState(params.runId, 'export_failed');
230
+ return {
231
+ attempted: true,
232
+ success: false,
233
+ reason: error instanceof Error ? error.message : String(error),
234
+ payload,
235
+ };
236
+ }
237
+ }
238
+ export function buildRunRootOtlpTraceRequest(runId) {
239
+ const run = loadRunRecord(runId);
240
+ if (!run) {
241
+ return null;
242
+ }
243
+ return projectRunRootToOtlpTraceRequest({ run });
244
+ }
245
+ export async function exportRunStepToOtlpHttpJsonBestEffort(runId, stepId, options = {}) {
246
+ if (!runId || !stepId) {
247
+ return { attempted: false, reason: 'step_missing' };
248
+ }
249
+ return exportRunStepToOtlpHttpJson({
250
+ runId,
251
+ stepId,
252
+ fetchImpl: options.fetchImpl,
253
+ });
254
+ }
255
+ export async function exportRunRootToOtlpHttpJsonBestEffort(runId, options = {}) {
256
+ if (!runId) {
257
+ return { attempted: false, reason: 'run_missing' };
258
+ }
259
+ return exportRunRootToOtlpHttpJson({
260
+ runId,
261
+ fetchImpl: options.fetchImpl,
262
+ });
263
+ }