mppx 0.3.4 → 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.
- package/README.md +0 -52
- package/dist/Challenge.d.ts +8 -0
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +20 -4
- package/dist/Challenge.js.map +1 -1
- package/dist/cli.js +193 -66
- package/dist/cli.js.map +1 -1
- package/dist/server/Mppx.d.ts +2 -0
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +4 -3
- package/dist/server/Mppx.js.map +1 -1
- package/package.json +1 -1
- package/src/Challenge.test.ts +201 -11
- package/src/Challenge.ts +34 -4
- package/src/Store.test.ts +93 -0
- package/src/cli.test.ts +233 -37
- package/src/cli.ts +229 -79
- package/src/client/Transport.test.ts +4 -4
- package/src/internal/env.test.ts +42 -0
- package/src/server/Mppx.test.ts +173 -0
- package/src/server/Mppx.ts +6 -3
- package/src/server/Transport.test.ts +5 -5
- package/src/tempo/server/Session.test.ts +52 -0
- package/src/tempo/server/internal/transport.test.ts +285 -0
package/src/cli.test.ts
CHANGED
|
@@ -11,6 +11,7 @@ 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
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
141
|
-
|
|
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', '
|
|
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', '
|
|
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
|
|
259
|
+
// Second request: reuse the channel via -M channel=<id>
|
|
242
260
|
const second = await runAsync(
|
|
243
|
-
[
|
|
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, '
|
|
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>
|
|
509
|
-
-d, --data <data>
|
|
510
|
-
-f, --fail
|
|
511
|
-
-i, --include
|
|
512
|
-
-k, --insecure
|
|
513
|
-
-r, --rpc-url <url>
|
|
514
|
-
-s, --silent
|
|
515
|
-
-v, --verbose
|
|
516
|
-
-A, --user-agent <ua>
|
|
517
|
-
-H, --header <header>
|
|
518
|
-
-L, --location
|
|
519
|
-
-X, --method <method>
|
|
520
|
-
--
|
|
521
|
-
--confirm
|
|
522
|
-
--
|
|
523
|
-
|
|
524
|
-
-
|
|
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
|