@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.
- package/README.md +56 -0
- package/dist/agentbrowse-assistive.d.ts +2 -0
- package/dist/agentbrowse-assistive.d.ts.map +1 -0
- package/dist/agentbrowse-assistive.js +66 -0
- package/dist/assistive-llm-client.d.ts +13 -0
- package/dist/assistive-llm-client.d.ts.map +1 -0
- package/dist/assistive-llm-client.js +152 -0
- package/dist/browser-bridge/act.d.ts +5 -0
- package/dist/browser-bridge/act.d.ts.map +1 -0
- package/dist/browser-bridge/act.js +8 -0
- package/dist/browser-bridge/observe.d.ts +5 -0
- package/dist/browser-bridge/observe.d.ts.map +1 -0
- package/dist/browser-bridge/observe.js +53 -0
- package/dist/browser-home.d.ts +7 -0
- package/dist/browser-home.d.ts.map +1 -0
- package/dist/browser-home.js +34 -0
- package/dist/commands/act.d.ts +5 -0
- package/dist/commands/act.d.ts.map +1 -0
- package/dist/commands/act.js +8 -0
- package/dist/commands/attach.d.ts +4 -0
- package/dist/commands/attach.d.ts.map +1 -0
- package/dist/commands/attach.js +10 -0
- package/dist/commands/browser-status.d.ts +5 -0
- package/dist/commands/browser-status.d.ts.map +1 -0
- package/dist/commands/browser-status.js +5 -0
- package/dist/commands/close.d.ts +4 -0
- package/dist/commands/close.d.ts.map +1 -0
- package/dist/commands/close.js +11 -0
- package/dist/commands/end-session.d.ts +22 -0
- package/dist/commands/end-session.d.ts.map +1 -0
- package/dist/commands/end-session.js +104 -0
- package/dist/commands/extract.d.ts +4 -0
- package/dist/commands/extract.d.ts.map +1 -0
- package/dist/commands/extract.js +14 -0
- package/dist/commands/fill-secret.d.ts +10 -0
- package/dist/commands/fill-secret.d.ts.map +1 -0
- package/dist/commands/fill-secret.js +483 -0
- package/dist/commands/find-form.d.ts +57 -0
- package/dist/commands/find-form.d.ts.map +1 -0
- package/dist/commands/find-form.js +138 -0
- package/dist/commands/get-secrets-catalog.d.ts +13 -0
- package/dist/commands/get-secrets-catalog.d.ts.map +1 -0
- package/dist/commands/get-secrets-catalog.js +103 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +28 -0
- package/dist/commands/launch.d.ts +4 -0
- package/dist/commands/launch.d.ts.map +1 -0
- package/dist/commands/launch.js +10 -0
- package/dist/commands/mock-approve-secret.d.ts +21 -0
- package/dist/commands/mock-approve-secret.d.ts.map +1 -0
- package/dist/commands/mock-approve-secret.js +69 -0
- package/dist/commands/mock-deny-secret.d.ts +21 -0
- package/dist/commands/mock-deny-secret.d.ts.map +1 -0
- package/dist/commands/mock-deny-secret.js +69 -0
- package/dist/commands/navigate.d.ts +4 -0
- package/dist/commands/navigate.d.ts.map +1 -0
- package/dist/commands/navigate.js +9 -0
- package/dist/commands/observe.d.ts +5 -0
- package/dist/commands/observe.d.ts.map +1 -0
- package/dist/commands/observe.js +53 -0
- package/dist/commands/poll-secret.d.ts +13 -0
- package/dist/commands/poll-secret.d.ts.map +1 -0
- package/dist/commands/poll-secret.js +87 -0
- package/dist/commands/request-secret.d.ts +15 -0
- package/dist/commands/request-secret.d.ts.map +1 -0
- package/dist/commands/request-secret.js +235 -0
- package/dist/commands/screenshot.d.ts +4 -0
- package/dist/commands/screenshot.d.ts.map +1 -0
- package/dist/commands/screenshot.js +11 -0
- package/dist/commands/solve-captcha.d.ts +19 -0
- package/dist/commands/solve-captcha.d.ts.map +1 -0
- package/dist/commands/solve-captcha.js +63 -0
- package/dist/commands/start-session.d.ts +28 -0
- package/dist/commands/start-session.d.ts.map +1 -0
- package/dist/commands/start-session.js +298 -0
- package/dist/commands/status.d.ts +27 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +51 -0
- package/dist/commands/submit-form.d.ts +36 -0
- package/dist/commands/submit-form.d.ts.map +1 -0
- package/dist/commands/submit-form.js +242 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +1 -0
- package/dist/fillable-form-state.d.ts +5 -0
- package/dist/fillable-form-state.d.ts.map +1 -0
- package/dist/fillable-form-state.js +22 -0
- package/dist/gateway.d.ts +10 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +49 -0
- package/dist/generated/build-config.d.ts +2 -0
- package/dist/generated/build-config.d.ts.map +1 -0
- package/dist/generated/build-config.js +2 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +398 -0
- package/dist/mock-secret-cabinet.d.ts +37 -0
- package/dist/mock-secret-cabinet.d.ts.map +1 -0
- package/dist/mock-secret-cabinet.js +321 -0
- package/dist/mock-secret-store.d.ts +12 -0
- package/dist/mock-secret-store.d.ts.map +1 -0
- package/dist/mock-secret-store.js +108 -0
- package/dist/mock-stored-secrets.json +180 -0
- package/dist/otel-exporter.d.ts +58 -0
- package/dist/otel-exporter.d.ts.map +1 -0
- package/dist/otel-exporter.js +241 -0
- package/dist/otel-projector.d.ts +59 -0
- package/dist/otel-projector.d.ts.map +1 -0
- package/dist/otel-projector.js +325 -0
- package/dist/output.d.ts +10 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +29 -0
- package/dist/package-version.d.ts +2 -0
- package/dist/package-version.d.ts.map +1 -0
- package/dist/package-version.js +14 -0
- package/dist/protected-exposure.d.ts +4 -0
- package/dist/protected-exposure.d.ts.map +1 -0
- package/dist/protected-exposure.js +17 -0
- package/dist/request-output.d.ts +4 -0
- package/dist/request-output.d.ts.map +1 -0
- package/dist/request-output.js +27 -0
- package/dist/run-store.d.ts +130 -0
- package/dist/run-store.d.ts.map +1 -0
- package/dist/run-store.js +245 -0
- package/dist/secret-backend.d.ts +28 -0
- package/dist/secret-backend.d.ts.map +1 -0
- package/dist/secret-backend.js +335 -0
- package/dist/secret-state.d.ts +7 -0
- package/dist/secret-state.d.ts.map +1 -0
- package/dist/secret-state.js +26 -0
- package/dist/session-backend.d.ts +34 -0
- package/dist/session-backend.d.ts.map +1 -0
- package/dist/session-backend.js +36 -0
- package/dist/update-check.d.ts +13 -0
- package/dist/update-check.d.ts.map +1 -0
- package/dist/update-check.js +175 -0
- package/dist/workflow-session-completion.d.ts +32 -0
- package/dist/workflow-session-completion.d.ts.map +1 -0
- package/dist/workflow-session-completion.js +149 -0
- package/dist/workflow-state.d.ts +2 -0
- package/dist/workflow-state.d.ts.map +1 -0
- package/dist/workflow-state.js +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACxB,MAAM,0BAA0B,CAAC;AAGlC,YAAY,EAAE,aAAa,EAAE,CAAC;AAO9B,wBAAsB,OAAO,CAC3B,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CAOxB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { extract as extractPrimitive, } from '@mercuryo-ai/agentbrowse';
|
|
2
|
+
import { saveMagicPaySession } from '../workflow-state.js';
|
|
3
|
+
async function ensureAssistiveRuntime() {
|
|
4
|
+
const { ensureMagicPayAgentbrowseAssistiveRuntime } = await import('../agentbrowse-assistive.js');
|
|
5
|
+
ensureMagicPayAgentbrowseAssistiveRuntime();
|
|
6
|
+
}
|
|
7
|
+
export async function extract(session, schema, scopeRef) {
|
|
8
|
+
await ensureAssistiveRuntime();
|
|
9
|
+
const result = await extractPrimitive(session, schema, scopeRef);
|
|
10
|
+
if (result.success || result.staleScope === true) {
|
|
11
|
+
saveMagicPaySession(session);
|
|
12
|
+
}
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type MagicPaySession } from '../workflow-state.js';
|
|
2
|
+
export type FillSecretResult = ({
|
|
3
|
+
success: true;
|
|
4
|
+
} & Record<string, unknown>) | ({
|
|
5
|
+
success: false;
|
|
6
|
+
error: string;
|
|
7
|
+
outcomeType?: string;
|
|
8
|
+
} & Record<string, unknown>);
|
|
9
|
+
export declare function fillSecret(session: MagicPaySession, fillRef: string, requestId: string): Promise<FillSecretResult>;
|
|
10
|
+
//# sourceMappingURL=fill-secret.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fill-secret.d.ts","sourceRoot":"","sources":["../../src/commands/fill-secret.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEjF,MAAM,MAAM,gBAAgB,GACxB,CAAC;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAC7C,CAAC;IACC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAsGjC,wBAAsB,UAAU,CAC9B,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,gBAAgB,CAAC,CAse3B"}
|
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import { prepareProtectedFillFromClaim } from '@mercuryo-ai/magicpay-sdk/agentbrowse';
|
|
2
|
+
import { describeSecretRequestStatus, evaluateSecretRequestForFill, resolveCatalogHost, } from '@mercuryo-ai/magicpay-sdk/core';
|
|
3
|
+
import { buildProtectedExposureArtifacts as buildProtectedExposureArtifactsFromFill, fillProtectedForm, } from '@mercuryo-ai/agentbrowse/protected-fill';
|
|
4
|
+
import { getFillableForm } from '../fillable-form-state.js';
|
|
5
|
+
import { exportRunStepToOtlpHttpJsonBestEffort } from '../otel-exporter.js';
|
|
6
|
+
import { activateProtectedExposure } from '../protected-exposure.js';
|
|
7
|
+
import { serializeSecretRequestContext } from '../request-output.js';
|
|
8
|
+
import { finishRunStep, startRunStep } from '../run-store.js';
|
|
9
|
+
import { getMagicPayClient } from '../secret-backend.js';
|
|
10
|
+
import { getSecretCatalog, getSecretRequestSnapshot, saveSecretRequestSnapshot } from '../secret-state.js';
|
|
11
|
+
import { observe } from './observe.js';
|
|
12
|
+
import { getResolvedSubmitTarget, submitForm } from './submit-form.js';
|
|
13
|
+
import { saveMagicPaySession } from '../workflow-state.js';
|
|
14
|
+
function withFillableFormPresence(fillableForm, payload) {
|
|
15
|
+
return {
|
|
16
|
+
...payload,
|
|
17
|
+
fillableFormPresence: fillableForm.presence ?? 'present',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function failureForUnfulfilledRequest(fillableForm, request, fillRef, requestId) {
|
|
21
|
+
const statusContract = describeSecretRequestStatus(request.status, request.requestType);
|
|
22
|
+
return {
|
|
23
|
+
success: false,
|
|
24
|
+
...withFillableFormPresence(fillableForm, {
|
|
25
|
+
error: 'secret_request_not_ready',
|
|
26
|
+
...(statusContract.outcomeType ? { outcomeType: statusContract.outcomeType } : {}),
|
|
27
|
+
message: request.status === 'pending'
|
|
28
|
+
? 'Protected fill is waiting for user approval in MagicPay.'
|
|
29
|
+
: statusContract.message,
|
|
30
|
+
reason: statusContract.reason,
|
|
31
|
+
fillRef,
|
|
32
|
+
requestId,
|
|
33
|
+
...serializeSecretRequestContext(request),
|
|
34
|
+
requestStatus: request.status,
|
|
35
|
+
nextAction: statusContract.nextAction ?? 'poll-secret',
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function markRequestClaimed(session, request, claimedAt) {
|
|
40
|
+
const nextRequest = saveSecretRequestSnapshot(session, {
|
|
41
|
+
...request,
|
|
42
|
+
claimedAt,
|
|
43
|
+
updatedAt: claimedAt,
|
|
44
|
+
});
|
|
45
|
+
if (session.currentRequestId === request.requestId) {
|
|
46
|
+
session.currentRequestId = undefined;
|
|
47
|
+
session.lastKnownStatus = 'in_progress';
|
|
48
|
+
}
|
|
49
|
+
saveMagicPaySession(session);
|
|
50
|
+
return nextRequest;
|
|
51
|
+
}
|
|
52
|
+
function formatUnknownError(error) {
|
|
53
|
+
if (error instanceof Error) {
|
|
54
|
+
return error.message;
|
|
55
|
+
}
|
|
56
|
+
if (typeof error === 'string') {
|
|
57
|
+
return error;
|
|
58
|
+
}
|
|
59
|
+
if (error && typeof error === 'object') {
|
|
60
|
+
try {
|
|
61
|
+
return JSON.stringify(error);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return Object.prototype.toString.call(error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return String(error);
|
|
68
|
+
}
|
|
69
|
+
function buildSuccessfulFillPayload(fillableForm, request, fillRef, requestId, filledFields) {
|
|
70
|
+
return withFillableFormPresence(fillableForm, {
|
|
71
|
+
fillRef,
|
|
72
|
+
requestId,
|
|
73
|
+
...serializeSecretRequestContext(request),
|
|
74
|
+
requestStatus: request.status,
|
|
75
|
+
filledFields,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async function ensureAssistiveRuntime() {
|
|
79
|
+
const { ensureMagicPayAgentbrowseAssistiveRuntime } = await import('../agentbrowse-assistive.js');
|
|
80
|
+
ensureMagicPayAgentbrowseAssistiveRuntime();
|
|
81
|
+
}
|
|
82
|
+
export async function fillSecret(session, fillRef, requestId) {
|
|
83
|
+
const step = session.activeRunId
|
|
84
|
+
? startRunStep({
|
|
85
|
+
runId: session.activeRunId,
|
|
86
|
+
command: 'fill-secret',
|
|
87
|
+
input: {
|
|
88
|
+
fillRef,
|
|
89
|
+
requestId,
|
|
90
|
+
},
|
|
91
|
+
refs: {
|
|
92
|
+
pageRef: session.runtime?.currentPageRef,
|
|
93
|
+
fillRef,
|
|
94
|
+
requestId,
|
|
95
|
+
},
|
|
96
|
+
protectedStep: true,
|
|
97
|
+
})
|
|
98
|
+
: null;
|
|
99
|
+
const finalizeStep = async (result) => {
|
|
100
|
+
if (step) {
|
|
101
|
+
finishRunStep({
|
|
102
|
+
runId: step.runId,
|
|
103
|
+
stepId: step.stepId,
|
|
104
|
+
success: result.success,
|
|
105
|
+
...('outcomeType' in result && typeof result.outcomeType === 'string'
|
|
106
|
+
? { outcomeType: result.outcomeType }
|
|
107
|
+
: {}),
|
|
108
|
+
...('message' in result && typeof result.message === 'string'
|
|
109
|
+
? { message: result.message }
|
|
110
|
+
: {}),
|
|
111
|
+
...('reason' in result && typeof result.reason === 'string'
|
|
112
|
+
? { reason: result.reason }
|
|
113
|
+
: {}),
|
|
114
|
+
});
|
|
115
|
+
await exportRunStepToOtlpHttpJsonBestEffort(step.runId, step.stepId);
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
};
|
|
119
|
+
try {
|
|
120
|
+
if (!session.intentSessionId) {
|
|
121
|
+
return finalizeStep({
|
|
122
|
+
success: false,
|
|
123
|
+
error: 'workflow_session_required',
|
|
124
|
+
outcomeType: 'blocked',
|
|
125
|
+
message: 'Protected fill was not started because no active workflow session is bound to this browser.',
|
|
126
|
+
reason: 'Run `magicpay start-session` first so MagicPay has a workflow session for protected requests.',
|
|
127
|
+
fillRef,
|
|
128
|
+
requestId,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const fillableForm = getFillableForm(session, fillRef);
|
|
132
|
+
if (!fillableForm) {
|
|
133
|
+
return finalizeStep({
|
|
134
|
+
success: false,
|
|
135
|
+
error: 'unknown_fill_ref',
|
|
136
|
+
outcomeType: 'blocked',
|
|
137
|
+
message: 'Protected fill was not started because the requested fill reference is unknown.',
|
|
138
|
+
reason: 'The provided fillRef does not match any currently observed protected fill target.',
|
|
139
|
+
fillRef,
|
|
140
|
+
requestId,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (fillableForm.presence === 'absent') {
|
|
144
|
+
return finalizeStep({
|
|
145
|
+
success: false,
|
|
146
|
+
...withFillableFormPresence(fillableForm, {
|
|
147
|
+
error: 'fillable_form_absent',
|
|
148
|
+
outcomeType: 'blocked',
|
|
149
|
+
message: 'Protected fill was not started because the requested fill target is no longer present on the page.',
|
|
150
|
+
reason: 'MagicPay CLI already observed this protected fill target as absent, so a new observe pass is required before protected fill can continue.',
|
|
151
|
+
fillRef,
|
|
152
|
+
requestId,
|
|
153
|
+
}),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const client = getMagicPayClient();
|
|
157
|
+
const latestRequest = getSecretRequestSnapshot(session, requestId);
|
|
158
|
+
if (!latestRequest) {
|
|
159
|
+
return finalizeStep({
|
|
160
|
+
success: false,
|
|
161
|
+
error: 'secret_request_not_found',
|
|
162
|
+
outcomeType: 'blocked',
|
|
163
|
+
message: 'Protected fill was not started because the requested secret request does not exist.',
|
|
164
|
+
reason: 'Run `magicpay request-secret` and `magicpay poll-secret` first so the latest request state is cached locally.',
|
|
165
|
+
fillRef,
|
|
166
|
+
requestId,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
const pageState = session.runtime?.pages?.[fillableForm.pageRef];
|
|
170
|
+
const observedHost = pageState?.url ? resolveCatalogHost(pageState.url) : null;
|
|
171
|
+
const fillReadiness = evaluateSecretRequestForFill(latestRequest, {
|
|
172
|
+
fillRef,
|
|
173
|
+
observedHost,
|
|
174
|
+
observedScopeRef: fillableForm.scopeRef,
|
|
175
|
+
});
|
|
176
|
+
if (fillReadiness.kind === 'fill_mismatch') {
|
|
177
|
+
return finalizeStep({
|
|
178
|
+
success: false,
|
|
179
|
+
...withFillableFormPresence(fillableForm, {
|
|
180
|
+
error: 'secret_request_fill_mismatch',
|
|
181
|
+
outcomeType: 'blocked',
|
|
182
|
+
message: 'Protected fill was blocked because the approved secret request belongs to a different fill target.',
|
|
183
|
+
reason: `The approved request belongs to fillRef ${fillReadiness.requestFillRef}, not ${fillRef}.`,
|
|
184
|
+
fillRef,
|
|
185
|
+
requestId,
|
|
186
|
+
...serializeSecretRequestContext(latestRequest),
|
|
187
|
+
requestFillRef: fillReadiness.requestFillRef,
|
|
188
|
+
requestStatus: latestRequest.status,
|
|
189
|
+
nextAction: 'reobserve',
|
|
190
|
+
}),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
if (fillReadiness.kind === 'already_claimed') {
|
|
194
|
+
return finalizeStep({
|
|
195
|
+
success: false,
|
|
196
|
+
...withFillableFormPresence(fillableForm, {
|
|
197
|
+
error: 'secret_request_already_claimed',
|
|
198
|
+
outcomeType: 'blocked',
|
|
199
|
+
message: 'Protected fill could not continue because this one-time secret request was already claimed earlier.',
|
|
200
|
+
reason: 'Secret values can only be claimed once for this request, so MagicPay CLI needs a new request before it can fill again.',
|
|
201
|
+
fillRef,
|
|
202
|
+
requestId,
|
|
203
|
+
...serializeSecretRequestContext(latestRequest),
|
|
204
|
+
requestStatus: latestRequest.status,
|
|
205
|
+
nextAction: 'request-secret',
|
|
206
|
+
}),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
if (fillReadiness.kind === 'not_ready') {
|
|
210
|
+
return finalizeStep(failureForUnfulfilledRequest(fillableForm, latestRequest, fillRef, requestId));
|
|
211
|
+
}
|
|
212
|
+
if (fillReadiness.kind === 'context_mismatch') {
|
|
213
|
+
return finalizeStep({
|
|
214
|
+
success: false,
|
|
215
|
+
...withFillableFormPresence(fillableForm, {
|
|
216
|
+
error: 'secret_request_context_mismatch',
|
|
217
|
+
outcomeType: 'blocked',
|
|
218
|
+
message: 'Protected fill was blocked because the page or protected surface no longer matches the approved request context.',
|
|
219
|
+
reason: 'The current page host or protected surface no longer matches the context that was approved for this request.',
|
|
220
|
+
fillRef,
|
|
221
|
+
requestId,
|
|
222
|
+
...serializeSecretRequestContext(latestRequest),
|
|
223
|
+
requestStatus: latestRequest.status,
|
|
224
|
+
nextAction: 'reobserve',
|
|
225
|
+
requestReusable: true,
|
|
226
|
+
mismatchReasons: fillReadiness.mismatchReasons,
|
|
227
|
+
...(fillReadiness.expectedHost ? { expectedHost: fillReadiness.expectedHost } : {}),
|
|
228
|
+
...(fillReadiness.observedHost ? { observedHost: fillReadiness.observedHost } : {}),
|
|
229
|
+
...(fillReadiness.expectedScopeRef
|
|
230
|
+
? { expectedScopeRef: fillReadiness.expectedScopeRef }
|
|
231
|
+
: {}),
|
|
232
|
+
...(fillReadiness.observedScopeRef
|
|
233
|
+
? { observedScopeRef: fillReadiness.observedScopeRef }
|
|
234
|
+
: {}),
|
|
235
|
+
}),
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
let claimedAt = new Date().toISOString();
|
|
239
|
+
let claimStoredSecretRef = latestRequest.storedSecretRef;
|
|
240
|
+
let claimKind = latestRequest.kind;
|
|
241
|
+
const claim = await client.secrets.claim(session.intentSessionId, requestId);
|
|
242
|
+
if (!claim.success) {
|
|
243
|
+
if (claim.kind === 'not_fulfilled') {
|
|
244
|
+
return finalizeStep(failureForUnfulfilledRequest(fillableForm, latestRequest, fillRef, requestId));
|
|
245
|
+
}
|
|
246
|
+
if (claim.kind === 'already_claimed') {
|
|
247
|
+
return finalizeStep({
|
|
248
|
+
success: false,
|
|
249
|
+
...withFillableFormPresence(fillableForm, {
|
|
250
|
+
error: 'secret_request_already_claimed',
|
|
251
|
+
outcomeType: 'blocked',
|
|
252
|
+
message: 'Protected fill could not continue because this one-time secret request was already claimed earlier.',
|
|
253
|
+
reason: 'Secret values can only be claimed once for this request, so MagicPay CLI needs a new request before it can fill again.',
|
|
254
|
+
fillRef,
|
|
255
|
+
requestId,
|
|
256
|
+
...serializeSecretRequestContext(latestRequest),
|
|
257
|
+
requestStatus: latestRequest.status,
|
|
258
|
+
nextAction: 'request-secret',
|
|
259
|
+
}),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
return finalizeStep({
|
|
263
|
+
success: false,
|
|
264
|
+
...withFillableFormPresence(fillableForm, {
|
|
265
|
+
error: 'secret_claim_failed',
|
|
266
|
+
outcomeType: 'blocked',
|
|
267
|
+
message: 'Protected fill could not continue because MagicPay CLI failed to claim the one-time secret payload.',
|
|
268
|
+
reason: claim.reason,
|
|
269
|
+
fillRef,
|
|
270
|
+
requestId,
|
|
271
|
+
...serializeSecretRequestContext(latestRequest),
|
|
272
|
+
requestStatus: latestRequest.status,
|
|
273
|
+
nextAction: 'ask-user',
|
|
274
|
+
}),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
claimedAt = claim.result.issuedAt;
|
|
278
|
+
claimStoredSecretRef = claim.result.secret.storedSecretRef ?? claimStoredSecretRef;
|
|
279
|
+
claimKind = claim.result.secret.kind ?? claimKind;
|
|
280
|
+
const claimedRequest = markRequestClaimed(session, latestRequest, claimedAt);
|
|
281
|
+
const persistedRequest = claimStoredSecretRef || claimKind
|
|
282
|
+
? saveSecretRequestSnapshot(session, {
|
|
283
|
+
...claimedRequest,
|
|
284
|
+
...(claimStoredSecretRef ? { storedSecretRef: claimStoredSecretRef } : {}),
|
|
285
|
+
...(claimKind ? { kind: claimKind } : {}),
|
|
286
|
+
})
|
|
287
|
+
: claimedRequest;
|
|
288
|
+
saveMagicPaySession(session);
|
|
289
|
+
const preparedFill = prepareProtectedFillFromClaim({
|
|
290
|
+
fillableForm,
|
|
291
|
+
catalog: observedHost ? getSecretCatalog(session, observedHost) : null,
|
|
292
|
+
claim: claim.result,
|
|
293
|
+
request: {
|
|
294
|
+
storedSecretRef: claimStoredSecretRef ?? latestRequest.storedSecretRef,
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
const { fillableForm: executableForm } = preparedFill;
|
|
298
|
+
const protectedExposureArtifacts = buildProtectedExposureArtifactsFromFill({
|
|
299
|
+
session,
|
|
300
|
+
fillableForm: executableForm,
|
|
301
|
+
protectedValues: preparedFill.protectedValues,
|
|
302
|
+
fieldPolicies: preparedFill.fieldPolicies,
|
|
303
|
+
});
|
|
304
|
+
await ensureAssistiveRuntime();
|
|
305
|
+
const browserFill = await fillProtectedForm({
|
|
306
|
+
session,
|
|
307
|
+
fillableForm: executableForm,
|
|
308
|
+
protectedValues: preparedFill.protectedValues,
|
|
309
|
+
fieldPolicies: preparedFill.fieldPolicies,
|
|
310
|
+
});
|
|
311
|
+
if (!browserFill.success) {
|
|
312
|
+
return finalizeStep({
|
|
313
|
+
success: false,
|
|
314
|
+
...withFillableFormPresence(fillableForm, {
|
|
315
|
+
error: browserFill.error,
|
|
316
|
+
outcomeType: 'blocked',
|
|
317
|
+
message: browserFill.message,
|
|
318
|
+
reason: browserFill.reason,
|
|
319
|
+
fillRef,
|
|
320
|
+
requestId,
|
|
321
|
+
...serializeSecretRequestContext(persistedRequest),
|
|
322
|
+
requestStatus: persistedRequest.status,
|
|
323
|
+
requestReusable: false,
|
|
324
|
+
nextAction: 'ask-user',
|
|
325
|
+
}),
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
saveMagicPaySession(session);
|
|
329
|
+
if (browserFill.execution.kind === 'success') {
|
|
330
|
+
activateProtectedExposure(session, fillableForm, requestId, 'protected_fill_success', protectedExposureArtifacts);
|
|
331
|
+
saveMagicPaySession(session);
|
|
332
|
+
const successfulFillPayload = buildSuccessfulFillPayload(fillableForm, persistedRequest, fillRef, requestId, browserFill.execution.filledFields);
|
|
333
|
+
const postFillObserve = await observe(session);
|
|
334
|
+
if (!postFillObserve.success) {
|
|
335
|
+
return finalizeStep({
|
|
336
|
+
success: true,
|
|
337
|
+
outcomeType: 'filled',
|
|
338
|
+
...successfulFillPayload,
|
|
339
|
+
autoSubmitAttempted: false,
|
|
340
|
+
autoSubmitSkippedReason: 'post_fill_observe_failed',
|
|
341
|
+
postFillObserve,
|
|
342
|
+
nextAction: 'ask-user',
|
|
343
|
+
message: 'Protected fill completed successfully, but MagicPay could not refresh the page after fill, so automatic submit was skipped.',
|
|
344
|
+
reason: postFillObserve.reason,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
const refreshedSubmitTarget = getResolvedSubmitTarget(session, fillRef);
|
|
348
|
+
if (!refreshedSubmitTarget) {
|
|
349
|
+
return finalizeStep({
|
|
350
|
+
success: true,
|
|
351
|
+
outcomeType: 'filled',
|
|
352
|
+
...successfulFillPayload,
|
|
353
|
+
autoSubmitAttempted: false,
|
|
354
|
+
autoSubmitSkippedReason: 'submit_target_not_found_after_fill',
|
|
355
|
+
postFillObserve,
|
|
356
|
+
nextAction: 'ask-user',
|
|
357
|
+
message: 'Protected fill completed successfully, but no live submit control was found after refresh, so automatic submit was skipped.',
|
|
358
|
+
reason: 'MagicPay refreshed the page after protected fill but still could not resolve one live submit target for the protected form.',
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
const submitResult = await submitForm(session, fillRef);
|
|
362
|
+
if (!submitResult.success) {
|
|
363
|
+
return finalizeStep({
|
|
364
|
+
success: false,
|
|
365
|
+
error: submitResult.error,
|
|
366
|
+
outcomeType: submitResult.outcomeType,
|
|
367
|
+
...successfulFillPayload,
|
|
368
|
+
autoSubmitAttempted: true,
|
|
369
|
+
submitTarget: refreshedSubmitTarget,
|
|
370
|
+
postFillObserve,
|
|
371
|
+
submitResult,
|
|
372
|
+
nextAction: submitResult.nextAction,
|
|
373
|
+
message: submitResult.message,
|
|
374
|
+
reason: submitResult.reason,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
const postSubmitObserve = await observe(session);
|
|
378
|
+
if (!postSubmitObserve.success) {
|
|
379
|
+
return finalizeStep({
|
|
380
|
+
success: true,
|
|
381
|
+
outcomeType: 'submitted',
|
|
382
|
+
...successfulFillPayload,
|
|
383
|
+
autoSubmitAttempted: true,
|
|
384
|
+
submitTarget: refreshedSubmitTarget,
|
|
385
|
+
postFillObserve,
|
|
386
|
+
submitResult,
|
|
387
|
+
postSubmitObserve,
|
|
388
|
+
nextAction: 'ask-user',
|
|
389
|
+
message: 'Protected fill completed successfully, MagicPay submitted the form, but the follow-up observe did not complete.',
|
|
390
|
+
reason: postSubmitObserve.reason,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
return finalizeStep({
|
|
394
|
+
success: true,
|
|
395
|
+
outcomeType: 'submitted',
|
|
396
|
+
...successfulFillPayload,
|
|
397
|
+
autoSubmitAttempted: true,
|
|
398
|
+
submitTarget: refreshedSubmitTarget,
|
|
399
|
+
postFillObserve,
|
|
400
|
+
submitResult,
|
|
401
|
+
postSubmitObserve,
|
|
402
|
+
message: 'Protected fill completed successfully, MagicPay submitted the form, and post-submit observe captured the updated page state.',
|
|
403
|
+
reason: 'MagicPay refreshed the page after protected fill, resolved a live submit control, submitted the protected form, and observed the resulting page state.',
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
if (browserFill.execution.kind === 'binding_stale') {
|
|
407
|
+
activateProtectedExposure(session, fillableForm, requestId, 'protected_fill_binding_stale', protectedExposureArtifacts);
|
|
408
|
+
saveMagicPaySession(session);
|
|
409
|
+
return finalizeStep({
|
|
410
|
+
success: false,
|
|
411
|
+
...withFillableFormPresence(fillableForm, {
|
|
412
|
+
error: 'secret_fill_binding_stale',
|
|
413
|
+
outcomeType: 'binding_stale',
|
|
414
|
+
message: 'Protected fill was blocked because the stored field bindings are no longer valid on the page.',
|
|
415
|
+
fillRef,
|
|
416
|
+
requestId,
|
|
417
|
+
...serializeSecretRequestContext(persistedRequest),
|
|
418
|
+
requestStatus: persistedRequest.status,
|
|
419
|
+
nextAction: 'reobserve',
|
|
420
|
+
requestReusable: false,
|
|
421
|
+
targetRef: browserFill.execution.targetRef,
|
|
422
|
+
fieldKeys: browserFill.execution.fieldKeys,
|
|
423
|
+
reason: browserFill.execution.reason,
|
|
424
|
+
attempts: browserFill.execution.attempts,
|
|
425
|
+
}),
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
if (browserFill.execution.kind === 'validation_failed') {
|
|
429
|
+
activateProtectedExposure(session, fillableForm, requestId, 'protected_fill_validation_failed', protectedExposureArtifacts);
|
|
430
|
+
saveMagicPaySession(session);
|
|
431
|
+
return finalizeStep({
|
|
432
|
+
success: false,
|
|
433
|
+
...withFillableFormPresence(fillableForm, {
|
|
434
|
+
error: 'secret_validation_failed',
|
|
435
|
+
outcomeType: 'blocked',
|
|
436
|
+
message: 'Protected fill stopped because the stored data did not pass client-side validation.',
|
|
437
|
+
reason: 'Protected execution reported client-side validation errors for one or more filled fields.',
|
|
438
|
+
fillRef,
|
|
439
|
+
requestId,
|
|
440
|
+
...serializeSecretRequestContext(persistedRequest),
|
|
441
|
+
requestStatus: persistedRequest.status,
|
|
442
|
+
executionResult: 'validation_failed',
|
|
443
|
+
nextAction: 'ask-user',
|
|
444
|
+
manualOverrideAllowed: true,
|
|
445
|
+
requestReusable: false,
|
|
446
|
+
filledFields: browserFill.execution.filledFields,
|
|
447
|
+
fieldErrors: browserFill.execution.fieldErrors,
|
|
448
|
+
}),
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
activateProtectedExposure(session, fillableForm, requestId, 'protected_fill_unexpected_error', protectedExposureArtifacts);
|
|
452
|
+
saveMagicPaySession(session);
|
|
453
|
+
return finalizeStep({
|
|
454
|
+
success: false,
|
|
455
|
+
...withFillableFormPresence(fillableForm, {
|
|
456
|
+
error: 'protected_fill_execution_failed',
|
|
457
|
+
outcomeType: 'blocked',
|
|
458
|
+
message: 'Protected fill failed during protected execution, so this claimed request must not be reused.',
|
|
459
|
+
fillRef,
|
|
460
|
+
requestId,
|
|
461
|
+
...serializeSecretRequestContext(persistedRequest),
|
|
462
|
+
requestStatus: persistedRequest.status,
|
|
463
|
+
requestReusable: false,
|
|
464
|
+
nextAction: 'ask-user',
|
|
465
|
+
reason: browserFill.execution.reason,
|
|
466
|
+
}),
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
if (step) {
|
|
471
|
+
finishRunStep({
|
|
472
|
+
runId: step.runId,
|
|
473
|
+
stepId: step.stepId,
|
|
474
|
+
success: false,
|
|
475
|
+
outcomeType: 'secret_fill_failed',
|
|
476
|
+
message: 'Protected fill failed.',
|
|
477
|
+
reason: formatUnknownError(error),
|
|
478
|
+
});
|
|
479
|
+
await exportRunStepToOtlpHttpJsonBestEffort(step.runId, step.stepId);
|
|
480
|
+
}
|
|
481
|
+
throw error;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { BrowserSessionState } from '@mercuryo-ai/agentbrowse';
|
|
2
|
+
import { type ResolvedSubmitTarget } from './submit-form.js';
|
|
3
|
+
export type FindFormPurpose = 'auto' | 'login' | 'identity' | 'payment_card';
|
|
4
|
+
type FindFormView = {
|
|
5
|
+
fillRef: string;
|
|
6
|
+
scopeRef?: string;
|
|
7
|
+
purpose: string;
|
|
8
|
+
presence: 'present' | 'unknown';
|
|
9
|
+
observedAt: string;
|
|
10
|
+
fields: Array<{
|
|
11
|
+
fieldKey: string;
|
|
12
|
+
targetRef: string;
|
|
13
|
+
label?: string;
|
|
14
|
+
required?: boolean;
|
|
15
|
+
valueHint?: string;
|
|
16
|
+
}>;
|
|
17
|
+
storedSecretCandidates: Array<{
|
|
18
|
+
storedSecretRef: string;
|
|
19
|
+
kind: string;
|
|
20
|
+
scope: string;
|
|
21
|
+
displayName: string;
|
|
22
|
+
matchConfidence: 'high' | 'medium' | 'low';
|
|
23
|
+
intentRequired: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
submit?: ResolvedSubmitTarget;
|
|
26
|
+
};
|
|
27
|
+
export type FindFormResult = {
|
|
28
|
+
success: true;
|
|
29
|
+
outcomeType: 'form_found';
|
|
30
|
+
pageRef: string;
|
|
31
|
+
url: string;
|
|
32
|
+
title: string;
|
|
33
|
+
host?: string;
|
|
34
|
+
purpose: string;
|
|
35
|
+
formCount: number;
|
|
36
|
+
selectedFillRef: string;
|
|
37
|
+
selectionReason: string;
|
|
38
|
+
nextAction: 'request-secret';
|
|
39
|
+
forms: FindFormView[];
|
|
40
|
+
} | {
|
|
41
|
+
success: false;
|
|
42
|
+
outcomeType: 'blocked';
|
|
43
|
+
error: 'browser_connection_failed' | 'page_resolution_failed' | 'protected_form_not_found' | 'protected_form_ambiguous';
|
|
44
|
+
message: string;
|
|
45
|
+
reason: string;
|
|
46
|
+
pageRef?: string;
|
|
47
|
+
url?: string;
|
|
48
|
+
title?: string;
|
|
49
|
+
purpose?: string;
|
|
50
|
+
forms?: FindFormView[];
|
|
51
|
+
nextAction: 'attach' | 'ask-user';
|
|
52
|
+
};
|
|
53
|
+
export declare function findForm(session: BrowserSessionState, options?: {
|
|
54
|
+
purpose?: FindFormPurpose;
|
|
55
|
+
}): Promise<FindFormResult>;
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=find-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-form.d.ts","sourceRoot":"","sources":["../../src/commands/find-form.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAKpE,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,cAAc,CAAC;AAE7E,KAAK,YAAY,GAAG;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,KAAK,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,sBAAsB,EAAE,KAAK,CAAC;QAC5B,eAAe,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;QAC3C,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,cAAc,GACtB;IACE,OAAO,EAAE,IAAI,CAAC;IACd,WAAW,EAAE,YAAY,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,GACD;IACE,OAAO,EAAE,KAAK,CAAC;IACf,WAAW,EAAE,SAAS,CAAC;IACvB,KAAK,EACD,2BAA2B,GAC3B,wBAAwB,GACxB,0BAA0B,GAC1B,0BAA0B,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB,UAAU,EAAE,QAAQ,GAAG,UAAU,CAAC;CACnC,CAAC;AA2EN,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,mBAAmB,EAC5B,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,eAAe,CAAA;CAAO,GAC1C,OAAO,CAAC,cAAc,CAAC,CAiFzB"}
|