mppx 0.3.3 → 0.3.5

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 (148) hide show
  1. package/README.md +0 -52
  2. package/dist/Challenge.d.ts +8 -0
  3. package/dist/Challenge.d.ts.map +1 -1
  4. package/dist/Challenge.js +20 -4
  5. package/dist/Challenge.js.map +1 -1
  6. package/dist/Errors.d.ts +7 -7
  7. package/dist/Errors.d.ts.map +1 -1
  8. package/dist/Errors.js +7 -7
  9. package/dist/Errors.js.map +1 -1
  10. package/dist/cli.js +280 -119
  11. package/dist/cli.js.map +1 -1
  12. package/dist/internal/env.js +2 -2
  13. package/dist/internal/env.js.map +1 -1
  14. package/dist/server/Mppx.d.ts +2 -0
  15. package/dist/server/Mppx.d.ts.map +1 -1
  16. package/dist/server/Mppx.js +4 -3
  17. package/dist/server/Mppx.js.map +1 -1
  18. package/dist/tempo/client/ChannelOps.d.ts +5 -5
  19. package/dist/tempo/client/ChannelOps.d.ts.map +1 -1
  20. package/dist/tempo/client/ChannelOps.js +3 -3
  21. package/dist/tempo/client/ChannelOps.js.map +1 -1
  22. package/dist/tempo/client/Session.d.ts +2 -2
  23. package/dist/tempo/client/Session.d.ts.map +1 -1
  24. package/dist/tempo/client/Session.js +3 -3
  25. package/dist/tempo/client/Session.js.map +1 -1
  26. package/dist/tempo/client/SessionManager.d.ts +4 -4
  27. package/dist/tempo/client/SessionManager.d.ts.map +1 -1
  28. package/dist/tempo/client/SessionManager.js +4 -4
  29. package/dist/tempo/client/SessionManager.js.map +1 -1
  30. package/dist/tempo/index.d.ts +1 -1
  31. package/dist/tempo/index.d.ts.map +1 -1
  32. package/dist/tempo/index.js +1 -1
  33. package/dist/tempo/index.js.map +1 -1
  34. package/dist/tempo/server/Charge.js +1 -1
  35. package/dist/tempo/server/Charge.js.map +1 -1
  36. package/dist/tempo/server/Methods.d.ts +1 -1
  37. package/dist/tempo/server/Methods.d.ts.map +1 -1
  38. package/dist/tempo/server/Session.d.ts +8 -8
  39. package/dist/tempo/server/Session.d.ts.map +1 -1
  40. package/dist/tempo/server/Session.js +24 -24
  41. package/dist/tempo/server/Session.js.map +1 -1
  42. package/dist/tempo/server/index.d.ts +2 -2
  43. package/dist/tempo/server/index.d.ts.map +1 -1
  44. package/dist/tempo/server/index.js +2 -2
  45. package/dist/tempo/server/index.js.map +1 -1
  46. package/dist/tempo/server/internal/transport.d.ts +4 -4
  47. package/dist/tempo/server/internal/transport.d.ts.map +1 -1
  48. package/dist/tempo/server/internal/transport.js +3 -3
  49. package/dist/tempo/server/internal/transport.js.map +1 -1
  50. package/dist/tempo/session/Chain.d.ts.map +1 -0
  51. package/dist/tempo/session/Chain.js.map +1 -0
  52. package/dist/tempo/session/Channel.d.ts.map +1 -0
  53. package/dist/tempo/session/Channel.js.map +1 -0
  54. package/dist/tempo/session/ChannelStore.d.ts.map +1 -0
  55. package/dist/tempo/session/ChannelStore.js.map +1 -0
  56. package/dist/tempo/session/Receipt.d.ts +22 -0
  57. package/dist/tempo/session/Receipt.d.ts.map +1 -0
  58. package/dist/tempo/{stream → session}/Receipt.js +6 -6
  59. package/dist/tempo/session/Receipt.js.map +1 -0
  60. package/dist/tempo/{stream → session}/Sse.d.ts +7 -7
  61. package/dist/tempo/session/Sse.d.ts.map +1 -0
  62. package/dist/tempo/{stream → session}/Sse.js +4 -4
  63. package/dist/tempo/session/Sse.js.map +1 -0
  64. package/dist/tempo/{stream → session}/Types.d.ts +4 -4
  65. package/dist/tempo/session/Types.d.ts.map +1 -0
  66. package/dist/tempo/{stream → session}/Types.js.map +1 -1
  67. package/dist/tempo/session/Voucher.d.ts.map +1 -0
  68. package/dist/tempo/session/Voucher.js.map +1 -0
  69. package/dist/tempo/{stream → session}/escrow.abi.d.ts.map +1 -1
  70. package/dist/tempo/session/escrow.abi.js.map +1 -0
  71. package/dist/tempo/session/index.d.ts.map +1 -0
  72. package/dist/tempo/session/index.js.map +1 -0
  73. package/package.json +1 -1
  74. package/src/Challenge.test.ts +201 -11
  75. package/src/Challenge.ts +34 -4
  76. package/src/Errors.test.ts +10 -10
  77. package/src/Errors.ts +7 -7
  78. package/src/Store.test.ts +93 -0
  79. package/src/cli.test.ts +234 -38
  80. package/src/cli.ts +340 -135
  81. package/src/client/Transport.test.ts +4 -4
  82. package/src/internal/env.test.ts +42 -0
  83. package/src/internal/env.ts +2 -2
  84. package/src/middlewares/express.test.ts +1 -1
  85. package/src/middlewares/hono.test.ts +1 -1
  86. package/src/middlewares/nextjs.test.ts +1 -1
  87. package/src/server/Mppx.test.ts +173 -0
  88. package/src/server/Mppx.ts +6 -3
  89. package/src/server/Transport.test.ts +6 -6
  90. package/src/tempo/client/ChannelOps.test.ts +2 -2
  91. package/src/tempo/client/ChannelOps.ts +8 -8
  92. package/src/tempo/client/Session.test.ts +3 -3
  93. package/src/tempo/client/Session.ts +9 -9
  94. package/src/tempo/client/SessionManager.test.ts +3 -3
  95. package/src/tempo/client/SessionManager.ts +9 -9
  96. package/src/tempo/index.ts +1 -1
  97. package/src/tempo/server/Charge.ts +1 -1
  98. package/src/tempo/server/Session.test.ts +61 -9
  99. package/src/tempo/server/Session.ts +47 -47
  100. package/src/tempo/server/Sse.test.ts +3 -3
  101. package/src/tempo/server/index.ts +2 -2
  102. package/src/tempo/server/internal/transport.test.ts +285 -0
  103. package/src/tempo/server/internal/transport.ts +6 -6
  104. package/src/tempo/{stream → session}/Chain.test.ts +1 -1
  105. package/src/tempo/{stream → session}/Receipt.test.ts +16 -12
  106. package/src/tempo/{stream → session}/Receipt.ts +9 -9
  107. package/src/tempo/{stream → session}/Sse.test.ts +5 -5
  108. package/src/tempo/{stream → session}/Sse.ts +11 -11
  109. package/src/tempo/{stream → session}/Types.ts +4 -4
  110. package/dist/tempo/stream/Chain.d.ts.map +0 -1
  111. package/dist/tempo/stream/Chain.js.map +0 -1
  112. package/dist/tempo/stream/Channel.d.ts.map +0 -1
  113. package/dist/tempo/stream/Channel.js.map +0 -1
  114. package/dist/tempo/stream/ChannelStore.d.ts.map +0 -1
  115. package/dist/tempo/stream/ChannelStore.js.map +0 -1
  116. package/dist/tempo/stream/Receipt.d.ts +0 -22
  117. package/dist/tempo/stream/Receipt.d.ts.map +0 -1
  118. package/dist/tempo/stream/Receipt.js.map +0 -1
  119. package/dist/tempo/stream/Sse.d.ts.map +0 -1
  120. package/dist/tempo/stream/Sse.js.map +0 -1
  121. package/dist/tempo/stream/Types.d.ts.map +0 -1
  122. package/dist/tempo/stream/Voucher.d.ts.map +0 -1
  123. package/dist/tempo/stream/Voucher.js.map +0 -1
  124. package/dist/tempo/stream/escrow.abi.js.map +0 -1
  125. package/dist/tempo/stream/index.d.ts.map +0 -1
  126. package/dist/tempo/stream/index.js.map +0 -1
  127. /package/dist/tempo/{stream → session}/Chain.d.ts +0 -0
  128. /package/dist/tempo/{stream → session}/Chain.js +0 -0
  129. /package/dist/tempo/{stream → session}/Channel.d.ts +0 -0
  130. /package/dist/tempo/{stream → session}/Channel.js +0 -0
  131. /package/dist/tempo/{stream → session}/ChannelStore.d.ts +0 -0
  132. /package/dist/tempo/{stream → session}/ChannelStore.js +0 -0
  133. /package/dist/tempo/{stream → session}/Types.js +0 -0
  134. /package/dist/tempo/{stream → session}/Voucher.d.ts +0 -0
  135. /package/dist/tempo/{stream → session}/Voucher.js +0 -0
  136. /package/dist/tempo/{stream → session}/escrow.abi.d.ts +0 -0
  137. /package/dist/tempo/{stream → session}/escrow.abi.js +0 -0
  138. /package/dist/tempo/{stream → session}/index.d.ts +0 -0
  139. /package/dist/tempo/{stream → session}/index.js +0 -0
  140. /package/src/tempo/{stream → session}/Chain.ts +0 -0
  141. /package/src/tempo/{stream → session}/Channel.test.ts +0 -0
  142. /package/src/tempo/{stream → session}/Channel.ts +0 -0
  143. /package/src/tempo/{stream → session}/ChannelStore.test.ts +0 -0
  144. /package/src/tempo/{stream → session}/ChannelStore.ts +0 -0
  145. /package/src/tempo/{stream → session}/Voucher.test.ts +0 -0
  146. /package/src/tempo/{stream → session}/Voucher.ts +0 -0
  147. /package/src/tempo/{stream → session}/escrow.abi.ts +0 -0
  148. /package/src/tempo/{stream → session}/index.ts +0 -0
package/src/cli.test.ts CHANGED
@@ -6,11 +6,12 @@ import { Addresses } from 'viem/tempo'
6
6
  import { afterAll, describe, expect, test } from 'vitest'
7
7
  import * as Http from '~test/Http.js'
8
8
  import { rpcUrl } from '~test/tempo/prool.js'
9
- import { deployEscrow } from '~test/tempo/stream.js'
9
+ import { deployEscrow } from '~test/tempo/session.js'
10
10
  import { accounts, asset, client, fundAccount } from '~test/tempo/viem.js'
11
11
  import * as Store from './Store.js'
12
12
  import * as Mppx_server from './server/Mppx.js'
13
13
  import { toNodeListener } from './server/Mppx.js'
14
+ import { stripe as stripe_server } from './stripe/server/Methods.js'
14
15
  import { tempo } from './tempo/server/Methods.js'
15
16
 
16
17
  const cliPath = path.resolve(import.meta.dirname, 'cli.ts')
@@ -44,12 +45,12 @@ function runRaw(
44
45
 
45
46
  function runAsync(
46
47
  args: string[],
47
- options?: { input?: string },
48
+ options?: { input?: string; env?: NodeJS.ProcessEnv },
48
49
  ): Promise<{ stdout: string; stderr: string }> {
49
50
  return new Promise((resolve, reject) => {
50
51
  const child = spawn('node', ['--import', 'tsx', cliPath, ...args], {
51
52
  cwd,
52
- env,
53
+ env: options?.env ?? env,
53
54
  stdio: ['pipe', 'pipe', 'pipe'],
54
55
  })
55
56
 
@@ -126,19 +127,36 @@ describe('basic charge (examples/basic)', () => {
126
127
  }
127
128
  })
128
129
 
129
- test('error: no account found', { timeout: 60_000 }, () => {
130
- const result = spawnSync(
131
- 'node',
132
- ['--import', 'tsx', cliPath, 'http://localhost:1', '--account', 'nonexistent-account'],
133
- {
134
- encoding: 'utf8',
135
- cwd,
136
- timeout: 60_000,
130
+ test('error: no account found', { timeout: 60_000 }, async () => {
131
+ const server = Mppx_server.create({
132
+ methods: [tempo.charge({ getClient: () => client })],
133
+ realm: 'cli-test-no-account',
134
+ secretKey: 'cli-test-secret',
135
+ })
136
+
137
+ const httpServer = await Http.createServer(async (req, res) => {
138
+ const result = await toNodeListener(
139
+ server.charge({
140
+ amount: '1',
141
+ currency: asset,
142
+ expires: new Date(Date.now() + 60_000).toISOString(),
143
+ recipient: accounts[0].address,
144
+ }),
145
+ )(req, res)
146
+ if (result.status === 402) return
147
+ res.end('paid')
148
+ })
149
+
150
+ try {
151
+ const result = await runAsync([httpServer.url, '--account', 'nonexistent-account'], {
152
+ input: '',
137
153
  env: { ...process.env, NODE_NO_WARNINGS: '1' },
138
- },
139
- )
140
- expect(result.status).not.toBe(0)
141
- expect(result.stdout).toContain('Account "nonexistent-account" not found')
154
+ }).catch((err) => err as Error)
155
+ expect(result).toBeInstanceOf(Error)
156
+ expect((result as Error).message).toContain('Account "nonexistent-account" not found')
157
+ } finally {
158
+ httpServer.close()
159
+ }
142
160
  })
143
161
  })
144
162
 
@@ -179,7 +197,7 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
179
197
 
180
198
  try {
181
199
  const { stdout } = await runAsync(
182
- [httpServer.url, '--rpc-url', rpcUrl, '-s', '--deposit', '10'],
200
+ [httpServer.url, '--rpc-url', rpcUrl, '-s', '-M', 'deposit=10'],
183
201
  { input: '' },
184
202
  )
185
203
  expect(stdout).toContain('scraped-content')
@@ -228,7 +246,7 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
228
246
  try {
229
247
  // First request: open a channel, answer "y" to proceed, "n" to close channel
230
248
  const first = await runAsync(
231
- [httpServer.url, '--rpc-url', rpcUrl, '--confirm', '--deposit', '10'],
249
+ [httpServer.url, '--rpc-url', rpcUrl, '--confirm', '-M', 'deposit=10'],
232
250
  { input: 'y\nn\n' },
233
251
  )
234
252
  expect(first.stdout).toContain('scraped-content')
@@ -238,9 +256,18 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
238
256
  expect(match).toBeTruthy()
239
257
  const channelId = match![1]!
240
258
 
241
- // Second request: reuse the channel via --channel
259
+ // Second request: reuse the channel via -M channel=<id>
242
260
  const second = await runAsync(
243
- [httpServer.url, '--rpc-url', rpcUrl, '-s', '--channel', channelId, '--deposit', '10'],
261
+ [
262
+ httpServer.url,
263
+ '--rpc-url',
264
+ rpcUrl,
265
+ '-s',
266
+ '-M',
267
+ `channel=${channelId}`,
268
+ '-M',
269
+ 'deposit=10',
270
+ ],
244
271
  { input: '' },
245
272
  )
246
273
  expect(second.stdout).toContain('scraped-content')
@@ -266,6 +293,53 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
266
293
  })
267
294
  })
268
295
 
296
+ describe.skipIf(!process.env.VITE_STRIPE_SECRET_KEY)('stripe charge (integration)', () => {
297
+ test('happy path: makes Stripe payment via real API', { timeout: 120_000 }, async () => {
298
+ const stripeSecretKey = process.env.VITE_STRIPE_SECRET_KEY!
299
+
300
+ const server = Mppx_server.create({
301
+ methods: [
302
+ stripe_server.charge({
303
+ secretKey: stripeSecretKey,
304
+ networkId: 'internal',
305
+ paymentMethodTypes: ['card'],
306
+ }),
307
+ ],
308
+ realm: 'cli-test-stripe',
309
+ secretKey: 'cli-test-secret',
310
+ })
311
+
312
+ const httpServer = await Http.createServer(async (req, res) => {
313
+ const result = await toNodeListener(
314
+ server.charge({
315
+ amount: '1',
316
+ currency: 'usd',
317
+ decimals: 2,
318
+ }),
319
+ )(req, res)
320
+ if (result.status === 402) return
321
+ res.end('paid')
322
+ })
323
+
324
+ try {
325
+ const { stdout } = await runAsync(
326
+ [httpServer.url, '-M', 'paymentMethod=pm_card_visa', '-s'],
327
+ {
328
+ input: '',
329
+ env: {
330
+ ...env,
331
+ MPPX_STRIPE_SECRET_KEY: stripeSecretKey,
332
+ MPPX_PRIVATE_KEY: undefined as unknown as string,
333
+ },
334
+ },
335
+ )
336
+ expect(stdout).toContain('paid')
337
+ } finally {
338
+ httpServer.close()
339
+ }
340
+ })
341
+ })
342
+
269
343
  describe('session sse (examples/session/sse)', () => {
270
344
  test('streams SSE tokens to stdout', { timeout: 120_000 }, async () => {
271
345
  await fundAccount({ address: testAccount.address, token: Addresses.pathUsd })
@@ -313,7 +387,7 @@ describe('session sse (examples/session/sse)', () => {
313
387
  })
314
388
 
315
389
  try {
316
- const { stdout } = await runAsync([httpServer.url, '--rpc-url', rpcUrl, '--deposit', '10'], {
390
+ const { stdout } = await runAsync([httpServer.url, '--rpc-url', rpcUrl, '-M', 'deposit=10'], {
317
391
  input: '',
318
392
  })
319
393
  expect(stdout.trim()).toBe('Hello world!')
@@ -337,6 +411,129 @@ describe('session sse (examples/session/sse)', () => {
337
411
  })
338
412
  })
339
413
 
414
+ describe('stripe charge', () => {
415
+ test('happy path: makes Stripe payment and receives response', { timeout: 60_000 }, async () => {
416
+ const mockStripeClient = {
417
+ paymentIntents: { create: async () => ({ id: 'pi_mock_cli_123', status: 'succeeded' }) },
418
+ }
419
+
420
+ const server = Mppx_server.create({
421
+ methods: [
422
+ stripe_server.charge({
423
+ client: mockStripeClient,
424
+ networkId: 'internal',
425
+ paymentMethodTypes: ['card'],
426
+ }),
427
+ ],
428
+ realm: 'cli-test-stripe',
429
+ secretKey: 'cli-test-secret',
430
+ })
431
+
432
+ const sptServer = await Http.createServer(async (_req, res) => {
433
+ res.writeHead(200, { 'Content-Type': 'application/json' })
434
+ res.end(JSON.stringify({ id: 'spt_mock_cli_test' }))
435
+ })
436
+
437
+ const appServer = await Http.createServer(async (req, res) => {
438
+ const result = await Mppx_server.toNodeListener(
439
+ server.charge({ amount: '1', currency: 'usd', decimals: 2 }),
440
+ )(req, res)
441
+ if (result.status === 402) return
442
+ res.end('paid')
443
+ })
444
+
445
+ try {
446
+ const { stdout } = await runAsync([appServer.url, '-s', '-M', 'paymentMethod=pm_card_visa'], {
447
+ input: '',
448
+ env: {
449
+ ...process.env,
450
+ NODE_NO_WARNINGS: '1',
451
+ MPPX_STRIPE_SECRET_KEY: 'sk_test_mock',
452
+ MPPX_STRIPE_SPT_URL: sptServer.url,
453
+ },
454
+ })
455
+ expect(stdout).toContain('paid')
456
+ } finally {
457
+ appServer.close()
458
+ sptServer.close()
459
+ }
460
+ })
461
+
462
+ test('error: missing MPPX_STRIPE_SECRET_KEY', { timeout: 60_000 }, async () => {
463
+ const server = Mppx_server.create({
464
+ methods: [
465
+ stripe_server.charge({
466
+ secretKey: 'sk_test_mock',
467
+ networkId: 'internal',
468
+ paymentMethodTypes: ['card'],
469
+ }),
470
+ ],
471
+ realm: 'cli-test-stripe-nokey',
472
+ secretKey: 'cli-test-secret',
473
+ })
474
+
475
+ const appServer = await Http.createServer(async (req, res) => {
476
+ const result = await Mppx_server.toNodeListener(
477
+ server.charge({ amount: '1', currency: 'usd', decimals: 2 }),
478
+ )(req, res)
479
+ if (result.status === 402) return
480
+ res.end('paid')
481
+ })
482
+
483
+ try {
484
+ const result = await runAsync([appServer.url, '-s', '-M', 'paymentMethod=pm_card_visa'], {
485
+ input: '',
486
+ env: {
487
+ ...process.env,
488
+ NODE_NO_WARNINGS: '1',
489
+ MPPX_STRIPE_SECRET_KEY: '',
490
+ },
491
+ }).catch((err) => err as Error)
492
+ expect(result).toBeInstanceOf(Error)
493
+ expect((result as Error).message).toContain('MPPX_STRIPE_SECRET_KEY')
494
+ } finally {
495
+ appServer.close()
496
+ }
497
+ })
498
+
499
+ test('error: production key rejected', { timeout: 60_000 }, async () => {
500
+ const server = Mppx_server.create({
501
+ methods: [
502
+ stripe_server.charge({
503
+ secretKey: 'sk_test_mock',
504
+ networkId: 'internal',
505
+ paymentMethodTypes: ['card'],
506
+ }),
507
+ ],
508
+ realm: 'cli-test-stripe-live',
509
+ secretKey: 'cli-test-secret',
510
+ })
511
+
512
+ const appServer = await Http.createServer(async (req, res) => {
513
+ const result = await Mppx_server.toNodeListener(
514
+ server.charge({ amount: '1', currency: 'usd', decimals: 2 }),
515
+ )(req, res)
516
+ if (result.status === 402) return
517
+ res.end('paid')
518
+ })
519
+
520
+ try {
521
+ const result = await runAsync([appServer.url, '-s', '-M', 'paymentMethod=pm_card_visa'], {
522
+ input: '',
523
+ env: {
524
+ ...process.env,
525
+ NODE_NO_WARNINGS: '1',
526
+ MPPX_STRIPE_SECRET_KEY: 'sk_live_fake',
527
+ },
528
+ }).catch((err) => err as Error)
529
+ expect(result).toBeInstanceOf(Error)
530
+ expect((result as Error).message).toContain('test mode')
531
+ } finally {
532
+ appServer.close()
533
+ }
534
+ })
535
+ })
536
+
340
537
  // ---------------------------------------------------------------------------
341
538
  // account [action]
342
539
  // TODO: investigate account tests timing out in CI (secret-tool/gnome-keyring hangs)
@@ -505,24 +702,23 @@ test('mppx --help', () => {
505
702
  view View account address
506
703
 
507
704
  Options:
508
- -a, --account <name> Account name (env: MPPX_ACCOUNT)
509
- -d, --data <data> Send request body (implies POST unless -X is set)
510
- -f, --fail Fail silently on HTTP errors (exit 22)
511
- -i, --include Include response headers in output
512
- -k, --insecure Skip TLS certificate verification (true for localhost/.local)
513
- -r, --rpc-url <url> RPC endpoint, defaults to public RPC for chain (env: MPPX_RPC_URL)
514
- -s, --silent Silent mode (suppress progress and info)
515
- -v, --verbose Show request/response headers
516
- -A, --user-agent <ua> Set User-Agent header
517
- -H, --header <header> Add header (repeatable)
518
- -L, --location Follow redirects
519
- -X, --method <method> HTTP method
520
- --channel <id> Reuse existing stream channel ID
521
- --confirm Show confirmation prompts
522
- --deposit <amount> Deposit amount for stream payments (human-readable units)
523
- --json <json> Send JSON body (sets Content-Type and Accept, implies POST)
524
- -V, --version Display version number
525
- -h, --help Display this message
705
+ -a, --account <name> Account name (env: MPPX_ACCOUNT)
706
+ -d, --data <data> Send request body (implies POST unless -X is set)
707
+ -f, --fail Fail silently on HTTP errors (exit 22)
708
+ -i, --include Include response headers in output
709
+ -k, --insecure Skip TLS certificate verification (true for localhost/.local)
710
+ -r, --rpc-url <url> RPC endpoint, defaults to public RPC for chain (env: MPPX_RPC_URL)
711
+ -s, --silent Silent mode (suppress progress and info)
712
+ -v, --verbose Show request/response headers
713
+ -A, --user-agent <ua> Set User-Agent header
714
+ -H, --header <header> Add header (repeatable)
715
+ -L, --location Follow redirects
716
+ -X, --method <method> HTTP method
717
+ -M, --method-opt <opt> Method-specific option (key=value, repeatable)
718
+ --confirm Show confirmation prompts
719
+ --json <json> Send JSON body (sets Content-Type and Accept, implies POST)
720
+ -V, --version Display version number
721
+ -h, --help Display this message
526
722
 
527
723
  Examples:
528
724
  mppx example.com/content