mppx 0.5.14 → 0.5.17
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 +21 -0
- package/dist/Method.d.ts +5 -2
- package/dist/Method.d.ts.map +1 -1
- package/dist/Method.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +30 -1
- package/dist/cli/cli.js.map +1 -1
- package/dist/mcp-sdk/server/Transport.d.ts.map +1 -1
- package/dist/mcp-sdk/server/Transport.js +8 -2
- package/dist/mcp-sdk/server/Transport.js.map +1 -1
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +17 -10
- package/dist/server/Mppx.js.map +1 -1
- package/dist/server/Request.js +5 -1
- package/dist/server/Request.js.map +1 -1
- package/dist/server/Transport.d.ts.map +1 -1
- package/dist/server/Transport.js +4 -0
- package/dist/server/Transport.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/Methods.d.ts.map +1 -1
- package/dist/tempo/Methods.js +4 -2
- package/dist/tempo/Methods.js.map +1 -1
- package/dist/tempo/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/client/SessionManager.js +20 -10
- package/dist/tempo/client/SessionManager.js.map +1 -1
- package/dist/tempo/internal/fee-payer.d.ts +4 -1
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js +92 -21
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/server/Session.d.ts.map +1 -1
- package/dist/tempo/server/Session.js +43 -20
- package/dist/tempo/server/Session.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/server/internal/transport.d.ts +0 -7
- package/dist/tempo/server/internal/transport.d.ts.map +1 -1
- package/dist/tempo/server/internal/transport.js +84 -13
- package/dist/tempo/server/internal/transport.js.map +1 -1
- package/package.json +3 -3
- package/src/Method.ts +5 -2
- package/src/cli/cli.test.ts +21 -0
- package/src/cli/cli.ts +33 -1
- package/src/internal/changeset.test.ts +106 -0
- package/src/mcp-sdk/client/McpClient.integration.test.ts +634 -0
- package/src/mcp-sdk/server/Transport.test.ts +1 -0
- package/src/mcp-sdk/server/Transport.ts +10 -2
- package/src/proxy/Proxy.test.ts +149 -1
- package/src/server/Mppx.test.ts +120 -0
- package/src/server/Mppx.ts +27 -11
- package/src/server/Request.test.ts +46 -1
- package/src/server/Request.ts +6 -1
- package/src/server/Transport.test.ts +2 -0
- package/src/server/Transport.ts +4 -0
- package/src/stripe/server/internal/html/package.json +1 -1
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/tempo/Methods.test.ts +13 -0
- package/src/tempo/Methods.ts +23 -16
- package/src/tempo/client/SessionManager.ts +32 -9
- package/src/tempo/internal/fee-payer.test.ts +40 -4
- package/src/tempo/internal/fee-payer.ts +105 -21
- package/src/tempo/server/Session.test.ts +760 -2
- package/src/tempo/server/Session.ts +59 -17
- package/src/tempo/server/internal/html/package.json +1 -1
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/server/internal/transport.test.ts +321 -10
- package/src/tempo/server/internal/transport.ts +101 -14
- package/src/viem/Client.test.ts +52 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.gen.js","sourceRoot":"","sources":["../../../../src/tempo/server/internal/html.gen.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,IAAI,GAAG,
|
|
1
|
+
{"version":3,"file":"html.gen.js","sourceRoot":"","sources":["../../../../src/tempo/server/internal/html.gen.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,IAAI,GAAG,27icAA27ic,CAAA"}
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tempo-specific SSE transport that wraps the base HTTP transport
|
|
3
|
-
* with metering logic (context capture from verified credentials, per-token
|
|
4
|
-
* charging via Sse.serve).
|
|
5
|
-
*
|
|
6
|
-
* @internal
|
|
7
|
-
*/
|
|
8
1
|
import * as Transport from '../../../server/Transport.js';
|
|
9
2
|
import * as ChannelStore from '../../session/ChannelStore.js';
|
|
10
3
|
import * as Sse_core from '../../session/Sse.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../../src/tempo/server/internal/transport.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../../src/tempo/server/internal/transport.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,SAAS,MAAM,8BAA8B,CAAA;AACzD,OAAO,KAAK,YAAY,MAAM,+BAA+B,CAAA;AAC7D,OAAO,KAAK,QAAQ,MAAM,sBAAsB,CAAA;AAGhD,mDAAmD;AACnD,MAAM,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAA;AAE3D;;;;;;;;GAQG;AACH,wBAAgB,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,GAAG;IAAE,KAAK,EAAE,YAAY,CAAC,YAAY,CAAA;CAAE,GAAG,GAAG,CAsKpF;AAED,MAAM,CAAC,OAAO,WAAW,GAAG,CAAC;IAC3B,KAAK,OAAO,GAAG;QACb;;;;;;;;;WASG;QACH,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;QAC1B,sDAAsD;QACtD,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KACrC,CAAA;CACF;AAED,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,OAAO,EAAE;IACpC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;IAC7E,WAAW,EAAE,MAAM,CAAA;CACpB,GAAG,QAAQ,CAwBX"}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @internal
|
|
7
7
|
*/
|
|
8
|
+
import * as Challenge from '../../../Challenge.js';
|
|
9
|
+
import * as Errors from '../../../Errors.js';
|
|
8
10
|
import * as Transport from '../../../server/Transport.js';
|
|
9
11
|
import * as ChannelStore from '../../session/ChannelStore.js';
|
|
10
12
|
import * as Sse_core from '../../session/Sse.js';
|
|
@@ -33,6 +35,7 @@ export function sse(options) {
|
|
|
33
35
|
name: 'sse',
|
|
34
36
|
captureRequest(request) {
|
|
35
37
|
return (base.captureRequest?.(request) ?? {
|
|
38
|
+
hasBody: request.body !== null,
|
|
36
39
|
headers: new Headers(request.headers),
|
|
37
40
|
method: request.method,
|
|
38
41
|
url: new URL(request.url),
|
|
@@ -52,6 +55,9 @@ export function sse(options) {
|
|
|
52
55
|
throw new Error('No SSE context available');
|
|
53
56
|
const channelId = payload.channelId;
|
|
54
57
|
const tickCost = BigInt(verifiedCredential.challenge.request.amount);
|
|
58
|
+
const unitType = typeof verifiedCredential.challenge.request.unitType === 'string'
|
|
59
|
+
? verifiedCredential.challenge.request.unitType
|
|
60
|
+
: undefined;
|
|
55
61
|
// Auto-detect upstream SSE responses and parse them into an
|
|
56
62
|
// AsyncIterable so they flow through the metered pipeline.
|
|
57
63
|
// This lets proxy consumers simply pass `result.withReceipt(upstreamRes)`
|
|
@@ -63,9 +69,7 @@ export function sse(options) {
|
|
|
63
69
|
// Pass async generator functions directly so Sse.serve gives them
|
|
64
70
|
// a SessionController for manual charge(). Pass raw AsyncIterables
|
|
65
71
|
// as-is so Sse.serve auto-charges per yielded value.
|
|
66
|
-
const generate =
|
|
67
|
-
? resolved
|
|
68
|
-
: resolved;
|
|
72
|
+
const generate = resolveMeteredGenerate(resolved, unitType);
|
|
69
73
|
const stream = Sse_core.serve({
|
|
70
74
|
store,
|
|
71
75
|
channelId,
|
|
@@ -85,22 +89,60 @@ export function sse(options) {
|
|
|
85
89
|
response: response,
|
|
86
90
|
challengeId: verifiedChallengeId,
|
|
87
91
|
});
|
|
92
|
+
if (!shouldChargePlainResponse(input, payload)) {
|
|
93
|
+
return baseResponse;
|
|
94
|
+
}
|
|
95
|
+
const currentReceipt = receipt;
|
|
96
|
+
const available = BigInt(currentReceipt.acceptedCumulative) - BigInt(currentReceipt.spent);
|
|
97
|
+
if (available < tickCost) {
|
|
98
|
+
const error = new Errors.InsufficientBalanceError({
|
|
99
|
+
reason: `requested ${tickCost}, available ${available}`,
|
|
100
|
+
});
|
|
101
|
+
return new Response(JSON.stringify(error.toProblemDetails(verifiedCredential.challenge.id)), {
|
|
102
|
+
status: error.status,
|
|
103
|
+
headers: {
|
|
104
|
+
'WWW-Authenticate': Challenge.serialize(verifiedCredential.challenge),
|
|
105
|
+
'Cache-Control': 'no-store',
|
|
106
|
+
'Content-Type': 'application/problem+json',
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const chargedReceipt = {
|
|
111
|
+
...currentReceipt,
|
|
112
|
+
spent: (BigInt(currentReceipt.spent) + tickCost).toString(),
|
|
113
|
+
units: (currentReceipt.units ?? 0) + 1,
|
|
114
|
+
};
|
|
115
|
+
const chargedResponse = base.respondReceipt({
|
|
116
|
+
credential: verifiedCredential,
|
|
117
|
+
envelope,
|
|
118
|
+
input,
|
|
119
|
+
receipt: chargedReceipt,
|
|
120
|
+
response: response,
|
|
121
|
+
challengeId: verifiedChallengeId,
|
|
122
|
+
});
|
|
88
123
|
// Non-SSE response (e.g. upstream returned JSON instead of event-stream).
|
|
89
124
|
// Need to deduct tickCost so request isn't free.
|
|
90
|
-
//
|
|
91
|
-
// response
|
|
92
|
-
if (isNullBodyStatus(
|
|
93
|
-
|
|
125
|
+
// For null-body statuses, the request shape determines whether the
|
|
126
|
+
// response is management (no charge) or plain content (charge one tick).
|
|
127
|
+
if (isNullBodyStatus(chargedResponse.status)) {
|
|
128
|
+
void ChannelStore.deductFromChannel(store, channelId, tickCost);
|
|
129
|
+
return chargedResponse;
|
|
94
130
|
}
|
|
95
131
|
const stream = new ReadableStream({
|
|
96
132
|
async start(controller) {
|
|
97
133
|
// deduction completes before consumer reads
|
|
98
|
-
await ChannelStore.deductFromChannel(store, channelId, tickCost);
|
|
99
|
-
if (!
|
|
134
|
+
const result = await ChannelStore.deductFromChannel(store, channelId, tickCost);
|
|
135
|
+
if (!result.ok) {
|
|
136
|
+
controller.error(new Errors.InsufficientBalanceError({
|
|
137
|
+
reason: `requested ${tickCost}, available ${result.channel.highestVoucherAmount - result.channel.spent}`,
|
|
138
|
+
}));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (!chargedResponse.body) {
|
|
100
142
|
controller.close();
|
|
101
143
|
return;
|
|
102
144
|
}
|
|
103
|
-
const reader =
|
|
145
|
+
const reader = chargedResponse.body.getReader();
|
|
104
146
|
try {
|
|
105
147
|
while (true) {
|
|
106
148
|
const { done, value } = await reader.read();
|
|
@@ -116,9 +158,9 @@ export function sse(options) {
|
|
|
116
158
|
},
|
|
117
159
|
});
|
|
118
160
|
return new Response(stream, {
|
|
119
|
-
status:
|
|
120
|
-
statusText:
|
|
121
|
-
headers:
|
|
161
|
+
status: chargedResponse.status,
|
|
162
|
+
statusText: chargedResponse.statusText,
|
|
163
|
+
headers: chargedResponse.headers,
|
|
122
164
|
});
|
|
123
165
|
},
|
|
124
166
|
});
|
|
@@ -158,7 +200,36 @@ function isAsyncGeneratorFunction(value) {
|
|
|
158
200
|
function isAsyncIterable(value) {
|
|
159
201
|
return value !== null && typeof value === 'object' && Symbol.asyncIterator in value;
|
|
160
202
|
}
|
|
203
|
+
function resolveMeteredGenerate(value, unitType) {
|
|
204
|
+
if (isAsyncGeneratorFunction(value))
|
|
205
|
+
return value;
|
|
206
|
+
if (unitType !== 'request')
|
|
207
|
+
return value;
|
|
208
|
+
const iterable = value;
|
|
209
|
+
return async function* chargeOnce(stream) {
|
|
210
|
+
let charged = false;
|
|
211
|
+
for await (const chunk of iterable) {
|
|
212
|
+
if (!charged) {
|
|
213
|
+
await stream.charge();
|
|
214
|
+
charged = true;
|
|
215
|
+
}
|
|
216
|
+
yield chunk;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
161
220
|
function isNullBodyStatus(status) {
|
|
162
221
|
return [101, 204, 205, 304].includes(status);
|
|
163
222
|
}
|
|
223
|
+
function shouldChargePlainResponse(input, payload) {
|
|
224
|
+
if (payload.action === 'close' || payload.action === 'topUp')
|
|
225
|
+
return false;
|
|
226
|
+
if (input.method !== 'POST')
|
|
227
|
+
return true;
|
|
228
|
+
const contentLength = input.headers.get('content-length');
|
|
229
|
+
if (contentLength !== null && contentLength !== '0')
|
|
230
|
+
return true;
|
|
231
|
+
if (input.headers.has('transfer-encoding'))
|
|
232
|
+
return true;
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
164
235
|
//# sourceMappingURL=transport.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../../src/tempo/server/internal/transport.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,SAAS,MAAM,8BAA8B,CAAA;AACzD,OAAO,KAAK,YAAY,MAAM,+BAA+B,CAAA;AAC7D,OAAO,KAAK,QAAQ,MAAM,sBAAsB,CAAA;AAMhD;;;;;;;;GAQG;AACH,MAAM,UAAU,GAAG,CAAC,OAA2D;IAC7E,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,GAAG,OAAO,CAAA;IAEzC,oEAAoE;IACpE,6EAA6E;IAC7E,qEAAqE;IACrE,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE;QAClB,IAAI,CAAC,IAAI;YAAE,OAAO,OAAO,CAAC,KAAK,CAAA;QAC/B,MAAM,EAAE,aAAa,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpD,OAAO,KAAK,CAAA;IACd,CAAC,CAAC,EAAE,CAAA;IAEJ,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,CAAA;IAC7B,OAAO,SAAS,CAAC,IAAI,CAAgE;QACnF,IAAI,EAAE,KAAK;QAEX,cAAc,CAAC,OAAO;YACpB,OAAO,CACL,IAAI,CAAC,cAAc,EAAE,CAAC,OAAO,CAAC,IAAI;gBAChC,OAAO,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;gBACrC,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,GAAG,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC;aAC1B,CACF,CAAA;QACH,CAAC;QAED,aAAa,CAAC,OAAO;YACnB,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;QACpC,CAAC;QAED,gBAAgB,CAAC,OAAO;YACtB,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAa,CAAA;QACnD,CAAC;QAED,cAAc,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE;YAC5E,MAAM,kBAAkB,GAAG,QAAQ,EAAE,UAAU,IAAI,UAAU,CAAA;YAC7D,MAAM,mBAAmB,GAAG,QAAQ,EAAE,SAAS,CAAC,EAAE,IAAI,WAAW,CAAA;YACjE,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAA4C,CAAA;YAC/E,IAAI,CAAC,OAAO,CAAC,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;YACnE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAA;YACnC,MAAM,QAAQ,GAAG,MAAM,CAAC,kBAAkB,CAAC,SAAS,CAAC,OAAO,CAAC,MAAgB,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../../src/tempo/server/internal/transport.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mppx",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.17",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"files": [
|
|
@@ -123,8 +123,8 @@
|
|
|
123
123
|
}
|
|
124
124
|
},
|
|
125
125
|
"dependencies": {
|
|
126
|
-
"incur": "^0.3.
|
|
127
|
-
"ox": "0.14.
|
|
126
|
+
"incur": "^0.3.25",
|
|
127
|
+
"ox": "0.14.15",
|
|
128
128
|
"zod": "^4.3.6"
|
|
129
129
|
},
|
|
130
130
|
"repository": {
|
package/src/Method.ts
CHANGED
|
@@ -69,6 +69,7 @@ export type AnyClient = Client<any, any>
|
|
|
69
69
|
|
|
70
70
|
/** Transport-captured request metadata used as the authoritative request snapshot. */
|
|
71
71
|
export type CapturedRequest = {
|
|
72
|
+
readonly hasBody?: boolean | undefined
|
|
72
73
|
readonly headers: Headers
|
|
73
74
|
readonly method: string
|
|
74
75
|
readonly url: URL
|
|
@@ -167,8 +168,10 @@ export type VerifyFn<method extends Method> = (
|
|
|
167
168
|
* response or generator. If it returns `undefined`, the server handler
|
|
168
169
|
* is expected to serve content via `withReceipt(response)`.
|
|
169
170
|
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
171
|
+
* Use `parameters.envelope?.capturedRequest` for any transport-agnostic
|
|
172
|
+
* authorization, billing, or routing decisions. The raw `input` should only
|
|
173
|
+
* be used for transport-specific response shaping (for example, HTTP content
|
|
174
|
+
* negotiation).
|
|
172
175
|
*/
|
|
173
176
|
export type RespondFn<method extends Method> = (
|
|
174
177
|
parameters: RespondContext<method>,
|
package/src/cli/cli.test.ts
CHANGED
|
@@ -1118,6 +1118,27 @@ describe.skipIf(!!process.env.CI)('account', () => {
|
|
|
1118
1118
|
expect(result.stdout).toContain('not found')
|
|
1119
1119
|
})
|
|
1120
1120
|
|
|
1121
|
+
// --- account export ---
|
|
1122
|
+
|
|
1123
|
+
test('export: prints private key for existing account', () => {
|
|
1124
|
+
const name = `${prefix}_export`
|
|
1125
|
+
createAccount(name)
|
|
1126
|
+
const result = accountRun(['account', 'export', '--account', name])
|
|
1127
|
+
expect(result.status).toBe(0)
|
|
1128
|
+
|
|
1129
|
+
const privateKey = result.stdout.match(/0x[0-9a-fA-F]{64}/)?.[0]
|
|
1130
|
+
expect(privateKey).toBeDefined()
|
|
1131
|
+
|
|
1132
|
+
const view = accountRun(['account', 'view', '--account', name])
|
|
1133
|
+
expect(view.stdout).toContain(privateKeyToAccount(privateKey as `0x${string}`).address)
|
|
1134
|
+
})
|
|
1135
|
+
|
|
1136
|
+
test('export: missing account exits non-zero', () => {
|
|
1137
|
+
const result = accountRun(['account', 'export', '--account', `${prefix}_missing_export`])
|
|
1138
|
+
expect(result.status).not.toBe(0)
|
|
1139
|
+
expect(result.stdout).toContain('not found')
|
|
1140
|
+
})
|
|
1141
|
+
|
|
1121
1142
|
// --- account list ---
|
|
1122
1143
|
|
|
1123
1144
|
test('list: includes created accounts', () => {
|
package/src/cli/cli.ts
CHANGED
|
@@ -497,7 +497,7 @@ const cli = Cli.create('mppx', {
|
|
|
497
497
|
})
|
|
498
498
|
|
|
499
499
|
const account = Cli.create('account', {
|
|
500
|
-
description: 'Manage accounts (create, default, delete, fund, list, view)',
|
|
500
|
+
description: 'Manage accounts (create, default, delete, export, fund, list, view)',
|
|
501
501
|
})
|
|
502
502
|
.command('create', {
|
|
503
503
|
description: 'Create new account',
|
|
@@ -713,6 +713,38 @@ const account = Cli.create('account', {
|
|
|
713
713
|
}
|
|
714
714
|
},
|
|
715
715
|
})
|
|
716
|
+
.command('export', {
|
|
717
|
+
description: 'Export the private key for a local account',
|
|
718
|
+
options: z.object({
|
|
719
|
+
account: z.string().optional().describe('Account name (env: MPPX_ACCOUNT)'),
|
|
720
|
+
}),
|
|
721
|
+
alias: { account: 'a' },
|
|
722
|
+
async run(c) {
|
|
723
|
+
const accountName = resolveAccountName(c.options.account)
|
|
724
|
+
|
|
725
|
+
if (isTempoAccount(accountName)) {
|
|
726
|
+
return c.error({
|
|
727
|
+
code: 'UNSUPPORTED_ACCOUNT',
|
|
728
|
+
message: `Account "${accountName}" is managed by Tempo wallet and does not expose a private key via mppx.`,
|
|
729
|
+
exitCode: 2,
|
|
730
|
+
})
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const key = await createKeychain(accountName).get()
|
|
734
|
+
if (!key) {
|
|
735
|
+
if (c.options.account)
|
|
736
|
+
return c.error({
|
|
737
|
+
code: 'ACCOUNT_NOT_FOUND',
|
|
738
|
+
message: `Account "${accountName}" not found.`,
|
|
739
|
+
exitCode: 69,
|
|
740
|
+
})
|
|
741
|
+
else
|
|
742
|
+
return c.error({ code: 'ACCOUNT_NOT_FOUND', message: 'No account found.', exitCode: 69 })
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
console.log(key)
|
|
746
|
+
},
|
|
747
|
+
})
|
|
716
748
|
.command('view', {
|
|
717
749
|
description: 'View account address',
|
|
718
750
|
options: z.object({
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as fs from 'node:fs'
|
|
2
|
+
import * as path from 'node:path'
|
|
3
|
+
|
|
4
|
+
import { describe, expect, test } from 'vp/test'
|
|
5
|
+
|
|
6
|
+
const changesetDir = path.resolve(import.meta.dirname, '../../.changeset')
|
|
7
|
+
const validPackages = new Set(['mppx'])
|
|
8
|
+
const validBumpTypes = new Set(['major', 'minor', 'patch', 'none'])
|
|
9
|
+
|
|
10
|
+
function getChangesetFiles() {
|
|
11
|
+
return fs
|
|
12
|
+
.readdirSync(changesetDir)
|
|
13
|
+
.filter((f) => f.endsWith('.md') && f !== 'README.md' && !f.startsWith('.'))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function parseFrontmatter(content: string) {
|
|
17
|
+
const match = content.match(/^\s*---\n([^]*?)\n\s*---/)
|
|
18
|
+
if (!match) return undefined
|
|
19
|
+
return match[1]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function parseReleases(frontmatter: string) {
|
|
23
|
+
return [...frontmatter.matchAll(/^['"]?([^'":\n]+?)['"]?\s*:\s*(.+)$/gm)].map(
|
|
24
|
+
([, name, type]) => ({ name: name!.trim(), type: type!.trim().replace(/['"]/g, '') }),
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('changesets', () => {
|
|
29
|
+
const files = getChangesetFiles()
|
|
30
|
+
|
|
31
|
+
test('all changeset files have valid frontmatter fences', () => {
|
|
32
|
+
for (const file of files) {
|
|
33
|
+
const content = fs.readFileSync(path.join(changesetDir, file), 'utf-8')
|
|
34
|
+
expect(content, `${file}: must start with "---"`).toMatch(/^---\n/)
|
|
35
|
+
const closingIdx = content.indexOf('---', 3)
|
|
36
|
+
expect(closingIdx, `${file}: missing closing "---" fence`).toBeGreaterThan(3)
|
|
37
|
+
|
|
38
|
+
// closing fence must not have trailing non-whitespace (parser chokes)
|
|
39
|
+
const afterClosing = content.slice(closingIdx + 3).split('\n')[0]!
|
|
40
|
+
expect(afterClosing.trim(), `${file}: trailing characters after closing "---"`).toBe('')
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('all changesets reference valid packages', () => {
|
|
45
|
+
for (const file of files) {
|
|
46
|
+
const content = fs.readFileSync(path.join(changesetDir, file), 'utf-8')
|
|
47
|
+
const frontmatter = parseFrontmatter(content)
|
|
48
|
+
if (!frontmatter) continue
|
|
49
|
+
for (const { name } of parseReleases(frontmatter)) {
|
|
50
|
+
expect(
|
|
51
|
+
validPackages.has(name),
|
|
52
|
+
`${file}: unknown package "${name}" (valid: ${[...validPackages].join(', ')})`,
|
|
53
|
+
).toBe(true)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('all changesets use valid bump types', () => {
|
|
59
|
+
for (const file of files) {
|
|
60
|
+
const content = fs.readFileSync(path.join(changesetDir, file), 'utf-8')
|
|
61
|
+
const frontmatter = parseFrontmatter(content)
|
|
62
|
+
if (!frontmatter) continue
|
|
63
|
+
for (const { name, type } of parseReleases(frontmatter)) {
|
|
64
|
+
expect(
|
|
65
|
+
validBumpTypes.has(type),
|
|
66
|
+
`${file}: invalid bump type "${type}" for "${name}" (valid: major, minor, patch, none)`,
|
|
67
|
+
).toBe(true)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test('no changeset uses major bump (pre-v1 policy)', () => {
|
|
73
|
+
for (const file of files) {
|
|
74
|
+
const content = fs.readFileSync(path.join(changesetDir, file), 'utf-8')
|
|
75
|
+
const frontmatter = parseFrontmatter(content)
|
|
76
|
+
if (!frontmatter) continue
|
|
77
|
+
for (const { name, type } of parseReleases(frontmatter)) {
|
|
78
|
+
expect(type, `${file}: "${name}" uses major bump — pre-v1 policy forbids this`).not.toBe(
|
|
79
|
+
'major',
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('all changesets have a non-empty description', () => {
|
|
86
|
+
for (const file of files) {
|
|
87
|
+
const content = fs.readFileSync(path.join(changesetDir, file), 'utf-8')
|
|
88
|
+
const match = content.match(/^\s*---[^]*?---\s*(.*)$/s)
|
|
89
|
+
expect(match?.[1]?.trim(), `${file}: changeset has an empty description`).toBeTruthy()
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('no duplicate package entries in frontmatter', () => {
|
|
94
|
+
for (const file of files) {
|
|
95
|
+
const content = fs.readFileSync(path.join(changesetDir, file), 'utf-8')
|
|
96
|
+
const frontmatter = parseFrontmatter(content)
|
|
97
|
+
if (!frontmatter) continue
|
|
98
|
+
const releases = parseReleases(frontmatter)
|
|
99
|
+
const seen = new Set<string>()
|
|
100
|
+
for (const { name } of releases) {
|
|
101
|
+
expect(seen.has(name), `${file}: duplicate entry for "${name}"`).toBe(false)
|
|
102
|
+
seen.add(name)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
})
|