@mercuryo-ai/magicpay-cli 0.1.0

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 (144) hide show
  1. package/README.md +56 -0
  2. package/dist/agentbrowse-assistive.d.ts +2 -0
  3. package/dist/agentbrowse-assistive.d.ts.map +1 -0
  4. package/dist/agentbrowse-assistive.js +66 -0
  5. package/dist/assistive-llm-client.d.ts +13 -0
  6. package/dist/assistive-llm-client.d.ts.map +1 -0
  7. package/dist/assistive-llm-client.js +152 -0
  8. package/dist/browser-bridge/act.d.ts +5 -0
  9. package/dist/browser-bridge/act.d.ts.map +1 -0
  10. package/dist/browser-bridge/act.js +8 -0
  11. package/dist/browser-bridge/observe.d.ts +5 -0
  12. package/dist/browser-bridge/observe.d.ts.map +1 -0
  13. package/dist/browser-bridge/observe.js +53 -0
  14. package/dist/browser-home.d.ts +7 -0
  15. package/dist/browser-home.d.ts.map +1 -0
  16. package/dist/browser-home.js +34 -0
  17. package/dist/commands/act.d.ts +5 -0
  18. package/dist/commands/act.d.ts.map +1 -0
  19. package/dist/commands/act.js +8 -0
  20. package/dist/commands/attach.d.ts +4 -0
  21. package/dist/commands/attach.d.ts.map +1 -0
  22. package/dist/commands/attach.js +10 -0
  23. package/dist/commands/browser-status.d.ts +5 -0
  24. package/dist/commands/browser-status.d.ts.map +1 -0
  25. package/dist/commands/browser-status.js +5 -0
  26. package/dist/commands/close.d.ts +4 -0
  27. package/dist/commands/close.d.ts.map +1 -0
  28. package/dist/commands/close.js +11 -0
  29. package/dist/commands/end-session.d.ts +22 -0
  30. package/dist/commands/end-session.d.ts.map +1 -0
  31. package/dist/commands/end-session.js +104 -0
  32. package/dist/commands/extract.d.ts +4 -0
  33. package/dist/commands/extract.d.ts.map +1 -0
  34. package/dist/commands/extract.js +14 -0
  35. package/dist/commands/fill-secret.d.ts +10 -0
  36. package/dist/commands/fill-secret.d.ts.map +1 -0
  37. package/dist/commands/fill-secret.js +483 -0
  38. package/dist/commands/find-form.d.ts +57 -0
  39. package/dist/commands/find-form.d.ts.map +1 -0
  40. package/dist/commands/find-form.js +138 -0
  41. package/dist/commands/get-secrets-catalog.d.ts +13 -0
  42. package/dist/commands/get-secrets-catalog.d.ts.map +1 -0
  43. package/dist/commands/get-secrets-catalog.js +103 -0
  44. package/dist/commands/init.d.ts +10 -0
  45. package/dist/commands/init.d.ts.map +1 -0
  46. package/dist/commands/init.js +28 -0
  47. package/dist/commands/launch.d.ts +4 -0
  48. package/dist/commands/launch.d.ts.map +1 -0
  49. package/dist/commands/launch.js +10 -0
  50. package/dist/commands/mock-approve-secret.d.ts +21 -0
  51. package/dist/commands/mock-approve-secret.d.ts.map +1 -0
  52. package/dist/commands/mock-approve-secret.js +69 -0
  53. package/dist/commands/mock-deny-secret.d.ts +21 -0
  54. package/dist/commands/mock-deny-secret.d.ts.map +1 -0
  55. package/dist/commands/mock-deny-secret.js +69 -0
  56. package/dist/commands/navigate.d.ts +4 -0
  57. package/dist/commands/navigate.d.ts.map +1 -0
  58. package/dist/commands/navigate.js +9 -0
  59. package/dist/commands/observe.d.ts +5 -0
  60. package/dist/commands/observe.d.ts.map +1 -0
  61. package/dist/commands/observe.js +53 -0
  62. package/dist/commands/poll-secret.d.ts +13 -0
  63. package/dist/commands/poll-secret.d.ts.map +1 -0
  64. package/dist/commands/poll-secret.js +87 -0
  65. package/dist/commands/request-secret.d.ts +15 -0
  66. package/dist/commands/request-secret.d.ts.map +1 -0
  67. package/dist/commands/request-secret.js +235 -0
  68. package/dist/commands/screenshot.d.ts +4 -0
  69. package/dist/commands/screenshot.d.ts.map +1 -0
  70. package/dist/commands/screenshot.js +11 -0
  71. package/dist/commands/solve-captcha.d.ts +19 -0
  72. package/dist/commands/solve-captcha.d.ts.map +1 -0
  73. package/dist/commands/solve-captcha.js +63 -0
  74. package/dist/commands/start-session.d.ts +28 -0
  75. package/dist/commands/start-session.d.ts.map +1 -0
  76. package/dist/commands/start-session.js +298 -0
  77. package/dist/commands/status.d.ts +27 -0
  78. package/dist/commands/status.d.ts.map +1 -0
  79. package/dist/commands/status.js +51 -0
  80. package/dist/commands/submit-form.d.ts +36 -0
  81. package/dist/commands/submit-form.d.ts.map +1 -0
  82. package/dist/commands/submit-form.js +242 -0
  83. package/dist/config.d.ts +2 -0
  84. package/dist/config.d.ts.map +1 -0
  85. package/dist/config.js +1 -0
  86. package/dist/fillable-form-state.d.ts +5 -0
  87. package/dist/fillable-form-state.d.ts.map +1 -0
  88. package/dist/fillable-form-state.js +22 -0
  89. package/dist/gateway.d.ts +10 -0
  90. package/dist/gateway.d.ts.map +1 -0
  91. package/dist/gateway.js +49 -0
  92. package/dist/generated/build-config.d.ts +2 -0
  93. package/dist/generated/build-config.d.ts.map +1 -0
  94. package/dist/generated/build-config.js +2 -0
  95. package/dist/index.d.ts +4 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +398 -0
  98. package/dist/mock-secret-cabinet.d.ts +37 -0
  99. package/dist/mock-secret-cabinet.d.ts.map +1 -0
  100. package/dist/mock-secret-cabinet.js +321 -0
  101. package/dist/mock-secret-store.d.ts +12 -0
  102. package/dist/mock-secret-store.d.ts.map +1 -0
  103. package/dist/mock-secret-store.js +108 -0
  104. package/dist/mock-stored-secrets.json +180 -0
  105. package/dist/otel-exporter.d.ts +58 -0
  106. package/dist/otel-exporter.d.ts.map +1 -0
  107. package/dist/otel-exporter.js +241 -0
  108. package/dist/otel-projector.d.ts +59 -0
  109. package/dist/otel-projector.d.ts.map +1 -0
  110. package/dist/otel-projector.js +325 -0
  111. package/dist/output.d.ts +10 -0
  112. package/dist/output.d.ts.map +1 -0
  113. package/dist/output.js +29 -0
  114. package/dist/package-version.d.ts +2 -0
  115. package/dist/package-version.d.ts.map +1 -0
  116. package/dist/package-version.js +14 -0
  117. package/dist/protected-exposure.d.ts +4 -0
  118. package/dist/protected-exposure.d.ts.map +1 -0
  119. package/dist/protected-exposure.js +17 -0
  120. package/dist/request-output.d.ts +4 -0
  121. package/dist/request-output.d.ts.map +1 -0
  122. package/dist/request-output.js +27 -0
  123. package/dist/run-store.d.ts +130 -0
  124. package/dist/run-store.d.ts.map +1 -0
  125. package/dist/run-store.js +245 -0
  126. package/dist/secret-backend.d.ts +28 -0
  127. package/dist/secret-backend.d.ts.map +1 -0
  128. package/dist/secret-backend.js +335 -0
  129. package/dist/secret-state.d.ts +7 -0
  130. package/dist/secret-state.d.ts.map +1 -0
  131. package/dist/secret-state.js +26 -0
  132. package/dist/session-backend.d.ts +34 -0
  133. package/dist/session-backend.d.ts.map +1 -0
  134. package/dist/session-backend.js +36 -0
  135. package/dist/update-check.d.ts +13 -0
  136. package/dist/update-check.d.ts.map +1 -0
  137. package/dist/update-check.js +175 -0
  138. package/dist/workflow-session-completion.d.ts +32 -0
  139. package/dist/workflow-session-completion.d.ts.map +1 -0
  140. package/dist/workflow-session-completion.js +149 -0
  141. package/dist/workflow-state.d.ts +2 -0
  142. package/dist/workflow-state.d.ts.map +1 -0
  143. package/dist/workflow-state.js +1 -0
  144. package/package.json +67 -0
@@ -0,0 +1,298 @@
1
+ import { isSessionAlive, resolveBrowserSessionId } from '@mercuryo-ai/agentbrowse';
2
+ import { buildCreateRemoteSessionInput } from '@mercuryo-ai/magicpay-sdk/core';
3
+ import { exportRunRootToOtlpHttpJsonBestEffort, exportRunStepToOtlpHttpJsonBestEffort } from '../otel-exporter.js';
4
+ import { info } from '../output.js';
5
+ import { getMagicPayClient } from '../session-backend.js';
6
+ import { finishRunRecord, finishRunStep, startCliRun, startRunStep, } from '../run-store.js';
7
+ import { clearWorkflowState, loadMagicPaySession, saveMagicPaySession, } from '../workflow-state.js';
8
+ import { completeWorkflowSessionRemote } from '../workflow-session-completion.js';
9
+ const MAX_START_SESSION_NAME_LENGTH = 256;
10
+ function formatUnknownError(error) {
11
+ if (error instanceof Error) {
12
+ return error.message;
13
+ }
14
+ if (typeof error === 'string') {
15
+ return error;
16
+ }
17
+ if (error && typeof error === 'object') {
18
+ try {
19
+ return JSON.stringify(error);
20
+ }
21
+ catch {
22
+ return Object.prototype.toString.call(error);
23
+ }
24
+ }
25
+ return String(error);
26
+ }
27
+ function buildStartSessionFailure(reason) {
28
+ return {
29
+ success: false,
30
+ error: 'browser_session_unavailable',
31
+ outcomeType: 'blocked',
32
+ message: 'Session start failed.',
33
+ reason,
34
+ };
35
+ }
36
+ function normalizeStartSessionName(value) {
37
+ if (typeof value !== 'string') {
38
+ return undefined;
39
+ }
40
+ const trimmed = value.trim();
41
+ if (trimmed.length === 0) {
42
+ return undefined;
43
+ }
44
+ return trimmed.length > MAX_START_SESSION_NAME_LENGTH
45
+ ? trimmed.slice(0, MAX_START_SESSION_NAME_LENGTH)
46
+ : trimmed;
47
+ }
48
+ function normalizeMerchantName(value) {
49
+ if (typeof value !== 'string') {
50
+ return undefined;
51
+ }
52
+ const trimmed = value.trim();
53
+ return trimmed.length > 0 ? trimmed : undefined;
54
+ }
55
+ function buildStartSessionDisplayName(options) {
56
+ return normalizeStartSessionName(options.requestedName) ?? normalizeStartSessionName(options.pageTitle);
57
+ }
58
+ function finalizePreviousRunBestEffort(runId) {
59
+ if (!runId) {
60
+ return false;
61
+ }
62
+ try {
63
+ finishRunRecord(runId, {
64
+ status: 'aborted',
65
+ finalOutcome: {
66
+ success: false,
67
+ outcomeType: 'replaced_by_start_session',
68
+ message: 'Workflow session was replaced by a new start-session command.',
69
+ reason: 'A new workflow session was started on the same browser session.',
70
+ },
71
+ });
72
+ return true;
73
+ }
74
+ catch (error) {
75
+ info(`[start-session] failed to finalize previous local run ${runId}: ${formatUnknownError(error)}`);
76
+ return false;
77
+ }
78
+ }
79
+ async function finalizeStartSessionStepBestEffort(runId, stepId, options) {
80
+ if (!stepId) {
81
+ return;
82
+ }
83
+ try {
84
+ finishRunStep({
85
+ runId,
86
+ stepId,
87
+ ...options,
88
+ });
89
+ await exportRunStepToOtlpHttpJsonBestEffort(runId, stepId);
90
+ }
91
+ catch (error) {
92
+ info(`[start-session] failed to finalize local run step ${stepId}: ${formatUnknownError(error)}`);
93
+ }
94
+ }
95
+ function finalizeRunRecordBestEffort(runId, options) {
96
+ try {
97
+ finishRunRecord(runId, {
98
+ status: options.status,
99
+ finalOutcome: {
100
+ success: options.success,
101
+ ...(options.outcomeType ? { outcomeType: options.outcomeType } : {}),
102
+ ...(options.message ? { message: options.message } : {}),
103
+ ...(options.reason ? { reason: options.reason } : {}),
104
+ },
105
+ });
106
+ }
107
+ catch (error) {
108
+ info(`[start-session] failed to finalize local run ${runId}: ${formatUnknownError(error)}`);
109
+ }
110
+ }
111
+ function isStalePreviousWorkflowCompletionFailure(error) {
112
+ return (error === 'workflow_session_unavailable' ||
113
+ error === 'workflow_session_already_closed' ||
114
+ error === 'workflow_run_unavailable' ||
115
+ error === 'workflow_step_unavailable');
116
+ }
117
+ async function completePreviousWorkflowSessionIfNeeded(session) {
118
+ if (!session.intentSessionId) {
119
+ return null;
120
+ }
121
+ const remoteCompletion = await completeWorkflowSessionRemote({
122
+ session,
123
+ stepId: undefined,
124
+ command: 'start-session',
125
+ terminalStatus: 'canceled',
126
+ runStatus: 'aborted',
127
+ summary: 'Previous workflow session canceled because a new start-session replaced it.',
128
+ outcomeType: 'workflow_session_canceled',
129
+ message: 'Previous workflow session canceled because a new start-session replaced it.',
130
+ reason: 'A new start-session command replaced the previous workflow session.',
131
+ allowMissingStep: true,
132
+ });
133
+ if (remoteCompletion.success) {
134
+ return null;
135
+ }
136
+ if (isStalePreviousWorkflowCompletionFailure(remoteCompletion.error)) {
137
+ info(`[start-session] clearing stale previous workflow binding before creating a new session: ${remoteCompletion.reason}`);
138
+ return null;
139
+ }
140
+ return {
141
+ success: false,
142
+ error: 'workflow_session_complete_failed',
143
+ outcomeType: 'failed',
144
+ message: 'Session start failed.',
145
+ reason: `Active workflow session cleanup blocked start-session: ${remoteCompletion.reason}`,
146
+ };
147
+ }
148
+ export async function startSession(requestedName, options = {}) {
149
+ const session = loadMagicPaySession();
150
+ if (!session) {
151
+ return buildStartSessionFailure('Launch or attach a browser first. No persisted browser session was found.');
152
+ }
153
+ const previousWorkflowCompletionFailure = await completePreviousWorkflowSessionIfNeeded(session);
154
+ if (previousWorkflowCompletionFailure) {
155
+ return previousWorkflowCompletionFailure;
156
+ }
157
+ const replacedPreviousRun = finalizePreviousRunBestEffort(session.activeRunId);
158
+ const nextSession = clearWorkflowState(session);
159
+ if (replacedPreviousRun ||
160
+ session.intentSessionId ||
161
+ session.currentRequestId ||
162
+ session.lastKnownStatus ||
163
+ typeof session.lastEventSeq === 'number' ||
164
+ session.transientSecretCache) {
165
+ saveMagicPaySession(nextSession);
166
+ }
167
+ if (!(await isSessionAlive(session))) {
168
+ return buildStartSessionFailure('The persisted browser session is no longer running. Launch or attach a fresh browser first.');
169
+ }
170
+ const currentPageRef = nextSession.runtime?.currentPageRef;
171
+ const currentPage = currentPageRef ? nextSession.runtime?.pages[currentPageRef] : undefined;
172
+ const sessionDisplayName = buildStartSessionDisplayName({
173
+ requestedName,
174
+ pageTitle: currentPage?.title,
175
+ });
176
+ const sessionDescription = sessionDisplayName ?? normalizeStartSessionName(currentPage?.url);
177
+ const merchantName = normalizeMerchantName(options.merchantName);
178
+ const run = startCliRun({
179
+ sessionId: resolveBrowserSessionId(nextSession),
180
+ entryCommand: 'start-session',
181
+ ...(sessionDisplayName ? { displayName: sessionDisplayName } : {}),
182
+ profile: nextSession.profile,
183
+ url: currentPage?.url,
184
+ });
185
+ await exportRunRootToOtlpHttpJsonBestEffort(run.runId);
186
+ const step = startRunStep({
187
+ runId: run.runId,
188
+ command: 'start-session',
189
+ input: {
190
+ hadPreviousRun: replacedPreviousRun,
191
+ ...(currentPageRef ? { pageRef: currentPageRef } : {}),
192
+ ...(currentPage?.url ? { url: currentPage.url } : {}),
193
+ ...(currentPage?.title ? { title: currentPage.title } : {}),
194
+ },
195
+ });
196
+ if (!step?.stepId) {
197
+ finalizeRunRecordBestEffort(run.runId, {
198
+ status: 'failed',
199
+ success: false,
200
+ outcomeType: 'backend_session_start_failed',
201
+ message: 'Workflow session start failed.',
202
+ reason: 'Local step creation failed before backend session start.',
203
+ });
204
+ return {
205
+ success: false,
206
+ error: 'backend_session_start_failed',
207
+ outcomeType: 'failed',
208
+ message: 'Session start failed.',
209
+ reason: 'Local step creation failed before backend session start.',
210
+ };
211
+ }
212
+ try {
213
+ const client = getMagicPayClient();
214
+ const remoteSession = await client.sessions.create(buildCreateRemoteSessionInput({
215
+ browserSessionId: resolveBrowserSessionId(nextSession),
216
+ description: sessionDescription,
217
+ ...(merchantName ? { merchantName } : {}),
218
+ page: {
219
+ ...(currentPageRef ? { ref: currentPageRef } : {}),
220
+ ...(currentPage?.url ? { url: currentPage.url } : {}),
221
+ ...(currentPage?.title ? { title: currentPage.title } : {}),
222
+ },
223
+ run: {
224
+ id: run.runId,
225
+ started_at: run.startedAt,
226
+ source: run.source,
227
+ status: run.status,
228
+ ...(run.profile ? { profile: run.profile } : {}),
229
+ ...(run.hostHints?.[0] ? { site_host: run.hostHints[0] } : {}),
230
+ protected_run: run.protectedRun,
231
+ },
232
+ step: {
233
+ id: step.stepId,
234
+ ordinal: step.ordinal,
235
+ command: step.command,
236
+ started_at: step.startedAt,
237
+ ...(currentPageRef ? { page_ref: currentPageRef } : {}),
238
+ },
239
+ context: {
240
+ client: 'magicpay-cli',
241
+ },
242
+ metadata: {
243
+ had_previous_run: replacedPreviousRun,
244
+ },
245
+ }));
246
+ nextSession.activeRunId = run.runId;
247
+ nextSession.browserSessionId = resolveBrowserSessionId(nextSession);
248
+ nextSession.intentSessionId = remoteSession.session.id;
249
+ nextSession.currentRequestId = remoteSession.current_request?.id ?? undefined;
250
+ nextSession.lastKnownStatus = remoteSession.session.status;
251
+ nextSession.lastEventSeq = remoteSession.session.last_event_seq;
252
+ saveMagicPaySession(nextSession);
253
+ await finalizeStartSessionStepBestEffort(run.runId, step.stepId, {
254
+ success: true,
255
+ outcomeType: 'workflow_session_started',
256
+ message: 'Workflow session started.',
257
+ });
258
+ return {
259
+ success: true,
260
+ runId: run.runId,
261
+ startedAt: run.startedAt,
262
+ browserSessionId: resolveBrowserSessionId(nextSession),
263
+ intentSessionId: remoteSession.session.id,
264
+ lastEventSeq: remoteSession.session.last_event_seq,
265
+ status: remoteSession.session.status,
266
+ currentRequestId: remoteSession.current_request?.id ?? null,
267
+ replacedPreviousRun,
268
+ profile: nextSession.profile,
269
+ pageRef: currentPageRef,
270
+ url: currentPage?.url,
271
+ title: currentPage?.title,
272
+ };
273
+ }
274
+ catch (error) {
275
+ const reason = formatUnknownError(error);
276
+ await finalizeStartSessionStepBestEffort(run.runId, step.stepId, {
277
+ success: false,
278
+ outcomeType: 'backend_session_start_failed',
279
+ message: 'Workflow session start failed.',
280
+ reason,
281
+ });
282
+ finalizeRunRecordBestEffort(run.runId, {
283
+ status: 'failed',
284
+ success: false,
285
+ outcomeType: 'backend_session_start_failed',
286
+ message: 'Workflow session start failed.',
287
+ reason,
288
+ });
289
+ saveMagicPaySession(nextSession);
290
+ return {
291
+ success: false,
292
+ error: 'backend_session_start_failed',
293
+ outcomeType: 'failed',
294
+ message: 'Session start failed.',
295
+ reason,
296
+ };
297
+ }
298
+ }
@@ -0,0 +1,27 @@
1
+ import { type AgentBackendProfile } from '../gateway.js';
2
+ import { type CliUpdateNotice } from '../update-check.js';
3
+ export type StatusSuccess = {
4
+ success: true;
5
+ healthy: true;
6
+ message: 'MagicPay gateway setup is healthy.';
7
+ apiUrl: string;
8
+ checkedEndpoint: string;
9
+ agent: AgentBackendProfile;
10
+ cliUpdate?: CliUpdateNotice;
11
+ };
12
+ export type StatusFailure = {
13
+ success: false;
14
+ error: 'agent_status_failed';
15
+ outcomeType: 'blocked' | 'failed';
16
+ message: 'MagicPay gateway status check failed.';
17
+ reason: string;
18
+ apiUrl: string;
19
+ checkedEndpoint: string;
20
+ httpStatus?: number;
21
+ cliUpdate?: CliUpdateNotice;
22
+ };
23
+ export type StatusResult = StatusSuccess | StatusFailure;
24
+ export declare function status(options?: {
25
+ fetchImpl?: typeof fetch;
26
+ }): Promise<StatusResult>;
27
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE7E,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,oCAAoC,CAAC;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,qBAAqB,CAAC;IAC7B,WAAW,EAAE,SAAS,GAAG,QAAQ,CAAC;IAClC,OAAO,EAAE,uCAAuC,CAAC;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,aAAa,CAAC;AAyBzD,wBAAsB,MAAM,CAC1B,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GACzC,OAAO,CAAC,YAAY,CAAC,CAkCvB"}
@@ -0,0 +1,51 @@
1
+ import { buildMagicPayAgentMeUrl, getAuthenticatedAgent, MagicPayRequestError, resolveMagicPayGatewayConfig, } from '../gateway.js';
2
+ import { checkForCliUpdate } from '../update-check.js';
3
+ function buildFailure(params) {
4
+ return {
5
+ success: false,
6
+ error: 'agent_status_failed',
7
+ outcomeType: params.httpStatus === 401 || params.httpStatus === 403 || params.httpStatus === 404
8
+ ? 'blocked'
9
+ : 'failed',
10
+ message: 'MagicPay gateway status check failed.',
11
+ reason: params.reason,
12
+ apiUrl: params.apiUrl,
13
+ checkedEndpoint: params.checkedEndpoint,
14
+ ...(params.cliUpdate ? { cliUpdate: params.cliUpdate } : {}),
15
+ ...(params.httpStatus !== undefined ? { httpStatus: params.httpStatus } : {}),
16
+ };
17
+ }
18
+ export async function status(options = {}) {
19
+ const cliUpdate = await checkForCliUpdate({ fetchImpl: options.fetchImpl }).catch(() => null);
20
+ const gateway = resolveMagicPayGatewayConfig();
21
+ const checkedEndpoint = buildMagicPayAgentMeUrl(gateway.apiUrl);
22
+ try {
23
+ const agent = await getAuthenticatedAgent(options);
24
+ return {
25
+ success: true,
26
+ healthy: true,
27
+ message: 'MagicPay gateway setup is healthy.',
28
+ apiUrl: gateway.apiUrl,
29
+ checkedEndpoint,
30
+ agent,
31
+ ...(cliUpdate ? { cliUpdate } : {}),
32
+ };
33
+ }
34
+ catch (error) {
35
+ if (error instanceof MagicPayRequestError) {
36
+ return buildFailure({
37
+ apiUrl: gateway.apiUrl,
38
+ checkedEndpoint,
39
+ reason: error.message,
40
+ httpStatus: error.status,
41
+ cliUpdate,
42
+ });
43
+ }
44
+ return buildFailure({
45
+ apiUrl: gateway.apiUrl,
46
+ checkedEndpoint,
47
+ reason: error instanceof Error ? error.message : String(error),
48
+ cliUpdate,
49
+ });
50
+ }
51
+ }
@@ -0,0 +1,36 @@
1
+ import type { BrowserSessionState } from '@mercuryo-ai/agentbrowse';
2
+ export type ResolvedSubmitTarget = {
3
+ targetRef: string;
4
+ pageRef: string;
5
+ label?: string;
6
+ surfaceRef?: string;
7
+ allowedActions: Array<'click' | 'press'>;
8
+ resolution: 'unique_form_bound_submit_target';
9
+ };
10
+ export type SubmitFormResult = {
11
+ success: true;
12
+ outcomeType: 'submitted';
13
+ fillRef: string;
14
+ submitTargetRef: string;
15
+ pageRef: string;
16
+ url?: string;
17
+ title?: string;
18
+ submissionState: 'progress_observed' | 'same_page_result_observed' | 'navigation_observed';
19
+ message: string;
20
+ reason: string;
21
+ } | {
22
+ success: false;
23
+ error: 'browser_connection_failed' | 'page_resolution_failed' | 'unknown_fill_ref' | 'fillable_form_absent' | 'submit_target_not_found' | 'submit_binding_stale' | 'validation_blocked' | 'no_observable_progress';
24
+ outcomeType: 'binding_stale' | 'blocked';
25
+ fillRef: string;
26
+ submitTargetRef?: string;
27
+ pageRef?: string;
28
+ url?: string;
29
+ title?: string;
30
+ message: string;
31
+ reason: string;
32
+ nextAction: 'find-form' | 'ask-user';
33
+ };
34
+ export declare function getResolvedSubmitTarget(session: BrowserSessionState, fillRef: string): ResolvedSubmitTarget | null;
35
+ export declare function submitForm(session: BrowserSessionState, fillRef: string): Promise<SubmitFormResult>;
36
+ //# sourceMappingURL=submit-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"submit-form.d.ts","sourceRoot":"","sources":["../../src/commands/submit-form.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAQpE,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC;IACzC,UAAU,EAAE,iCAAiC,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,gBAAgB,GACxB;IACE,OAAO,EAAE,IAAI,CAAC;IACd,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,mBAAmB,GAAG,2BAA2B,GAAG,qBAAqB,CAAC;IAC3F,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EACD,2BAA2B,GAC3B,wBAAwB,GACxB,kBAAkB,GAClB,sBAAsB,GACtB,yBAAyB,GACzB,sBAAsB,GACtB,oBAAoB,GACpB,wBAAwB,CAAC;IAC7B,WAAW,EAAE,eAAe,GAAG,SAAS,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,WAAW,GAAG,UAAU,CAAC;CACtC,CAAC;AAgHN,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,MAAM,GACd,oBAAoB,GAAG,IAAI,CAG7B;AAkCD,wBAAsB,UAAU,CAC9B,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,gBAAgB,CAAC,CAkK3B"}
@@ -0,0 +1,242 @@
1
+ import { act } from '../browser-bridge/act.js';
2
+ import { getFillableForm } from '../fillable-form-state.js';
3
+ function readString(value) {
4
+ return typeof value === 'string' ? value : undefined;
5
+ }
6
+ function readStringArray(value) {
7
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : [];
8
+ }
9
+ function submitTargetLabelMatches(label) {
10
+ return /submit|continue|sign in|log in|pay|buy|book|reserve|checkout|next|done|оплат|войти|далее/i.test(label ?? '');
11
+ }
12
+ function listSubmitCandidates(session, fillRef) {
13
+ const form = getFillableForm(session, fillRef);
14
+ if (!form) {
15
+ return [];
16
+ }
17
+ const fieldTargets = form.fields
18
+ .map((field) => session.runtime?.targets?.[field.targetRef])
19
+ .filter((target) => Boolean(target));
20
+ const fieldSurfaceRefs = new Set(fieldTargets
21
+ .map((target) => target.surfaceRef?.trim())
22
+ .filter((surfaceRef) => Boolean(surfaceRef)));
23
+ const fieldTargetRefs = new Set(fieldTargets.map((target) => target.ref));
24
+ return Object.values(session.runtime?.targets ?? {})
25
+ .filter((target) => Boolean(target) &&
26
+ target.pageRef === form.pageRef &&
27
+ target.lifecycle === 'live' &&
28
+ target.capability === 'actionable' &&
29
+ (target.allowedActions.includes('click') || target.allowedActions.includes('press')) &&
30
+ (target.acceptancePolicy === 'submit' || target.inputType === 'submit'))
31
+ .map((target) => {
32
+ let score = 0;
33
+ if (form.scopeRef && target.surfaceRef === form.scopeRef) {
34
+ score += 100;
35
+ }
36
+ if (target.surfaceRef && fieldSurfaceRefs.has(target.surfaceRef)) {
37
+ score += 60;
38
+ }
39
+ if (target.ownerRef && fieldTargetRefs.has(target.ownerRef)) {
40
+ score += 30;
41
+ }
42
+ if (target.acceptancePolicy === 'submit') {
43
+ score += 20;
44
+ }
45
+ if (target.inputType === 'submit') {
46
+ score += 10;
47
+ }
48
+ if (submitTargetLabelMatches(target.label)) {
49
+ score += 5;
50
+ }
51
+ return { target, score };
52
+ })
53
+ .sort((left, right) => right.score - left.score || (left.target.label ?? '').localeCompare(right.target.label ?? ''));
54
+ }
55
+ function resolveSubmitTarget(session, fillRef) {
56
+ const candidates = listSubmitCandidates(session, fillRef);
57
+ const best = candidates[0];
58
+ if (!best || best.score <= 0) {
59
+ return null;
60
+ }
61
+ const tied = candidates.filter((candidate) => candidate.score === best.score);
62
+ if (tied.length > 1) {
63
+ return null;
64
+ }
65
+ return best.target;
66
+ }
67
+ function toResolvedSubmitTarget(target) {
68
+ return {
69
+ targetRef: target.ref,
70
+ pageRef: target.pageRef,
71
+ ...(target.label ? { label: target.label } : {}),
72
+ ...(target.surfaceRef ? { surfaceRef: target.surfaceRef } : {}),
73
+ allowedActions: target.allowedActions.filter((action) => action === 'click' || action === 'press'),
74
+ resolution: 'unique_form_bound_submit_target',
75
+ };
76
+ }
77
+ export function getResolvedSubmitTarget(session, fillRef) {
78
+ const target = resolveSubmitTarget(session, fillRef);
79
+ return target ? toResolvedSubmitTarget(target) : null;
80
+ }
81
+ function pageSnapshot(session, pageRef) {
82
+ if (!pageRef) {
83
+ return {};
84
+ }
85
+ const page = session.runtime?.pages?.[pageRef];
86
+ return {
87
+ pageRef,
88
+ ...(page?.url ? { url: page.url } : {}),
89
+ ...(page?.title ? { title: page.title } : {}),
90
+ };
91
+ }
92
+ function submissionStateFor(initialPageRef, result) {
93
+ const pageRef = readString(result.pageRef);
94
+ if (pageRef && pageRef !== initialPageRef) {
95
+ return 'navigation_observed';
96
+ }
97
+ if (readStringArray(result.attempts).some((attempt) => attempt.startsWith('submit-resolution:'))) {
98
+ return 'same_page_result_observed';
99
+ }
100
+ return 'progress_observed';
101
+ }
102
+ export async function submitForm(session, fillRef) {
103
+ const fillableForm = getFillableForm(session, fillRef);
104
+ if (!fillableForm) {
105
+ return {
106
+ success: false,
107
+ error: 'unknown_fill_ref',
108
+ outcomeType: 'blocked',
109
+ fillRef,
110
+ message: 'Protected form submit was not started because the requested fill reference is unknown.',
111
+ reason: 'The provided fillRef does not match any currently observed protected form.',
112
+ nextAction: 'find-form',
113
+ };
114
+ }
115
+ if (fillableForm.presence === 'absent') {
116
+ return {
117
+ success: false,
118
+ error: 'fillable_form_absent',
119
+ outcomeType: 'blocked',
120
+ fillRef,
121
+ ...pageSnapshot(session, fillableForm.pageRef),
122
+ message: 'Protected form submit was not started because the requested form is no longer present on the page.',
123
+ reason: 'MagicPay already marked this protected form as absent, so a fresh `find-form` pass is required before submit can continue.',
124
+ nextAction: 'find-form',
125
+ };
126
+ }
127
+ const submitTarget = resolveSubmitTarget(session, fillRef);
128
+ if (!submitTarget) {
129
+ return {
130
+ success: false,
131
+ error: 'submit_target_not_found',
132
+ outcomeType: 'blocked',
133
+ fillRef,
134
+ ...pageSnapshot(session, fillableForm.pageRef),
135
+ message: 'Protected form submit could not find a submit control for the selected form.',
136
+ reason: 'MagicPay could not resolve one live submit-like target that belongs to the selected protected form.',
137
+ nextAction: 'find-form',
138
+ };
139
+ }
140
+ const result = await act(session, submitTarget.ref, 'click');
141
+ const currentPageRef = readString(result.pageRef) ?? fillableForm.pageRef;
142
+ const currentPage = pageSnapshot(session, currentPageRef);
143
+ if (result.success) {
144
+ return {
145
+ success: true,
146
+ outcomeType: 'submitted',
147
+ fillRef,
148
+ submitTargetRef: submitTarget.ref,
149
+ pageRef: currentPageRef,
150
+ ...(currentPage.url ? { url: currentPage.url } : {}),
151
+ ...(currentPage.title ? { title: currentPage.title } : {}),
152
+ submissionState: submissionStateFor(fillableForm.pageRef, result),
153
+ message: 'Protected form submit completed successfully.',
154
+ reason: 'MagicPay activated the submit control bound to the selected protected form.',
155
+ };
156
+ }
157
+ if (result.error === 'browser_connection_failed') {
158
+ return {
159
+ success: false,
160
+ error: 'browser_connection_failed',
161
+ outcomeType: 'blocked',
162
+ fillRef,
163
+ submitTargetRef: submitTarget.ref,
164
+ ...currentPage,
165
+ message: 'Protected form submit could not reconnect to the browser.',
166
+ reason: readString(result.reason) ?? 'AgentBrowse failed to reconnect to the browser session.',
167
+ nextAction: 'ask-user',
168
+ };
169
+ }
170
+ if (result.error === 'stale_target' ||
171
+ result.error === 'stale_target_ref' ||
172
+ result.error === 'target_surface_not_live' ||
173
+ result.error === 'target_surface_unavailable' ||
174
+ result.error === 'target_surface_inactive') {
175
+ return {
176
+ success: false,
177
+ error: 'submit_binding_stale',
178
+ outcomeType: 'binding_stale',
179
+ fillRef,
180
+ submitTargetRef: submitTarget.ref,
181
+ ...currentPage,
182
+ message: 'Protected form submit was blocked because the saved submit binding is no longer valid.',
183
+ reason: readString(result.reason) ?? 'The submit control binding is stale.',
184
+ nextAction: 'find-form',
185
+ };
186
+ }
187
+ if (result.error === 'unknown_target_ref' ||
188
+ result.error === 'target_not_actionable' ||
189
+ result.error === 'target_disabled' ||
190
+ result.error === 'target_gated' ||
191
+ result.error === 'action_not_allowed_for_target') {
192
+ return {
193
+ success: false,
194
+ error: 'submit_target_not_found',
195
+ outcomeType: 'blocked',
196
+ fillRef,
197
+ submitTargetRef: submitTarget.ref,
198
+ ...currentPage,
199
+ message: 'Protected form submit could not find a usable submit control for the selected form.',
200
+ reason: readString(result.reason) ?? 'The previously resolved submit control is no longer usable.',
201
+ nextAction: 'find-form',
202
+ };
203
+ }
204
+ if (result.error === 'validation_blocked') {
205
+ return {
206
+ success: false,
207
+ error: 'validation_blocked',
208
+ outcomeType: 'blocked',
209
+ fillRef,
210
+ submitTargetRef: submitTarget.ref,
211
+ ...currentPage,
212
+ message: 'Protected form submit was processed, but the page surfaced validation blockers that require human attention.',
213
+ reason: readString(result.reason) ??
214
+ 'The page surfaced validation blockers after the submit action was accepted.',
215
+ nextAction: 'ask-user',
216
+ };
217
+ }
218
+ if (result.error === 'no_observable_progress' || result.error === 'act_failed') {
219
+ return {
220
+ success: false,
221
+ error: 'no_observable_progress',
222
+ outcomeType: 'blocked',
223
+ fillRef,
224
+ submitTargetRef: submitTarget.ref,
225
+ ...currentPage,
226
+ message: 'Protected form submit produced no defensible progress signal on the page.',
227
+ reason: readString(result.reason) ?? 'No observable page progress was detected after submit.',
228
+ nextAction: 'ask-user',
229
+ };
230
+ }
231
+ return {
232
+ success: false,
233
+ error: 'page_resolution_failed',
234
+ outcomeType: 'blocked',
235
+ fillRef,
236
+ submitTargetRef: submitTarget.ref,
237
+ ...currentPage,
238
+ message: 'Protected form submit could not resolve the current page state.',
239
+ reason: readString(result.reason) ?? 'AgentBrowse returned an unsupported submit failure shape.',
240
+ nextAction: 'ask-user',
241
+ };
242
+ }
@@ -0,0 +1,2 @@
1
+ export { getMagicPayConfigPath as getConfigPath, getMagicPayHomeDir as getStateDir, readMagicPayConfig as readConfig, writeMagicPayConfig as writeConfig, type MagicPaySharedConfig as MagicPayCliConfig, } from '@mercuryo-ai/magicpay-home';
2
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,IAAI,aAAa,EACtC,kBAAkB,IAAI,WAAW,EACjC,kBAAkB,IAAI,UAAU,EAChC,mBAAmB,IAAI,WAAW,EAClC,KAAK,oBAAoB,IAAI,iBAAiB,GAC/C,MAAM,4BAA4B,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1 @@
1
+ export { getMagicPayConfigPath as getConfigPath, getMagicPayHomeDir as getStateDir, readMagicPayConfig as readConfig, writeMagicPayConfig as writeConfig, } from '@mercuryo-ai/magicpay-home';
@@ -0,0 +1,5 @@
1
+ import type { BrowserSessionState } from '@mercuryo-ai/agentbrowse';
2
+ import type { ProtectedFillForm } from '@mercuryo-ai/agentbrowse/protected-fill';
3
+ export declare function getFillableForm(session: BrowserSessionState, fillRef: string): ProtectedFillForm | null;
4
+ export declare function listFillableForms(session: BrowserSessionState): ProtectedFillForm[];
5
+ //# sourceMappingURL=fillable-form-state.d.ts.map