teppi-check 0.2.1 → 0.3.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.
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # teppi-check
2
2
 
3
- Send one unpaid request to any paid endpoint and see what it actually advertises.
3
+ Send one unpaid request to any paid endpoint and see what it actually advertises, and what is
4
+ already broken about the listing before you pay anything.
4
5
 
5
6
  ```bash
6
7
  npx teppi-check https://some-seller.example/v1/extract
@@ -36,13 +37,16 @@ in the record something you can check rather than something you are asked to acc
36
37
  payment terms are not cached fail
37
38
  no cache-control, so a proxy may serve stale terms
38
39
 
40
+ prose_in_the_url defect
41
+ a query value spells out what to put there instead of putting it
42
+
39
43
  advertised
40
44
  10000 0x2222...2222 on eip155:8453 via exact
41
45
 
42
46
  request sha256:ba6f7ff0...0878
43
47
  response sha256:0a83a213...2496
44
48
 
45
- 2 checks failed
49
+ 3 problems found
46
50
  ```
47
51
 
48
52
  ## Why this exists
@@ -71,6 +75,23 @@ takes a purchase, which the full prober does on a schedule.
71
75
  Terms are read from the `payment-required` header (base64 or plain JSON) or from the response
72
76
  body, because deployed servers use both.
73
77
 
78
+ ## What it finds wrong with the listing
79
+
80
+ A check reads the answer. A defect reads the listing itself, and an endpoint can answer perfectly
81
+ while being impossible to buy from.
82
+
83
+ | Defect | Means |
84
+ |---|---|
85
+ | `prose_in_the_url` | a query value describes the parameter instead of filling it in, or a path segment is still `:id` or `{id}`, or an address is forty repeated characters |
86
+ | `path_not_found` | the listed path answered 404 or 410, so there is nothing there to buy |
87
+
88
+ These are the names the public record uses, with the same sentence beside them. A test runs this
89
+ rule and the one the record is built from over the same list of urls and fails if they ever give
90
+ different answers, so the id you read in a Teppi entry is the id you get here.
91
+
92
+ Nothing our own bad request causes is a defect. A 400, a 422 or a 429 says something about the
93
+ caller, not the seller, and none of them appear.
94
+
74
95
  ## Remote MCP servers
75
96
 
76
97
  `--mcp` runs the handshake instead: `initialize`, the initialized notification, then `tools/list`
@@ -97,14 +118,15 @@ which is what makes it useful for noticing that a server's instructions changed
97
118
 
98
119
  ```
99
120
  --mcp handshake a remote mcp server instead of an x402 endpoint
100
- --method <verb> default POST
121
+ --method <verb> tries GET then POST when not given
101
122
  --body <json|@file> default {}
102
123
  --json machine readable output
103
124
  --timeout <ms> default 15000
104
125
  --user-agent <ua> default node
105
126
  ```
106
127
 
107
- Exit codes: `0` every check passed, `1` a check failed, `2` the endpoint could not be reached.
128
+ Exit codes: `0` nothing to report, `1` a check failed or a defect was found, `2` the endpoint
129
+ could not be reached.
108
130
 
109
131
  Requests go over HTTPS only, apart from localhost.
110
132
 
@@ -123,7 +145,10 @@ the failure mode nobody notices until buyers quietly stop calling.
123
145
  import { probe } from 'teppi-check';
124
146
 
125
147
  const result = await probe({ url: 'https://some-seller.example/v1/extract' });
126
- console.log(result.verdict, result.checks);
148
+ console.log(result.verdict, result.checks, result.defects);
127
149
  ```
128
150
 
151
+ `urlHasPlaceholder(url)` and `defectsOf(url, httpStatus)` are exported on their own, for reading a
152
+ list of urls without sending anything.
153
+
129
154
  MIT.
package/npm/checks.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type PaymentTerms } from './terms.js';
2
+ import type { Defect } from './defects.js';
2
3
  export type CheckStatus = 'pass' | 'fail' | 'skip';
3
4
  export type Check = {
4
5
  readonly id: string;
@@ -16,5 +17,5 @@ export type HandshakeInput = {
16
17
  };
17
18
  export declare function runHandshakeChecks(input: HandshakeInput): Check[];
18
19
  export declare function unreachable(reason: string): Check[];
19
- export declare function verdictOf(checks: readonly Check[]): 'pass' | 'fail';
20
+ export declare function verdictOf(checks: readonly Check[], defects: readonly Defect[]): 'pass' | 'fail';
20
21
  //# sourceMappingURL=checks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../src/checks.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2B,KAAK,YAAY,EAAsB,MAAM,mBAAmB,CAAC;AAGnG,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD,MAAM,MAAM,KAAK,GAAG;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAqBF,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,KAAK,CAa7E;AAED,MAAM,MAAM,cAAc,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB,CAAC;AA+BF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,GAAG,KAAK,EAAE,CA4CjE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,CAEnD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,MAAM,GAAG,MAAM,CAEnE"}
1
+ {"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../src/checks.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2B,KAAK,YAAY,EAAsB,MAAM,mBAAmB,CAAC;AAEnG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD,MAAM,MAAM,KAAK,GAAG;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAqBF,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,KAAK,CAa7E;AAED,MAAM,MAAM,cAAc,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB,CAAC;AA+BF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,GAAG,KAAK,EAAE,CA4CjE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,CAEnD;AAGD,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,MAAM,CAG/F"}
package/npm/cli.js CHANGED
@@ -7134,125 +7134,6 @@ function toolsDigest(tools) {
7134
7134
  return createHash("sha256").update(canonical).digest("hex");
7135
7135
  }
7136
7136
 
7137
- // src/mcp.ts
7138
- function sha256(input) {
7139
- return `sha256:${createHash2("sha256").update(input).digest("hex")}`;
7140
- }
7141
- var pass = (id, label, detail) => detail === void 0 ? { id, label, status: "pass" } : { id, label, status: "pass", detail };
7142
- var fail = (id, label, detail) => ({
7143
- id,
7144
- label,
7145
- status: "fail",
7146
- detail
7147
- });
7148
- var skip = (id, label, detail) => ({
7149
- id,
7150
- label,
7151
- status: "skip",
7152
- detail
7153
- });
7154
- var REACH_LABEL = {
7155
- readable: "listed its tools",
7156
- auth_required: "asked for credentials",
7157
- payment_required: "asked for payment",
7158
- dead: "did not answer",
7159
- error: "answered with something else"
7160
- };
7161
- function checksFor(seen) {
7162
- const checks = [];
7163
- checks.push(
7164
- seen.reach === "dead" ? fail("reachable", "server answered", seen.note ?? "no answer") : pass("reachable", "server answered", REACH_LABEL[seen.reach])
7165
- );
7166
- if (seen.reach === "dead") return checks;
7167
- checks.push(
7168
- seen.protocolVersion === null ? skip("protocol", "named a protocol version", "it never got as far as saying") : pass("protocol", "named a protocol version", seen.protocolVersion)
7169
- );
7170
- checks.push(
7171
- seen.serverName === null ? skip("identity", "named itself", "it never got as far as saying") : pass(
7172
- "identity",
7173
- "named itself",
7174
- `${seen.serverName}${seen.serverVersion === null ? "" : ` ${seen.serverVersion}`}`
7175
- )
7176
- );
7177
- if (seen.tools === null) {
7178
- checks.push(skip("tools", "listed its tools", REACH_LABEL[seen.reach]));
7179
- return checks;
7180
- }
7181
- checks.push(pass("tools", "listed its tools", `${seen.tools.length} tool(s)`));
7182
- const bare = seen.tools.filter((t) => t.inputSchema === null).map((t) => t.name);
7183
- checks.push(
7184
- bare.length === 0 ? pass("input-schemas", "every tool declared an input schema") : fail(
7185
- "input-schemas",
7186
- "every tool declared an input schema",
7187
- `${bare.length} without one: ${bare.slice(0, 5).join(", ")}`
7188
- )
7189
- );
7190
- const unnamed = seen.tools.filter((t) => t.description === null).map((t) => t.name);
7191
- checks.push(
7192
- unnamed.length === 0 ? pass("descriptions", "every tool said what it does") : fail(
7193
- "descriptions",
7194
- "every tool said what it does",
7195
- `${unnamed.length} without one: ${unnamed.slice(0, 5).join(", ")}`
7196
- )
7197
- );
7198
- checks.push(pass("digest", "tool list digest", toolsDigest(seen.tools)));
7199
- return checks;
7200
- }
7201
- async function probeMcpEndpoint(options) {
7202
- const seen = await probeMcp(options.url, {
7203
- ...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs },
7204
- ...options.fetchImpl === void 0 ? {} : { fetchImpl: options.fetchImpl },
7205
- ...options.userAgent === void 0 ? {} : { userAgent: options.userAgent }
7206
- });
7207
- const checks = checksFor(seen);
7208
- return {
7209
- url: options.url,
7210
- method: "MCP",
7211
- observedAt: seen.observedAt,
7212
- reachable: seen.reach !== "dead",
7213
- httpStatus: seen.httpStatus,
7214
- handshakeMs: seen.handshakeMs,
7215
- termsSource: seen.reach === "payment_required" ? "mcp 402" : "none",
7216
- advertised: [],
7217
- requestHash: sha256(`MCP ${options.url}`),
7218
- responseHash: seen.tools === null ? null : `sha256:${toolsDigest(seen.tools)}`,
7219
- checks,
7220
- verdict: checks.some((c) => c.status === "fail") ? "fail" : "pass"
7221
- };
7222
- }
7223
-
7224
- // src/probe.ts
7225
- import { createHash as createHash3 } from "node:crypto";
7226
-
7227
- // ../x402/src/handshake.ts
7228
- var HANDSHAKE_TIMEOUT_MS2 = 3e4;
7229
- var sendsBody = (method) => method !== "GET" && method !== "HEAD";
7230
- function handshakeInit(request2) {
7231
- const carries = sendsBody(request2.method);
7232
- const headers = { accept: "application/json" };
7233
- if (carries) headers["content-type"] = "application/json";
7234
- if (request2.userAgent !== void 0) headers["user-agent"] = request2.userAgent;
7235
- if (request2.payment) headers[request2.payment.header] = request2.payment.value;
7236
- return {
7237
- method: request2.method,
7238
- headers,
7239
- ...carries ? { body: JSON.stringify(request2.input ?? {}) } : {},
7240
- signal: AbortSignal.timeout(request2.timeoutMs ?? HANDSHAKE_TIMEOUT_MS2)
7241
- };
7242
- }
7243
- async function handshake2(request2, deps = {}) {
7244
- const doFetch = deps.fetchImpl ?? fetch;
7245
- const now = deps.now ?? Date.now;
7246
- const started = now();
7247
- const since = () => Math.max(0, now() - started);
7248
- try {
7249
- const response = await doFetch(request2.url, handshakeInit(request2));
7250
- return { ok: true, response, body: await response.text(), latencyMs: since() };
7251
- } catch (err) {
7252
- return { ok: false, reason: err.message, latencyMs: since() };
7253
- }
7254
- }
7255
-
7256
7137
  // ../x402/src/terms.ts
7257
7138
  var HEADER_NAMES = ["payment-required", "x-payment-required", "www-authenticate-payment"];
7258
7139
  function asTerms(value) {
@@ -7324,24 +7205,24 @@ var ajv = new import_ajv.Ajv({
7324
7205
  validateSchema: true,
7325
7206
  validateFormats: false
7326
7207
  });
7327
- function pass2(id, label, detail) {
7208
+ function pass(id, label, detail) {
7328
7209
  return detail === void 0 ? { id, label, status: "pass" } : { id, label, status: "pass", detail };
7329
7210
  }
7330
- function fail2(id, label, detail) {
7211
+ function fail(id, label, detail) {
7331
7212
  return { id, label, status: "fail", detail };
7332
7213
  }
7333
7214
  function schemaCheck(id, label, schema) {
7334
7215
  if (schema === null || schema === void 0) {
7335
- return fail2(id, label, "not declared, so no response can ever be checked against it");
7216
+ return fail(id, label, "not declared, so no response can ever be checked against it");
7336
7217
  }
7337
7218
  if (typeof schema !== "object") {
7338
- return fail2(id, label, `declared as ${typeof schema}, not an object`);
7219
+ return fail(id, label, `declared as ${typeof schema}, not an object`);
7339
7220
  }
7340
7221
  try {
7341
7222
  ajv.compile(schema);
7342
- return pass2(id, label);
7223
+ return pass(id, label);
7343
7224
  } catch (err) {
7344
- return fail2(id, label, `declared but invalid: ${err.message.split("\n")[0]}`);
7225
+ return fail(id, label, `declared but invalid: ${err.message.split("\n")[0]}`);
7345
7226
  }
7346
7227
  }
7347
7228
  function requirementChecks(terms) {
@@ -7349,7 +7230,7 @@ function requirementChecks(terms) {
7349
7230
  const report = (id, label, has) => {
7350
7231
  const gaps = terms.accepts.map((r, i) => has(r) ? -1 : i).filter((i) => i >= 0);
7351
7232
  out.push(
7352
- gaps.length === 0 ? pass2(id, label) : fail2(id, label, `absent from accepts[${gaps.join("], accepts[")}]`)
7233
+ gaps.length === 0 ? pass(id, label) : fail(id, label, `absent from accepts[${gaps.join("], accepts[")}]`)
7353
7234
  );
7354
7235
  };
7355
7236
  report("price_declared", "price is stated", (r) => priceOf(r) !== null);
@@ -7366,29 +7247,29 @@ function requirementChecks(terms) {
7366
7247
  return out;
7367
7248
  }
7368
7249
  function runHandshakeChecks(input) {
7369
- const checks = [pass2("reachable", "endpoint answered")];
7250
+ const checks = [pass("reachable", "endpoint answered")];
7370
7251
  checks.push(
7371
- input.status === 402 ? pass2("status_402", "asks for payment") : fail2("status_402", "asks for payment", `returned ${input.status}, not 402`)
7252
+ input.status === 402 ? pass("status_402", "asks for payment") : fail("status_402", "asks for payment", `returned ${input.status}, not 402`)
7372
7253
  );
7373
7254
  if (input.terms === null) {
7374
7255
  checks.push(
7375
- fail2("terms_parseable", "payment terms parse", input.parseError ?? "no terms found")
7256
+ fail("terms_parseable", "payment terms parse", input.parseError ?? "no terms found")
7376
7257
  );
7377
7258
  return checks;
7378
7259
  }
7379
- checks.push(pass2("terms_parseable", "payment terms parse", `read from the ${input.source}`));
7260
+ checks.push(pass("terms_parseable", "payment terms parse", `read from the ${input.source}`));
7380
7261
  checks.push(
7381
- input.terms.accepts.length > 0 ? pass2(
7262
+ input.terms.accepts.length > 0 ? pass(
7382
7263
  "accepts_present",
7383
7264
  "offers at least one way to pay",
7384
7265
  `${input.terms.accepts.length} option(s)`
7385
- ) : fail2("accepts_present", "offers at least one way to pay", "accepts is empty")
7266
+ ) : fail("accepts_present", "offers at least one way to pay", "accepts is empty")
7386
7267
  );
7387
7268
  if (input.terms.accepts.length === 0) return checks;
7388
7269
  checks.push(...requirementChecks(input.terms));
7389
7270
  const cache = input.headers.get("cache-control") ?? "";
7390
7271
  checks.push(
7391
- /no-store|no-cache/i.test(cache) ? pass2("not_cacheable", "payment terms are not cached") : fail2(
7272
+ /no-store|no-cache/i.test(cache) ? pass("not_cacheable", "payment terms are not cached") : fail(
7392
7273
  "not_cacheable",
7393
7274
  "payment terms are not cached",
7394
7275
  cache === "" ? "no cache-control, so a proxy may serve stale terms" : `cache-control: ${cache}`
@@ -7397,12 +7278,165 @@ function runHandshakeChecks(input) {
7397
7278
  return checks;
7398
7279
  }
7399
7280
  function unreachable(reason) {
7400
- return [fail2("reachable", "endpoint answered", reason)];
7281
+ return [fail("reachable", "endpoint answered", reason)];
7401
7282
  }
7402
- function verdictOf(checks) {
7283
+ function verdictOf(checks, defects) {
7284
+ if (defects.length > 0) return "fail";
7403
7285
  return checks.some((c) => c.status === "fail") ? "fail" : "pass";
7404
7286
  }
7405
7287
 
7288
+ // src/defects.ts
7289
+ var PLACEHOLDER = [
7290
+ /[?&][^=&]+=(?:string|integer|number|boolean|array|object)(?:&|$)/,
7291
+ /=0x([0-9a-fA-F])\1{39}(?:&|$)/,
7292
+ /\/:[A-Za-z_][A-Za-z0-9_]*(?:\/|[?]|$)/,
7293
+ /\/[{][A-Za-z_][A-Za-z0-9_]*[}](?:\/|[?]|$)/,
7294
+ // An em dash and an e.g. belong to a sentence, and a value is not one.
7295
+ /[?&][^=&]+=[^&]*(?:%E2%80%94|—)/i,
7296
+ /[?&][^=&]+=[^&]*e\.g\./i,
7297
+ // Anchored at the start of the value, since a real one can mention these words later.
7298
+ /[?&][^=&]+=(?:required|optional)(?:[^a-z]|$)/i,
7299
+ // A value that spells out its own parameter name is describing it, not answering it.
7300
+ /[?&]([a-z_]{3,})=[^&]*\1[^&]*(?:\+|%20)/i
7301
+ ];
7302
+ function urlHasPlaceholder(url) {
7303
+ return PLACEHOLDER.some((marker) => marker.test(url));
7304
+ }
7305
+ var PROSE_SAYS = "a query value spells out what to put there instead of putting it";
7306
+ var GONE = /* @__PURE__ */ new Set([404, 410]);
7307
+ function pathNotFoundSays(httpStatus) {
7308
+ return `the listed path answered ${httpStatus}`;
7309
+ }
7310
+ function defectsOf(url, httpStatus) {
7311
+ const found = [];
7312
+ if (urlHasPlaceholder(url)) found.push({ defect: "prose_in_the_url", says: PROSE_SAYS });
7313
+ if (httpStatus !== null && GONE.has(httpStatus)) {
7314
+ found.push({ defect: "path_not_found", says: pathNotFoundSays(httpStatus) });
7315
+ }
7316
+ return found;
7317
+ }
7318
+
7319
+ // src/mcp.ts
7320
+ function sha256(input) {
7321
+ return `sha256:${createHash2("sha256").update(input).digest("hex")}`;
7322
+ }
7323
+ var pass2 = (id, label, detail) => detail === void 0 ? { id, label, status: "pass" } : { id, label, status: "pass", detail };
7324
+ var fail2 = (id, label, detail) => ({
7325
+ id,
7326
+ label,
7327
+ status: "fail",
7328
+ detail
7329
+ });
7330
+ var skip = (id, label, detail) => ({
7331
+ id,
7332
+ label,
7333
+ status: "skip",
7334
+ detail
7335
+ });
7336
+ var REACH_LABEL = {
7337
+ readable: "listed its tools",
7338
+ auth_required: "asked for credentials",
7339
+ payment_required: "asked for payment",
7340
+ dead: "did not answer",
7341
+ error: "answered with something else"
7342
+ };
7343
+ function checksFor(seen) {
7344
+ const checks = [];
7345
+ checks.push(
7346
+ seen.reach === "dead" ? fail2("reachable", "server answered", seen.note ?? "no answer") : pass2("reachable", "server answered", REACH_LABEL[seen.reach])
7347
+ );
7348
+ if (seen.reach === "dead") return checks;
7349
+ checks.push(
7350
+ seen.protocolVersion === null ? skip("protocol", "named a protocol version", "it never got as far as saying") : pass2("protocol", "named a protocol version", seen.protocolVersion)
7351
+ );
7352
+ checks.push(
7353
+ seen.serverName === null ? skip("identity", "named itself", "it never got as far as saying") : pass2(
7354
+ "identity",
7355
+ "named itself",
7356
+ `${seen.serverName}${seen.serverVersion === null ? "" : ` ${seen.serverVersion}`}`
7357
+ )
7358
+ );
7359
+ if (seen.tools === null) {
7360
+ checks.push(skip("tools", "listed its tools", REACH_LABEL[seen.reach]));
7361
+ return checks;
7362
+ }
7363
+ checks.push(pass2("tools", "listed its tools", `${seen.tools.length} tool(s)`));
7364
+ const bare = seen.tools.filter((t) => t.inputSchema === null).map((t) => t.name);
7365
+ checks.push(
7366
+ bare.length === 0 ? pass2("input-schemas", "every tool declared an input schema") : fail2(
7367
+ "input-schemas",
7368
+ "every tool declared an input schema",
7369
+ `${bare.length} without one: ${bare.slice(0, 5).join(", ")}`
7370
+ )
7371
+ );
7372
+ const unnamed = seen.tools.filter((t) => t.description === null).map((t) => t.name);
7373
+ checks.push(
7374
+ unnamed.length === 0 ? pass2("descriptions", "every tool said what it does") : fail2(
7375
+ "descriptions",
7376
+ "every tool said what it does",
7377
+ `${unnamed.length} without one: ${unnamed.slice(0, 5).join(", ")}`
7378
+ )
7379
+ );
7380
+ checks.push(pass2("digest", "tool list digest", toolsDigest(seen.tools)));
7381
+ return checks;
7382
+ }
7383
+ async function probeMcpEndpoint(options) {
7384
+ const seen = await probeMcp(options.url, {
7385
+ ...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs },
7386
+ ...options.fetchImpl === void 0 ? {} : { fetchImpl: options.fetchImpl },
7387
+ ...options.userAgent === void 0 ? {} : { userAgent: options.userAgent }
7388
+ });
7389
+ const checks = checksFor(seen);
7390
+ const defects = defectsOf(options.url, seen.httpStatus);
7391
+ return {
7392
+ url: options.url,
7393
+ method: "MCP",
7394
+ observedAt: seen.observedAt,
7395
+ reachable: seen.reach !== "dead",
7396
+ httpStatus: seen.httpStatus,
7397
+ handshakeMs: seen.handshakeMs,
7398
+ termsSource: seen.reach === "payment_required" ? "mcp 402" : "none",
7399
+ advertised: [],
7400
+ requestHash: sha256(`MCP ${options.url}`),
7401
+ responseHash: seen.tools === null ? null : `sha256:${toolsDigest(seen.tools)}`,
7402
+ checks,
7403
+ defects,
7404
+ verdict: verdictOf(checks, defects)
7405
+ };
7406
+ }
7407
+
7408
+ // src/probe.ts
7409
+ import { createHash as createHash3 } from "node:crypto";
7410
+
7411
+ // ../x402/src/handshake.ts
7412
+ var HANDSHAKE_TIMEOUT_MS2 = 3e4;
7413
+ var sendsBody = (method) => method !== "GET" && method !== "HEAD";
7414
+ function handshakeInit(request2) {
7415
+ const carries = sendsBody(request2.method);
7416
+ const headers = { accept: "application/json" };
7417
+ if (carries) headers["content-type"] = "application/json";
7418
+ if (request2.userAgent !== void 0) headers["user-agent"] = request2.userAgent;
7419
+ if (request2.payment) headers[request2.payment.header] = request2.payment.value;
7420
+ return {
7421
+ method: request2.method,
7422
+ headers,
7423
+ ...carries ? { body: JSON.stringify(request2.input ?? {}) } : {},
7424
+ signal: AbortSignal.timeout(request2.timeoutMs ?? HANDSHAKE_TIMEOUT_MS2)
7425
+ };
7426
+ }
7427
+ async function handshake2(request2, deps = {}) {
7428
+ const doFetch = deps.fetchImpl ?? fetch;
7429
+ const now = deps.now ?? Date.now;
7430
+ const started = now();
7431
+ const since = () => Math.max(0, now() - started);
7432
+ try {
7433
+ const response = await doFetch(request2.url, handshakeInit(request2));
7434
+ return { ok: true, response, body: await response.text(), latencyMs: since() };
7435
+ } catch (err) {
7436
+ return { ok: false, reason: err.message, latencyMs: since() };
7437
+ }
7438
+ }
7439
+
7406
7440
  // src/probe.ts
7407
7441
  function sha2562(input) {
7408
7442
  return `sha256:${createHash3("sha256").update(input).digest("hex")}`;
@@ -7449,6 +7483,7 @@ ${body}`);
7449
7483
  handshakeMs: answer.latencyMs,
7450
7484
  responseHash: null,
7451
7485
  checks: unreachable(answer.reason),
7486
+ defects: defectsOf(options.url, null),
7452
7487
  verdict: "fail"
7453
7488
  };
7454
7489
  }
@@ -7462,6 +7497,7 @@ ${body}`);
7462
7497
  parseError: parsed.parseError,
7463
7498
  source: parsed.source
7464
7499
  });
7500
+ const defects = defectsOf(options.url, response.status);
7465
7501
  return {
7466
7502
  ...base,
7467
7503
  reachable: true,
@@ -7471,7 +7507,8 @@ ${body}`);
7471
7507
  advertised: advertisedFrom(parsed.terms),
7472
7508
  responseHash: sha2562(text2),
7473
7509
  checks,
7474
- verdict: verdictOf(checks)
7510
+ defects,
7511
+ verdict: verdictOf(checks, defects)
7475
7512
  };
7476
7513
  }
7477
7514
  function askedForPayment(result) {
@@ -7524,6 +7561,13 @@ function formatText(result, colour) {
7524
7561
  lines.push(` ${check.label.padEnd(width)}${mark}`);
7525
7562
  if (check.detail) lines.push(` ${paint("dim", check.detail, colour)}`);
7526
7563
  }
7564
+ if (result.defects.length > 0) {
7565
+ lines.push("");
7566
+ for (const found of result.defects) {
7567
+ lines.push(` ${found.defect.padEnd(width)}${paint("red", "defect", colour)}`);
7568
+ lines.push(` ${paint("dim", found.says, colour)}`);
7569
+ }
7570
+ }
7527
7571
  if (result.advertised.length > 0) {
7528
7572
  lines.push("");
7529
7573
  lines.push(` ${paint("dim", "advertised", colour)}`);
@@ -7536,9 +7580,9 @@ function formatText(result, colour) {
7536
7580
  if (result.responseHash)
7537
7581
  lines.push(` ${paint("dim", "response ", colour)}${result.responseHash}`);
7538
7582
  lines.push("");
7539
- const failed = result.checks.filter((c) => c.status === "fail").length;
7583
+ const failed = result.checks.filter((c) => c.status === "fail").length + result.defects.length;
7540
7584
  lines.push(
7541
- result.verdict === "pass" ? ` ${paint("green", "every handshake check passed", colour)}` : ` ${paint("red", `${failed} check${failed === 1 ? "" : "s"} failed`, colour)}`
7585
+ result.verdict === "pass" ? ` ${paint("green", "every handshake check passed", colour)}` : ` ${paint("red", `${failed} problem${failed === 1 ? "" : "s"} found`, colour)}`
7542
7586
  );
7543
7587
  lines.push(` ${paint("dim", "this is the unpaid half. correctness needs a purchase.", colour)}`);
7544
7588
  lines.push("");
@@ -7552,8 +7596,8 @@ function formatJson(result) {
7552
7596
  var USAGE = `
7553
7597
  teppi-check <url> [options]
7554
7598
 
7555
- Sends one unpaid request and reports what the endpoint advertises, in the
7556
- same form the public record uses.
7599
+ Sends one unpaid request and reports what the endpoint advertises, plus
7600
+ anything wrong with the listing itself, in the form the public record uses.
7557
7601
 
7558
7602
  --mcp handshake a remote mcp server instead of an x402 endpoint
7559
7603
  --method <verb> tries get then post when not given
@@ -7562,7 +7606,8 @@ var USAGE = `
7562
7606
  --timeout <ms> default 15000
7563
7607
  --user-agent <ua> default node
7564
7608
 
7565
- Exit codes: 0 every check passed, 1 a check failed, 2 could not be reached.
7609
+ Exit codes: 0 nothing to report, 1 a check failed or a defect was found,
7610
+ 2 could not be reached.
7566
7611
  `;
7567
7612
  function refuseUnlessUsable(url) {
7568
7613
  let parsed;
@@ -0,0 +1,9 @@
1
+ export type Defect = {
2
+ readonly defect: string;
3
+ readonly says: string;
4
+ };
5
+ export declare function urlHasPlaceholder(url: string): boolean;
6
+ export declare const PROSE_SAYS = "a query value spells out what to put there instead of putting it";
7
+ export declare function pathNotFoundSays(httpStatus: number): string;
8
+ export declare function defectsOf(url: string, httpStatus: number | null): Defect[];
9
+ //# sourceMappingURL=defects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defects.d.ts","sourceRoot":"","sources":["../src/defects.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,GAAG;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB,CAAC;AAkBF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,eAAO,MAAM,UAAU,qEAAqE,CAAC;AAK7F,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,EAAE,CAO1E"}
package/npm/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { type PaymentRequirement, type PaymentTerms, parseTerms, priceOf, schemasOf, } from './terms.js';
2
2
  export { type Check, type CheckStatus, runHandshakeChecks, schemaCheck, verdictOf, } from './checks.js';
3
+ export { type Defect, defectsOf, PROSE_SAYS, pathNotFoundSays, urlHasPlaceholder, } from './defects.js';
3
4
  export { checksFor, type McpProbeOptions, probeMcpEndpoint, REACH_LABEL, } from './mcp.js';
4
5
  export { DEFAULT_USER_AGENT, type ProbeOptions, type ProbeResult, probe } from './probe.js';
5
6
  export { formatJson, formatText, shouldColour } from './report.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,UAAU,EACV,OAAO,EACP,SAAS,GACT,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,kBAAkB,EAClB,WAAW,EACX,SAAS,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EACN,SAAS,EACT,KAAK,eAAe,EACpB,gBAAgB,EAChB,WAAW,GACX,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC5F,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,UAAU,EACV,OAAO,EACP,SAAS,GACT,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,kBAAkB,EAClB,WAAW,EACX,SAAS,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EACN,KAAK,MAAM,EACX,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,iBAAiB,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,SAAS,EACT,KAAK,eAAe,EACpB,gBAAgB,EAChB,WAAW,GACX,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC5F,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
package/npm/index.js CHANGED
@@ -7011,10 +7011,42 @@ function runHandshakeChecks(input) {
7011
7011
  function unreachable(reason) {
7012
7012
  return [fail("reachable", "endpoint answered", reason)];
7013
7013
  }
7014
- function verdictOf(checks) {
7014
+ function verdictOf(checks, defects) {
7015
+ if (defects.length > 0) return "fail";
7015
7016
  return checks.some((c) => c.status === "fail") ? "fail" : "pass";
7016
7017
  }
7017
7018
 
7019
+ // src/defects.ts
7020
+ var PLACEHOLDER = [
7021
+ /[?&][^=&]+=(?:string|integer|number|boolean|array|object)(?:&|$)/,
7022
+ /=0x([0-9a-fA-F])\1{39}(?:&|$)/,
7023
+ /\/:[A-Za-z_][A-Za-z0-9_]*(?:\/|[?]|$)/,
7024
+ /\/[{][A-Za-z_][A-Za-z0-9_]*[}](?:\/|[?]|$)/,
7025
+ // An em dash and an e.g. belong to a sentence, and a value is not one.
7026
+ /[?&][^=&]+=[^&]*(?:%E2%80%94|—)/i,
7027
+ /[?&][^=&]+=[^&]*e\.g\./i,
7028
+ // Anchored at the start of the value, since a real one can mention these words later.
7029
+ /[?&][^=&]+=(?:required|optional)(?:[^a-z]|$)/i,
7030
+ // A value that spells out its own parameter name is describing it, not answering it.
7031
+ /[?&]([a-z_]{3,})=[^&]*\1[^&]*(?:\+|%20)/i
7032
+ ];
7033
+ function urlHasPlaceholder(url) {
7034
+ return PLACEHOLDER.some((marker) => marker.test(url));
7035
+ }
7036
+ var PROSE_SAYS = "a query value spells out what to put there instead of putting it";
7037
+ var GONE = /* @__PURE__ */ new Set([404, 410]);
7038
+ function pathNotFoundSays(httpStatus) {
7039
+ return `the listed path answered ${httpStatus}`;
7040
+ }
7041
+ function defectsOf(url, httpStatus) {
7042
+ const found = [];
7043
+ if (urlHasPlaceholder(url)) found.push({ defect: "prose_in_the_url", says: PROSE_SAYS });
7044
+ if (httpStatus !== null && GONE.has(httpStatus)) {
7045
+ found.push({ defect: "path_not_found", says: pathNotFoundSays(httpStatus) });
7046
+ }
7047
+ return found;
7048
+ }
7049
+
7018
7050
  // src/mcp.ts
7019
7051
  import { createHash as createHash2 } from "node:crypto";
7020
7052
 
@@ -7349,6 +7381,7 @@ async function probeMcpEndpoint(options) {
7349
7381
  ...options.userAgent === void 0 ? {} : { userAgent: options.userAgent }
7350
7382
  });
7351
7383
  const checks = checksFor(seen);
7384
+ const defects = defectsOf(options.url, seen.httpStatus);
7352
7385
  return {
7353
7386
  url: options.url,
7354
7387
  method: "MCP",
@@ -7361,7 +7394,8 @@ async function probeMcpEndpoint(options) {
7361
7394
  requestHash: sha256(`MCP ${options.url}`),
7362
7395
  responseHash: seen.tools === null ? null : `sha256:${toolsDigest(seen.tools)}`,
7363
7396
  checks,
7364
- verdict: checks.some((c) => c.status === "fail") ? "fail" : "pass"
7397
+ defects,
7398
+ verdict: verdictOf(checks, defects)
7365
7399
  };
7366
7400
  }
7367
7401
 
@@ -7444,6 +7478,7 @@ ${body}`);
7444
7478
  handshakeMs: answer.latencyMs,
7445
7479
  responseHash: null,
7446
7480
  checks: unreachable(answer.reason),
7481
+ defects: defectsOf(options.url, null),
7447
7482
  verdict: "fail"
7448
7483
  };
7449
7484
  }
@@ -7457,6 +7492,7 @@ ${body}`);
7457
7492
  parseError: parsed.parseError,
7458
7493
  source: parsed.source
7459
7494
  });
7495
+ const defects = defectsOf(options.url, response.status);
7460
7496
  return {
7461
7497
  ...base,
7462
7498
  reachable: true,
@@ -7466,7 +7502,8 @@ ${body}`);
7466
7502
  advertised: advertisedFrom(parsed.terms),
7467
7503
  responseHash: sha2562(text2),
7468
7504
  checks,
7469
- verdict: verdictOf(checks)
7505
+ defects,
7506
+ verdict: verdictOf(checks, defects)
7470
7507
  };
7471
7508
  }
7472
7509
 
@@ -7506,6 +7543,13 @@ function formatText(result, colour) {
7506
7543
  lines.push(` ${check.label.padEnd(width)}${mark}`);
7507
7544
  if (check.detail) lines.push(` ${paint("dim", check.detail, colour)}`);
7508
7545
  }
7546
+ if (result.defects.length > 0) {
7547
+ lines.push("");
7548
+ for (const found of result.defects) {
7549
+ lines.push(` ${found.defect.padEnd(width)}${paint("red", "defect", colour)}`);
7550
+ lines.push(` ${paint("dim", found.says, colour)}`);
7551
+ }
7552
+ }
7509
7553
  if (result.advertised.length > 0) {
7510
7554
  lines.push("");
7511
7555
  lines.push(` ${paint("dim", "advertised", colour)}`);
@@ -7518,9 +7562,9 @@ function formatText(result, colour) {
7518
7562
  if (result.responseHash)
7519
7563
  lines.push(` ${paint("dim", "response ", colour)}${result.responseHash}`);
7520
7564
  lines.push("");
7521
- const failed = result.checks.filter((c) => c.status === "fail").length;
7565
+ const failed = result.checks.filter((c) => c.status === "fail").length + result.defects.length;
7522
7566
  lines.push(
7523
- result.verdict === "pass" ? ` ${paint("green", "every handshake check passed", colour)}` : ` ${paint("red", `${failed} check${failed === 1 ? "" : "s"} failed`, colour)}`
7567
+ result.verdict === "pass" ? ` ${paint("green", "every handshake check passed", colour)}` : ` ${paint("red", `${failed} problem${failed === 1 ? "" : "s"} found`, colour)}`
7524
7568
  );
7525
7569
  lines.push(` ${paint("dim", "this is the unpaid half. correctness needs a purchase.", colour)}`);
7526
7570
  lines.push("");
@@ -7531,11 +7575,14 @@ function formatJson(result) {
7531
7575
  }
7532
7576
  export {
7533
7577
  DEFAULT_USER_AGENT,
7578
+ PROSE_SAYS,
7534
7579
  REACH_LABEL,
7535
7580
  checksFor,
7581
+ defectsOf,
7536
7582
  formatJson,
7537
7583
  formatText,
7538
7584
  parseTerms,
7585
+ pathNotFoundSays,
7539
7586
  priceOf,
7540
7587
  probe,
7541
7588
  probeMcpEndpoint,
@@ -7543,5 +7590,6 @@ export {
7543
7590
  schemaCheck,
7544
7591
  schemasOf,
7545
7592
  shouldColour,
7593
+ urlHasPlaceholder,
7546
7594
  verdictOf
7547
7595
  };
package/npm/mcp.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type McpObservation } from './mcp-probe/index.js';
2
- import type { Check } from './checks.js';
2
+ import { type Check } from './checks.js';
3
3
  import type { ProbeResult } from './probe.js';
4
4
  export type McpProbeOptions = {
5
5
  readonly url: string;
package/npm/mcp.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAyB,MAAM,kBAAkB,CAAC;AAC9E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAClC,CAAC;AAwBF,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAMzE,CAAC;AAEF,wBAAgB,SAAS,CAAC,IAAI,EAAE,cAAc,GAAG,KAAK,EAAE,CA2DvD;AAGD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAsBrF"}
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAyB,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,KAAK,KAAK,EAAa,MAAM,aAAa,CAAC;AAEpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAClC,CAAC;AAwBF,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAMzE,CAAC;AAEF,wBAAgB,SAAS,CAAC,IAAI,EAAE,cAAc,GAAG,KAAK,EAAE,CA2DvD;AAGD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAwBrF"}
package/npm/probe.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type Check } from './checks.js';
2
+ import { type Defect } from './defects.js';
2
3
  export declare const DEFAULT_USER_AGENT = "node";
3
4
  export type ProbeOptions = {
4
5
  readonly url: string;
@@ -25,6 +26,7 @@ export type ProbeResult = {
25
26
  readonly requestHash: string;
26
27
  readonly responseHash: string | null;
27
28
  readonly checks: readonly Check[];
29
+ readonly defects: readonly Defect[];
28
30
  readonly verdict: 'pass' | 'fail';
29
31
  };
30
32
  export declare function probe(options: ProbeOptions): Promise<ProbeResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../src/probe.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,KAAK,EAA8C,MAAM,aAAa,CAAC;AAGrF,eAAO,MAAM,kBAAkB,SAAS,CAAC;AAEzC,MAAM,MAAM,YAAY,GAAG;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1F,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAClC,CAAC;AAgBF,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA+DvE;AAGD,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAE5D;AAGD,wBAAsB,eAAe,CACpC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,EAC7C,QAAQ,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CAStB"}
1
+ {"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../src/probe.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,KAAK,EAA8C,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,KAAK,MAAM,EAAa,MAAM,cAAc,CAAC;AAGtD,eAAO,MAAM,kBAAkB,SAAS,CAAC;AAEzC,MAAM,MAAM,YAAY,GAAG;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1F,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAClC,CAAC;AAgBF,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAmEvE;AAGD,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAE5D;AAGD,wBAAsB,eAAe,CACpC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,EAC7C,QAAQ,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CAStB"}
@@ -1 +1 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAW9C,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAI7F;AASD,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CA6CvE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEtD"}
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAW9C,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAI7F;AASD,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAsDvE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEtD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teppi-check",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Probe any paid endpoint and print what it actually returns for the money. Reproduces any entry in the Teppi record.",
5
5
  "keywords": [
6
6
  "x402",