@visa/cli 4.1.0-rc.31 → 4.1.0-rc.310

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 (86) hide show
  1. package/README.md +284 -46
  2. package/dist/cli.js +618 -338
  3. package/dist/managed-runtime/resolve-and-update.mjs +268 -0
  4. package/dist/managed-runtime/runtime-readiness.mjs +126 -0
  5. package/dist/managed-runtime/update-and-restart.mjs +1079 -0
  6. package/dist/mcp-apps/ucp-checkout.html +280 -0
  7. package/dist/mcp-server/index.js +513 -258
  8. package/dist/merchant-ucp-mcp/index.js +7 -0
  9. package/dist/skills/pair-visa-agent/RUNTIMES.md +122 -79
  10. package/dist/skills/pair-visa-agent/SKILL.md +390 -333
  11. package/dist/skills/pair-visa-agent/scripts/__tests__/setup.test.mjs +407 -0
  12. package/dist/skills/pair-visa-agent/scripts/setup.mjs +310 -30
  13. package/dist/skills/visa-shopify-checkout/SKILL.md +122 -0
  14. package/dist/skills/visa-shopify-checkout/references/evidence-and-states.md +37 -0
  15. package/dist/skills/visa-ucp-shopping/SKILL.md +86 -0
  16. package/dist/subway-direct.mjs +1 -0
  17. package/install.ps1 +7 -6
  18. package/install.sh +3 -3
  19. package/native/bin/darwin-arm64/visa-runtime-signer +0 -0
  20. package/native/bin/darwin-x64/visa-runtime-signer +0 -0
  21. package/native/bin/linux-arm64/visa-runtime-signer +0 -0
  22. package/native/bin/linux-x64/visa-runtime-signer +0 -0
  23. package/native/bin/win32-arm64/visa-runtime-signer.exe +0 -0
  24. package/native/bin/win32-x64/visa-keychain-win.exe +0 -0
  25. package/native/bin/win32-x64/visa-runtime-signer.exe +0 -0
  26. package/package.json +32 -30
  27. package/server.json +4 -4
  28. package/dist/checkout-engine/adapters/generic.d.ts +0 -19
  29. package/dist/checkout-engine/adapters/generic.js +0 -201
  30. package/dist/checkout-engine/adapters/index.d.ts +0 -7
  31. package/dist/checkout-engine/adapters/index.js +0 -17
  32. package/dist/checkout-engine/adapters/stripe-like.d.ts +0 -10
  33. package/dist/checkout-engine/adapters/stripe-like.js +0 -21
  34. package/dist/checkout-engine/browser-launch.d.ts +0 -46
  35. package/dist/checkout-engine/browser-launch.js +0 -81
  36. package/dist/checkout-engine/ceremony.d.ts +0 -64
  37. package/dist/checkout-engine/ceremony.js +0 -261
  38. package/dist/checkout-engine/cli-engine.d.ts +0 -208
  39. package/dist/checkout-engine/cli-engine.js +0 -584
  40. package/dist/checkout-engine/detect.d.ts +0 -61
  41. package/dist/checkout-engine/detect.js +0 -392
  42. package/dist/checkout-engine/evidence.d.ts +0 -25
  43. package/dist/checkout-engine/evidence.js +0 -104
  44. package/dist/checkout-engine/executor.d.ts +0 -174
  45. package/dist/checkout-engine/executor.js +0 -1306
  46. package/dist/checkout-engine/hosted-approval.d.ts +0 -135
  47. package/dist/checkout-engine/hosted-approval.js +0 -311
  48. package/dist/checkout-engine/index.d.ts +0 -6
  49. package/dist/checkout-engine/index.js +0 -8
  50. package/dist/checkout-engine/inline-target.d.ts +0 -13
  51. package/dist/checkout-engine/inline-target.js +0 -37
  52. package/dist/checkout-engine/instrument.d.ts +0 -55
  53. package/dist/checkout-engine/instrument.js +0 -87
  54. package/dist/checkout-engine/live-fill-approval.d.ts +0 -43
  55. package/dist/checkout-engine/live-fill-approval.js +0 -90
  56. package/dist/checkout-engine/mandate/card-mandate.d.ts +0 -117
  57. package/dist/checkout-engine/mandate/card-mandate.js +0 -221
  58. package/dist/checkout-engine/mandate/mandate-ledger.d.ts +0 -135
  59. package/dist/checkout-engine/mandate/mandate-ledger.js +0 -318
  60. package/dist/checkout-engine/mandate.d.ts +0 -25
  61. package/dist/checkout-engine/mandate.js +0 -100
  62. package/dist/checkout-engine/outcome.d.ts +0 -30
  63. package/dist/checkout-engine/outcome.js +0 -225
  64. package/dist/checkout-engine/owner-only-file.d.ts +0 -19
  65. package/dist/checkout-engine/owner-only-file.js +0 -41
  66. package/dist/checkout-engine/package.json +0 -3
  67. package/dist/checkout-engine/pay-args.d.ts +0 -14
  68. package/dist/checkout-engine/pay-args.js +0 -44
  69. package/dist/checkout-engine/pay.d.ts +0 -1
  70. package/dist/checkout-engine/pay.js +0 -13
  71. package/dist/checkout-engine/receipt.d.ts +0 -81
  72. package/dist/checkout-engine/receipt.js +0 -109
  73. package/dist/checkout-engine/repo-env.d.ts +0 -11
  74. package/dist/checkout-engine/repo-env.js +0 -23
  75. package/dist/checkout-engine/run-live-fill.d.ts +0 -1
  76. package/dist/checkout-engine/run-live-fill.js +0 -493
  77. package/dist/checkout-engine/types.d.ts +0 -39
  78. package/dist/checkout-engine/types.js +0 -2
  79. package/dist/checkout-engine/vgs-gateway/fetch-credential.d.mts +0 -74
  80. package/dist/checkout-engine/vgs-gateway/fetch-credential.mjs +0 -248
  81. package/dist/checkout-engine/vgs-gateway/server-mint-client.d.ts +0 -82
  82. package/dist/checkout-engine/vgs-gateway/server-mint-client.js +0 -178
  83. package/dist/checkout-engine/vgs-live-instrument.d.ts +0 -168
  84. package/dist/checkout-engine/vgs-live-instrument.js +0 -289
  85. package/dist/checkout-engine/vic-confirmation.d.ts +0 -34
  86. package/dist/checkout-engine/vic-confirmation.js +0 -39
@@ -0,0 +1,268 @@
1
+ #!/usr/bin/env node
2
+ /** Resolve the server-authorized exact RC, then run one managed convergence transaction. */
3
+
4
+ import { randomUUID } from 'node:crypto'
5
+ import { realpathSync } from 'node:fs'
6
+ import { fileURLToPath } from 'node:url'
7
+ import { resolve } from 'node:path'
8
+ import {
9
+ ManagedRuntimeUpdateError,
10
+ compareExactRc,
11
+ readCurrentRelease,
12
+ resolveManagedUpdateConfig,
13
+ runManagedRuntimeUpdate,
14
+ } from './update-and-restart.mjs'
15
+
16
+ const PREFIX = '[managed-runtime-converge]'
17
+ const BOOTSTRAP_RC = '0.0.0-rc.0'
18
+ const EXACT_RC_RE = /^\d+\.\d+\.\d+-rc\.\d+$/
19
+ const DEFAULT_DISCOVERY_TIMEOUT_MS = 5_000
20
+ const MAX_DISCOVERY_TIMEOUT_MS = 60_000
21
+
22
+ function event(logger, name, fields = {}) {
23
+ logger.log(`${PREFIX} ${JSON.stringify({ event: name, ...fields })}`)
24
+ }
25
+
26
+ function positiveInteger(value, fallback, name) {
27
+ if (value === undefined || value === '') return fallback
28
+ const parsed = Number(value)
29
+ if (!Number.isSafeInteger(parsed) || parsed <= 0 || parsed > MAX_DISCOVERY_TIMEOUT_MS) {
30
+ throw new ManagedRuntimeUpdateError(
31
+ 'invalid_configuration',
32
+ `${name} must be a positive integer no greater than ${MAX_DISCOVERY_TIMEOUT_MS}`
33
+ )
34
+ }
35
+ return parsed
36
+ }
37
+
38
+ export function resolveReleaseSourceConfig(env = process.env) {
39
+ const raw = env.VISA_RUNTIME_RELEASE_SOURCE_URL?.trim() || ''
40
+ if (!raw) {
41
+ throw new ManagedRuntimeUpdateError(
42
+ 'release_source_required',
43
+ 'VISA_RUNTIME_RELEASE_SOURCE_URL must name the trusted Visa /v1/status endpoint'
44
+ )
45
+ }
46
+
47
+ let source
48
+ try {
49
+ source = new URL(raw)
50
+ } catch (error) {
51
+ throw new ManagedRuntimeUpdateError(
52
+ 'invalid_configuration',
53
+ 'VISA_RUNTIME_RELEASE_SOURCE_URL must be an absolute URL',
54
+ { cause: error }
55
+ )
56
+ }
57
+
58
+ const expectedEnvironment = env.VISA_RUNTIME_EXPECTED_ENVIRONMENT?.trim() || ''
59
+ const loopback = source.hostname === '127.0.0.1' || source.hostname === 'localhost'
60
+ if (
61
+ source.username ||
62
+ source.password ||
63
+ source.search ||
64
+ source.hash ||
65
+ (source.protocol !== 'https:' && !(expectedEnvironment === 'development' && loopback))
66
+ ) {
67
+ throw new ManagedRuntimeUpdateError(
68
+ 'invalid_configuration',
69
+ 'release source must be credential-free HTTPS (loopback HTTP is development-only) with no query or fragment'
70
+ )
71
+ }
72
+
73
+ return {
74
+ sourceUrl: source.toString(),
75
+ timeoutMs: positiveInteger(
76
+ env.VISA_RUNTIME_RELEASE_DISCOVERY_TIMEOUT_MS,
77
+ DEFAULT_DISCOVERY_TIMEOUT_MS,
78
+ 'VISA_RUNTIME_RELEASE_DISCOVERY_TIMEOUT_MS'
79
+ ),
80
+ }
81
+ }
82
+
83
+ export async function discoverExactRc(env = process.env, dependencies = {}) {
84
+ const config = resolveReleaseSourceConfig(env)
85
+ const fetchImpl = dependencies.fetch ?? globalThis.fetch
86
+ const logger = dependencies.logger ?? console
87
+ const correlationId = dependencies.correlationId ?? randomUUID()
88
+ const currentVersion = dependencies.currentVersion ?? BOOTSTRAP_RC
89
+ if (!EXACT_RC_RE.test(currentVersion)) {
90
+ throw new ManagedRuntimeUpdateError(
91
+ 'invalid_runtime_state',
92
+ 'current managed runtime version must be an exact RC'
93
+ )
94
+ }
95
+
96
+ const controller = new AbortController()
97
+ const timer = setTimeout(() => controller.abort(), config.timeoutMs)
98
+ timer.unref?.()
99
+ const startedAt = Date.now()
100
+ event(logger, 'external_request', {
101
+ correlationId,
102
+ service: 'visa_release_authority',
103
+ operation: 'discover_exact_rc',
104
+ currentVersion,
105
+ })
106
+
107
+ try {
108
+ const response = await fetchImpl(config.sourceUrl, {
109
+ method: 'GET',
110
+ redirect: 'error',
111
+ signal: controller.signal,
112
+ headers: {
113
+ Accept: 'application/json',
114
+ 'User-Agent': 'visa-managed-runtime-converge',
115
+ 'X-Visa-CLI-Version': currentVersion,
116
+ },
117
+ })
118
+ const targetVersion = response.headers.get('x-latest-version')?.trim() || ''
119
+ const acceptedStatus = response.status === 200 || response.status === 401
120
+ await response.body?.cancel?.()
121
+ event(logger, 'external_response', {
122
+ correlationId,
123
+ service: 'visa_release_authority',
124
+ operation: 'discover_exact_rc',
125
+ ok: acceptedStatus && EXACT_RC_RE.test(targetVersion),
126
+ status: response.status,
127
+ durationMs: Date.now() - startedAt,
128
+ })
129
+ if (!acceptedStatus || !EXACT_RC_RE.test(targetVersion)) {
130
+ throw new ManagedRuntimeUpdateError(
131
+ 'target_not_authorized',
132
+ 'Visa release authority did not advertise an exact RC'
133
+ )
134
+ }
135
+ return { correlationId, targetVersion }
136
+ } catch (error) {
137
+ if (error instanceof ManagedRuntimeUpdateError) throw error
138
+ event(logger, 'external_response', {
139
+ correlationId,
140
+ service: 'visa_release_authority',
141
+ operation: 'discover_exact_rc',
142
+ ok: false,
143
+ status: null,
144
+ durationMs: Date.now() - startedAt,
145
+ })
146
+ throw new ManagedRuntimeUpdateError(
147
+ 'release_source_unavailable',
148
+ 'Visa release authority could not be reached',
149
+ { cause: error }
150
+ )
151
+ } finally {
152
+ clearTimeout(timer)
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Read the known-good release, tolerating the receipt-less `current` pointer an
158
+ * activation interrupted between `activateRelease()` and `writeReceipt()` leaves behind.
159
+ * `runManagedRuntimeUpdate` repairs exactly that state with `recoverInterruptedActivation()`
160
+ * under the update lock, so a scheduled convergence must reach the transaction instead of
161
+ * exiting non-zero on every invocation until a human intervenes.
162
+ */
163
+ function readKnownGood(config, dependencies, logger, correlationId) {
164
+ const read = dependencies.readCurrentRelease ?? readCurrentRelease
165
+ try {
166
+ return read(config.managedDir)
167
+ } catch (error) {
168
+ if (!(error instanceof ManagedRuntimeUpdateError) || error.code !== 'invalid_runtime_state') {
169
+ throw error
170
+ }
171
+ event(logger, 'known_good_unreadable', {
172
+ correlationId,
173
+ reason: error.message,
174
+ repair: 'delegated_to_update_transaction',
175
+ })
176
+ return null
177
+ }
178
+ }
179
+
180
+ export async function runManagedRuntimeConvergence(env = process.env, dependencies = {}) {
181
+ const bootstrapConfig = resolveManagedUpdateConfig({
182
+ ...env,
183
+ VISA_CLI_EXACT_VERSION: BOOTSTRAP_RC,
184
+ })
185
+ const logger = dependencies.logger ?? console
186
+ const correlationId = dependencies.correlationId ?? randomUUID()
187
+ const current = readKnownGood(bootstrapConfig, dependencies, logger, correlationId)
188
+ if (current && current.receipt.targetEnvironment !== bootstrapConfig.expectedEnvironment) {
189
+ // Distinct from `invalid_runtime_state`: a mismatched environment always needs a human,
190
+ // where a wedged runtime state is repaired by the update transaction. On-call triages
191
+ // the two differently, so the exit codes must differ. Matches the updater's own code.
192
+ throw new ManagedRuntimeUpdateError(
193
+ 'target_environment_mismatch',
194
+ `known-good runtime targets ${current.receipt.targetEnvironment}, expected ${bootstrapConfig.expectedEnvironment}`
195
+ )
196
+ }
197
+ const currentVersion = current?.receipt.version ?? BOOTSTRAP_RC
198
+ const discovery = await discoverExactRc(env, {
199
+ fetch: dependencies.fetch,
200
+ logger,
201
+ correlationId,
202
+ currentVersion,
203
+ })
204
+
205
+ if (current) {
206
+ const order = compareExactRc(discovery.targetVersion, currentVersion)
207
+ if (order <= 0) {
208
+ const status = order === 0 ? 'current' : 'ahead_of_recommendation'
209
+ event(logger, 'convergence_not_required', {
210
+ correlationId,
211
+ status,
212
+ currentVersion,
213
+ recommendedVersion: discovery.targetVersion,
214
+ })
215
+ return {
216
+ ok: true,
217
+ status,
218
+ correlationId,
219
+ version: currentVersion,
220
+ recommendedVersion: discovery.targetVersion,
221
+ updated: false,
222
+ }
223
+ }
224
+ }
225
+
226
+ const result = await (dependencies.runManagedRuntimeUpdate ?? runManagedRuntimeUpdate)(
227
+ {
228
+ ...env,
229
+ VISA_CLI_EXACT_VERSION: discovery.targetVersion,
230
+ },
231
+ {
232
+ ...dependencies.updateDependencies,
233
+ logger,
234
+ correlationId,
235
+ }
236
+ )
237
+ return {
238
+ ...result,
239
+ status: 'updated',
240
+ correlationId,
241
+ recommendedVersion: discovery.targetVersion,
242
+ updated: true,
243
+ }
244
+ }
245
+
246
+ async function main() {
247
+ try {
248
+ const result = await runManagedRuntimeConvergence(process.env)
249
+ process.stdout.write(`${JSON.stringify(result)}\n`)
250
+ } catch (error) {
251
+ const code = error instanceof ManagedRuntimeUpdateError ? error.code : 'convergence_failed'
252
+ process.stderr.write(`${JSON.stringify({ ok: false, code, message: error.message })}\n`)
253
+ process.exitCode = 1
254
+ }
255
+ }
256
+
257
+ function isDirectExecution() {
258
+ if (!process.argv[1]) return false
259
+ try {
260
+ return realpathSync(resolve(process.argv[1])) === realpathSync(fileURLToPath(import.meta.url))
261
+ } catch {
262
+ return false
263
+ }
264
+ }
265
+
266
+ if (isDirectExecution()) {
267
+ void main()
268
+ }
@@ -0,0 +1,126 @@
1
+ export const DEFAULT_RUNTIME_READINESS_TIMEOUT_MS = 60_000
2
+ const COMMERCE_RUNTIMES = new Set(['openclaw', 'hermes'])
3
+
4
+ /** Fixed argv only: callers cannot substitute a package, command, or script path. */
5
+ export function commerceSkillArgs(env = process.env, check = false) {
6
+ const runtime = env.VISA_COMMERCE_RUNTIME || ''
7
+ if (runtime && !COMMERCE_RUNTIMES.has(runtime)) {
8
+ throw new Error('VISA_COMMERCE_RUNTIME must be openclaw or hermes')
9
+ }
10
+ return [
11
+ 'agent',
12
+ 'skill',
13
+ '--commerce',
14
+ ...(runtime ? ['--runtime', runtime] : []),
15
+ ...(check ? ['--check'] : []),
16
+ '--format',
17
+ 'json',
18
+ ]
19
+ }
20
+
21
+ function isPlainRecord(value) {
22
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
23
+ }
24
+
25
+ function buildFrom(value) {
26
+ if (!isPlainRecord(value)) return null
27
+ return value?.data?.build ?? value?.build ?? value
28
+ }
29
+
30
+ export function parseRuntimeReadiness(raw) {
31
+ let parsed
32
+ try {
33
+ parsed = JSON.parse(raw)
34
+ } catch {
35
+ throw new Error('runtime readiness command must emit one JSON document')
36
+ }
37
+
38
+ const build = buildFrom(parsed)
39
+ if (
40
+ !isPlainRecord(build) ||
41
+ typeof build.version !== 'string' ||
42
+ build.version.trim() === '' ||
43
+ typeof build.targetEnvironment !== 'string' ||
44
+ build.targetEnvironment.trim() === ''
45
+ ) {
46
+ throw new Error('runtime readiness JSON must include version and targetEnvironment')
47
+ }
48
+
49
+ return {
50
+ version: build.version,
51
+ targetEnvironment: build.targetEnvironment,
52
+ shopifyRuntime: isPlainRecord(build.shopifyRuntime)
53
+ ? build.shopifyRuntime
54
+ : isPlainRecord(parsed.shopifyRuntime)
55
+ ? parsed.shopifyRuntime
56
+ : null,
57
+ }
58
+ }
59
+
60
+ export function assertSameShopifyRuntime(actual, expected) {
61
+ if (!isPlainRecord(actual)) {
62
+ throw new Error('loaded runtime readiness must attest the named shopify-ucp MCP')
63
+ }
64
+ if (actual.serverName !== 'shopify-ucp') {
65
+ throw new Error('loaded runtime readiness did not attest the canonical shopify-ucp mount')
66
+ }
67
+ for (const field of ['installedVersion', 'toolSchemaFingerprint', 'command']) {
68
+ if (actual[field] !== expected[field]) {
69
+ throw new Error(`loaded Shopify UCP ${field} does not match the certified local runtime`)
70
+ }
71
+ }
72
+ if (
73
+ !Array.isArray(actual.args) ||
74
+ actual.args.length !== expected.args.length ||
75
+ !actual.args.every((value, index) => value === expected.args[index])
76
+ ) {
77
+ throw new Error('loaded Shopify UCP command arguments do not match the certified local runtime')
78
+ }
79
+ if (!Array.isArray(actual.blockedTools) || !actual.blockedTools.includes('checkout_complete')) {
80
+ throw new Error(
81
+ 'loaded Shopify UCP runtime does not attest the checkout completion deny boundary'
82
+ )
83
+ }
84
+ }
85
+
86
+ export function resolveRuntimeUpdateConfig(env = process.env) {
87
+ const restartCommand = env.RUNTIME_RESTART_COMMAND || env.OPENCLAW_RESTART_COMMAND || ''
88
+ const readinessCommand = env.RUNTIME_READINESS_COMMAND || ''
89
+ if (restartCommand && !readinessCommand) {
90
+ throw new Error(
91
+ 'RUNTIME_READINESS_COMMAND is required with a restart command so the loaded MCP build is verified'
92
+ )
93
+ }
94
+
95
+ let readinessTimeoutMs = DEFAULT_RUNTIME_READINESS_TIMEOUT_MS
96
+ if (env.RUNTIME_READINESS_TIMEOUT_MS !== undefined) {
97
+ const candidate = Number(env.RUNTIME_READINESS_TIMEOUT_MS)
98
+ if (!Number.isSafeInteger(candidate) || candidate <= 0) {
99
+ throw new Error('RUNTIME_READINESS_TIMEOUT_MS must be a positive integer')
100
+ }
101
+ readinessTimeoutMs = candidate
102
+ }
103
+
104
+ return { restartCommand, readinessCommand, readinessTimeoutMs }
105
+ }
106
+
107
+ export function runtimeReadinessExecOptions(timeout) {
108
+ return {
109
+ encoding: 'utf-8',
110
+ stdio: ['ignore', 'pipe', 'pipe'],
111
+ timeout,
112
+ }
113
+ }
114
+
115
+ export function assertSameRuntimeBuild(actual, expected) {
116
+ if (actual.version !== expected.version) {
117
+ throw new Error(
118
+ `loaded MCP version ${actual.version} does not match installed CLI ${expected.version}`
119
+ )
120
+ }
121
+ if (actual.targetEnvironment !== expected.targetEnvironment) {
122
+ throw new Error(
123
+ `loaded MCP environment ${actual.targetEnvironment} does not match installed CLI ${expected.targetEnvironment}`
124
+ )
125
+ }
126
+ }