@wasit-dev/cli 0.1.1 → 0.2.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 (3) hide show
  1. package/README.md +4 -3
  2. package/dist/index.js +125 -11
  3. package/package.json +7 -10
package/README.md CHANGED
@@ -131,14 +131,15 @@ All keys are **testnet only**. `wasit` also reads a `.env` file in the current w
131
131
 
132
132
  ## What's checked
133
133
 
134
- Thirteen checks across x402 and MPP, each traced to a written spec clause — the full catalogue, with pass criteria and spec references, is in [`docs/CHECKS.md`](https://github.com/dzakwannajmi/wasit/blob/main/docs/CHECKS.md).
134
+ Thirteen checks across x402 and MPP, each traced to a written spec clause — the full catalogue, with pass criteria and spec references, is in [`docs/CHECKS.md`](https://github.com/wasit-dev/wasit/blob/main/docs/CHECKS.md).
135
135
 
136
136
  ## Related
137
137
 
138
+ - [Website](https://usewasit.dev)
138
139
  - [`@wasit-dev/core`](https://www.npmjs.com/package/@wasit-dev/core) — the check suite this CLI runs, if you're building your own tooling on top
139
140
  - [`@wasit-dev/server`](https://www.npmjs.com/package/@wasit-dev/server) — the same checks as MCP tools, for Claude Code and other agents
140
- - [Full documentation](https://github.com/dzakwannajmi/wasit) — CLI guide, MCP guide, configuration, design notes
141
+ - [Full documentation](https://github.com/wasit-dev/wasit) — CLI guide, MCP guide, configuration, design notes
141
142
 
142
143
  ## License
143
144
 
144
- Apache-2.0 — see [LICENSE](https://github.com/dzakwannajmi/wasit/blob/main/LICENSE).
145
+ Apache-2.0 — see [LICENSE](https://github.com/wasit-dev/wasit/blob/main/LICENSE).
package/dist/index.js CHANGED
@@ -1,7 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import "dotenv/config";
3
3
  import { Command } from "commander";
4
- import { checkStatus, runMppChannelSuite, runMppChargeSuite, runX402PaymentChecks, runX402ReadChecks, summarize, } from "@wasit-dev/core";
4
+ import { CHECK_CATALOGUE, PROTOCOL_IDS, checkStatus, runMppChannelSuite, runMppChargeSuite, runX402PaymentChecks, runX402ReadChecks, summarize, toStructuredRun, } from "@wasit-dev/core";
5
+ /** Which wasit subcommand runs each protocol's checks — for `wasit checks` output. */
6
+ const COMMAND_BY_PROTOCOL = {
7
+ x402: "test",
8
+ "mpp-charge": "mpp-charge",
9
+ "mpp-channel": "mpp-channel",
10
+ };
5
11
  /**
6
12
  * Accumulates repeated --header flags into one object.
7
13
  *
@@ -23,21 +29,54 @@ const program = new Command();
23
29
  program
24
30
  .name("wasit")
25
31
  .description("Protocol-compliance testing for x402 / MPP on Stellar")
26
- .version("0.1.0");
32
+ .version("0.1.0")
33
+ .addHelpText("after", `
34
+ Examples:
35
+ $ wasit checks
36
+ $ wasit test --target https://api.example.com/paid-endpoint --read-only
37
+ $ wasit mpp-charge --target https://api.example.com/paid-endpoint --payer-key S...
38
+ $ wasit mpp-channel --target https://api.example.com/paid-endpoint
39
+
40
+ Run "wasit <command> --help" for that command's own options and cost notes.
41
+ Add --json to test/mpp-charge/mpp-channel for machine-readable output.
42
+ Full check catalogue (every check ID, spec reference, pass criteria):
43
+ https://github.com/wasit-dev/wasit/blob/main/docs/CHECKS.md`);
44
+ /**
45
+ * Prints an advisory/status line meant for a human watching the terminal —
46
+ * never part of the run's actual result. Routed to stderr when --json is
47
+ * set, so stdout stays parseable JSON and a script piping it (jq, etc.)
48
+ * never has to skip past prose first.
49
+ */
50
+ function note(json, message) {
51
+ if (json) {
52
+ console.error(message);
53
+ }
54
+ else {
55
+ console.log(message);
56
+ }
57
+ }
27
58
  /**
28
59
  * Prints results and returns the process exit code.
29
60
  *
30
61
  * A skipped check carries `pass: false` so it can never be counted as
31
62
  * conformance, but it is not a failure and must not fail the run. An errored
32
63
  * check produced no verdict at all and is reported separately again.
64
+ *
65
+ * `--json` reuses `toStructuredRun()` from core — the same reshape the MCP
66
+ * server calls for its own structured output — rather than defining a
67
+ * second JSON shape here that could drift from it.
33
68
  */
34
- function report(results) {
69
+ function report(results, json) {
70
+ const counts = summarize(results);
71
+ if (json) {
72
+ console.log(JSON.stringify(toStructuredRun(results), null, 2));
73
+ return counts.exitCode;
74
+ }
35
75
  for (const result of results) {
36
76
  const flag = result.destructive ? " [destructive]" : "";
37
77
  console.log(`${checkStatus(result)} ${result.id} ${result.name}${flag}`);
38
78
  console.log(` ${result.detail}\n`);
39
79
  }
40
- const counts = summarize(results);
41
80
  const line = [`${counts.passed} passed`];
42
81
  if (counts.failed > 0)
43
82
  line.push(`${counts.failed} failed`);
@@ -52,6 +91,48 @@ function report(results) {
52
91
  }
53
92
  return counts.exitCode;
54
93
  }
94
+ program
95
+ .command("checks")
96
+ .description("List the check catalogue: every check wasit can run, by ID")
97
+ .option("--protocol <name>", `Filter to one protocol (${PROTOCOL_IDS.join(", ")}) — matches the wasit subcommand that runs it`)
98
+ .option("--json", "Print as JSON instead of a formatted list", false)
99
+ .addHelpText("after", `
100
+ Examples:
101
+ $ wasit checks
102
+ $ wasit checks --protocol mpp-channel
103
+ $ wasit checks --json
104
+
105
+ Full pass-criteria prose, spec citations, and revision notes for every check
106
+ live in docs/CHECKS.md — this command is a quick reference, not a
107
+ replacement for it.`)
108
+ .action((opts) => {
109
+ const protocol = opts.protocol;
110
+ if (protocol !== undefined && !PROTOCOL_IDS.includes(protocol)) {
111
+ console.error(`Unknown --protocol "${protocol}". Expected one of: ${PROTOCOL_IDS.join(", ")}.`);
112
+ process.exit(2);
113
+ }
114
+ const entries = CHECK_CATALOGUE.filter((entry) => protocol === undefined || entry.protocol === protocol);
115
+ if (opts.json === true) {
116
+ console.log(JSON.stringify(entries, null, 2));
117
+ return;
118
+ }
119
+ let currentProtocol;
120
+ for (const entry of entries) {
121
+ if (entry.protocol !== currentProtocol) {
122
+ currentProtocol = entry.protocol;
123
+ console.log(`\n${currentProtocol} (wasit ${COMMAND_BY_PROTOCOL[currentProtocol]})`);
124
+ }
125
+ const flags = [
126
+ entry.negative ? "negative" : null,
127
+ entry.destructive ? "destructive" : null,
128
+ entry.costsFunds ? "costs funds" : null,
129
+ ].filter((flag) => flag !== null);
130
+ const flagText = flags.length > 0 ? ` [${flags.join(", ")}]` : "";
131
+ console.log(` ${entry.id} ${entry.name}${flagText}`);
132
+ console.log(` ${entry.summary}`);
133
+ }
134
+ console.log(`\n${entries.length} check${entries.length === 1 ? "" : "s"}.`);
135
+ });
55
136
  program
56
137
  .command("test")
57
138
  .description("Run x402 compliance checks against a target service")
@@ -63,7 +144,19 @@ program
63
144
  .option("--body <json>", "Request body, sent verbatim. Implies Content-Type: application/json.")
64
145
  .option("--header <name:value>", "Extra request header the endpoint needs before it will issue a challenge. Repeatable.", collectHeader)
65
146
  .option("--read-only", "Skip payment checks (X402-06/07)", false)
147
+ .option("--json", "Print results as JSON instead of formatted text", false)
148
+ .addHelpText("after", `
149
+ Examples:
150
+ $ wasit test --target https://api.example.com/paid-endpoint --read-only
151
+ $ wasit test --target https://api.example.com/paid-endpoint --payer-key S...
152
+ $ wasit test --target https://api.example.com/paid-endpoint --read-only --json
153
+
154
+ X402-01..05 (challenge/header checks) always run and cost nothing. X402-06/07
155
+ (real payment checks) run only when a payer key is available and --read-only
156
+ is not set: X402-06 settles a payment, X402-07 attempts one with a corrupted
157
+ signature. See docs/CHECKS.md for what each check ID verifies.`)
66
158
  .action(async (opts) => {
159
+ const jsonMode = opts.json === true;
67
160
  const shape = {
68
161
  ...(opts.method ? { method: opts.method } : {}),
69
162
  ...(opts.body !== undefined ? { body: opts.body } : {}),
@@ -72,13 +165,13 @@ program
72
165
  const results = await runX402ReadChecks({ target: opts.target, ...shape });
73
166
  const payerKey = opts.payerKey ?? process.env.STELLAR_PRIVATE_KEY;
74
167
  if (opts.readOnly) {
75
- console.log("(--read-only set: skipping payment checks)\n");
168
+ note(jsonMode, "(--read-only set: skipping payment checks)\n");
76
169
  }
77
170
  else if (!payerKey) {
78
- console.log("(no payer key: set STELLAR_PRIVATE_KEY in .env or pass --payer-key — skipping payment checks)\n");
171
+ note(jsonMode, "(no payer key: set STELLAR_PRIVATE_KEY in .env or pass --payer-key — skipping payment checks)\n");
79
172
  }
80
173
  else {
81
- console.log("X402-06 settles a real payment and X402-07 attempts one. Testnet funds will move.\n");
174
+ note(jsonMode, "X402-06 settles a real payment and X402-07 attempts one. Testnet funds will move.\n");
82
175
  results.push(...(await runX402PaymentChecks({
83
176
  target: opts.target,
84
177
  network: opts.network,
@@ -86,7 +179,7 @@ program
86
179
  ...shape,
87
180
  })));
88
181
  }
89
- process.exit(report(results));
182
+ process.exit(report(results, jsonMode));
90
183
  });
91
184
  program
92
185
  .command("mpp-channel")
@@ -103,7 +196,19 @@ program
103
196
  .option("--expect-refund-period <ledgers>", "MPP-10: expected refund waiting period")
104
197
  .option("--allow-destructive", "Enable MPP-13. Closing settles on-chain and permanently ends the channel.", false)
105
198
  .option("--destructive-channel <address>", "Channel MPP-13 is permitted to close (default: CHANNEL_CONTRACT_DISPOSABLE)")
199
+ .option("--json", "Print results as JSON instead of formatted text", false)
200
+ .addHelpText("after", `
201
+ Examples:
202
+ $ wasit mpp-channel --target https://api.example.com/paid-endpoint
203
+ $ wasit mpp-channel --target https://api.example.com/paid-endpoint --allow-destructive --destructive-channel C...
204
+ $ wasit mpp-channel --target https://api.example.com/paid-endpoint --json
205
+
206
+ MPP-10, MPP-11, MPP-12, MPP-14 run by default and are non-destructive.
207
+ MPP-13 (channel close) only runs with --allow-destructive, and only against
208
+ the channel named by --destructive-channel — running it permanently ends
209
+ that channel.`)
106
210
  .action(async (opts) => {
211
+ const jsonMode = opts.json === true;
107
212
  const commitmentSecretHex = opts.commitmentKey ?? process.env.COMMITMENT_SECRET_HEX;
108
213
  if (!commitmentSecretHex) {
109
214
  console.error("No commitment key. Pass --commitment-key or set COMMITMENT_SECRET_HEX in .env.");
@@ -128,7 +233,7 @@ program
128
233
  ...(Number.isInteger(refundWaitingPeriod) ? { refundWaitingPeriod } : {}),
129
234
  },
130
235
  });
131
- process.exit(report(results));
236
+ process.exit(report(results, jsonMode));
132
237
  });
133
238
  program
134
239
  .command("mpp-charge")
@@ -137,19 +242,28 @@ program
137
242
  .option("--payer-key <key>", "Payer secret key, S... (default: MPP_PAYER_SECRET)")
138
243
  .option("--network <network>", "CAIP-2 network id (default: MPP_STELLAR_NETWORK)")
139
244
  .option("--rpc-url <url>", "Override the default Soroban RPC endpoint")
245
+ .option("--json", "Print results as JSON instead of formatted text", false)
246
+ .addHelpText("after", `
247
+ Examples:
248
+ $ wasit mpp-charge --target https://api.example.com/paid-endpoint --payer-key S...
249
+ $ wasit mpp-charge --target https://api.example.com/paid-endpoint --payer-key S... --json
250
+
251
+ Runs MPP-01 only. Not idempotent and has no read-only mode: every run settles
252
+ a real payment and moves testnet funds, because charge mode has no dry run.`)
140
253
  .action(async (opts) => {
254
+ const jsonMode = opts.json === true;
141
255
  const payerSecretKey = opts.payerKey ?? process.env.MPP_PAYER_SECRET;
142
256
  if (!payerSecretKey) {
143
257
  console.error("No payer key. Pass --payer-key or set MPP_PAYER_SECRET in .env.");
144
258
  process.exit(2);
145
259
  }
146
- console.log("MPP-01 settles a real payment. If the target is reachable, testnet funds will move.\n");
260
+ note(jsonMode, "MPP-01 settles a real payment. If the target is reachable, testnet funds will move.\n");
147
261
  const results = await runMppChargeSuite({
148
262
  target: opts.target,
149
263
  network: opts.network ?? process.env.MPP_STELLAR_NETWORK ?? "stellar:testnet",
150
264
  payerSecretKey,
151
265
  ...(opts.rpcUrl ? { rpcUrl: opts.rpcUrl } : {}),
152
266
  });
153
- process.exit(report(results));
267
+ process.exit(report(results, jsonMode));
154
268
  });
155
269
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wasit-dev/cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "CLI for x402/MPP protocol-conformance testing on Stellar \u2014 verifies a service's payment flow settles on-chain per spec.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,12 +11,12 @@
11
11
  ],
12
12
  "repository": {
13
13
  "type": "git",
14
- "url": "git+https://github.com/dzakwannajmi/wasit.git",
14
+ "url": "git+https://github.com/wasit-dev/wasit.git",
15
15
  "directory": "packages/cli"
16
16
  },
17
- "homepage": "https://github.com/dzakwannajmi/wasit#readme",
17
+ "homepage": "https://github.com/wasit-dev/wasit#readme",
18
18
  "bugs": {
19
- "url": "https://github.com/dzakwannajmi/wasit/issues"
19
+ "url": "https://github.com/wasit-dev/wasit/issues"
20
20
  },
21
21
  "keywords": [
22
22
  "stellar",
@@ -39,11 +39,8 @@
39
39
  "build": "tsc -p tsconfig.json"
40
40
  },
41
41
  "dependencies": {
42
- "@stellar/mpp": "^0.7.1",
43
- "@stellar/stellar-sdk": "^16.1.0",
44
- "@wasit-dev/core": "^0.1.0",
45
- "commander": "^12.0.0",
46
- "dotenv": "^16.0.0",
47
- "mppx": "^0.8.14"
42
+ "@wasit-dev/core": "^0.2.0",
43
+ "commander": "^15.0.0",
44
+ "dotenv": "^17.4.2"
48
45
  }
49
46
  }