@visa/cli 4.1.0-rc.99 → 5.0.0-rc.335

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 (85) hide show
  1. package/README.md +282 -156
  2. package/dist/cli.js +333 -532
  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 +234 -387
  8. package/dist/merchant-ucp-mcp/index.js +7 -0
  9. package/dist/skills/pair-visa-agent/RUNTIMES.md +56 -26
  10. package/dist/skills/pair-visa-agent/SKILL.md +319 -320
  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/install.ps1 +10 -6
  17. package/install.sh +7 -2
  18. package/native/bin/darwin-arm64/visa-runtime-signer +0 -0
  19. package/native/bin/darwin-x64/visa-runtime-signer +0 -0
  20. package/native/bin/linux-arm64/visa-runtime-signer +0 -0
  21. package/native/bin/linux-x64/visa-runtime-signer +0 -0
  22. package/native/bin/win32-arm64/visa-runtime-signer.exe +0 -0
  23. package/native/bin/win32-x64/visa-keychain-win.exe +0 -0
  24. package/native/bin/win32-x64/visa-runtime-signer.exe +0 -0
  25. package/package.json +23 -31
  26. package/server.json +3 -3
  27. package/dist/checkout-engine/adapters/generic.d.ts +0 -23
  28. package/dist/checkout-engine/adapters/generic.js +0 -216
  29. package/dist/checkout-engine/adapters/index.d.ts +0 -10
  30. package/dist/checkout-engine/adapters/index.js +0 -24
  31. package/dist/checkout-engine/adapters/shopify.d.ts +0 -31
  32. package/dist/checkout-engine/adapters/shopify.js +0 -423
  33. package/dist/checkout-engine/adapters/stripe-like.d.ts +0 -10
  34. package/dist/checkout-engine/adapters/stripe-like.js +0 -21
  35. package/dist/checkout-engine/amount.d.ts +0 -15
  36. package/dist/checkout-engine/amount.js +0 -72
  37. package/dist/checkout-engine/browser-launch.d.ts +0 -46
  38. package/dist/checkout-engine/browser-launch.js +0 -81
  39. package/dist/checkout-engine/ceremony.d.ts +0 -64
  40. package/dist/checkout-engine/ceremony.js +0 -261
  41. package/dist/checkout-engine/cli-engine.d.ts +0 -227
  42. package/dist/checkout-engine/cli-engine.js +0 -779
  43. package/dist/checkout-engine/detect.d.ts +0 -61
  44. package/dist/checkout-engine/detect.js +0 -398
  45. package/dist/checkout-engine/evidence.d.ts +0 -25
  46. package/dist/checkout-engine/evidence.js +0 -104
  47. package/dist/checkout-engine/executor.d.ts +0 -176
  48. package/dist/checkout-engine/executor.js +0 -1325
  49. package/dist/checkout-engine/hosted-approval.d.ts +0 -187
  50. package/dist/checkout-engine/hosted-approval.js +0 -478
  51. package/dist/checkout-engine/index.d.ts +0 -6
  52. package/dist/checkout-engine/index.js +0 -8
  53. package/dist/checkout-engine/inline-target.d.ts +0 -13
  54. package/dist/checkout-engine/inline-target.js +0 -37
  55. package/dist/checkout-engine/instrument.d.ts +0 -61
  56. package/dist/checkout-engine/instrument.js +0 -87
  57. package/dist/checkout-engine/live-fill-approval.d.ts +0 -43
  58. package/dist/checkout-engine/live-fill-approval.js +0 -90
  59. package/dist/checkout-engine/mandate/card-mandate.d.ts +0 -121
  60. package/dist/checkout-engine/mandate/card-mandate.js +0 -227
  61. package/dist/checkout-engine/mandate/mandate-ledger.d.ts +0 -142
  62. package/dist/checkout-engine/mandate/mandate-ledger.js +0 -338
  63. package/dist/checkout-engine/mandate.d.ts +0 -25
  64. package/dist/checkout-engine/mandate.js +0 -100
  65. package/dist/checkout-engine/outcome.d.ts +0 -30
  66. package/dist/checkout-engine/outcome.js +0 -225
  67. package/dist/checkout-engine/owner-only-file.d.ts +0 -19
  68. package/dist/checkout-engine/owner-only-file.js +0 -41
  69. package/dist/checkout-engine/package.json +0 -3
  70. package/dist/checkout-engine/receipt.d.ts +0 -81
  71. package/dist/checkout-engine/receipt.js +0 -109
  72. package/dist/checkout-engine/repo-env.d.ts +0 -11
  73. package/dist/checkout-engine/repo-env.js +0 -23
  74. package/dist/checkout-engine/trace-handles.d.ts +0 -8
  75. package/dist/checkout-engine/trace-handles.js +0 -12
  76. package/dist/checkout-engine/types.d.ts +0 -44
  77. package/dist/checkout-engine/types.js +0 -2
  78. package/dist/checkout-engine/vgs-gateway/fetch-credential.d.mts +0 -74
  79. package/dist/checkout-engine/vgs-gateway/fetch-credential.mjs +0 -248
  80. package/dist/checkout-engine/vgs-gateway/server-mint-client.d.ts +0 -82
  81. package/dist/checkout-engine/vgs-gateway/server-mint-client.js +0 -180
  82. package/dist/checkout-engine/vgs-live-instrument.d.ts +0 -179
  83. package/dist/checkout-engine/vgs-live-instrument.js +0 -296
  84. package/dist/checkout-engine/vic-confirmation.d.ts +0 -34
  85. 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
+ }