@obolos_tech/cli 0.3.3 → 0.5.0

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 (72) hide show
  1. package/README.md +35 -1
  2. package/dist/commands/anp.d.ts +277 -0
  3. package/dist/commands/anp.js +440 -0
  4. package/dist/commands/anp.js.map +1 -0
  5. package/dist/commands/index.d.ts +3 -0
  6. package/dist/commands/index.js +34 -0
  7. package/dist/commands/index.js.map +1 -0
  8. package/dist/commands/jobs.d.ts +111 -0
  9. package/dist/commands/jobs.js +294 -0
  10. package/dist/commands/jobs.js.map +1 -0
  11. package/dist/commands/listings.d.ts +128 -0
  12. package/dist/commands/listings.js +246 -0
  13. package/dist/commands/listings.js.map +1 -0
  14. package/dist/commands/marketplace.d.ts +87 -0
  15. package/dist/commands/marketplace.js +133 -0
  16. package/dist/commands/marketplace.js.map +1 -0
  17. package/dist/commands/reputation.d.ts +27 -0
  18. package/dist/commands/reputation.js +114 -0
  19. package/dist/commands/reputation.js.map +1 -0
  20. package/dist/commands/setup.d.ts +78 -0
  21. package/dist/commands/setup.js +133 -0
  22. package/dist/commands/setup.js.map +1 -0
  23. package/dist/commands/wallet.d.ts +30 -0
  24. package/dist/commands/wallet.js +66 -0
  25. package/dist/commands/wallet.js.map +1 -0
  26. package/dist/index.d.ts +4 -24
  27. package/dist/index.js +61 -2516
  28. package/dist/index.js.map +1 -1
  29. package/dist/registry.d.ts +53 -0
  30. package/dist/registry.js +39 -0
  31. package/dist/registry.js.map +1 -0
  32. package/dist/runtime/acp.d.ts +162 -0
  33. package/dist/runtime/acp.js +132 -0
  34. package/dist/runtime/acp.js.map +1 -0
  35. package/dist/runtime/anp.d.ts +214 -0
  36. package/dist/runtime/anp.js +255 -0
  37. package/dist/runtime/anp.js.map +1 -0
  38. package/dist/runtime/argv.d.ts +18 -0
  39. package/dist/runtime/argv.js +114 -0
  40. package/dist/runtime/argv.js.map +1 -0
  41. package/dist/runtime/config.d.ts +14 -0
  42. package/dist/runtime/config.js +34 -0
  43. package/dist/runtime/config.js.map +1 -0
  44. package/dist/runtime/dispatch.d.ts +13 -0
  45. package/dist/runtime/dispatch.js +123 -0
  46. package/dist/runtime/dispatch.js.map +1 -0
  47. package/dist/runtime/display.d.ts +21 -0
  48. package/dist/runtime/display.js +68 -0
  49. package/dist/runtime/display.js.map +1 -0
  50. package/dist/runtime/errors.d.ts +19 -0
  51. package/dist/runtime/errors.js +23 -0
  52. package/dist/runtime/errors.js.map +1 -0
  53. package/dist/runtime/http.d.ts +9 -0
  54. package/dist/runtime/http.js +36 -0
  55. package/dist/runtime/http.js.map +1 -0
  56. package/dist/runtime/output.d.ts +19 -0
  57. package/dist/runtime/output.js +12 -0
  58. package/dist/runtime/output.js.map +1 -0
  59. package/dist/runtime/payment.d.ts +21 -0
  60. package/dist/runtime/payment.js +91 -0
  61. package/dist/runtime/payment.js.map +1 -0
  62. package/dist/runtime/wallet.d.ts +32 -0
  63. package/dist/runtime/wallet.js +44 -0
  64. package/dist/runtime/wallet.js.map +1 -0
  65. package/dist/schema/json-schema.d.ts +10 -0
  66. package/dist/schema/json-schema.js +34 -0
  67. package/dist/schema/json-schema.js.map +1 -0
  68. package/dist/schema/zod-shape.d.ts +9 -0
  69. package/dist/schema/zod-shape.js +36 -0
  70. package/dist/schema/zod-shape.js.map +1 -0
  71. package/package.json +46 -4
  72. package/scripts/lint-stdout-purity.mjs +62 -0
@@ -0,0 +1,91 @@
1
+ /**
2
+ * x402 payment handling — sign EIP-3009 TransferWithAuthorization for a
3
+ * 402 challenge and retry the original request. Supports v1 "exact" scheme
4
+ * and v2 "x402x-router-settlement" (atomic fee split).
5
+ *
6
+ * Extracted from cmdCall so the CLI `call` command and any MCP adapter
7
+ * share the same flow.
8
+ */
9
+ import { getClients, USDC_BASE } from './wallet.js';
10
+ import { paymentError } from './errors.js';
11
+ export async function callWithPayment(config, apiId, opts = {}) {
12
+ const method = (opts.method ?? 'GET').toUpperCase();
13
+ const url = `${config.apiUrl}/api/proxy/${encodeURIComponent(apiId)}`;
14
+ const fetchOpts = { method };
15
+ if (opts.body !== undefined && method !== 'GET') {
16
+ fetchOpts.headers = { 'Content-Type': 'application/json' };
17
+ fetchOpts.body = JSON.stringify(opts.body);
18
+ }
19
+ let res = await fetch(url, fetchOpts);
20
+ let paid = false;
21
+ if (res.status === 402) {
22
+ if (!config.privateKey) {
23
+ throw paymentError('API requires payment but no wallet is configured. Run `obolos setup`.');
24
+ }
25
+ const paymentInfo = await res.json();
26
+ const { header, value } = await signX402(config, paymentInfo);
27
+ res = await fetch(url, { ...fetchOpts, headers: { ...(fetchOpts.headers || {}), [header]: value } });
28
+ paid = true;
29
+ }
30
+ const contentType = res.headers.get('content-type') || '';
31
+ const body = contentType.includes('json') ? await res.json() : await res.text();
32
+ return { status: res.status, statusText: res.statusText, contentType, body, paid };
33
+ }
34
+ async function signX402(config, paymentInfo) {
35
+ const { keccak256, encodePacked } = await import('viem');
36
+ const { account, walletClient } = await getClients(config);
37
+ const accepts = paymentInfo.accepts?.[0];
38
+ if (!accepts)
39
+ throw paymentError('No payment options in 402 response');
40
+ const amount = BigInt(accepts.maxAmountRequired || accepts.amount || '0');
41
+ const payTo = accepts.payTo;
42
+ const asset = (accepts.asset || USDC_BASE);
43
+ const scheme = accepts.scheme || 'exact';
44
+ const rawNetwork = accepts.network || 'base';
45
+ const network = rawNetwork.startsWith('eip155:') ? rawNetwork : 'eip155:8453';
46
+ const deadline = BigInt(Math.floor(Date.now() / 1000) + 300);
47
+ const domain = {
48
+ name: accepts.extra?.name || 'USD Coin',
49
+ version: accepts.extra?.version || '2',
50
+ chainId: 8453n,
51
+ verifyingContract: asset,
52
+ };
53
+ const types = {
54
+ TransferWithAuthorization: [
55
+ { name: 'from', type: 'address' }, { name: 'to', type: 'address' },
56
+ { name: 'value', type: 'uint256' }, { name: 'validAfter', type: 'uint256' },
57
+ { name: 'validBefore', type: 'uint256' }, { name: 'nonce', type: 'bytes32' },
58
+ ],
59
+ };
60
+ const settlementKey = 'x402x-router-settlement';
61
+ const settlementExt = accepts.extra?.[settlementKey];
62
+ const settlementInfo = settlementExt?.info;
63
+ let nonce;
64
+ if (settlementInfo?.settlementRouter && settlementInfo?.salt) {
65
+ nonce = keccak256(encodePacked(['string', 'uint256', 'address', 'address', 'address', 'uint256', 'uint256', 'uint256', 'bytes32', 'address', 'uint256', 'address', 'bytes32'], ['X402/settle/v1', 8453n, settlementInfo.settlementRouter, asset,
66
+ account.address, amount, 0n, deadline, settlementInfo.salt,
67
+ (settlementInfo.finalPayTo || payTo), BigInt(settlementInfo.facilitatorFee || '0'),
68
+ settlementInfo.hook, keccak256(settlementInfo.hookData)]));
69
+ }
70
+ else {
71
+ const bytes = new Uint8Array(32);
72
+ crypto.getRandomValues(bytes);
73
+ nonce = `0x${Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')}`;
74
+ }
75
+ const signature = await walletClient.signTypedData({
76
+ account, domain, types, primaryType: 'TransferWithAuthorization',
77
+ message: { from: account.address, to: payTo, value: amount, validAfter: 0n, validBefore: deadline, nonce },
78
+ });
79
+ const authorization = { from: account.address, to: payTo, value: amount.toString(), validAfter: '0', validBefore: deadline.toString(), nonce };
80
+ if (paymentInfo.x402Version === 2) {
81
+ const payload = {
82
+ x402Version: 2, scheme, network, payload: { signature, authorization }, accepted: { ...accepts, network },
83
+ };
84
+ if (settlementExt)
85
+ payload.extensions = { [settlementKey]: settlementExt };
86
+ return { header: 'payment-signature', value: Buffer.from(JSON.stringify(payload)).toString('base64') };
87
+ }
88
+ const payload = { x402Version: 1, scheme, network, payload: { signature, authorization } };
89
+ return { header: 'x-payment', value: Buffer.from(JSON.stringify(payload)).toString('base64') };
90
+ }
91
+ //# sourceMappingURL=payment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment.js","sourceRoot":"","sources":["../../src/runtime/payment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Wallet helpers — lazy viem imports so commands that never touch a wallet
3
+ * don't pay the import cost.
4
+ */
5
+ import type { ObolosConfig } from './config.js';
6
+ export declare const USDC_BASE: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
7
+ export declare const BASE_CHAIN_ID = 8453;
8
+ export declare function requirePrivateKey(config: ObolosConfig): string;
9
+ export declare function getAccount(config: ObolosConfig): Promise<{
10
+ address: import("viem").Address;
11
+ nonceManager?: import("viem").NonceManager | undefined;
12
+ sign: (parameters: {
13
+ hash: import("viem").Hash;
14
+ }) => Promise<import("viem").Hex>;
15
+ signAuthorization: (parameters: import("viem").AuthorizationRequest) => Promise<import("viem/accounts").SignAuthorizationReturnType>;
16
+ signMessage: ({ message }: {
17
+ message: import("viem").SignableMessage;
18
+ }) => Promise<import("viem").Hex>;
19
+ signTransaction: <serializer extends import("viem").SerializeTransactionFn<import("viem").TransactionSerializable> = import("viem").SerializeTransactionFn<import("viem").TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: {
20
+ serializer?: serializer | undefined;
21
+ } | undefined) => Promise<import("viem").Hex>;
22
+ signTypedData: <const typedData extends import("viem").TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: import("viem").TypedDataDefinition<typedData, primaryType>) => Promise<import("viem").Hex>;
23
+ publicKey: import("viem").Hex;
24
+ source: "privateKey";
25
+ type: "local";
26
+ }>;
27
+ export declare function getClients(config: ObolosConfig): Promise<any>;
28
+ export declare function getUsdcBalance(config: ObolosConfig): Promise<{
29
+ address: string;
30
+ balance: string;
31
+ raw: bigint;
32
+ }>;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Wallet helpers — lazy viem imports so commands that never touch a wallet
3
+ * don't pay the import cost.
4
+ */
5
+ import { paymentError } from './errors.js';
6
+ export const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
7
+ export const BASE_CHAIN_ID = 8453;
8
+ export function requirePrivateKey(config) {
9
+ if (!config.privateKey) {
10
+ throw paymentError('No wallet configured. Run `obolos setup` first.');
11
+ }
12
+ return config.privateKey.startsWith('0x') ? config.privateKey : `0x${config.privateKey}`;
13
+ }
14
+ export async function getAccount(config) {
15
+ const { privateKeyToAccount } = await import('viem/accounts');
16
+ return privateKeyToAccount(requirePrivateKey(config));
17
+ }
18
+ export async function getClients(config) {
19
+ const { createPublicClient, createWalletClient, http } = await import('viem');
20
+ const { base } = await import('viem/chains');
21
+ const account = await getAccount(config);
22
+ return {
23
+ account,
24
+ publicClient: createPublicClient({ chain: base, transport: http() }),
25
+ walletClient: createWalletClient({ account, chain: base, transport: http() }),
26
+ };
27
+ }
28
+ export async function getUsdcBalance(config) {
29
+ const { formatUnits } = await import('viem');
30
+ const { publicClient, account } = await getClients(config);
31
+ const raw = (await publicClient.readContract({
32
+ address: USDC_BASE,
33
+ abi: [{
34
+ inputs: [{ name: 'account', type: 'address' }],
35
+ name: 'balanceOf',
36
+ outputs: [{ name: '', type: 'uint256' }],
37
+ stateMutability: 'view', type: 'function',
38
+ }],
39
+ functionName: 'balanceOf',
40
+ args: [account.address],
41
+ }));
42
+ return { address: account.address, balance: formatUnits(raw, 6), raw };
43
+ }
44
+ //# sourceMappingURL=wallet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallet.js","sourceRoot":"","sources":["../../src/runtime/wallet.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,MAAM,CAAC,MAAM,SAAS,GAAG,4CAAqD,CAAC;AAC/E,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC;AAElC,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACvB,MAAM,YAAY,CAAC,iDAAiD,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,UAAU,EAAE,CAAC;AAC3F,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAoB;IACnD,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;IAC9D,OAAO,mBAAmB,CAAC,iBAAiB,CAAC,MAAM,CAAkB,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAoB;IACnD,MAAM,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9E,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;IACzC,OAAO;QACL,OAAO;QACP,YAAY,EAAE,kBAAkB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC;QACpE,YAAY,EAAE,kBAAkB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC;KAC9E,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAoB;IACvD,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;IAC3D,MAAM,GAAG,GAAG,CAAC,MAAM,YAAY,CAAC,YAAY,CAAC;QAC3C,OAAO,EAAE,SAAS;QAClB,GAAG,EAAE,CAAC;gBACJ,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;gBAC9C,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;gBACxC,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU;aAC1C,CAAC;QACF,YAAY,EAAE,WAAW;QACzB,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;KACxB,CAAC,CAAW,CAAC;IACd,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;AACzE,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Convert an InputSchema to JSON Schema (draft-07, MCP-compatible).
3
+ * Used by the MCP adapter to auto-generate tool schemas.
4
+ */
5
+ import type { InputSchema } from '../registry.js';
6
+ export declare function toJsonSchema(schema: InputSchema): {
7
+ type: 'object';
8
+ properties: Record<string, unknown>;
9
+ required: string[];
10
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Convert an InputSchema to JSON Schema (draft-07, MCP-compatible).
3
+ * Used by the MCP adapter to auto-generate tool schemas.
4
+ */
5
+ export function toJsonSchema(schema) {
6
+ const properties = {};
7
+ const required = [];
8
+ for (const [name, field] of Object.entries(schema)) {
9
+ const prop = { description: field.description };
10
+ switch (field.type) {
11
+ case 'string':
12
+ prop.type = 'string';
13
+ if (field.enum)
14
+ prop.enum = field.enum;
15
+ break;
16
+ case 'number':
17
+ prop.type = 'number';
18
+ break;
19
+ case 'boolean':
20
+ prop.type = 'boolean';
21
+ break;
22
+ case 'json':
23
+ // accept any JSON — MCP clients will pass objects directly
24
+ break;
25
+ }
26
+ if (field.default !== undefined)
27
+ prop.default = field.default;
28
+ properties[name] = prop;
29
+ if (field.required)
30
+ required.push(name);
31
+ }
32
+ return { type: 'object', properties, required };
33
+ }
34
+ //# sourceMappingURL=json-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-schema.js","sourceRoot":"","sources":["../../src/schema/json-schema.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,UAAU,YAAY,CAAC,MAAmB;IAK9C,MAAM,UAAU,GAA4B,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,GAA4B,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;QACzE,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC;gBACrB,IAAI,KAAK,CAAC,IAAI;oBAAE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;gBACvC,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC;gBACrB,MAAM;YACR,KAAK,SAAS;gBACZ,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;gBACtB,MAAM;YACR,KAAK,MAAM;gBACT,2DAA2D;gBAC3D,MAAM;QACV,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;YAAE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC9D,UAAU,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACxB,IAAI,KAAK,CAAC,QAAQ;YAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAClD,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Convert an InputSchema to a Zod raw shape (Record<string, ZodType>),
3
+ * used by the MCP SDK's `registerTool({ inputSchema })` form.
4
+ *
5
+ * Lives in the CLI package so the adapter can import it directly.
6
+ */
7
+ import { type ZodTypeAny } from 'zod';
8
+ import type { InputSchema } from '../registry.js';
9
+ export declare function toZodShape(schema: InputSchema): Record<string, ZodTypeAny>;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Convert an InputSchema to a Zod raw shape (Record<string, ZodType>),
3
+ * used by the MCP SDK's `registerTool({ inputSchema })` form.
4
+ *
5
+ * Lives in the CLI package so the adapter can import it directly.
6
+ */
7
+ import { z } from 'zod';
8
+ export function toZodShape(schema) {
9
+ const shape = {};
10
+ for (const [name, field] of Object.entries(schema)) {
11
+ let t;
12
+ switch (field.type) {
13
+ case 'string':
14
+ t = field.enum ? z.enum(field.enum) : z.string();
15
+ break;
16
+ case 'number':
17
+ t = z.number();
18
+ break;
19
+ case 'boolean':
20
+ t = z.boolean();
21
+ break;
22
+ case 'json':
23
+ t = z.any();
24
+ break;
25
+ }
26
+ if (field.description)
27
+ t = t.describe(field.description);
28
+ if (!field.required)
29
+ t = t.optional();
30
+ if (field.default !== undefined)
31
+ t = t.default(field.default);
32
+ shape[name] = t;
33
+ }
34
+ return shape;
35
+ }
36
+ //# sourceMappingURL=zod-shape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-shape.js","sourceRoot":"","sources":["../../src/schema/zod-shape.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAmB,MAAM,KAAK,CAAC;AAGzC,MAAM,UAAU,UAAU,CAAC,MAAmB;IAC5C,MAAM,KAAK,GAA+B,EAAE,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,IAAI,CAAa,CAAC;QAClB,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,QAAQ;gBACX,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAA6B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;gBAC1E,MAAM;YACR,KAAK,QAAQ;gBAAG,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;gBAAC,MAAM;YACtC,KAAK,SAAS;gBAAE,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;gBAAC,MAAM;YACvC,KAAK,MAAM;gBAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;gBAAC,MAAM;QACrC,CAAC;QACD,IAAI,KAAK,CAAC,WAAW;YAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACzD,IAAI,CAAC,KAAK,CAAC,QAAQ;YAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;YAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAgB,CAAC,CAAC;QACvE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json CHANGED
@@ -1,20 +1,61 @@
1
1
  {
2
2
  "name": "@obolos_tech/cli",
3
- "version": "0.3.3",
3
+ "version": "0.5.0",
4
4
  "description": "CLI for the Obolos x402 API marketplace — search, negotiate (ANP), and call pay-per-use APIs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "files": [
8
8
  "dist",
9
+ "scripts",
9
10
  "README.md"
10
11
  ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "./registry": {
18
+ "types": "./dist/registry.d.ts",
19
+ "default": "./dist/registry.js"
20
+ },
21
+ "./commands": {
22
+ "types": "./dist/commands/index.d.ts",
23
+ "default": "./dist/commands/index.js"
24
+ },
25
+ "./runtime/config": {
26
+ "types": "./dist/runtime/config.d.ts",
27
+ "default": "./dist/runtime/config.js"
28
+ },
29
+ "./runtime/http": {
30
+ "types": "./dist/runtime/http.d.ts",
31
+ "default": "./dist/runtime/http.js"
32
+ },
33
+ "./runtime/output": {
34
+ "types": "./dist/runtime/output.d.ts",
35
+ "default": "./dist/runtime/output.js"
36
+ },
37
+ "./runtime/errors": {
38
+ "types": "./dist/runtime/errors.d.ts",
39
+ "default": "./dist/runtime/errors.js"
40
+ },
41
+ "./schema/json-schema": {
42
+ "types": "./dist/schema/json-schema.d.ts",
43
+ "default": "./dist/schema/json-schema.js"
44
+ },
45
+ "./schema/zod-shape": {
46
+ "types": "./dist/schema/zod-shape.d.ts",
47
+ "default": "./dist/schema/zod-shape.js"
48
+ }
49
+ },
11
50
  "bin": {
12
51
  "obolos": "dist/index.js"
13
52
  },
14
53
  "scripts": {
15
54
  "build": "tsc",
16
55
  "dev": "tsx src/index.ts",
17
- "start": "node dist/index.js"
56
+ "start": "node dist/index.js",
57
+ "lint": "node scripts/lint-stdout-purity.mjs",
58
+ "prebuild": "npm run lint"
18
59
  },
19
60
  "keywords": [
20
61
  "x402",
@@ -29,8 +70,9 @@
29
70
  ],
30
71
  "license": "MIT",
31
72
  "dependencies": {
32
- "@obolos_tech/anp-sdk": "^0.1.0",
33
- "viem": "^2.45.2"
73
+ "@obolos_tech/anp-sdk": "^0.2.0",
74
+ "viem": "^2.45.2",
75
+ "zod": "^3.25.76"
34
76
  },
35
77
  "devDependencies": {
36
78
  "@types/node": "^22.0.0",
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Enforces the core Command invariant: run() MUST return structured data and
4
+ * MUST NOT write to stdout/stderr. Only format() renders text, and even then
5
+ * it returns a string — the runtime does the write.
6
+ *
7
+ * This keeps MCP output clean (no ANSI bleed into JSON) and makes commands
8
+ * unit-testable without stdio capture.
9
+ *
10
+ * Scans cli/src/commands/**\/*.ts for forbidden patterns and exits non-zero
11
+ * on any hit. Run manually: `node scripts/lint-stdout-purity.mjs`.
12
+ */
13
+
14
+ import { readdirSync, readFileSync, statSync } from 'fs';
15
+ import { join, relative } from 'path';
16
+ import { fileURLToPath } from 'url';
17
+
18
+ const ROOT = fileURLToPath(new URL('../src/commands/', import.meta.url));
19
+
20
+ const FORBIDDEN = [
21
+ { pattern: /\bconsole\.(log|error|warn|info|debug)\b/, why: 'console.* writes to stdio — return data from run() or render via format()' },
22
+ { pattern: /\bprocess\.stdout\.write\b/, why: 'process.stdout.write is the runtime\'s job — return from run()' },
23
+ { pattern: /\bprocess\.stderr\.write\b/, why: 'throw a CliError instead of writing to stderr' },
24
+ { pattern: /\bprocess\.exit\b/, why: 'throw a CliError instead of calling process.exit' },
25
+ ];
26
+
27
+ function walk(dir) {
28
+ const out = [];
29
+ for (const entry of readdirSync(dir)) {
30
+ const full = join(dir, entry);
31
+ const st = statSync(full);
32
+ if (st.isDirectory()) out.push(...walk(full));
33
+ else if (entry.endsWith('.ts')) out.push(full);
34
+ }
35
+ return out;
36
+ }
37
+
38
+ const offenders = [];
39
+ for (const file of walk(ROOT)) {
40
+ const src = readFileSync(file, 'utf-8');
41
+ const lines = src.split('\n');
42
+ for (let i = 0; i < lines.length; i++) {
43
+ const line = lines[i];
44
+ if (line.trim().startsWith('//') || line.trim().startsWith('*')) continue;
45
+ for (const { pattern, why } of FORBIDDEN) {
46
+ if (pattern.test(line)) offenders.push({ file: relative(process.cwd(), file), line: i + 1, code: line.trim(), why });
47
+ }
48
+ }
49
+ }
50
+
51
+ if (offenders.length === 0) {
52
+ console.error(`[stdout-purity] ok — ${walk(ROOT).length} command files, 0 violations`);
53
+ process.exit(0);
54
+ }
55
+
56
+ console.error(`[stdout-purity] ${offenders.length} violation(s):\n`);
57
+ for (const o of offenders) {
58
+ console.error(` ${o.file}:${o.line}`);
59
+ console.error(` ${o.code}`);
60
+ console.error(` → ${o.why}\n`);
61
+ }
62
+ process.exit(1);