mppx 0.8.12 → 0.8.13
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/CHANGELOG.md +18 -0
- package/README.md +16 -1
- package/dist/cli/account.d.ts +88 -0
- package/dist/cli/account.d.ts.map +1 -1
- package/dist/cli/account.js +39 -9
- package/dist/cli/account.js.map +1 -1
- package/dist/cli/cli.d.ts +2 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +59 -2
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/sessions/Manager.d.ts +31 -0
- package/dist/cli/sessions/Manager.d.ts.map +1 -0
- package/dist/cli/sessions/Manager.js +56 -0
- package/dist/cli/sessions/Manager.js.map +1 -0
- package/dist/cli/sessions/commands.d.ts +36 -0
- package/dist/cli/sessions/commands.d.ts.map +1 -0
- package/dist/cli/sessions/commands.js +407 -0
- package/dist/cli/sessions/commands.js.map +1 -0
- package/dist/cli/sessions/request.d.ts +35 -0
- package/dist/cli/sessions/request.d.ts.map +1 -0
- package/dist/cli/sessions/request.js +271 -0
- package/dist/cli/sessions/request.js.map +1 -0
- package/dist/cli/sessions/store.d.ts +134 -0
- package/dist/cli/sessions/store.d.ts.map +1 -0
- package/dist/cli/sessions/store.js +723 -0
- package/dist/cli/sessions/store.js.map +1 -0
- package/dist/cli/utils.d.ts +2 -0
- package/dist/cli/utils.d.ts.map +1 -1
- package/dist/cli/utils.js +12 -0
- package/dist/cli/utils.js.map +1 -1
- package/dist/cli/validate/index.d.ts.map +1 -1
- package/dist/cli/validate/index.js +12 -6
- package/dist/cli/validate/index.js.map +1 -1
- package/dist/cli/validate/messages.d.ts +2 -0
- package/dist/cli/validate/messages.d.ts.map +1 -0
- package/dist/cli/validate/messages.js +8 -0
- package/dist/cli/validate/messages.js.map +1 -0
- package/dist/client/node.d.ts +29 -0
- package/dist/client/node.d.ts.map +1 -0
- package/dist/client/node.js +177 -0
- package/dist/client/node.js.map +1 -0
- package/dist/internal/types.d.ts +24 -0
- package/dist/internal/types.d.ts.map +1 -1
- package/dist/stripe/server/Charge.d.ts +8 -4
- package/dist/stripe/server/Charge.d.ts.map +1 -1
- package/dist/stripe/server/Charge.js.map +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
- package/dist/stripe/server/internal/html.gen.js +1 -1
- package/dist/stripe/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +1 -1
- package/dist/tempo/internal/types.d.ts +10 -1
- package/dist/tempo/internal/types.d.ts.map +1 -1
- package/dist/tempo/legacy/client/Session.d.ts +2 -2
- package/dist/tempo/legacy/server/Session.d.ts +3 -3
- package/dist/tempo/legacy/server/Session.d.ts.map +1 -1
- package/dist/tempo/legacy/server/Session.js.map +1 -1
- package/dist/tempo/server/Charge.d.ts +3 -2
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +2 -23
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Methods.js +2 -2
- package/dist/tempo/server/Methods.js.map +1 -1
- package/dist/tempo/server/Subscription.d.ts +3 -2
- package/dist/tempo/server/Subscription.d.ts.map +1 -1
- package/dist/tempo/server/Subscription.js.map +1 -1
- package/dist/tempo/server/index.d.ts +1 -1
- package/dist/tempo/server/index.d.ts.map +1 -1
- package/dist/tempo/server/index.js.map +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/session/Snapshot.d.ts +9 -0
- package/dist/tempo/session/Snapshot.d.ts.map +1 -1
- package/dist/tempo/session/Snapshot.js +6 -0
- package/dist/tempo/session/Snapshot.js.map +1 -1
- package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -1
- package/dist/tempo/session/client/ChannelOps.js +11 -2
- package/dist/tempo/session/client/ChannelOps.js.map +1 -1
- package/dist/tempo/session/client/CredentialState.d.ts +21 -1
- package/dist/tempo/session/client/CredentialState.d.ts.map +1 -1
- package/dist/tempo/session/client/CredentialState.js +66 -1
- package/dist/tempo/session/client/CredentialState.js.map +1 -1
- package/dist/tempo/session/client/Session.d.ts +1 -1
- package/dist/tempo/session/client/SessionManager.d.ts +2 -2
- package/dist/tempo/session/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/session/client/SessionManager.js +128 -30
- package/dist/tempo/session/client/SessionManager.js.map +1 -1
- package/dist/tempo/session/client/Transports.d.ts +11 -2
- package/dist/tempo/session/client/Transports.d.ts.map +1 -1
- package/dist/tempo/session/client/Transports.js +33 -9
- package/dist/tempo/session/client/Transports.js.map +1 -1
- package/dist/tempo/session/client/internal/SessionManager.d.ts +19 -0
- package/dist/tempo/session/client/internal/SessionManager.d.ts.map +1 -0
- package/dist/tempo/session/client/internal/SessionManager.js +13 -0
- package/dist/tempo/session/client/internal/SessionManager.js.map +1 -0
- package/dist/tempo/session/precompile/Chain.js +13 -4
- package/dist/tempo/session/precompile/Chain.js.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.d.ts +3 -0
- package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.js +14 -0
- package/dist/tempo/session/server/CredentialVerification.js.map +1 -1
- package/dist/tempo/session/server/MeteredStream.d.ts +7 -1
- package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -1
- package/dist/tempo/session/server/MeteredStream.js +5 -4
- package/dist/tempo/session/server/MeteredStream.js.map +1 -1
- package/dist/tempo/session/server/RequestState.d.ts +5 -1
- package/dist/tempo/session/server/RequestState.d.ts.map +1 -1
- package/dist/tempo/session/server/RequestState.js +10 -4
- package/dist/tempo/session/server/RequestState.js.map +1 -1
- package/dist/tempo/session/server/Session.d.ts +21 -6
- package/dist/tempo/session/server/Session.d.ts.map +1 -1
- package/dist/tempo/session/server/Session.js +4 -1
- package/dist/tempo/session/server/Session.js.map +1 -1
- package/dist/tempo/session/server/Settlement.d.ts +20 -0
- package/dist/tempo/session/server/Settlement.d.ts.map +1 -1
- package/dist/tempo/session/server/Settlement.js +20 -0
- package/dist/tempo/session/server/Settlement.js.map +1 -1
- package/dist/tempo/session/server/Ws.d.ts +2 -0
- package/dist/tempo/session/server/Ws.d.ts.map +1 -1
- package/dist/tempo/session/server/Ws.js.map +1 -1
- package/dist/tempo/session/server/index.d.ts +1 -1
- package/dist/tempo/session/server/index.d.ts.map +1 -1
- package/dist/tempo/subscription/KeyAuthorization.d.ts +21 -21
- package/dist/validation/core.d.ts.map +1 -1
- package/dist/validation/core.js +6 -2
- package/dist/validation/core.js.map +1 -1
- package/dist/viem/Client.d.ts.map +1 -1
- package/dist/viem/Client.js +24 -17
- package/dist/viem/Client.js.map +1 -1
- package/package.json +6 -1
- package/src/cli/account.ts +45 -10
- package/src/cli/cli.test.ts +165 -70
- package/src/cli/cli.ts +67 -2
- package/src/cli/mcp.test.ts +11 -0
- package/src/cli/sessions/Manager.test.ts +249 -0
- package/src/cli/sessions/Manager.ts +93 -0
- package/src/cli/sessions/commands.ts +444 -0
- package/src/cli/sessions/request.test.ts +51 -0
- package/src/cli/sessions/request.ts +353 -0
- package/src/cli/sessions/store.test.ts +581 -0
- package/src/cli/sessions/store.ts +940 -0
- package/src/cli/utils.test.ts +23 -0
- package/src/cli/utils.ts +10 -0
- package/src/cli/validate/index.ts +12 -14
- package/src/cli/validate/messages.ts +7 -0
- package/src/cli/validate.test.ts +38 -5
- package/src/client/node.test.ts +115 -0
- package/src/client/node.ts +247 -0
- package/src/internal/types.test-d.ts +21 -0
- package/src/internal/types.ts +33 -0
- package/src/server/Mppx.test-d.ts +42 -0
- package/src/stripe/Methods.test.ts +10 -0
- package/src/stripe/server/Charge.test-d.ts +66 -0
- package/src/stripe/server/Charge.ts +8 -5
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/tempo/internal/types.ts +13 -2
- package/src/tempo/legacy/server/Defaults.test-d.ts +9 -0
- package/src/tempo/legacy/server/Session.ts +6 -5
- package/src/tempo/server/Charge.ts +6 -4
- package/src/tempo/server/Methods.ts +5 -4
- package/src/tempo/server/Subscription.ts +6 -4
- package/src/tempo/server/index.ts +5 -1
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/session/README.md +201 -0
- package/src/tempo/session/Snapshot.ts +21 -0
- package/src/tempo/session/client/ChannelOps.test.ts +40 -0
- package/src/tempo/session/client/ChannelOps.ts +12 -2
- package/src/tempo/session/client/CredentialState.test.ts +2 -2
- package/src/tempo/session/client/CredentialState.ts +103 -1
- package/src/tempo/session/client/Session.test.ts +3 -3
- package/src/tempo/session/client/SessionManager.test.ts +137 -4
- package/src/tempo/session/client/SessionManager.ts +139 -33
- package/src/tempo/session/client/Transports.test.ts +150 -8
- package/src/tempo/session/client/Transports.ts +48 -10
- package/src/tempo/session/client/internal/SessionManager.ts +36 -0
- package/src/tempo/session/precompile/Chain.test.ts +26 -0
- package/src/tempo/session/precompile/Chain.ts +17 -4
- package/src/tempo/session/server/CredentialVerification.ts +18 -1
- package/src/tempo/session/server/MeteredStream.ts +12 -5
- package/src/tempo/session/server/RequestState.test.ts +39 -3
- package/src/tempo/session/server/RequestState.ts +13 -6
- package/src/tempo/session/server/Session.test.ts +461 -0
- package/src/tempo/session/server/Session.ts +31 -8
- package/src/tempo/session/server/Settlement.ts +45 -0
- package/src/tempo/session/server/Ws.test.ts +113 -0
- package/src/tempo/session/server/Ws.ts +2 -0
- package/src/tempo/session/server/index.ts +2 -0
- package/src/validation/core.ts +7 -1
- package/src/viem/Client.test.ts +44 -1
- package/src/viem/Client.ts +31 -17
package/src/cli/cli.test.ts
CHANGED
|
@@ -32,6 +32,7 @@ import cli from './cli.js'
|
|
|
32
32
|
const testPrivateKey = generatePrivateKey()
|
|
33
33
|
const testAccount = privateKeyToAccount(testPrivateKey)
|
|
34
34
|
const cliTestXdgDataHome = fs.mkdtempSync(path.join(os.tmpdir(), 'mppx-cli-xdg-'))
|
|
35
|
+
let cliServeInvocation = 0
|
|
35
36
|
const unfundedToken = '0x20c0000000000000000000000000000000000002' as Address
|
|
36
37
|
const cliSessionFeePayerPolicy = {
|
|
37
38
|
maxGas: 2_250_000n,
|
|
@@ -47,7 +48,13 @@ async function serve(argv: string[], options?: { env?: Record<string, string | u
|
|
|
47
48
|
let stderr = ''
|
|
48
49
|
let exitCode: number | undefined
|
|
49
50
|
const saved: Record<string, string | undefined> = {}
|
|
50
|
-
const
|
|
51
|
+
const invocationHome = path.join(cliTestXdgDataHome, `serve-${cliServeInvocation++}`)
|
|
52
|
+
const env = {
|
|
53
|
+
XDG_CONFIG_HOME: path.join(invocationHome, 'config'),
|
|
54
|
+
XDG_DATA_HOME: cliTestXdgDataHome,
|
|
55
|
+
XDG_STATE_HOME: path.join(invocationHome, 'state'),
|
|
56
|
+
...options?.env,
|
|
57
|
+
}
|
|
51
58
|
for (const [key, value] of Object.entries(env)) {
|
|
52
59
|
saved[key] = process.env[key]
|
|
53
60
|
if (value === undefined) delete process.env[key]
|
|
@@ -1049,10 +1056,10 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
|
|
|
1049
1056
|
|
|
1050
1057
|
try {
|
|
1051
1058
|
const { output } = await serve(
|
|
1052
|
-
[httpServer.url, '--rpc-url', rpcUrl, '
|
|
1059
|
+
[httpServer.url, '--rpc-url', rpcUrl, '--include', '-M', 'deposit=10'],
|
|
1053
1060
|
{ env: { MPPX_PRIVATE_KEY: testPrivateKey } },
|
|
1054
1061
|
)
|
|
1055
|
-
expect(output).
|
|
1062
|
+
expect(output).toBe('scraped-content')
|
|
1056
1063
|
} finally {
|
|
1057
1064
|
httpServer.close()
|
|
1058
1065
|
}
|
|
@@ -1133,71 +1140,108 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
|
|
|
1133
1140
|
}
|
|
1134
1141
|
})
|
|
1135
1142
|
|
|
1136
|
-
test(
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
const tickAmount = '0.001'
|
|
1143
|
-
const server = Mppx_server.create({
|
|
1144
|
-
methods: [
|
|
1145
|
-
tempo.session({
|
|
1146
|
-
account: accounts[0],
|
|
1147
|
-
store,
|
|
1148
|
-
getClient: () => client,
|
|
1149
|
-
currency: asset,
|
|
1150
|
-
escrowContract: escrow,
|
|
1151
|
-
chainId: client.chain.id,
|
|
1152
|
-
feePayer: true,
|
|
1153
|
-
feePayerPolicy: cliSessionFeePayerPolicy,
|
|
1154
|
-
}),
|
|
1155
|
-
],
|
|
1156
|
-
realm: 'cli-test-double-charge',
|
|
1157
|
-
secretKey: 'cli-test-secret-cli-test-secret-32',
|
|
1158
|
-
})
|
|
1143
|
+
test(
|
|
1144
|
+
'selects retained non-SSE channels with auto, explicit, and new modes',
|
|
1145
|
+
{ timeout: 120_000 },
|
|
1146
|
+
async () => {
|
|
1147
|
+
await fundAccount({ address: testAccount.address, token: Addresses.pathUsd })
|
|
1148
|
+
await fundAccount({ address: testAccount.address, token: asset })
|
|
1159
1149
|
|
|
1160
|
-
|
|
1161
|
-
|
|
1150
|
+
const escrow = tip20ChannelEscrow
|
|
1151
|
+
const store = Store.memory()
|
|
1152
|
+
const tickAmount = '0.001'
|
|
1153
|
+
const server = Mppx_server.create({
|
|
1154
|
+
methods: [
|
|
1155
|
+
tempo.session({
|
|
1156
|
+
account: accounts[0],
|
|
1157
|
+
store,
|
|
1158
|
+
getClient: () => client,
|
|
1159
|
+
currency: asset,
|
|
1160
|
+
escrowContract: escrow,
|
|
1161
|
+
chainId: client.chain.id,
|
|
1162
|
+
feePayer: true,
|
|
1163
|
+
feePayerPolicy: cliSessionFeePayerPolicy,
|
|
1164
|
+
}),
|
|
1165
|
+
],
|
|
1166
|
+
realm: 'cli-test-double-charge',
|
|
1167
|
+
secretKey: 'cli-test-secret-cli-test-secret-32',
|
|
1168
|
+
})
|
|
1162
1169
|
|
|
1163
|
-
|
|
1164
|
-
const
|
|
1165
|
-
if (authHeader) {
|
|
1166
|
-
try {
|
|
1167
|
-
const cred = Credential.deserialize<SessionCredentialPayload>(authHeader)
|
|
1168
|
-
if (cred.payload.action === 'voucher' && 'cumulativeAmount' in cred.payload) {
|
|
1169
|
-
voucherAmounts.push(cred.payload.cumulativeAmount)
|
|
1170
|
-
}
|
|
1171
|
-
} catch {}
|
|
1172
|
-
}
|
|
1170
|
+
const voucherAmounts: string[] = []
|
|
1171
|
+
const credentials: { action: string; channelId: string }[] = []
|
|
1173
1172
|
|
|
1174
|
-
const
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1173
|
+
const httpServer = await Http.createServer(async (req, res) => {
|
|
1174
|
+
const authHeader = req.headers.authorization
|
|
1175
|
+
if (authHeader) {
|
|
1176
|
+
try {
|
|
1177
|
+
const cred = Credential.deserialize<SessionCredentialPayload>(authHeader)
|
|
1178
|
+
credentials.push({
|
|
1179
|
+
action: cred.payload.action,
|
|
1180
|
+
channelId: cred.payload.channelId,
|
|
1181
|
+
})
|
|
1182
|
+
if (cred.payload.action === 'voucher' && 'cumulativeAmount' in cred.payload) {
|
|
1183
|
+
voucherAmounts.push(cred.payload.cumulativeAmount)
|
|
1184
|
+
}
|
|
1185
|
+
} catch {}
|
|
1186
|
+
}
|
|
1185
1187
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1188
|
+
const result = await toNodeListener(
|
|
1189
|
+
server.session({
|
|
1190
|
+
amount: tickAmount,
|
|
1191
|
+
recipient: accounts[0].address,
|
|
1192
|
+
unitType: 'page',
|
|
1193
|
+
}),
|
|
1194
|
+
)(req, res)
|
|
1195
|
+
if (result.status === 402) return
|
|
1196
|
+
// Non-SSE: plain text response (not text/event-stream)
|
|
1197
|
+
res.end('scraped-content')
|
|
1189
1198
|
})
|
|
1190
1199
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1200
|
+
try {
|
|
1201
|
+
const stateHome = fs.mkdtempSync(path.join(cliTestXdgDataHome, 'reuse-state-'))
|
|
1202
|
+
const env = { MPPX_PRIVATE_KEY: testPrivateKey, XDG_STATE_HOME: stateHome }
|
|
1203
|
+
const runRequest = async (argv: string[]) => {
|
|
1204
|
+
const result = await serve(argv, { env })
|
|
1205
|
+
expect(result.exitCode, `${result.output}\n${result.stderr}`).toBeUndefined()
|
|
1206
|
+
}
|
|
1207
|
+
await runRequest([httpServer.url, '--rpc-url', rpcUrl, '-s', '-M', 'deposit=10'])
|
|
1208
|
+
const retainedChannelId = credentials.find(({ action }) => action === 'open')!.channelId
|
|
1209
|
+
await runRequest([httpServer.url, '--rpc-url', rpcUrl, '-s', '-M', 'deposit=10'])
|
|
1210
|
+
await runRequest([
|
|
1211
|
+
httpServer.url,
|
|
1212
|
+
'--rpc-url',
|
|
1213
|
+
rpcUrl,
|
|
1214
|
+
'--session',
|
|
1215
|
+
retainedChannelId,
|
|
1216
|
+
'-s',
|
|
1217
|
+
'-M',
|
|
1218
|
+
'deposit=10',
|
|
1219
|
+
])
|
|
1220
|
+
await runRequest([
|
|
1221
|
+
httpServer.url,
|
|
1222
|
+
'--rpc-url',
|
|
1223
|
+
rpcUrl,
|
|
1224
|
+
'--session',
|
|
1225
|
+
'new',
|
|
1226
|
+
'-s',
|
|
1227
|
+
'-M',
|
|
1228
|
+
'deposit=10',
|
|
1229
|
+
])
|
|
1230
|
+
|
|
1231
|
+
const payments = credentials.filter(({ action }) => ['open', 'voucher'].includes(action))
|
|
1232
|
+
expect(voucherAmounts).toEqual(['2000', '3000'])
|
|
1233
|
+
expect(payments.map(({ action }) => action)).toEqual(['open', 'voucher', 'voucher', 'open'])
|
|
1234
|
+
expect(new Set(payments.slice(0, 3).map(({ channelId }) => channelId))).toEqual(
|
|
1235
|
+
new Set([retainedChannelId]),
|
|
1236
|
+
)
|
|
1237
|
+
expect(payments[3]?.channelId).not.toBe(retainedChannelId)
|
|
1238
|
+
} finally {
|
|
1239
|
+
httpServer.close()
|
|
1240
|
+
}
|
|
1241
|
+
},
|
|
1242
|
+
)
|
|
1199
1243
|
|
|
1200
|
-
test('
|
|
1244
|
+
test('retains a session until `sessions close` is invoked', { timeout: 120_000 }, async () => {
|
|
1201
1245
|
await fundAccount({ address: testAccount.address, token: Addresses.pathUsd })
|
|
1202
1246
|
await fundAccount({ address: testAccount.address, token: asset })
|
|
1203
1247
|
|
|
@@ -1212,7 +1256,7 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
|
|
|
1212
1256
|
currency: asset,
|
|
1213
1257
|
escrowContract: escrow,
|
|
1214
1258
|
chainId: client.chain.id,
|
|
1215
|
-
feePayer:
|
|
1259
|
+
feePayer: accounts[1],
|
|
1216
1260
|
feePayerPolicy: cliSessionFeePayerPolicy,
|
|
1217
1261
|
}),
|
|
1218
1262
|
],
|
|
@@ -1220,8 +1264,8 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
|
|
|
1220
1264
|
secretKey: 'cli-test-secret-cli-test-secret-32',
|
|
1221
1265
|
})
|
|
1222
1266
|
|
|
1223
|
-
// Track the credential payload action from the close request
|
|
1224
1267
|
const credentialActions: string[] = []
|
|
1268
|
+
let channelId: string | undefined
|
|
1225
1269
|
|
|
1226
1270
|
const httpServer = await Http.createServer(async (req, res) => {
|
|
1227
1271
|
// Capture credential action from every request with Authorization header
|
|
@@ -1230,6 +1274,7 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
|
|
|
1230
1274
|
try {
|
|
1231
1275
|
const cred = Credential.deserialize<SessionCredentialPayload>(authHeader)
|
|
1232
1276
|
credentialActions.push(cred.payload.action)
|
|
1277
|
+
channelId = cred.payload.channelId
|
|
1233
1278
|
} catch {}
|
|
1234
1279
|
}
|
|
1235
1280
|
|
|
@@ -1245,13 +1290,31 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
|
|
|
1245
1290
|
})
|
|
1246
1291
|
|
|
1247
1292
|
try {
|
|
1293
|
+
const stateHome = fs.mkdtempSync(path.join(cliTestXdgDataHome, 'close-state-'))
|
|
1294
|
+
const env = { MPPX_PRIVATE_KEY: testPrivateKey, XDG_STATE_HOME: stateHome }
|
|
1248
1295
|
await serve([httpServer.url, '--rpc-url', rpcUrl, '-s', '-M', 'deposit=10'], {
|
|
1249
|
-
env
|
|
1296
|
+
env,
|
|
1297
|
+
})
|
|
1298
|
+
|
|
1299
|
+
expect(credentialActions).toEqual(['open'])
|
|
1300
|
+
expect(channelId).toMatch(/^0x[0-9a-f]{64}$/)
|
|
1301
|
+
|
|
1302
|
+
const listed = await serve(['sessions', 'list', '--json'], { env })
|
|
1303
|
+
expect(JSON.parse(listed.output).sessions).toEqual([
|
|
1304
|
+
expect.objectContaining({ channelId, status: 'open' }),
|
|
1305
|
+
])
|
|
1306
|
+
|
|
1307
|
+
const closed = await serve(['sessions', 'close', channelId!, '--rpc-url', rpcUrl, '--json'], {
|
|
1308
|
+
env,
|
|
1250
1309
|
})
|
|
1310
|
+
expect(closed.exitCode, `${closed.output}\n${closed.stderr}`).toBeUndefined()
|
|
1311
|
+
expect(JSON.parse(closed.output)).toEqual(
|
|
1312
|
+
expect.objectContaining({ channelId, status: 'closed' }),
|
|
1313
|
+
)
|
|
1314
|
+
expect(credentialActions.at(-1)).toBe('close')
|
|
1251
1315
|
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
expect(lastAction).toBe('close')
|
|
1316
|
+
const empty = await serve(['sessions', 'list', '--json'], { env })
|
|
1317
|
+
expect(JSON.parse(empty.output)).toEqual({ sessions: [] })
|
|
1255
1318
|
} finally {
|
|
1256
1319
|
httpServer.close()
|
|
1257
1320
|
}
|
|
@@ -1298,7 +1361,9 @@ describe('session sse (examples/session/sse)', () => {
|
|
|
1298
1361
|
secretKey: 'cli-test-secret-cli-test-secret-32',
|
|
1299
1362
|
})
|
|
1300
1363
|
|
|
1364
|
+
const paidAcceptHeaders: (string | undefined)[] = []
|
|
1301
1365
|
const httpServer = await Http.createServer(async (req, res) => {
|
|
1366
|
+
if (req.headers.authorization) paidAcceptHeaders.push(req.headers.accept)
|
|
1302
1367
|
const result = await toNodeListener(
|
|
1303
1368
|
server.session({
|
|
1304
1369
|
amount: '0.001',
|
|
@@ -1322,10 +1387,12 @@ describe('session sse (examples/session/sse)', () => {
|
|
|
1322
1387
|
})
|
|
1323
1388
|
|
|
1324
1389
|
try {
|
|
1325
|
-
const { output } = await serve(
|
|
1326
|
-
|
|
1327
|
-
|
|
1390
|
+
const { output } = await serve(
|
|
1391
|
+
[httpServer.url, '--rpc-url', rpcUrl, '-H', 'Accept: application/json', '-M', 'deposit=10'],
|
|
1392
|
+
{ env: { MPPX_PRIVATE_KEY: testPrivateKey } },
|
|
1393
|
+
)
|
|
1328
1394
|
expect(output.trim()).toBe('Hello world!')
|
|
1395
|
+
expect(paidAcceptHeaders).toEqual(['application/json'])
|
|
1329
1396
|
} finally {
|
|
1330
1397
|
httpServer.close()
|
|
1331
1398
|
}
|
|
@@ -1836,8 +1903,36 @@ test('mppx --help', async () => {
|
|
|
1836
1903
|
expect(output).toContain('mppx')
|
|
1837
1904
|
expect(output).toContain('<url>')
|
|
1838
1905
|
expect(output).toContain('account')
|
|
1906
|
+
expect(output).toContain('sessions')
|
|
1839
1907
|
expect(output).toContain('services')
|
|
1840
1908
|
expect(output).toContain('sign')
|
|
1909
|
+
expect(output).toContain('--session')
|
|
1910
|
+
})
|
|
1911
|
+
|
|
1912
|
+
test('mppx sessions --help', async () => {
|
|
1913
|
+
const { output } = await serve(['sessions', '--help'])
|
|
1914
|
+
expect(output).toContain('close Cooperatively close')
|
|
1915
|
+
expect(output).toContain('list List persistent')
|
|
1916
|
+
expect(output).toContain('view View a persistent')
|
|
1917
|
+
})
|
|
1918
|
+
|
|
1919
|
+
test('mppx sessions close --help', async () => {
|
|
1920
|
+
const { output } = await serve(['sessions', 'close', '--help'])
|
|
1921
|
+
expect(output).toContain('--all')
|
|
1922
|
+
expect(output).toContain('--yes')
|
|
1923
|
+
expect(output).toContain('--url')
|
|
1924
|
+
})
|
|
1925
|
+
|
|
1926
|
+
test('mppx sessions close --all requires confirmation', async () => {
|
|
1927
|
+
const { exitCode, output } = await serve(['sessions', 'close', '--all'])
|
|
1928
|
+
expect(exitCode).toBe(2)
|
|
1929
|
+
expect(output).toContain('requires --yes')
|
|
1930
|
+
})
|
|
1931
|
+
|
|
1932
|
+
test('mppx sessions close --all returns an empty structured result', async () => {
|
|
1933
|
+
const { exitCode, output } = await serve(['sessions', 'close', '--all', '--yes', '--json'])
|
|
1934
|
+
expect(exitCode).toBeUndefined()
|
|
1935
|
+
expect(JSON.parse(output)).toEqual({ closed: [], failed: [] })
|
|
1841
1936
|
})
|
|
1842
1937
|
|
|
1843
1938
|
describe('account fund help', () => {
|
package/src/cli/cli.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { normalizeHeaders } from '../client/internal/Fetch.js'
|
|
|
13
13
|
import * as Mppx from '../client/Mppx.js'
|
|
14
14
|
import * as Constants from '../Constants.js'
|
|
15
15
|
import { validate as validateDiscovery } from '../discovery/Validate.js'
|
|
16
|
+
import { isTempoSessionChallenge } from '../tempo/session/client/Transports.js'
|
|
16
17
|
import { createDefaultStore, createKeychain, resolveAccountName } from './account.js'
|
|
17
18
|
import { loadConfig, resolveAcceptPayment, selectChallenge } from './internal.js'
|
|
18
19
|
import type { Plugin } from './plugins/plugin.js'
|
|
@@ -21,6 +22,9 @@ import {
|
|
|
21
22
|
readTempoKeystore,
|
|
22
23
|
resolveTempoAccount,
|
|
23
24
|
} from './plugins/tempo.js'
|
|
25
|
+
import sessions, { sessionCommandError } from './sessions/commands.js'
|
|
26
|
+
import { runPersistentSessionRequest } from './sessions/request.js'
|
|
27
|
+
import { createSessionRegistry } from './sessions/store.js'
|
|
24
28
|
import {
|
|
25
29
|
chainName,
|
|
26
30
|
confirm,
|
|
@@ -244,6 +248,15 @@ const cli = Cli.create('mppx', {
|
|
|
244
248
|
.string()
|
|
245
249
|
.optional()
|
|
246
250
|
.describe('RPC endpoint, defaults to public RPC for chain (env: MPPX_RPC_URL)'),
|
|
251
|
+
session: z
|
|
252
|
+
.string()
|
|
253
|
+
.optional()
|
|
254
|
+
.default('auto')
|
|
255
|
+
.refine(
|
|
256
|
+
(value) => value === 'auto' || value === 'new' || /^0x[0-9a-fA-F]{64}$/.test(value),
|
|
257
|
+
'Expected auto, new, or a 32-byte channel ID',
|
|
258
|
+
)
|
|
259
|
+
.describe('Session selection: auto, new, or channel ID'),
|
|
247
260
|
silent: z.boolean().default(false).describe('Silent mode (suppress progress and info)'),
|
|
248
261
|
slippage: z.number().optional().describe('Tempo auto-swap max slippage percentage'),
|
|
249
262
|
userAgent: z
|
|
@@ -347,6 +360,9 @@ const cli = Cli.create('mppx', {
|
|
|
347
360
|
}
|
|
348
361
|
|
|
349
362
|
try {
|
|
363
|
+
const methodOpts = parseMethodOpts(c.options.methodOpt)
|
|
364
|
+
const sessionRegistry = createSessionRegistry()
|
|
365
|
+
|
|
350
366
|
const init: RequestInit = { redirect: c.options.location ? 'follow' : 'manual' }
|
|
351
367
|
if (c.options.jsonBody) {
|
|
352
368
|
init.body = c.options.jsonBody
|
|
@@ -368,6 +384,12 @@ const cli = Cli.create('mppx', {
|
|
|
368
384
|
if (c.options.verbose >= 2) printRequestHeaders(url, init, info)
|
|
369
385
|
const challengeResponse = await targetFetch(fetchUrl, init)
|
|
370
386
|
if (challengeResponse.status !== 402) {
|
|
387
|
+
if (c.options.session !== 'auto')
|
|
388
|
+
return c.error({
|
|
389
|
+
code: 'UNSUPPORTED_SESSION',
|
|
390
|
+
message: '--session requires a tempo/session payment challenge.',
|
|
391
|
+
exitCode: 2,
|
|
392
|
+
})
|
|
371
393
|
if (c.options.fail && challengeResponse.status >= 400)
|
|
372
394
|
return c.error({
|
|
373
395
|
code: 'HTTP_ERROR',
|
|
@@ -379,7 +401,6 @@ const cli = Cli.create('mppx', {
|
|
|
379
401
|
return
|
|
380
402
|
}
|
|
381
403
|
|
|
382
|
-
const methodOpts = parseMethodOpts(c.options.methodOpt)
|
|
383
404
|
const offeredChallenges = Challenge.fromResponseList(challengeResponse)
|
|
384
405
|
const currencyChallenges = filterChallengesByCurrency(offeredChallenges, c.options.currency)
|
|
385
406
|
if (c.options.currency && currencyChallenges.length === 0) {
|
|
@@ -446,7 +467,12 @@ const cli = Cli.create('mppx', {
|
|
|
446
467
|
// Display challenge
|
|
447
468
|
const shownKeys = new Set<string>()
|
|
448
469
|
{
|
|
449
|
-
printResponseHeaders(
|
|
470
|
+
printResponseHeaders(
|
|
471
|
+
challengeResponse,
|
|
472
|
+
challenge.method === 'tempo' && challenge.intent === 'session' && c.options.include
|
|
473
|
+
? { ...headerOpts, include: false, verbose: Math.max(headerOpts.verbose, 2) }
|
|
474
|
+
: headerOpts,
|
|
475
|
+
)
|
|
450
476
|
|
|
451
477
|
const challengeRows = (() => {
|
|
452
478
|
const skip = new Set(['id', 'request'])
|
|
@@ -517,6 +543,44 @@ const cli = Cli.create('mppx', {
|
|
|
517
543
|
}
|
|
518
544
|
}
|
|
519
545
|
|
|
546
|
+
const persistentSessionAccount =
|
|
547
|
+
process.env.MPPX_PRIVATE_KEY?.trim() ||
|
|
548
|
+
!isTempoAccount(resolveAccountName(c.options.account))
|
|
549
|
+
if (isTempoSessionChallenge(challenge) && persistentSessionAccount) {
|
|
550
|
+
try {
|
|
551
|
+
await runPersistentSessionRequest({
|
|
552
|
+
challenge,
|
|
553
|
+
challengeResponse: selectedChallengeResponse,
|
|
554
|
+
endpoint: url,
|
|
555
|
+
fetch: targetFetch,
|
|
556
|
+
fetchInput: fetchUrl,
|
|
557
|
+
init,
|
|
558
|
+
info,
|
|
559
|
+
methodOptions: methodOpts,
|
|
560
|
+
options: {
|
|
561
|
+
account: c.options.account,
|
|
562
|
+
fail: c.options.fail,
|
|
563
|
+
include: c.options.include,
|
|
564
|
+
network: c.options.network,
|
|
565
|
+
rpcUrl: c.options.rpcUrl,
|
|
566
|
+
session: c.options.session,
|
|
567
|
+
silent: c.options.silent,
|
|
568
|
+
verbose: c.options.verbose,
|
|
569
|
+
},
|
|
570
|
+
registry: sessionRegistry,
|
|
571
|
+
})
|
|
572
|
+
} catch (error) {
|
|
573
|
+
return sessionCommandError(error, 'SESSION_REQUEST_FAILED')
|
|
574
|
+
}
|
|
575
|
+
return
|
|
576
|
+
}
|
|
577
|
+
if (c.options.session !== 'auto')
|
|
578
|
+
return c.error({
|
|
579
|
+
code: 'UNSUPPORTED_SESSION',
|
|
580
|
+
message: '--session requires a tempo/session payment challenge.',
|
|
581
|
+
exitCode: 2,
|
|
582
|
+
})
|
|
583
|
+
|
|
520
584
|
// Create credential
|
|
521
585
|
let credential: string
|
|
522
586
|
if (pluginResult?.createCredential)
|
|
@@ -1578,6 +1642,7 @@ cli.command(account)
|
|
|
1578
1642
|
cli.command(discover)
|
|
1579
1643
|
cli.command(init)
|
|
1580
1644
|
cli.command(services)
|
|
1645
|
+
cli.command(sessions)
|
|
1581
1646
|
cli.command(sign)
|
|
1582
1647
|
cli.command(validate)
|
|
1583
1648
|
|
package/src/cli/mcp.test.ts
CHANGED
|
@@ -193,6 +193,9 @@ test('tools/list exposes mppx commands with input and output schemas', async ()
|
|
|
193
193
|
'services_endpoints',
|
|
194
194
|
'services_list',
|
|
195
195
|
'services_show',
|
|
196
|
+
'sessions_close',
|
|
197
|
+
'sessions_list',
|
|
198
|
+
'sessions_view',
|
|
196
199
|
'sign',
|
|
197
200
|
'validate',
|
|
198
201
|
])
|
|
@@ -208,6 +211,14 @@ test('tools/list exposes mppx commands with input and output schemas', async ()
|
|
|
208
211
|
type: 'object',
|
|
209
212
|
}),
|
|
210
213
|
)
|
|
214
|
+
expect(
|
|
215
|
+
tools.find((tool: { name: string }) => tool.name === 'sessions_list').outputSchema,
|
|
216
|
+
).toEqual(
|
|
217
|
+
expect.objectContaining({
|
|
218
|
+
properties: expect.objectContaining({ sessions: expect.any(Object) }),
|
|
219
|
+
type: 'object',
|
|
220
|
+
}),
|
|
221
|
+
)
|
|
211
222
|
expect(client.nonJsonLines).toEqual([])
|
|
212
223
|
})
|
|
213
224
|
|