mppx 0.6.17 → 0.6.18

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # mppx
2
2
 
3
+ ## 0.6.18
4
+
5
+ ### Patch Changes
6
+
7
+ - d365b41: Added Tempo CLI auto-swap and payment source token options.
8
+ - 9f660c2: Added Tempo CLI network shortcuts and challenge chain mismatch checks.
9
+ - 538c7fb: Added CLI commands for browsing the MPP services registry.
10
+
3
11
  ## 0.6.17
4
12
 
5
13
  ### Patch Changes
package/dist/cli/cli.d.ts CHANGED
@@ -10,6 +10,7 @@ declare const cli: Cli.Cli<{
10
10
  userAgent: string;
11
11
  verbose: number;
12
12
  account?: string | undefined;
13
+ autoSwap?: boolean | undefined;
13
14
  config?: string | undefined;
14
15
  data?: string | undefined;
15
16
  fail?: boolean | undefined;
@@ -20,7 +21,10 @@ declare const cli: Cli.Cli<{
20
21
  location?: boolean | undefined;
21
22
  method?: string | undefined;
22
23
  methodOpt?: string[] | undefined;
24
+ network?: "mainnet" | "testnet" | undefined;
25
+ payWith?: string | undefined;
23
26
  rpcUrl?: string | undefined;
27
+ slippage?: number | undefined;
24
28
  };
25
29
  };
26
30
  }, undefined, undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,GAAG,EAAa,MAAM,OAAO,CAAA;AA6EtC,QAAA,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;wBAscP,CAAA;AA8uBF,eAAe,GAAG,CAAA"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,GAAG,EAAa,MAAM,OAAO,CAAA;AAsLtC,QAAA,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAidP,CAAA;AA82BF,eAAe,GAAG,CAAA"}
package/dist/cli/cli.js CHANGED
@@ -32,6 +32,21 @@ const discoveryIssueSchema = z.object({
32
32
  path: z.string(),
33
33
  severity: z.string(),
34
34
  });
35
+ const serviceSummarySchema = z.object({
36
+ description: z.string().optional(),
37
+ id: z.string(),
38
+ name: z.string().optional(),
39
+ paidEndpoints: z.number(),
40
+ status: z.string().optional(),
41
+ url: z.string().optional(),
42
+ });
43
+ const serviceEndpointSchema = z.object({
44
+ description: z.string().optional(),
45
+ method: z.string(),
46
+ path: z.string(),
47
+ payment: z.unknown().optional(),
48
+ });
49
+ const servicesRegistryUrl = 'https://mpp.dev/api/services';
35
50
  function shouldReturnStructured(c) {
36
51
  return c.format === 'json' && c.formatExplicit;
37
52
  }
@@ -46,6 +61,77 @@ function canReadCommandStdin() {
46
61
  return false;
47
62
  return process.stdin.listenerCount('data') === 0 && process.stdin.listenerCount('readable') === 0;
48
63
  }
64
+ function getString(value) {
65
+ return typeof value === 'string' ? value : undefined;
66
+ }
67
+ function getEndpoints(service) {
68
+ return Array.isArray(service.endpoints)
69
+ ? service.endpoints.filter((endpoint) => typeof endpoint === 'object' && endpoint !== null)
70
+ : [];
71
+ }
72
+ function summarizeService(service) {
73
+ const endpoints = getEndpoints(service);
74
+ return {
75
+ ...(getString(service.description) ? { description: getString(service.description) } : {}),
76
+ id: getString(service.id) ?? getString(service.name) ?? 'unknown',
77
+ ...(getString(service.name) ? { name: getString(service.name) } : {}),
78
+ paidEndpoints: endpoints.filter((endpoint) => endpoint.payment).length,
79
+ ...(getString(service.status) ? { status: getString(service.status) } : {}),
80
+ ...(getString(service.serviceUrl) || getString(service.url)
81
+ ? { url: getString(service.serviceUrl) ?? getString(service.url) }
82
+ : {}),
83
+ };
84
+ }
85
+ function summarizeEndpoint(endpoint) {
86
+ return {
87
+ ...(getString(endpoint.description) ? { description: getString(endpoint.description) } : {}),
88
+ method: getString(endpoint.method) ?? 'GET',
89
+ path: getString(endpoint.path) ?? '/',
90
+ ...(endpoint.payment !== undefined ? { payment: endpoint.payment } : {}),
91
+ };
92
+ }
93
+ function formatPayment(payment) {
94
+ if (!payment)
95
+ return 'free';
96
+ if (typeof payment !== 'object')
97
+ return String(payment);
98
+ const p = payment;
99
+ const amount = getString(p.amount);
100
+ const currency = getString(p.currency);
101
+ const method = getString(p.method);
102
+ const intent = getString(p.intent);
103
+ return [amount, currency, method && intent ? `${method}/${intent}` : (method ?? intent)]
104
+ .filter(Boolean)
105
+ .join(' ');
106
+ }
107
+ async function fetchServicesRegistry() {
108
+ const url = process.env.MPPX_SERVICES_URL ?? servicesRegistryUrl;
109
+ const response = await globalThis.fetch(url);
110
+ if (!response.ok)
111
+ throw new Errors.IncurError({
112
+ code: 'SERVICES_FETCH_FAILED',
113
+ message: `Failed to fetch services registry: HTTP ${response.status}`,
114
+ exitCode: 1,
115
+ });
116
+ const json = (await response.json());
117
+ if (!json ||
118
+ typeof json !== 'object' ||
119
+ !Array.isArray(json.services))
120
+ throw new Errors.IncurError({
121
+ code: 'SERVICES_INVALID',
122
+ message: 'Services registry response did not contain a services array.',
123
+ exitCode: 1,
124
+ });
125
+ return json.services;
126
+ }
127
+ function findService(services, id) {
128
+ const needle = id.toLowerCase();
129
+ return services.find((service) => {
130
+ const serviceId = getString(service.id)?.toLowerCase();
131
+ const serviceName = getString(service.name)?.toLowerCase();
132
+ return serviceId === needle || serviceName === needle;
133
+ });
134
+ }
49
135
  const cli = Cli.create('mppx', {
50
136
  version: packageJson.version,
51
137
  description: 'Make HTTP requests with automatic payment handling',
@@ -55,6 +141,7 @@ const cli = Cli.create('mppx', {
55
141
  }),
56
142
  options: z.object({
57
143
  account: z.string().optional().describe('Account name (env: MPPX_ACCOUNT)'),
144
+ autoSwap: z.boolean().optional().describe('Auto-swap source tokens into payment currency'),
58
145
  config: z.string().optional().describe('Path to config file'),
59
146
  confirm: z.boolean().optional().default(false).describe('Show confirmation prompts'),
60
147
  data: z.string().optional().describe('Send request body (implies POST unless -X is set)'),
@@ -75,11 +162,14 @@ const cli = Cli.create('mppx', {
75
162
  .array(z.string())
76
163
  .optional()
77
164
  .describe('Method-specific option (key=value, repeatable)'),
165
+ network: z.enum(['mainnet', 'testnet']).optional().describe('Tempo network'),
166
+ payWith: z.string().optional().describe('Source token for Tempo auto-swap'),
78
167
  rpcUrl: z
79
168
  .string()
80
169
  .optional()
81
170
  .describe('RPC endpoint, defaults to public RPC for chain (env: MPPX_RPC_URL)'),
82
171
  silent: z.boolean().default(false).describe('Silent mode (suppress progress and info)'),
172
+ slippage: z.number().optional().describe('Tempo auto-swap max slippage percentage'),
83
173
  userAgent: z
84
174
  .string()
85
175
  .optional()
@@ -232,7 +322,14 @@ const cli = Cli.create('mppx', {
232
322
  if (plugin) {
233
323
  pluginResult = await plugin.setup({
234
324
  challenge,
235
- options: { account: c.options.account, rpcUrl: c.options.rpcUrl },
325
+ options: {
326
+ account: c.options.account,
327
+ autoSwap: c.options.autoSwap,
328
+ network: c.options.network,
329
+ payWith: c.options.payWith,
330
+ rpcUrl: c.options.rpcUrl,
331
+ slippage: c.options.slippage,
332
+ },
236
333
  methodOpts: parseMethodOpts(c.options.methodOpt),
237
334
  });
238
335
  tokenSymbol = pluginResult.tokenSymbol;
@@ -497,6 +594,7 @@ const account = Cli.create('account', {
497
594
  description: 'Create new account',
498
595
  options: z.object({
499
596
  account: z.string().optional().describe('Account name (env: MPPX_ACCOUNT)'),
597
+ network: z.enum(['mainnet', 'testnet']).optional().describe('Tempo network'),
500
598
  rpcUrl: z.string().optional().describe('RPC endpoint (env: MPPX_RPC_URL)'),
501
599
  }),
502
600
  output: z.object({ address: z.string(), name: z.string() }),
@@ -546,8 +644,8 @@ const account = Cli.create('account', {
546
644
  const addrDisplay = explorerUrl
547
645
  ? link(`${explorerUrl}/address/${acct.address}`, acct.address)
548
646
  : acct.address;
549
- const rpcUrl = resolveRpcUrl(c.options.rpcUrl);
550
- resolveChain({ rpcUrl })
647
+ const rpcUrl = resolveRpcUrl(c.options.rpcUrl, { network: c.options.network });
648
+ resolveChain({ network: c.options.network, rpcUrl })
551
649
  .then((chain) => createClient({ chain, transport: http(rpcUrl) }))
552
650
  .then((client) => import('viem/tempo').then(({ Actions }) => Actions.faucet.fund(client, { account: acct }).catch(() => { })));
553
651
  return outputResult(c, { address: acct.address, name: resolvedName }, () => {
@@ -659,6 +757,7 @@ const account = Cli.create('account', {
659
757
  description: 'Fund account with testnet tokens',
660
758
  options: z.object({
661
759
  account: z.string().optional().describe('Account name (env: MPPX_ACCOUNT)'),
760
+ network: z.enum(['mainnet', 'testnet']).optional().describe('Tempo network'),
662
761
  rpcUrl: z.string().optional().describe('RPC endpoint (env: MPPX_RPC_URL)'),
663
762
  }),
664
763
  output: z.object({ account: z.string(), chain: z.string(), transactions: z.array(z.string()) }),
@@ -679,8 +778,8 @@ const account = Cli.create('account', {
679
778
  return c.error({ code: 'ACCOUNT_NOT_FOUND', message: 'No account found.', exitCode: 69 });
680
779
  }
681
780
  const acct = privateKeyToAccount(key);
682
- const rpcUrl = resolveRpcUrl(c.options.rpcUrl);
683
- const chain = await resolveChain({ rpcUrl });
781
+ const rpcUrl = resolveRpcUrl(c.options.rpcUrl, { network: c.options.network });
782
+ const chain = await resolveChain({ network: c.options.network, rpcUrl });
684
783
  const client = createClient({ chain, transport: http(rpcUrl) });
685
784
  if (!structured)
686
785
  console.log(`Funding "${accountName}" on ${chainName(chain)}`);
@@ -797,6 +896,7 @@ const account = Cli.create('account', {
797
896
  description: 'View account address',
798
897
  options: z.object({
799
898
  account: z.string().optional().describe('Account name (env: MPPX_ACCOUNT)'),
899
+ network: z.enum(['mainnet', 'testnet']).optional().describe('Tempo network'),
800
900
  rpcUrl: z.string().optional().describe('RPC endpoint (env: MPPX_RPC_URL)'),
801
901
  }),
802
902
  output: accountViewSchema,
@@ -813,8 +913,8 @@ const account = Cli.create('account', {
813
913
  });
814
914
  }
815
915
  const address = tempoEntry.wallet_address;
816
- const rpcUrl = resolveRpcUrl(c.options.rpcUrl);
817
- const chain = await resolveChain({ rpcUrl });
916
+ const rpcUrl = resolveRpcUrl(c.options.rpcUrl, { network: c.options.network });
917
+ const chain = await resolveChain({ network: c.options.network, rpcUrl });
818
918
  const explorerUrl = chain.blockExplorers?.default?.url;
819
919
  const addrDisplay = explorerUrl
820
920
  ? link(`${explorerUrl}/address/${address}`, address)
@@ -846,8 +946,8 @@ const account = Cli.create('account', {
846
946
  return c.error({ code: 'ACCOUNT_NOT_FOUND', message: 'No account found.', exitCode: 69 });
847
947
  }
848
948
  const acct = privateKeyToAccount(key);
849
- const rpcUrl = resolveRpcUrl(c.options.rpcUrl);
850
- const chain = await resolveChain({ rpcUrl });
949
+ const rpcUrl = resolveRpcUrl(c.options.rpcUrl, { network: c.options.network });
950
+ const chain = await resolveChain({ network: c.options.network, rpcUrl });
851
951
  const explorerUrl = chain.blockExplorers?.default?.url;
852
952
  const addrDisplay = explorerUrl
853
953
  ? link(`${explorerUrl}/address/${acct.address}`, acct.address)
@@ -872,14 +972,18 @@ const sign = Cli.create('sign', {
872
972
  challenge: z.string().optional().describe('WWW-Authenticate challenge value'),
873
973
  config: z.string().optional().describe('Path to config file'),
874
974
  dryRun: z.boolean().optional().describe('Validate and parse the challenge without signing'),
975
+ autoSwap: z.boolean().optional().describe('Auto-swap source tokens into payment currency'),
875
976
  methodOpt: z
876
977
  .array(z.string())
877
978
  .optional()
878
979
  .describe('Method-specific option (key=value, repeatable)'),
980
+ network: z.enum(['mainnet', 'testnet']).optional().describe('Tempo network'),
981
+ payWith: z.string().optional().describe('Source token for Tempo auto-swap'),
879
982
  rpcUrl: z
880
983
  .string()
881
984
  .optional()
882
985
  .describe('RPC endpoint, defaults to public RPC for chain (env: MPPX_RPC_URL)'),
986
+ slippage: z.number().optional().describe('Tempo auto-swap max slippage percentage'),
883
987
  }),
884
988
  output: z.object({ authorization: z.string() }),
885
989
  alias: {
@@ -947,7 +1051,14 @@ const sign = Cli.create('sign', {
947
1051
  if (plugin) {
948
1052
  const result = await plugin.setup({
949
1053
  challenge,
950
- options: { account: c.options.account, rpcUrl: c.options.rpcUrl },
1054
+ options: {
1055
+ account: c.options.account,
1056
+ autoSwap: c.options.autoSwap,
1057
+ network: c.options.network,
1058
+ payWith: c.options.payWith,
1059
+ rpcUrl: c.options.rpcUrl,
1060
+ slippage: c.options.slippage,
1061
+ },
951
1062
  methodOpts,
952
1063
  });
953
1064
  if (result.createCredential) {
@@ -1017,6 +1128,114 @@ export default defineConfig({
1017
1128
  });
1018
1129
  },
1019
1130
  });
1131
+ const services = Cli.create('services', {
1132
+ description: 'Browse the MPP services registry',
1133
+ })
1134
+ .command('list', {
1135
+ description: 'List registered MPP services',
1136
+ options: z.object({
1137
+ query: z.string().optional().describe('Filter by id, name, category, tag, or description'),
1138
+ }),
1139
+ output: z.object({ services: z.array(serviceSummarySchema) }),
1140
+ alias: { query: 'q' },
1141
+ async run(c) {
1142
+ const query = c.options.query?.toLowerCase();
1143
+ const registry = await fetchServicesRegistry();
1144
+ const filtered = query
1145
+ ? registry.filter((service) => [
1146
+ service.id,
1147
+ service.name,
1148
+ service.description,
1149
+ ...(Array.isArray(service.categories) ? service.categories : []),
1150
+ ...(Array.isArray(service.tags) ? service.tags : []),
1151
+ ]
1152
+ .filter((value) => typeof value === 'string')
1153
+ .some((value) => value.toLowerCase().includes(query)))
1154
+ : registry;
1155
+ const summaries = filtered.map(summarizeService).sort((a, b) => a.id.localeCompare(b.id));
1156
+ return outputResult(c, { services: summaries }, () => {
1157
+ if (summaries.length === 0) {
1158
+ console.log('No services found.');
1159
+ return;
1160
+ }
1161
+ const idWidth = Math.max(...summaries.map((service) => service.id.length));
1162
+ const paidWidth = Math.max(...summaries.map((service) => String(service.paidEndpoints).length));
1163
+ for (const service of summaries) {
1164
+ const name = service.name && service.name !== service.id ? ` ${service.name}` : '';
1165
+ const url = service.url ? ` ${pc.dim(service.url)}` : '';
1166
+ console.log(`${service.id.padEnd(idWidth)} ${String(service.paidEndpoints).padStart(paidWidth)} paid${name}${url}`);
1167
+ }
1168
+ });
1169
+ },
1170
+ })
1171
+ .command('show', {
1172
+ description: 'Show one registered MPP service',
1173
+ args: z.object({
1174
+ service: z.string().describe('Service id or name'),
1175
+ }),
1176
+ output: z.object({ service: z.record(z.string(), z.unknown()) }),
1177
+ async run(c) {
1178
+ const registry = await fetchServicesRegistry();
1179
+ const service = findService(registry, c.args.service);
1180
+ if (!service)
1181
+ return c.error({
1182
+ code: 'SERVICE_NOT_FOUND',
1183
+ message: `Service not found: ${c.args.service}`,
1184
+ exitCode: 1,
1185
+ });
1186
+ const summary = summarizeService(service);
1187
+ const endpoints = getEndpoints(service);
1188
+ return outputResult(c, { service }, () => {
1189
+ console.log(`${summary.name ?? summary.id} ${pc.dim(`(${summary.id})`)}`);
1190
+ if (summary.description)
1191
+ console.log(summary.description);
1192
+ if (summary.url)
1193
+ console.log(`${pc.dim('URL')} ${link(summary.url, summary.url)}`);
1194
+ if (summary.status)
1195
+ console.log(`${pc.dim('Status')} ${summary.status}`);
1196
+ console.log(`${pc.dim('Endpoints')} ${endpoints.length} (${summary.paidEndpoints} paid)`);
1197
+ const docs = service.docs;
1198
+ const homepage = docs && getString(docs.homepage);
1199
+ if (homepage)
1200
+ console.log(`${pc.dim('Docs')} ${link(homepage, homepage)}`);
1201
+ });
1202
+ },
1203
+ })
1204
+ .command('endpoints', {
1205
+ description: 'List endpoints for a registered MPP service',
1206
+ args: z.object({
1207
+ service: z.string().describe('Service id or name'),
1208
+ }),
1209
+ output: z.object({
1210
+ endpoints: z.array(serviceEndpointSchema),
1211
+ service: serviceSummarySchema,
1212
+ }),
1213
+ async run(c) {
1214
+ const registry = await fetchServicesRegistry();
1215
+ const service = findService(registry, c.args.service);
1216
+ if (!service)
1217
+ return c.error({
1218
+ code: 'SERVICE_NOT_FOUND',
1219
+ message: `Service not found: ${c.args.service}`,
1220
+ exitCode: 1,
1221
+ });
1222
+ const summary = summarizeService(service);
1223
+ const endpoints = getEndpoints(service).map(summarizeEndpoint);
1224
+ return outputResult(c, { endpoints, service: summary }, () => {
1225
+ if (endpoints.length === 0) {
1226
+ console.log(`No endpoints found for ${summary.id}.`);
1227
+ return;
1228
+ }
1229
+ const methodWidth = Math.max(...endpoints.map((endpoint) => endpoint.method.length));
1230
+ const pathWidth = Math.max(...endpoints.map((endpoint) => endpoint.path.length));
1231
+ for (const endpoint of endpoints) {
1232
+ const payment = formatPayment(endpoint.payment);
1233
+ const description = endpoint.description ? ` ${pc.dim(endpoint.description)}` : '';
1234
+ console.log(`${endpoint.method.padEnd(methodWidth)} ${endpoint.path.padEnd(pathWidth)} ${payment}${description}`);
1235
+ }
1236
+ });
1237
+ },
1238
+ });
1020
1239
  const discover = Cli.create('discover', {
1021
1240
  description: 'Discovery tooling',
1022
1241
  })
@@ -1186,6 +1405,7 @@ const discover = Cli.create('discover', {
1186
1405
  cli.command(account);
1187
1406
  cli.command(discover);
1188
1407
  cli.command(init);
1408
+ cli.command(services);
1189
1409
  cli.command(sign);
1190
1410
  export default cli;
1191
1411
  //# sourceMappingURL=cli.js.map