@agentcash/telemetry 0.3.0 → 0.3.1

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,22 +1,41 @@
1
- # @merit-systems/x402-server-telemetry
1
+ # @agentcash/telemetry
2
2
 
3
- [![npm](https://img.shields.io/npm/v/@merit-systems/x402-server-telemetry)](https://www.npmjs.com/package/@merit-systems/x402-server-telemetry)
3
+ [![npm](https://img.shields.io/npm/v/@agentcash/telemetry)](https://www.npmjs.com/package/@agentcash/telemetry)
4
4
 
5
- Shared telemetry for Merit Systems x402 servers. Extracts identity headers, logs invocations to ClickHouse, and auto-extracts verified wallets from x402 payments and SIWX auth.
5
+ ClickHouse telemetry for x402/MPP/SIWX API services. Logs invocations to ClickHouse, extracts verified wallets from x402 payments and SIWX auth.
6
6
 
7
- [Telemetry spec](docs/telemetry-spec.md) | [npm](https://www.npmjs.com/package/@merit-systems/x402-server-telemetry) | [GitHub](https://github.com/Merit-Systems/x402-server-telemetry)
7
+ [Telemetry spec](docs/telemetry-spec.md) | [npm](https://www.npmjs.com/package/@agentcash/telemetry) | [GitHub](https://github.com/Merit-Systems/agentcash-telemetry)
8
8
 
9
9
  ## Install
10
10
 
11
11
  ```bash
12
- npm install @merit-systems/x402-server-telemetry @clickhouse/client
12
+ pnpm add @agentcash/telemetry @clickhouse/client
13
13
  ```
14
14
 
15
- ## Quick start
15
+ ## Quick start — Router Plugin (recommended)
16
+
17
+ ```typescript
18
+ import { createRouter } from '@agentcash/router';
19
+ import { createTelemetryPlugin } from '@agentcash/telemetry/plugin';
20
+
21
+ const router = createRouter({
22
+ payeeAddress: '0x...',
23
+ plugin: createTelemetryPlugin({
24
+ clickhouse: {
25
+ url: process.env.TELEM_CLICKHOUSE_URL!,
26
+ database: process.env.TELEM_CLICKHOUSE_DATABASE,
27
+ username: process.env.TELEM_CLICKHOUSE_USERNAME,
28
+ password: process.env.TELEM_CLICKHOUSE_PASSWORD,
29
+ },
30
+ }),
31
+ });
32
+ ```
33
+
34
+ ## Quick start — Legacy wrapper
16
35
 
17
36
  ```typescript
18
37
  // lib/telemetry.ts (or wherever your route wrappers live)
19
- import { initTelemetry, withTelemetry } from '@merit-systems/x402-server-telemetry';
38
+ import { initTelemetry, withTelemetry } from '@agentcash/telemetry';
20
39
 
21
40
  initTelemetry({
22
41
  clickhouse: {
@@ -40,26 +59,38 @@ export const POST = withTelemetry(async (request, ctx) => {
40
59
  });
41
60
  ```
42
61
 
43
- ## Three entrypoints
62
+ ## Four entrypoints
63
+
64
+ ### Router Plugin (`@agentcash/telemetry/plugin`)
65
+
66
+ **Primary integration path.** Hooks into `@agentcash/router`'s orchestrate lifecycle.
67
+
68
+ Requires: `@clickhouse/client`
69
+
70
+ ```typescript
71
+ import { createTelemetryPlugin } from '@agentcash/telemetry/plugin';
72
+ ```
73
+
74
+ - `createTelemetryPlugin(config)` — returns a `RouterPlugin` that captures request metadata, payment verification, settlement, response, errors, alerts, and provider quota
44
75
 
45
- ### Core (`@merit-systems/x402-server-telemetry`)
76
+ ### Core (`@agentcash/telemetry`)
46
77
 
47
78
  Requires: `@clickhouse/client`, `next`
48
79
 
49
80
  ```typescript
50
- import { initTelemetry, withTelemetry } from '@merit-systems/x402-server-telemetry';
81
+ import { initTelemetry, withTelemetry } from '@agentcash/telemetry';
51
82
  ```
52
83
 
53
84
  - `initTelemetry(config)` — synchronous, call once at module level. Pass `verify: true` to ping ClickHouse on startup (fire-and-forget, never blocks)
54
85
  - `withTelemetry(handler)` — wrap any Next.js route handler
55
86
  - `extractVerifiedWallet(headers)` — extract wallet from x402 payment headers
56
87
 
57
- ### SIWX (`@merit-systems/x402-server-telemetry/siwx`)
88
+ ### SIWX (`@agentcash/telemetry/siwx`)
58
89
 
59
90
  Requires: `@x402/extensions`, `@x402/core`
60
91
 
61
92
  ```typescript
62
- import { withSiwxTelemetry } from '@merit-systems/x402-server-telemetry/siwx';
93
+ import { withSiwxTelemetry } from '@agentcash/telemetry/siwx';
63
94
 
64
95
  export const GET = withSiwxTelemetry(async (request, ctx) => {
65
96
  // ctx.verifiedWallet is guaranteed to be set
@@ -67,12 +98,12 @@ export const GET = withSiwxTelemetry(async (request, ctx) => {
67
98
  });
68
99
  ```
69
100
 
70
- ### Route Builder (`@merit-systems/x402-server-telemetry/builder`)
101
+ ### Route Builder (`@agentcash/telemetry/builder`)
71
102
 
72
103
  Requires: `@x402/next`, `zod` (^4), `@x402/extensions`
73
104
 
74
105
  ```typescript
75
- import { createRouteBuilder } from '@merit-systems/x402-server-telemetry/builder';
106
+ import { createRouteBuilder } from '@agentcash/telemetry/builder';
76
107
 
77
108
  const route = createRouteBuilder({ x402Server });
78
109
 
@@ -102,14 +133,14 @@ Call `initTelemetry()` in the same module that imports your route wrappers:
102
133
 
103
134
  ```typescript
104
135
  // lib/telemetry.ts — CORRECT
105
- import { initTelemetry, withTelemetry } from '@merit-systems/x402-server-telemetry';
136
+ import { initTelemetry, withTelemetry } from '@agentcash/telemetry';
106
137
  initTelemetry({ clickhouse: { ... } });
107
138
  export { withTelemetry };
108
139
  ```
109
140
 
110
141
  ```typescript
111
142
  // instrumentation.ts — WRONG: singleton won't be shared with handlers
112
- import { initTelemetry } from '@merit-systems/x402-server-telemetry';
143
+ import { initTelemetry } from '@agentcash/telemetry';
113
144
  export async function register() {
114
145
  initTelemetry({ clickhouse: { ... } }); // handlers can't see this
115
146
  }
@@ -117,11 +148,11 @@ export async function register() {
117
148
 
118
149
  ### Subpath exports isolate heavy deps
119
150
 
120
- The `/siwx` and `/builder` entrypoints have additional peer dependencies. If you only use the core `withTelemetry`, you don't need `zod`, `@x402/next`, or `@x402/extensions` installed.
151
+ The `/siwx` and `/builder` entrypoints have additional peer dependencies. If you only use the core `withTelemetry` or `./plugin`, you don't need `zod`, `@x402/next`, or `@x402/extensions` installed.
121
152
 
122
153
  ### After updating, commit both `package.json` and lockfile
123
154
 
124
- `pnpm update @merit-systems/x402-server-telemetry` bumps the version specifier in **both** `package.json` and `pnpm-lock.yaml`. Vercel's `frozen-lockfile` mode will reject deploys if only the lockfile is committed. Always:
155
+ `pnpm update @agentcash/telemetry` bumps the version specifier in **both** `package.json` and `pnpm-lock.yaml`. Vercel's `frozen-lockfile` mode will reject deploys if only the lockfile is committed. Always:
125
156
 
126
157
  ```bash
127
158
  git add package.json pnpm-lock.yaml
package/dist/builder.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkGE7VBMQPjs = require('./chunk-GE7VBMQP.js');
6
- require('./chunk-P63MLKU3.js');
5
+ var _chunk2EFPFJCBjs = require('./chunk-2EFPFJCB.js');
6
+ require('./chunk-QEJ7ZGGH.js');
7
7
 
8
8
  // src/route-builder.ts
9
9
  var _server = require('next/server');
@@ -84,10 +84,10 @@ var RouteBuilder = class _RouteBuilder {
84
84
  handler(fn) {
85
85
  const { accepts, bodySchema, querySchema, outputSchema, outputExample, description } = this.config;
86
86
  const coreHandler = async (request) => {
87
- const meta = _chunkGE7VBMQPjs.extractRequestMeta.call(void 0, request);
88
- const ctx = _chunkGE7VBMQPjs.buildTelemetryContext.call(void 0, meta);
87
+ const meta = _chunk2EFPFJCBjs.extractRequestMeta.call(void 0, request);
88
+ const ctx = _chunk2EFPFJCBjs.buildTelemetryContext.call(void 0, meta);
89
89
  const log = (status, responseBody, resp) => {
90
- _chunkGE7VBMQPjs.recordInvocation.call(void 0, meta, requestBodyString, {
90
+ _chunk2EFPFJCBjs.recordInvocation.call(void 0, meta, requestBodyString, {
91
91
  status,
92
92
  body: responseBody,
93
93
  headers: JSON.stringify(Object.fromEntries(resp.headers.entries())),
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/builder.js","../src/route-builder.ts"],"names":[],"mappings":"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","file":"/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/builder.js","sourcesContent":[null,"/**\n * Convenience route builder that composes telemetry + validation + x402 wrapping.\n * This is optional — servers can use withTelemetry directly.\n *\n * Import from '@agentcash/telemetry/builder'.\n * Requires peer deps: @x402/next, zod (^4), @x402/extensions\n */\n\nimport { type NextRequest, NextResponse } from 'next/server';\nimport { withX402 } from '@x402/next';\nimport { z, type ZodType } from 'zod';\nimport { declareDiscoveryExtension } from '@x402/extensions/bazaar';\nimport type { TelemetryContext } from './types';\nimport { extractRequestMeta, buildTelemetryContext, recordInvocation } from './telemetry-core';\n\nexport class HttpError extends Error {\n constructor(\n message: string,\n public status: number,\n ) {\n super(message);\n this.name = 'HttpError';\n }\n}\n\ntype AcceptsOption = {\n amount: string;\n network: string;\n asset?: string;\n};\n\ntype BuilderConfig<TBody = unknown, TQuery = unknown, TOutput = unknown> = {\n accepts: AcceptsOption[];\n bodySchema?: ZodType<TBody>;\n querySchema?: ZodType<TQuery>;\n outputSchema?: ZodType<TOutput>;\n outputExample?: TOutput;\n description?: string;\n};\n\ntype HandlerContext<TBody, TQuery> = {\n body: TBody;\n query: TQuery;\n request: NextRequest;\n telemetry: TelemetryContext;\n};\n\ntype HandlerFn<TBody, TQuery, TResponse> = (\n ctx: HandlerContext<TBody, TQuery>,\n) => Promise<TResponse>;\n\nfunction formatValidationError(error: import('zod').ZodError): string {\n const issues = error.issues;\n if (issues.length === 0) return 'Validation failed';\n\n if (issues.length === 1) {\n const issue = issues[0];\n const path = issue.path.join('.');\n const field = path || 'request';\n if (issue.message) return `${field}: ${issue.message}`;\n return `${field}: Invalid value`;\n }\n\n const errors = issues.map((issue) => {\n const path = issue.path.join('.');\n const field = path || 'request';\n return issue.message ? `${field}: ${issue.message}` : `${field}: Invalid`;\n });\n\n return `Validation failed: ${errors.join('; ')}`;\n}\n\nexport interface RouteBuilderOptions {\n /** The x402 resource server instance (from @x402/core/server). Required when using .price(). */\n x402Server?: unknown;\n}\n\nclass RouteBuilder<TBody = unknown, TQuery = unknown, TOutput = unknown> {\n private config: BuilderConfig<TBody, TQuery, TOutput>;\n private options: RouteBuilderOptions;\n\n constructor(\n config?: Partial<BuilderConfig<TBody, TQuery, TOutput>>,\n options?: RouteBuilderOptions,\n ) {\n this.config = { accepts: [], ...config };\n this.options = options ?? {};\n }\n\n price(amount: string, network: string, asset?: string) {\n return new RouteBuilder<TBody, TQuery, TOutput>(\n { ...this.config, accepts: [{ amount, network, asset }] },\n this.options,\n );\n }\n\n accepts(options: AcceptsOption[]) {\n return new RouteBuilder<TBody, TQuery, TOutput>(\n { ...this.config, accepts: options },\n this.options,\n );\n }\n\n body<T>(schema: ZodType<T>) {\n return new RouteBuilder<T, TQuery, TOutput>(\n { ...this.config, bodySchema: schema as ZodType<unknown> } as BuilderConfig<\n T,\n TQuery,\n TOutput\n >,\n this.options,\n );\n }\n\n query<T>(schema: ZodType<T>) {\n return new RouteBuilder<TBody, T, TOutput>(\n { ...this.config, querySchema: schema as ZodType<unknown> } as BuilderConfig<\n TBody,\n T,\n TOutput\n >,\n this.options,\n );\n }\n\n output<T>(schema: ZodType<T>, example?: T) {\n return new RouteBuilder<TBody, TQuery, T>(\n {\n ...this.config,\n outputSchema: schema as ZodType<unknown>,\n outputExample: example,\n } as BuilderConfig<TBody, TQuery, T>,\n this.options,\n );\n }\n\n description(text: string) {\n return new RouteBuilder<TBody, TQuery, TOutput>(\n { ...this.config, description: text },\n this.options,\n );\n }\n\n handler<TResponse>(fn: HandlerFn<TBody, TQuery, TResponse>) {\n const { accepts, bodySchema, querySchema, outputSchema, outputExample, description } =\n this.config;\n\n const coreHandler = async (request: NextRequest): Promise<NextResponse> => {\n const meta = extractRequestMeta(request);\n const ctx = buildTelemetryContext(meta);\n\n const log = (status: number, responseBody: string | null, resp: NextResponse) => {\n recordInvocation(meta, requestBodyString, {\n status,\n body: responseBody,\n headers: JSON.stringify(Object.fromEntries(resp.headers.entries())),\n contentType: resp.headers.get('content-type') ?? null,\n });\n };\n\n // Parse and validate body\n let body: TBody = undefined as TBody;\n let query: TQuery = undefined as TQuery;\n let requestBodyString: string | null = null;\n\n if (bodySchema) {\n let rawBody: unknown;\n try {\n rawBody = await request.json();\n requestBodyString = JSON.stringify(rawBody);\n } catch {\n const errorResp = NextResponse.json(\n { success: false, error: 'Invalid JSON body' },\n { status: 400 },\n );\n log(400, JSON.stringify({ success: false, error: 'Invalid JSON body' }), errorResp);\n return errorResp;\n }\n\n const parsed = bodySchema.safeParse(rawBody);\n if (!parsed.success) {\n const message = formatValidationError(parsed.error);\n const errorBody = {\n success: false,\n error: 'Validation failed',\n message,\n details: parsed.error.flatten(),\n };\n const errorResp = NextResponse.json(errorBody, { status: 400 });\n log(400, JSON.stringify(errorBody), errorResp);\n return errorResp;\n }\n body = parsed.data;\n }\n\n // Parse and validate query\n if (querySchema) {\n const searchParams = Object.fromEntries(request.nextUrl.searchParams);\n const parsed = querySchema.safeParse(searchParams);\n if (!parsed.success) {\n const message = formatValidationError(parsed.error);\n const errorBody = {\n success: false,\n error: 'Query validation failed',\n message,\n details: parsed.error.flatten(),\n };\n const errorResp = NextResponse.json(errorBody, { status: 400 });\n log(400, JSON.stringify(errorBody), errorResp);\n return errorResp;\n }\n query = parsed.data;\n }\n\n // Execute user handler\n let response: TResponse;\n try {\n response = await fn({ body, query, request, telemetry: ctx });\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n const status = error instanceof HttpError ? error.status : 500;\n const errorBody = { success: false, error: message };\n const errorResp = NextResponse.json(errorBody, { status });\n log(status, JSON.stringify(errorBody), errorResp);\n return errorResp;\n }\n\n // Detect { success: false } to prevent x402 settlement\n if (\n response &&\n typeof response === 'object' &&\n 'success' in response &&\n !(response as { success: boolean }).success\n ) {\n const errorResp = NextResponse.json(response, { status: 500 });\n log(500, JSON.stringify(response), errorResp);\n return errorResp;\n }\n\n // Success\n const successResp = NextResponse.json(response);\n let responseBodyString: string | null = null;\n try {\n responseBodyString = JSON.stringify(response);\n } catch {\n // Not serializable — that's fine\n }\n log(200, responseBodyString, successResp);\n return successResp;\n };\n\n // If no pricing, return the core handler directly (no x402 wrapping)\n if (accepts.length === 0) {\n return coreHandler;\n }\n\n // Wrap with x402\n const X402_BYPASS = process.env.X402_BYPASS === 'true';\n if (X402_BYPASS) {\n return coreHandler;\n }\n\n const X402_PAYEE_ADDRESS = process.env.X402_PAYEE_ADDRESS;\n if (!X402_PAYEE_ADDRESS) {\n throw new Error('X402_PAYEE_ADDRESS environment variable is required when using .price()');\n }\n\n const routeConfig = {\n description,\n accepts: accepts.map(({ amount, network, asset }) => ({\n scheme: 'exact' as const,\n network: network as `${string}:${string}`,\n price: amount,\n payTo: X402_PAYEE_ADDRESS,\n ...(asset && { extra: { asset } }),\n })),\n extensions: buildDiscoveryExtensions(bodySchema, querySchema, outputSchema, outputExample),\n };\n\n if (!this.options.x402Server) {\n throw new Error(\n 'x402Server is required when using .price(). Pass it to createRouteBuilder({ x402Server }).',\n );\n }\n\n return withX402(coreHandler, routeConfig, this.options.x402Server as never);\n }\n}\n\nfunction buildDiscoveryExtensions(\n bodySchema?: ZodType<unknown>,\n querySchema?: ZodType<unknown>,\n outputSchema?: ZodType<unknown>,\n outputExample?: unknown,\n): Record<string, unknown> | undefined {\n const inputJsonSchema = bodySchema\n ? z.toJSONSchema(bodySchema, { target: 'draft-2020-12' })\n : querySchema\n ? z.toJSONSchema(querySchema, { target: 'draft-2020-12' })\n : undefined;\n\n const outputJsonSchema = outputSchema\n ? z.toJSONSchema(outputSchema, { target: 'draft-2020-12' })\n : undefined;\n\n if (!inputJsonSchema) return undefined;\n\n const config = {\n bodyType: bodySchema ? 'json' : undefined,\n inputSchema: inputJsonSchema,\n output: outputJsonSchema\n ? { schema: outputJsonSchema, example: outputExample ?? {} }\n : undefined,\n };\n\n return { ...declareDiscoveryExtension(config as never) };\n}\n\n/**\n * Create a new route builder instance.\n */\nexport function createRouteBuilder(options?: RouteBuilderOptions) {\n return new RouteBuilder(undefined, options);\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/builder.js","../src/route-builder.ts"],"names":[],"mappings":"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","file":"/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/builder.js","sourcesContent":[null,"/**\n * Convenience route builder that composes telemetry + validation + x402 wrapping.\n * This is optional — servers can use withTelemetry directly.\n *\n * Import from '@agentcash/telemetry/builder'.\n * Requires peer deps: @x402/next, zod (^4), @x402/extensions\n */\n\nimport { type NextRequest, NextResponse } from 'next/server';\nimport { withX402 } from '@x402/next';\nimport { z, type ZodType } from 'zod';\nimport { declareDiscoveryExtension } from '@x402/extensions/bazaar';\nimport type { TelemetryContext } from './types';\nimport { extractRequestMeta, buildTelemetryContext, recordInvocation } from './telemetry-core';\n\nexport class HttpError extends Error {\n constructor(\n message: string,\n public status: number,\n ) {\n super(message);\n this.name = 'HttpError';\n }\n}\n\ntype AcceptsOption = {\n amount: string;\n network: string;\n asset?: string;\n};\n\ntype BuilderConfig<TBody = unknown, TQuery = unknown, TOutput = unknown> = {\n accepts: AcceptsOption[];\n bodySchema?: ZodType<TBody>;\n querySchema?: ZodType<TQuery>;\n outputSchema?: ZodType<TOutput>;\n outputExample?: TOutput;\n description?: string;\n};\n\ntype HandlerContext<TBody, TQuery> = {\n body: TBody;\n query: TQuery;\n request: NextRequest;\n telemetry: TelemetryContext;\n};\n\ntype HandlerFn<TBody, TQuery, TResponse> = (\n ctx: HandlerContext<TBody, TQuery>,\n) => Promise<TResponse>;\n\nfunction formatValidationError(error: import('zod').ZodError): string {\n const issues = error.issues;\n if (issues.length === 0) return 'Validation failed';\n\n if (issues.length === 1) {\n const issue = issues[0];\n const path = issue.path.join('.');\n const field = path || 'request';\n if (issue.message) return `${field}: ${issue.message}`;\n return `${field}: Invalid value`;\n }\n\n const errors = issues.map((issue) => {\n const path = issue.path.join('.');\n const field = path || 'request';\n return issue.message ? `${field}: ${issue.message}` : `${field}: Invalid`;\n });\n\n return `Validation failed: ${errors.join('; ')}`;\n}\n\nexport interface RouteBuilderOptions {\n /** The x402 resource server instance (from @x402/core/server). Required when using .price(). */\n x402Server?: unknown;\n}\n\nclass RouteBuilder<TBody = unknown, TQuery = unknown, TOutput = unknown> {\n private config: BuilderConfig<TBody, TQuery, TOutput>;\n private options: RouteBuilderOptions;\n\n constructor(\n config?: Partial<BuilderConfig<TBody, TQuery, TOutput>>,\n options?: RouteBuilderOptions,\n ) {\n this.config = { accepts: [], ...config };\n this.options = options ?? {};\n }\n\n price(amount: string, network: string, asset?: string) {\n return new RouteBuilder<TBody, TQuery, TOutput>(\n { ...this.config, accepts: [{ amount, network, asset }] },\n this.options,\n );\n }\n\n accepts(options: AcceptsOption[]) {\n return new RouteBuilder<TBody, TQuery, TOutput>(\n { ...this.config, accepts: options },\n this.options,\n );\n }\n\n body<T>(schema: ZodType<T>) {\n return new RouteBuilder<T, TQuery, TOutput>(\n { ...this.config, bodySchema: schema as ZodType<unknown> } as BuilderConfig<\n T,\n TQuery,\n TOutput\n >,\n this.options,\n );\n }\n\n query<T>(schema: ZodType<T>) {\n return new RouteBuilder<TBody, T, TOutput>(\n { ...this.config, querySchema: schema as ZodType<unknown> } as BuilderConfig<\n TBody,\n T,\n TOutput\n >,\n this.options,\n );\n }\n\n output<T>(schema: ZodType<T>, example?: T) {\n return new RouteBuilder<TBody, TQuery, T>(\n {\n ...this.config,\n outputSchema: schema as ZodType<unknown>,\n outputExample: example,\n } as BuilderConfig<TBody, TQuery, T>,\n this.options,\n );\n }\n\n description(text: string) {\n return new RouteBuilder<TBody, TQuery, TOutput>(\n { ...this.config, description: text },\n this.options,\n );\n }\n\n handler<TResponse>(fn: HandlerFn<TBody, TQuery, TResponse>) {\n const { accepts, bodySchema, querySchema, outputSchema, outputExample, description } =\n this.config;\n\n const coreHandler = async (request: NextRequest): Promise<NextResponse> => {\n const meta = extractRequestMeta(request);\n const ctx = buildTelemetryContext(meta);\n\n const log = (status: number, responseBody: string | null, resp: NextResponse) => {\n recordInvocation(meta, requestBodyString, {\n status,\n body: responseBody,\n headers: JSON.stringify(Object.fromEntries(resp.headers.entries())),\n contentType: resp.headers.get('content-type') ?? null,\n });\n };\n\n // Parse and validate body\n let body: TBody = undefined as TBody;\n let query: TQuery = undefined as TQuery;\n let requestBodyString: string | null = null;\n\n if (bodySchema) {\n let rawBody: unknown;\n try {\n rawBody = await request.json();\n requestBodyString = JSON.stringify(rawBody);\n } catch {\n const errorResp = NextResponse.json(\n { success: false, error: 'Invalid JSON body' },\n { status: 400 },\n );\n log(400, JSON.stringify({ success: false, error: 'Invalid JSON body' }), errorResp);\n return errorResp;\n }\n\n const parsed = bodySchema.safeParse(rawBody);\n if (!parsed.success) {\n const message = formatValidationError(parsed.error);\n const errorBody = {\n success: false,\n error: 'Validation failed',\n message,\n details: parsed.error.flatten(),\n };\n const errorResp = NextResponse.json(errorBody, { status: 400 });\n log(400, JSON.stringify(errorBody), errorResp);\n return errorResp;\n }\n body = parsed.data;\n }\n\n // Parse and validate query\n if (querySchema) {\n const searchParams = Object.fromEntries(request.nextUrl.searchParams);\n const parsed = querySchema.safeParse(searchParams);\n if (!parsed.success) {\n const message = formatValidationError(parsed.error);\n const errorBody = {\n success: false,\n error: 'Query validation failed',\n message,\n details: parsed.error.flatten(),\n };\n const errorResp = NextResponse.json(errorBody, { status: 400 });\n log(400, JSON.stringify(errorBody), errorResp);\n return errorResp;\n }\n query = parsed.data;\n }\n\n // Execute user handler\n let response: TResponse;\n try {\n response = await fn({ body, query, request, telemetry: ctx });\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n const status = error instanceof HttpError ? error.status : 500;\n const errorBody = { success: false, error: message };\n const errorResp = NextResponse.json(errorBody, { status });\n log(status, JSON.stringify(errorBody), errorResp);\n return errorResp;\n }\n\n // Detect { success: false } to prevent x402 settlement\n if (\n response &&\n typeof response === 'object' &&\n 'success' in response &&\n !(response as { success: boolean }).success\n ) {\n const errorResp = NextResponse.json(response, { status: 500 });\n log(500, JSON.stringify(response), errorResp);\n return errorResp;\n }\n\n // Success\n const successResp = NextResponse.json(response);\n let responseBodyString: string | null = null;\n try {\n responseBodyString = JSON.stringify(response);\n } catch {\n // Not serializable — that's fine\n }\n log(200, responseBodyString, successResp);\n return successResp;\n };\n\n // If no pricing, return the core handler directly (no x402 wrapping)\n if (accepts.length === 0) {\n return coreHandler;\n }\n\n // Wrap with x402\n const X402_BYPASS = process.env.X402_BYPASS === 'true';\n if (X402_BYPASS) {\n return coreHandler;\n }\n\n const X402_PAYEE_ADDRESS = process.env.X402_PAYEE_ADDRESS;\n if (!X402_PAYEE_ADDRESS) {\n throw new Error('X402_PAYEE_ADDRESS environment variable is required when using .price()');\n }\n\n const routeConfig = {\n description,\n accepts: accepts.map(({ amount, network, asset }) => ({\n scheme: 'exact' as const,\n network: network as `${string}:${string}`,\n price: amount,\n payTo: X402_PAYEE_ADDRESS,\n ...(asset && { extra: { asset } }),\n })),\n extensions: buildDiscoveryExtensions(bodySchema, querySchema, outputSchema, outputExample),\n };\n\n if (!this.options.x402Server) {\n throw new Error(\n 'x402Server is required when using .price(). Pass it to createRouteBuilder({ x402Server }).',\n );\n }\n\n return withX402(coreHandler, routeConfig, this.options.x402Server as never);\n }\n}\n\nfunction buildDiscoveryExtensions(\n bodySchema?: ZodType<unknown>,\n querySchema?: ZodType<unknown>,\n outputSchema?: ZodType<unknown>,\n outputExample?: unknown,\n): Record<string, unknown> | undefined {\n const inputJsonSchema = bodySchema\n ? z.toJSONSchema(bodySchema, { target: 'draft-2020-12' })\n : querySchema\n ? z.toJSONSchema(querySchema, { target: 'draft-2020-12' })\n : undefined;\n\n const outputJsonSchema = outputSchema\n ? z.toJSONSchema(outputSchema, { target: 'draft-2020-12' })\n : undefined;\n\n if (!inputJsonSchema) return undefined;\n\n const config = {\n bodyType: bodySchema ? 'json' : undefined,\n inputSchema: inputJsonSchema,\n output: outputJsonSchema\n ? { schema: outputJsonSchema, example: outputExample ?? {} }\n : undefined,\n };\n\n return { ...declareDiscoveryExtension(config as never) };\n}\n\n/**\n * Create a new route builder instance.\n */\nexport function createRouteBuilder(options?: RouteBuilderOptions) {\n return new RouteBuilder(undefined, options);\n}\n"]}
package/dist/builder.mjs CHANGED
@@ -2,8 +2,8 @@ import {
2
2
  buildTelemetryContext,
3
3
  extractRequestMeta,
4
4
  recordInvocation
5
- } from "./chunk-VOY67KA4.mjs";
6
- import "./chunk-O2PCP6KV.mjs";
5
+ } from "./chunk-PZWW7EMH.mjs";
6
+ import "./chunk-V553T6WE.mjs";
7
7
 
8
8
  // src/route-builder.ts
9
9
  import { NextResponse } from "next/server";
@@ -2,17 +2,17 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkP63MLKU3js = require('./chunk-P63MLKU3.js');
5
+ var _chunkQEJ7ZGGHjs = require('./chunk-QEJ7ZGGH.js');
6
6
 
7
7
  // src/init.ts
8
8
  var configuredOrigin;
9
9
  function initTelemetry(config) {
10
- _chunkP63MLKU3js.initClickhouse.call(void 0, config.clickhouse);
10
+ _chunkQEJ7ZGGHjs.initClickhouse.call(void 0, config.clickhouse);
11
11
  if (config.origin) {
12
12
  configuredOrigin = config.origin;
13
13
  }
14
14
  if (config.verify) {
15
- _chunkP63MLKU3js.pingClickhouse.call(void 0, );
15
+ _chunkQEJ7ZGGHjs.pingClickhouse.call(void 0, );
16
16
  }
17
17
  }
18
18
  function getOrigin() {
@@ -108,7 +108,7 @@ function recordInvocation(meta, requestBody, response) {
108
108
  response_body: response.body,
109
109
  created_at: /* @__PURE__ */ new Date()
110
110
  };
111
- _chunkP63MLKU3js.insertInvocation.call(void 0, invocation);
111
+ _chunkQEJ7ZGGHjs.insertInvocation.call(void 0, invocation);
112
112
  } catch (e4) {
113
113
  }
114
114
  }
@@ -146,4 +146,4 @@ function statusTextFromCode(code) {
146
146
 
147
147
 
148
148
  exports.initTelemetry = initTelemetry; exports.extractVerifiedWallet = extractVerifiedWallet; exports.extractRequestMeta = extractRequestMeta; exports.buildTelemetryContext = buildTelemetryContext; exports.recordInvocation = recordInvocation;
149
- //# sourceMappingURL=chunk-GE7VBMQP.js.map
149
+ //# sourceMappingURL=chunk-2EFPFJCB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/chunk-2EFPFJCB.js","../src/init.ts","../src/extract-wallet.ts","../src/telemetry-core.ts"],"names":[],"mappings":"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","file":"/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/chunk-2EFPFJCB.js","sourcesContent":[null,"import type { TelemetryConfig } from './types';\nimport { initClickhouse, pingClickhouse } from './clickhouse';\n\nlet configuredOrigin: string | undefined;\n\n/**\n * Initialize the telemetry package. Call once at module level.\n *\n * This is synchronous — createClient() does not connect until first query.\n *\n * IMPORTANT: On Vercel, instrumentation.ts runs in a separate module scope\n * from route handlers. Call this in the same module that imports your route\n * wrappers (withTelemetry, createRouteBuilder, etc.), NOT in instrumentation.ts.\n *\n * ```typescript\n * import { initTelemetry, withTelemetry } from '@agentcash/telemetry';\n *\n * initTelemetry({\n * clickhouse: {\n * url: process.env.TELEM_CLICKHOUSE_URL!,\n * database: process.env.TELEM_CLICKHOUSE_DATABASE,\n * username: process.env.TELEM_CLICKHOUSE_USERNAME,\n * password: process.env.TELEM_CLICKHOUSE_PASSWORD,\n * },\n * });\n * ```\n */\nexport function initTelemetry(config: TelemetryConfig): void {\n initClickhouse(config.clickhouse);\n if (config.origin) {\n configuredOrigin = config.origin;\n }\n if (config.verify) {\n pingClickhouse();\n }\n}\n\n/** Get the configured origin, or undefined if not set. */\nexport function getOrigin(): string | undefined {\n return configuredOrigin;\n}\n","/**\n * Extract verified wallet address from x402 payment headers.\n *\n * Checks multiple sources in priority order:\n * 1. x-payer-address — injected by @x402/next's withX402 after verification (highest confidence)\n * 2. PAYMENT-SIGNATURE / X-PAYMENT — decode the payment header directly\n *\n * If the handler is executing, withX402 has already verified the payment signature.\n * For manual x402 flows, the app verifies before calling business logic.\n * Either way, the header content is trustworthy when this runs.\n */\nexport function extractVerifiedWallet(headers: Headers): string | null {\n try {\n // 1. x-payer-address: injected by @x402/next's withX402 after verification\n const payerAddress = headers.get('x-payer-address');\n if (payerAddress) {\n return payerAddress.toLowerCase();\n }\n\n // 2. Decode from PAYMENT-SIGNATURE or X-PAYMENT header\n const paymentHeader =\n headers.get('PAYMENT-SIGNATURE') ??\n headers.get('payment-signature') ??\n headers.get('X-PAYMENT') ??\n headers.get('x-payment');\n\n if (!paymentHeader) return null;\n\n // Decode the base64 payment header to extract the payer address.\n // The header is a base64-encoded JSON object with the structure:\n // { payload: { authorization: { from: \"0x...\" }, signature: \"0x...\" } }\n try {\n const decoded = JSON.parse(Buffer.from(paymentHeader, 'base64').toString()) as {\n payload?: { authorization?: { from?: string }; from?: string };\n };\n const from = decoded?.payload?.authorization?.from ?? decoded?.payload?.from;\n return typeof from === 'string' ? from.toLowerCase() : null;\n } catch {\n return null;\n }\n } catch {\n return null;\n }\n}\n","/**\n * Shared telemetry primitives used by withTelemetry and the route builder.\n * Extracts request metadata, builds telemetry context, and records invocations.\n */\n\nimport { type NextRequest } from 'next/server';\nimport { randomUUID } from 'crypto';\nimport type { TelemetryContext, McpResourceInvocation, RequestMeta } from './types';\nimport { insertInvocation } from './clickhouse';\nimport { extractVerifiedWallet } from './extract-wallet';\nimport { getOrigin } from './init';\n\n/**\n * Extract identity headers, route info, and verified wallet from a request.\n * All wrapped in try/catch — returns safe defaults on failure.\n */\nexport function extractRequestMeta(request: NextRequest): RequestMeta {\n const meta: RequestMeta = {\n requestId: randomUUID(),\n startTime: Date.now(),\n walletAddress: null,\n clientId: null,\n sessionId: null,\n verifiedWallet: null,\n route: '',\n method: '',\n origin: '',\n referer: null,\n requestContentType: null,\n requestHeadersJson: null,\n };\n\n try {\n meta.walletAddress = request.headers.get('X-Wallet-Address')?.toLowerCase() ?? null;\n meta.clientId = request.headers.get('X-Client-ID') ?? null;\n meta.sessionId = request.headers.get('X-Session-ID') ?? null;\n meta.referer = request.headers.get('Referer') ?? null;\n meta.requestContentType = request.headers.get('content-type') ?? null;\n meta.route = request.nextUrl.pathname;\n meta.method = request.method;\n meta.origin = getOrigin() ?? request.nextUrl.origin;\n meta.verifiedWallet = extractVerifiedWallet(request.headers);\n meta.requestHeadersJson = JSON.stringify(Object.fromEntries(request.headers.entries()));\n } catch {\n // Header extraction failed — continue with defaults\n }\n\n return meta;\n}\n\n/**\n * Build a TelemetryContext from extracted request metadata.\n * setVerifiedWallet mutates meta.verifiedWallet so recordInvocation sees the update.\n */\nexport function buildTelemetryContext(meta: RequestMeta): TelemetryContext {\n const ctx: TelemetryContext = {\n walletAddress: meta.walletAddress,\n clientId: meta.clientId,\n sessionId: meta.sessionId,\n verifiedWallet: meta.verifiedWallet,\n setVerifiedWallet: (address: string) => {\n meta.verifiedWallet = address.toLowerCase();\n ctx.verifiedWallet = meta.verifiedWallet;\n },\n };\n return ctx;\n}\n\n/**\n * Record an invocation to ClickHouse. Fire-and-forget, fully wrapped in try/catch.\n */\nexport function recordInvocation(\n meta: RequestMeta,\n requestBody: string | null,\n response: {\n status: number;\n body: string | null;\n headers: string | null;\n contentType: string | null;\n },\n): void {\n try {\n const invocation: McpResourceInvocation = {\n id: meta.requestId,\n x_wallet_address: meta.walletAddress,\n x_client_id: meta.clientId,\n session_id: meta.sessionId,\n verified_wallet_address: meta.verifiedWallet,\n method: meta.method,\n route: meta.route,\n origin: meta.origin,\n referer: meta.referer,\n request_content_type: meta.requestContentType,\n request_headers: meta.requestHeadersJson,\n request_body: requestBody,\n status_code: response.status,\n status_text: statusTextFromCode(response.status),\n duration: Date.now() - meta.startTime,\n response_content_type: response.contentType,\n response_headers: response.headers,\n response_body: response.body,\n created_at: new Date(),\n };\n insertInvocation(invocation);\n } catch {\n // Never affects the response\n }\n}\n\nfunction statusTextFromCode(code: number): string {\n switch (code) {\n case 200:\n return 'OK';\n case 201:\n return 'Created';\n case 204:\n return 'No Content';\n case 400:\n return 'Bad Request';\n case 401:\n return 'Unauthorized';\n case 402:\n return 'Payment Required';\n case 403:\n return 'Forbidden';\n case 404:\n return 'Not Found';\n case 500:\n return 'Internal Server Error';\n case 504:\n return 'Gateway Timeout';\n default:\n return String(code);\n }\n}\n"]}
@@ -2,14 +2,14 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkGE7VBMQPjs = require('./chunk-GE7VBMQP.js');
5
+ var _chunk2EFPFJCBjs = require('./chunk-2EFPFJCB.js');
6
6
 
7
7
  // src/telemetry.ts
8
8
  var _server = require('next/server');
9
9
  function withTelemetry(handler) {
10
10
  return async (request) => {
11
- const meta = _chunkGE7VBMQPjs.extractRequestMeta.call(void 0, request);
12
- const ctx = _chunkGE7VBMQPjs.buildTelemetryContext.call(void 0, meta);
11
+ const meta = _chunk2EFPFJCBjs.extractRequestMeta.call(void 0, request);
12
+ const ctx = _chunk2EFPFJCBjs.buildTelemetryContext.call(void 0, meta);
13
13
  let requestBodyString = null;
14
14
  if (meta.method === "POST" || meta.method === "PUT" || meta.method === "PATCH") {
15
15
  try {
@@ -36,12 +36,14 @@ function withTelemetry(handler) {
36
36
  responseBodyString = await response.clone().text();
37
37
  } catch (e2) {
38
38
  }
39
- _chunkGE7VBMQPjs.recordInvocation.call(void 0, meta, requestBodyString, {
40
- status: response.status,
41
- body: responseBodyString,
42
- headers: JSON.stringify(Object.fromEntries(response.headers.entries())),
43
- contentType: _nullishCoalesce(response.headers.get("content-type"), () => ( null))
44
- });
39
+ if (response.status !== 402) {
40
+ _chunk2EFPFJCBjs.recordInvocation.call(void 0, meta, requestBodyString, {
41
+ status: response.status,
42
+ body: responseBodyString,
43
+ headers: JSON.stringify(Object.fromEntries(response.headers.entries())),
44
+ contentType: _nullishCoalesce(response.headers.get("content-type"), () => ( null))
45
+ });
46
+ }
45
47
  if (handlerError && !(handlerError instanceof _server.NextResponse)) {
46
48
  throw handlerError;
47
49
  }
@@ -52,4 +54,4 @@ function withTelemetry(handler) {
52
54
 
53
55
 
54
56
  exports.withTelemetry = withTelemetry;
55
- //# sourceMappingURL=chunk-FJ3YM6KO.js.map
57
+ //# sourceMappingURL=chunk-CIAESB2C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/chunk-CIAESB2C.js","../src/telemetry.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACF,sDAA4B;AAC5B;AACA;ACAA,qCAA+C;AAcxC,SAAS,aAAA,CAAc,OAAA,EAA2B;AACvD,EAAA,OAAO,MAAA,CAAO,OAAA,EAAA,GAAgD;AAC5D,IAAA,MAAM,KAAA,EAAO,iDAAA,OAA0B,CAAA;AACvC,IAAA,MAAM,IAAA,EAAM,oDAAA,IAA0B,CAAA;AAGtC,IAAA,IAAI,kBAAA,EAAmC,IAAA;AACvC,IAAA,GAAA,CAAI,IAAA,CAAK,OAAA,IAAW,OAAA,GAAU,IAAA,CAAK,OAAA,IAAW,MAAA,GAAS,IAAA,CAAK,OAAA,IAAW,OAAA,EAAS;AAC9E,MAAA,IAAI;AACF,QAAA,MAAM,KAAA,EAAO,MAAM,OAAA,CAAQ,KAAA,CAAM,CAAA,CAAE,IAAA,CAAK,CAAA;AACxC,QAAA,GAAA,CAAI,IAAA,EAAM,kBAAA,EAAoB,IAAA;AAAA,MAChC,EAAA,UAAQ;AAAA,MAER;AAAA,IACF;AAGA,IAAA,IAAI,QAAA;AACJ,IAAA,IAAI,aAAA,EAAwB,IAAA;AAE5B,IAAA,IAAI;AACF,MAAA,SAAA,EAAW,MAAM,OAAA,CAAQ,OAAA,EAAS,GAAG,CAAA;AAAA,IACvC,EAAA,MAAA,CAAS,KAAA,EAAgB;AACvB,MAAA,aAAA,EAAe,KAAA;AACf,MAAA,GAAA,CAAI,MAAA,WAAiB,oBAAA,EAAc;AACjC,QAAA,SAAA,EAAW,KAAA;AAAA,MACb,EAAA,KAAO;AACL,QAAA,MAAM,QAAA,EAAU,MAAA,WAAiB,MAAA,EAAQ,KAAA,CAAM,QAAA,EAAU,uBAAA;AACzD,QAAA,SAAA,EAAW,oBAAA,CAAa,IAAA,CAAK,EAAE,OAAA,EAAS,KAAA,EAAO,KAAA,EAAO,QAAQ,CAAA,EAAG,EAAE,MAAA,EAAQ,IAAI,CAAC,CAAA;AAAA,MAClF;AAAA,IACF;AAGA,IAAA,IAAI,mBAAA,EAAoC,IAAA;AACxC,IAAA,IAAI;AACF,MAAA,mBAAA,EAAqB,MAAM,QAAA,CAAS,KAAA,CAAM,CAAA,CAAE,IAAA,CAAK,CAAA;AAAA,IACnD,EAAA,WAAQ;AAAA,IAER;AAGA,IAAA,GAAA,CAAI,QAAA,CAAS,OAAA,IAAW,GAAA,EAAK;AAC3B,MAAA,+CAAA,IAAiB,EAAM,iBAAA,EAAmB;AAAA,QACxC,MAAA,EAAQ,QAAA,CAAS,MAAA;AAAA,QACjB,IAAA,EAAM,kBAAA;AAAA,QACN,OAAA,EAAS,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,WAAA,CAAY,QAAA,CAAS,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAC,CAAA;AAAA,QACtE,WAAA,mBAAa,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,UAAK;AAAA,MACvD,CAAC,CAAA;AAAA,IACH;AAGA,IAAA,GAAA,CAAI,aAAA,GAAgB,CAAA,CAAE,aAAA,WAAwB,oBAAA,CAAA,EAAe;AAC3D,MAAA,MAAM,YAAA;AAAA,IACR;AAEA,IAAA,OAAO,QAAA;AAAA,EACT,CAAA;AACF;ADzBA;AACA;AACE;AACF,sCAAC","file":"/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/chunk-CIAESB2C.js","sourcesContent":[null,"/**\n * Core telemetry wrapper for Next.js route handlers.\n * Extracts identity headers, logs to ClickHouse, extracts verified wallet.\n * This is a passive observer — it never influences the response.\n */\n\nimport { type NextRequest, NextResponse } from 'next/server';\nimport type { TelemetryContext } from './types';\nimport { extractRequestMeta, buildTelemetryContext, recordInvocation } from './telemetry-core';\n\ntype TelemetryHandler = (request: NextRequest, ctx: TelemetryContext) => Promise<NextResponse>;\n\n/**\n * Wrap a Next.js route handler with telemetry.\n * Extracts identity headers, logs the invocation to ClickHouse,\n * and auto-extracts verified wallet from x402 payment headers.\n *\n * The entire telemetry code path is wrapped in try/catch.\n * Telemetry failures never affect the response.\n */\nexport function withTelemetry(handler: TelemetryHandler) {\n return async (request: NextRequest): Promise<NextResponse> => {\n const meta = extractRequestMeta(request);\n const ctx = buildTelemetryContext(meta);\n\n // Capture request body for logging (only for methods with bodies)\n let requestBodyString: string | null = null;\n if (meta.method === 'POST' || meta.method === 'PUT' || meta.method === 'PATCH') {\n try {\n const body = await request.clone().text();\n if (body) requestBodyString = body;\n } catch {\n // Body read failed — that's fine\n }\n }\n\n // Execute the actual handler\n let response: NextResponse;\n let handlerError: unknown = null;\n\n try {\n response = await handler(request, ctx);\n } catch (error: unknown) {\n handlerError = error;\n if (error instanceof NextResponse) {\n response = error;\n } else {\n const message = error instanceof Error ? error.message : 'Internal server error';\n response = NextResponse.json({ success: false, error: message }, { status: 500 });\n }\n }\n\n // Log to ClickHouse (fire-and-forget)\n let responseBodyString: string | null = null;\n try {\n responseBodyString = await response.clone().text();\n } catch {\n // Response body read failed — that's fine\n }\n\n // 402 is the x402/MPP payment challenge — not a real invocation, skip logging\n if (response.status !== 402) {\n recordInvocation(meta, requestBodyString, {\n status: response.status,\n body: responseBodyString,\n headers: JSON.stringify(Object.fromEntries(response.headers.entries())),\n contentType: response.headers.get('content-type') ?? null,\n });\n }\n\n // Re-throw the original error if it wasn't a NextResponse\n if (handlerError && !(handlerError instanceof NextResponse)) {\n throw handlerError;\n }\n\n return response;\n };\n}\n"]}
@@ -2,7 +2,7 @@ import {
2
2
  initClickhouse,
3
3
  insertInvocation,
4
4
  pingClickhouse
5
- } from "./chunk-O2PCP6KV.mjs";
5
+ } from "./chunk-V553T6WE.mjs";
6
6
 
7
7
  // src/init.ts
8
8
  var configuredOrigin;
@@ -146,4 +146,4 @@ export {
146
146
  buildTelemetryContext,
147
147
  recordInvocation
148
148
  };
149
- //# sourceMappingURL=chunk-VOY67KA4.mjs.map
149
+ //# sourceMappingURL=chunk-PZWW7EMH.mjs.map
@@ -7,7 +7,12 @@ function initClickhouse(config) {
7
7
  url: config.url,
8
8
  database: _nullishCoalesce(config.database, () => ( "default")),
9
9
  username: _nullishCoalesce(config.username, () => ( "default")),
10
- password: _nullishCoalesce(config.password, () => ( ""))
10
+ password: _nullishCoalesce(config.password, () => ( "")),
11
+ // Serverless-safe defaults: disable keep-alive to prevent stale pooled
12
+ // sockets after Lambda freeze/thaw, and shorten the request timeout since
13
+ // inserts are fire-and-forget (a fast failure is fine).
14
+ keep_alive: { enabled: false },
15
+ request_timeout: 5e3
11
16
  });
12
17
  }
13
18
  function pingClickhouse() {
@@ -57,4 +62,4 @@ function insertInvocation(data) {
57
62
 
58
63
 
59
64
  exports.initClickhouse = initClickhouse; exports.pingClickhouse = pingClickhouse; exports.insertInvocation = insertInvocation;
60
- //# sourceMappingURL=chunk-P63MLKU3.js.map
65
+ //# sourceMappingURL=chunk-QEJ7ZGGH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/chunk-QEJ7ZGGH.js","../src/clickhouse.ts"],"names":[],"mappings":"AAAA;ACAA,4CAA6B;AAG7B,IAAI,iBAAA,EAA2D,IAAA;AAE/D,IAAM,MAAA,EAAQ,0BAAA;AAMP,SAAS,cAAA,CAAe,MAAA,EAA6C;AAC1E,EAAA,iBAAA,EAAmB,kCAAA;AAAa,IAC9B,GAAA,EAAK,MAAA,CAAO,GAAA;AAAA,IACZ,QAAA,mBAAU,MAAA,CAAO,QAAA,UAAY,WAAA;AAAA,IAC7B,QAAA,mBAAU,MAAA,CAAO,QAAA,UAAY,WAAA;AAAA,IAC7B,QAAA,mBAAU,MAAA,CAAO,QAAA,UAAY,IAAA;AAAA;AAAA;AAAA;AAAA,IAI7B,UAAA,EAAY,EAAE,OAAA,EAAS,MAAM,CAAA;AAAA,IAC7B,eAAA,EAAiB;AAAA,EACnB,CAAC,CAAA;AACH;AAKO,SAAS,cAAA,CAAA,EAAuB;AACrC,EAAA,GAAA,CAAI,CAAC,gBAAA,EAAkB;AACrB,IAAA,OAAA,CAAQ,KAAA,CAAM,+DAA+D,CAAA;AAC7E,IAAA,MAAA;AAAA,EACF;AACA,EAAA,gBAAA,CACG,IAAA,CAAK,CAAA,CACL,IAAA,CAAK,CAAC,MAAA,EAAA,GAAW;AAChB,IAAA,GAAA,CAAI,MAAA,CAAO,OAAA,EAAS;AAClB,MAAA,OAAA,CAAQ,GAAA,CAAI,kCAAkC,CAAA;AAAA,IAChD,EAAA,KAAO;AACL,MAAA,OAAA,CAAQ,KAAA,CAAM,oCAAoC,CAAA;AAAA,IACpD;AAAA,EACF,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,KAAA,EAAA,GAAmB;AACzB,IAAA,MAAM,QAAA,EAAU,MAAA,WAAiB,MAAA,EAAQ,KAAA,CAAM,QAAA,EAAU,MAAA,CAAO,KAAK,CAAA;AACrE,IAAA,OAAA,CAAQ,KAAA,CAAM,qCAAA,EAAuC,OAAO,CAAA;AAAA,EAC9D,CAAC,CAAA;AACL;AAMO,SAAS,gBAAA,CAAiB,IAAA,EAAmC;AAClE,EAAA,IAAI;AACF,IAAA,GAAA,CAAI,CAAC,gBAAA,EAAkB;AACrB,MAAA,OAAA,CAAQ,KAAA,CAAM,4EAA4E,CAAA;AAC1F,MAAA,MAAA;AAAA,IACF;AAGA,IAAA,gBAAA,CACG,MAAA,CAA8B;AAAA,MAC7B,KAAA,EAAO,KAAA;AAAA,MACP,MAAA,EAAQ,CAAC,IAAI,CAAA;AAAA,MACb,MAAA,EAAQ;AAAA,IACV,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,KAAA,EAAA,GAAmB;AACzB,MAAA,IAAI;AACF,QAAA,MAAM,QAAA,EAAU,MAAA,WAAiB,MAAA,EAAQ,KAAA,CAAM,QAAA,EAAU,MAAA,CAAO,KAAK,CAAA;AACrE,QAAA,OAAA,CAAQ,KAAA,CAAM,uCAAA,EAAyC,OAAO,CAAA;AAAA,MAChE,EAAA,UAAQ;AAAA,MAER;AAAA,IACF,CAAC,CAAA;AAAA,EACL,EAAA,MAAA,CAAS,KAAA,EAAgB;AACvB,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,EAAU,MAAA,WAAiB,MAAA,EAAQ,KAAA,CAAM,QAAA,EAAU,MAAA,CAAO,KAAK,CAAA;AACrE,MAAA,OAAA,CAAQ,KAAA,CAAM,oDAAA,EAAsD,OAAO,CAAA;AAAA,IAC7E,EAAA,WAAQ;AAAA,IAER;AAAA,EACF;AACF;ADxBA;AACA;AACE;AACA;AACA;AACF,8HAAC","file":"/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/chunk-QEJ7ZGGH.js","sourcesContent":[null,"import { createClient } from '@clickhouse/client';\nimport type { McpResourceInvocation, TelemetryConfig } from './types';\n\nlet clickhouseClient: ReturnType<typeof createClient> | null = null;\n\nconst TABLE = 'mcp_resource_invocations';\n\n/**\n * Initialize the ClickHouse client singleton.\n * createClient() is synchronous — no async needed.\n */\nexport function initClickhouse(config: TelemetryConfig['clickhouse']): void {\n clickhouseClient = createClient({\n url: config.url,\n database: config.database ?? 'default',\n username: config.username ?? 'default',\n password: config.password ?? '',\n // Serverless-safe defaults: disable keep-alive to prevent stale pooled\n // sockets after Lambda freeze/thaw, and shorten the request timeout since\n // inserts are fire-and-forget (a fast failure is fine).\n keep_alive: { enabled: false },\n request_timeout: 5_000,\n });\n}\n\n/**\n * Ping ClickHouse to verify the connection. Fire-and-forget, logs result.\n */\nexport function pingClickhouse(): void {\n if (!clickhouseClient) {\n console.error('[telemetry] Cannot verify: ClickHouse client not initialized.');\n return;\n }\n clickhouseClient\n .ping()\n .then((result) => {\n if (result.success) {\n console.log('[telemetry] ClickHouse connected');\n } else {\n console.error('[telemetry] ClickHouse ping failed');\n }\n })\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error('[telemetry] ClickHouse ping failed:', message);\n });\n}\n\n/**\n * Fire-and-forget insert into mcp_resource_invocations.\n * Wrapped in try/catch — never throws, never blocks.\n */\nexport function insertInvocation(data: McpResourceInvocation): void {\n try {\n if (!clickhouseClient) {\n console.error('[telemetry] ClickHouse client not initialized. Call initTelemetry() first.');\n return;\n }\n\n // Fire and forget — do NOT await\n clickhouseClient\n .insert<McpResourceInvocation>({\n table: TABLE,\n values: [data],\n format: 'JSONEachRow',\n })\n .catch((error: unknown) => {\n try {\n const message = error instanceof Error ? error.message : String(error);\n console.error('[telemetry] ClickHouse insert failed:', message);\n } catch {\n // Absolutely nothing escapes\n }\n });\n } catch (error: unknown) {\n try {\n const message = error instanceof Error ? error.message : String(error);\n console.error('[telemetry] ClickHouse insert threw synchronously:', message);\n } catch {\n // Absolutely nothing escapes\n }\n }\n}\n"]}
@@ -7,7 +7,12 @@ function initClickhouse(config) {
7
7
  url: config.url,
8
8
  database: config.database ?? "default",
9
9
  username: config.username ?? "default",
10
- password: config.password ?? ""
10
+ password: config.password ?? "",
11
+ // Serverless-safe defaults: disable keep-alive to prevent stale pooled
12
+ // sockets after Lambda freeze/thaw, and shorten the request timeout since
13
+ // inserts are fire-and-forget (a fast failure is fine).
14
+ keep_alive: { enabled: false },
15
+ request_timeout: 5e3
11
16
  });
12
17
  }
13
18
  function pingClickhouse() {
@@ -57,4 +62,4 @@ export {
57
62
  pingClickhouse,
58
63
  insertInvocation
59
64
  };
60
- //# sourceMappingURL=chunk-O2PCP6KV.mjs.map
65
+ //# sourceMappingURL=chunk-V553T6WE.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/clickhouse.ts"],"sourcesContent":["import { createClient } from '@clickhouse/client';\nimport type { McpResourceInvocation, TelemetryConfig } from './types';\n\nlet clickhouseClient: ReturnType<typeof createClient> | null = null;\n\nconst TABLE = 'mcp_resource_invocations';\n\n/**\n * Initialize the ClickHouse client singleton.\n * createClient() is synchronous — no async needed.\n */\nexport function initClickhouse(config: TelemetryConfig['clickhouse']): void {\n clickhouseClient = createClient({\n url: config.url,\n database: config.database ?? 'default',\n username: config.username ?? 'default',\n password: config.password ?? '',\n // Serverless-safe defaults: disable keep-alive to prevent stale pooled\n // sockets after Lambda freeze/thaw, and shorten the request timeout since\n // inserts are fire-and-forget (a fast failure is fine).\n keep_alive: { enabled: false },\n request_timeout: 5_000,\n });\n}\n\n/**\n * Ping ClickHouse to verify the connection. Fire-and-forget, logs result.\n */\nexport function pingClickhouse(): void {\n if (!clickhouseClient) {\n console.error('[telemetry] Cannot verify: ClickHouse client not initialized.');\n return;\n }\n clickhouseClient\n .ping()\n .then((result) => {\n if (result.success) {\n console.log('[telemetry] ClickHouse connected');\n } else {\n console.error('[telemetry] ClickHouse ping failed');\n }\n })\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error('[telemetry] ClickHouse ping failed:', message);\n });\n}\n\n/**\n * Fire-and-forget insert into mcp_resource_invocations.\n * Wrapped in try/catch — never throws, never blocks.\n */\nexport function insertInvocation(data: McpResourceInvocation): void {\n try {\n if (!clickhouseClient) {\n console.error('[telemetry] ClickHouse client not initialized. Call initTelemetry() first.');\n return;\n }\n\n // Fire and forget — do NOT await\n clickhouseClient\n .insert<McpResourceInvocation>({\n table: TABLE,\n values: [data],\n format: 'JSONEachRow',\n })\n .catch((error: unknown) => {\n try {\n const message = error instanceof Error ? error.message : String(error);\n console.error('[telemetry] ClickHouse insert failed:', message);\n } catch {\n // Absolutely nothing escapes\n }\n });\n } catch (error: unknown) {\n try {\n const message = error instanceof Error ? error.message : String(error);\n console.error('[telemetry] ClickHouse insert threw synchronously:', message);\n } catch {\n // Absolutely nothing escapes\n }\n }\n}\n"],"mappings":";AAAA,SAAS,oBAAoB;AAG7B,IAAI,mBAA2D;AAE/D,IAAM,QAAQ;AAMP,SAAS,eAAe,QAA6C;AAC1E,qBAAmB,aAAa;AAAA,IAC9B,KAAK,OAAO;AAAA,IACZ,UAAU,OAAO,YAAY;AAAA,IAC7B,UAAU,OAAO,YAAY;AAAA,IAC7B,UAAU,OAAO,YAAY;AAAA;AAAA;AAAA;AAAA,IAI7B,YAAY,EAAE,SAAS,MAAM;AAAA,IAC7B,iBAAiB;AAAA,EACnB,CAAC;AACH;AAKO,SAAS,iBAAuB;AACrC,MAAI,CAAC,kBAAkB;AACrB,YAAQ,MAAM,+DAA+D;AAC7E;AAAA,EACF;AACA,mBACG,KAAK,EACL,KAAK,CAAC,WAAW;AAChB,QAAI,OAAO,SAAS;AAClB,cAAQ,IAAI,kCAAkC;AAAA,IAChD,OAAO;AACL,cAAQ,MAAM,oCAAoC;AAAA,IACpD;AAAA,EACF,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAQ,MAAM,uCAAuC,OAAO;AAAA,EAC9D,CAAC;AACL;AAMO,SAAS,iBAAiB,MAAmC;AAClE,MAAI;AACF,QAAI,CAAC,kBAAkB;AACrB,cAAQ,MAAM,4EAA4E;AAC1F;AAAA,IACF;AAGA,qBACG,OAA8B;AAAA,MAC7B,OAAO;AAAA,MACP,QAAQ,CAAC,IAAI;AAAA,MACb,QAAQ;AAAA,IACV,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,UAAI;AACF,cAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,gBAAQ,MAAM,yCAAyC,OAAO;AAAA,MAChE,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAAA,EACL,SAAS,OAAgB;AACvB,QAAI;AACF,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,cAAQ,MAAM,sDAAsD,OAAO;AAAA,IAC7E,QAAQ;AAAA,IAER;AAAA,EACF;AACF;","names":[]}
@@ -2,7 +2,7 @@ import {
2
2
  buildTelemetryContext,
3
3
  extractRequestMeta,
4
4
  recordInvocation
5
- } from "./chunk-VOY67KA4.mjs";
5
+ } from "./chunk-PZWW7EMH.mjs";
6
6
 
7
7
  // src/telemetry.ts
8
8
  import { NextResponse } from "next/server";
@@ -36,12 +36,14 @@ function withTelemetry(handler) {
36
36
  responseBodyString = await response.clone().text();
37
37
  } catch {
38
38
  }
39
- recordInvocation(meta, requestBodyString, {
40
- status: response.status,
41
- body: responseBodyString,
42
- headers: JSON.stringify(Object.fromEntries(response.headers.entries())),
43
- contentType: response.headers.get("content-type") ?? null
44
- });
39
+ if (response.status !== 402) {
40
+ recordInvocation(meta, requestBodyString, {
41
+ status: response.status,
42
+ body: responseBodyString,
43
+ headers: JSON.stringify(Object.fromEntries(response.headers.entries())),
44
+ contentType: response.headers.get("content-type") ?? null
45
+ });
46
+ }
45
47
  if (handlerError && !(handlerError instanceof NextResponse)) {
46
48
  throw handlerError;
47
49
  }
@@ -52,4 +54,4 @@ function withTelemetry(handler) {
52
54
  export {
53
55
  withTelemetry
54
56
  };
55
- //# sourceMappingURL=chunk-JVLKV7CX.mjs.map
57
+ //# sourceMappingURL=chunk-YAHKVH4T.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/telemetry.ts"],"sourcesContent":["/**\n * Core telemetry wrapper for Next.js route handlers.\n * Extracts identity headers, logs to ClickHouse, extracts verified wallet.\n * This is a passive observer — it never influences the response.\n */\n\nimport { type NextRequest, NextResponse } from 'next/server';\nimport type { TelemetryContext } from './types';\nimport { extractRequestMeta, buildTelemetryContext, recordInvocation } from './telemetry-core';\n\ntype TelemetryHandler = (request: NextRequest, ctx: TelemetryContext) => Promise<NextResponse>;\n\n/**\n * Wrap a Next.js route handler with telemetry.\n * Extracts identity headers, logs the invocation to ClickHouse,\n * and auto-extracts verified wallet from x402 payment headers.\n *\n * The entire telemetry code path is wrapped in try/catch.\n * Telemetry failures never affect the response.\n */\nexport function withTelemetry(handler: TelemetryHandler) {\n return async (request: NextRequest): Promise<NextResponse> => {\n const meta = extractRequestMeta(request);\n const ctx = buildTelemetryContext(meta);\n\n // Capture request body for logging (only for methods with bodies)\n let requestBodyString: string | null = null;\n if (meta.method === 'POST' || meta.method === 'PUT' || meta.method === 'PATCH') {\n try {\n const body = await request.clone().text();\n if (body) requestBodyString = body;\n } catch {\n // Body read failed — that's fine\n }\n }\n\n // Execute the actual handler\n let response: NextResponse;\n let handlerError: unknown = null;\n\n try {\n response = await handler(request, ctx);\n } catch (error: unknown) {\n handlerError = error;\n if (error instanceof NextResponse) {\n response = error;\n } else {\n const message = error instanceof Error ? error.message : 'Internal server error';\n response = NextResponse.json({ success: false, error: message }, { status: 500 });\n }\n }\n\n // Log to ClickHouse (fire-and-forget)\n let responseBodyString: string | null = null;\n try {\n responseBodyString = await response.clone().text();\n } catch {\n // Response body read failed — that's fine\n }\n\n recordInvocation(meta, requestBodyString, {\n status: response.status,\n body: responseBodyString,\n headers: JSON.stringify(Object.fromEntries(response.headers.entries())),\n contentType: response.headers.get('content-type') ?? null,\n });\n\n // Re-throw the original error if it wasn't a NextResponse\n if (handlerError && !(handlerError instanceof NextResponse)) {\n throw handlerError;\n }\n\n return response;\n };\n}\n"],"mappings":";;;;;;;AAMA,SAA2B,oBAAoB;AAcxC,SAAS,cAAc,SAA2B;AACvD,SAAO,OAAO,YAAgD;AAC5D,UAAM,OAAO,mBAAmB,OAAO;AACvC,UAAM,MAAM,sBAAsB,IAAI;AAGtC,QAAI,oBAAmC;AACvC,QAAI,KAAK,WAAW,UAAU,KAAK,WAAW,SAAS,KAAK,WAAW,SAAS;AAC9E,UAAI;AACF,cAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,KAAK;AACxC,YAAI,KAAM,qBAAoB;AAAA,MAChC,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,QAAI;AACJ,QAAI,eAAwB;AAE5B,QAAI;AACF,iBAAW,MAAM,QAAQ,SAAS,GAAG;AAAA,IACvC,SAAS,OAAgB;AACvB,qBAAe;AACf,UAAI,iBAAiB,cAAc;AACjC,mBAAW;AAAA,MACb,OAAO;AACL,cAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,mBAAW,aAAa,KAAK,EAAE,SAAS,OAAO,OAAO,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MAClF;AAAA,IACF;AAGA,QAAI,qBAAoC;AACxC,QAAI;AACF,2BAAqB,MAAM,SAAS,MAAM,EAAE,KAAK;AAAA,IACnD,QAAQ;AAAA,IAER;AAEA,qBAAiB,MAAM,mBAAmB;AAAA,MACxC,QAAQ,SAAS;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,KAAK,UAAU,OAAO,YAAY,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAAA,MACtE,aAAa,SAAS,QAAQ,IAAI,cAAc,KAAK;AAAA,IACvD,CAAC;AAGD,QAAI,gBAAgB,EAAE,wBAAwB,eAAe;AAC3D,YAAM;AAAA,IACR;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/telemetry.ts"],"sourcesContent":["/**\n * Core telemetry wrapper for Next.js route handlers.\n * Extracts identity headers, logs to ClickHouse, extracts verified wallet.\n * This is a passive observer — it never influences the response.\n */\n\nimport { type NextRequest, NextResponse } from 'next/server';\nimport type { TelemetryContext } from './types';\nimport { extractRequestMeta, buildTelemetryContext, recordInvocation } from './telemetry-core';\n\ntype TelemetryHandler = (request: NextRequest, ctx: TelemetryContext) => Promise<NextResponse>;\n\n/**\n * Wrap a Next.js route handler with telemetry.\n * Extracts identity headers, logs the invocation to ClickHouse,\n * and auto-extracts verified wallet from x402 payment headers.\n *\n * The entire telemetry code path is wrapped in try/catch.\n * Telemetry failures never affect the response.\n */\nexport function withTelemetry(handler: TelemetryHandler) {\n return async (request: NextRequest): Promise<NextResponse> => {\n const meta = extractRequestMeta(request);\n const ctx = buildTelemetryContext(meta);\n\n // Capture request body for logging (only for methods with bodies)\n let requestBodyString: string | null = null;\n if (meta.method === 'POST' || meta.method === 'PUT' || meta.method === 'PATCH') {\n try {\n const body = await request.clone().text();\n if (body) requestBodyString = body;\n } catch {\n // Body read failed — that's fine\n }\n }\n\n // Execute the actual handler\n let response: NextResponse;\n let handlerError: unknown = null;\n\n try {\n response = await handler(request, ctx);\n } catch (error: unknown) {\n handlerError = error;\n if (error instanceof NextResponse) {\n response = error;\n } else {\n const message = error instanceof Error ? error.message : 'Internal server error';\n response = NextResponse.json({ success: false, error: message }, { status: 500 });\n }\n }\n\n // Log to ClickHouse (fire-and-forget)\n let responseBodyString: string | null = null;\n try {\n responseBodyString = await response.clone().text();\n } catch {\n // Response body read failed — that's fine\n }\n\n // 402 is the x402/MPP payment challenge — not a real invocation, skip logging\n if (response.status !== 402) {\n recordInvocation(meta, requestBodyString, {\n status: response.status,\n body: responseBodyString,\n headers: JSON.stringify(Object.fromEntries(response.headers.entries())),\n contentType: response.headers.get('content-type') ?? null,\n });\n }\n\n // Re-throw the original error if it wasn't a NextResponse\n if (handlerError && !(handlerError instanceof NextResponse)) {\n throw handlerError;\n }\n\n return response;\n };\n}\n"],"mappings":";;;;;;;AAMA,SAA2B,oBAAoB;AAcxC,SAAS,cAAc,SAA2B;AACvD,SAAO,OAAO,YAAgD;AAC5D,UAAM,OAAO,mBAAmB,OAAO;AACvC,UAAM,MAAM,sBAAsB,IAAI;AAGtC,QAAI,oBAAmC;AACvC,QAAI,KAAK,WAAW,UAAU,KAAK,WAAW,SAAS,KAAK,WAAW,SAAS;AAC9E,UAAI;AACF,cAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,KAAK;AACxC,YAAI,KAAM,qBAAoB;AAAA,MAChC,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,QAAI;AACJ,QAAI,eAAwB;AAE5B,QAAI;AACF,iBAAW,MAAM,QAAQ,SAAS,GAAG;AAAA,IACvC,SAAS,OAAgB;AACvB,qBAAe;AACf,UAAI,iBAAiB,cAAc;AACjC,mBAAW;AAAA,MACb,OAAO;AACL,cAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,mBAAW,aAAa,KAAK,EAAE,SAAS,OAAO,OAAO,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MAClF;AAAA,IACF;AAGA,QAAI,qBAAoC;AACxC,QAAI;AACF,2BAAqB,MAAM,SAAS,MAAM,EAAE,KAAK;AAAA,IACnD,QAAQ;AAAA,IAER;AAGA,QAAI,SAAS,WAAW,KAAK;AAC3B,uBAAiB,MAAM,mBAAmB;AAAA,QACxC,QAAQ,SAAS;AAAA,QACjB,MAAM;AAAA,QACN,SAAS,KAAK,UAAU,OAAO,YAAY,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAAA,QACtE,aAAa,SAAS,QAAQ,IAAI,cAAc,KAAK;AAAA,MACvD,CAAC;AAAA,IACH;AAGA,QAAI,gBAAgB,EAAE,wBAAwB,eAAe;AAC3D,YAAM;AAAA,IACR;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkFJ3YM6KOjs = require('./chunk-FJ3YM6KO.js');
3
+ var _chunkCIAESB2Cjs = require('./chunk-CIAESB2C.js');
4
4
 
5
5
 
6
6
 
7
- var _chunkGE7VBMQPjs = require('./chunk-GE7VBMQP.js');
8
- require('./chunk-P63MLKU3.js');
7
+ var _chunk2EFPFJCBjs = require('./chunk-2EFPFJCB.js');
8
+ require('./chunk-QEJ7ZGGH.js');
9
9
 
10
10
 
11
11
 
12
12
 
13
- exports.extractVerifiedWallet = _chunkGE7VBMQPjs.extractVerifiedWallet; exports.initTelemetry = _chunkGE7VBMQPjs.initTelemetry; exports.withTelemetry = _chunkFJ3YM6KOjs.withTelemetry;
13
+ exports.extractVerifiedWallet = _chunk2EFPFJCBjs.extractVerifiedWallet; exports.initTelemetry = _chunk2EFPFJCBjs.initTelemetry; exports.withTelemetry = _chunkCIAESB2Cjs.withTelemetry;
14
14
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/index.js"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B;AACE;AACA;AACF,sDAA4B;AAC5B,+BAA4B;AAC5B;AACE;AACA;AACA;AACF,uLAAC","file":"/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/index.js"}
1
+ {"version":3,"sources":["/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/index.js"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B;AACE;AACA;AACF,sDAA4B;AAC5B,+BAA4B;AAC5B;AACE;AACA;AACA;AACF,uLAAC","file":"/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/index.js"}
package/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  withTelemetry
3
- } from "./chunk-JVLKV7CX.mjs";
3
+ } from "./chunk-YAHKVH4T.mjs";
4
4
  import {
5
5
  extractVerifiedWallet,
6
6
  initTelemetry
7
- } from "./chunk-VOY67KA4.mjs";
8
- import "./chunk-O2PCP6KV.mjs";
7
+ } from "./chunk-PZWW7EMH.mjs";
8
+ import "./chunk-V553T6WE.mjs";
9
9
  export {
10
10
  extractVerifiedWallet,
11
11
  initTelemetry,
@@ -2,13 +2,13 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkP63MLKU3js = require('./chunk-P63MLKU3.js');
5
+ var _chunkQEJ7ZGGHjs = require('./chunk-QEJ7ZGGH.js');
6
6
 
7
7
  // src/router-plugin.ts
8
8
  function createTelemetryPlugin(config) {
9
- _chunkP63MLKU3js.initClickhouse.call(void 0, config.clickhouse);
9
+ _chunkQEJ7ZGGHjs.initClickhouse.call(void 0, config.clickhouse);
10
10
  if (config.verify) {
11
- _chunkP63MLKU3js.pingClickhouse.call(void 0, );
11
+ _chunkQEJ7ZGGHjs.pingClickhouse.call(void 0, );
12
12
  }
13
13
  const log = _nullishCoalesce(config.console, () => ( false));
14
14
  return {
@@ -48,6 +48,9 @@ function createTelemetryPlugin(config) {
48
48
  `[telemetry] ${meta.route} \u2192 ${response.statusCode} (${response.duration}ms)${wallet}`
49
49
  );
50
50
  }
51
+ if (response.statusCode === 402) {
52
+ return;
53
+ }
51
54
  const row = {
52
55
  id: meta.requestId,
53
56
  x_wallet_address: _nullishCoalesce(_optionalChain([meta, 'access', _ => _.walletAddress, 'optionalAccess', _2 => _2.toLowerCase, 'call', _3 => _3()]), () => ( null)),
@@ -69,7 +72,7 @@ function createTelemetryPlugin(config) {
69
72
  response_body: null,
70
73
  created_at: /* @__PURE__ */ new Date()
71
74
  };
72
- _chunkP63MLKU3js.insertInvocation.call(void 0, row);
75
+ _chunkQEJ7ZGGHjs.insertInvocation.call(void 0, row);
73
76
  },
74
77
  onError(ctx, error) {
75
78
  if (log) {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/router-plugin.js","../src/router-plugin.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACF,sDAA4B;AAC5B;AACA;ACmIO,SAAS,qBAAA,CAAsB,MAAA,EAA6C;AAEjF,EAAA,6CAAA,MAAe,CAAO,UAAU,CAAA;AAChC,EAAA,GAAA,CAAI,MAAA,CAAO,MAAA,EAAQ;AACjB,IAAA,6CAAA,CAAe;AAAA,EACjB;AAEA,EAAA,MAAM,IAAA,mBAAM,MAAA,CAAO,OAAA,UAAW,OAAA;AAE9B,EAAA,OAAO;AAAA,IACL,SAAA,CAAU,IAAA,EAAkC;AAC1C,MAAA,MAAM,IAAA,EAA8B;AAAA,QAClC,SAAA,EAAW,IAAA,CAAK,SAAA;AAAA,QAChB,KAAA,EAAO,IAAA,CAAK,KAAA;AAAA,QACZ,aAAA,EAAe,IAAA,CAAK,aAAA;AAAA,QACpB,QAAA,EAAU,IAAA,CAAK,QAAA;AAAA,QACf,SAAA,EAAW,IAAA,CAAK,SAAA;AAAA,QAChB,cAAA,EAAgB,IAAA;AAAA,QAChB,iBAAA,CAAkB,OAAA,EAAiB;AACjC,UAAA,GAAA,CAAI,eAAA,EAAiB,OAAA;AAAA,QACvB,CAAA;AAAA,QACA,KAAA,EAAO;AAAA,MACT,CAAA;AACA,MAAA,OAAO,GAAA;AAAA,IACT,CAAA;AAAA,IAEA,iBAAA,CAAkB,GAAA,EAAoB,OAAA,EAAuB;AAC3D,MAAC,GAAA,CAA+B,SAAA,EAAW,OAAA;AAC3C,MAAA,GAAA,CAAI,GAAA,EAAK;AACP,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,qBAAA,EAAwB,OAAA,CAAQ,QAAQ,CAAA,CAAA,EAAI,OAAA,CAAQ,KAAK,CAAA,CAAA,EAAI,OAAA,CAAQ,MAAM,CAAA,CAAA;AACzF,MAAA;AACF,IAAA;AAEkE,IAAA;AAClB,MAAA;AACrC,MAAA;AAC8E,QAAA;AACvF,MAAA;AACF,IAAA;AAEuD,IAAA;AACxC,MAAA;AACK,MAAA;AAET,MAAA;AAC+D,QAAA;AAC9D,QAAA;AACwE,UAAA;AAChF,QAAA;AACF,MAAA;AAEmC,MAAA;AACxB,QAAA;AAC8C,QAAA;AACrC,QAAA;AACD,QAAA;AAC6C,QAAA;AAEjD,QAAA;AACD,QAAA;AACC,QAAA;AACC,QAAA;AACa,QAAA;AACiB,QAAA;AAC9B,QAAA;AAEQ,QAAA;AACA,QAAA;AACH,QAAA;AACa,QAAA;AACiB,QAAA;AAClC,QAAA;AAEM,QAAA;AACvB,MAAA;AAEoB,MAAA;AACtB,IAAA;AAE+C,IAAA;AACpC,MAAA;AAC4D,QAAA;AACrE,MAAA;AACF,IAAA;AAE+C,IAAA;AACpC,MAAA;AAID,QAAA;AAGN,QAAA;AAC2E,UAAA;AAC3D,2BAAA;AAChB,QAAA;AACF,MAAA;AACF,IAAA;AAE+D,IAAA;AACpD,MAAA;AAKC,QAAA;AAEgF,QAAA;AAC1F,MAAA;AACF,IAAA;AACF,EAAA;AACF;AD1JgG;AACA;AACA","file":"/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/router-plugin.js","sourcesContent":[null,"/**\n * RouterPlugin adapter for @agentcash/router.\n *\n * Bridges the router's plugin hooks into ClickHouse telemetry.\n * Uses the same mcp_resource_invocations table as the legacy withTelemetry wrapper.\n *\n * Usage:\n * import { createRouter } from '@agentcash/router';\n * import { createTelemetryPlugin } from '@agentcash/telemetry/plugin';\n *\n * const router = createRouter({\n * payeeAddress: '...',\n * plugin: createTelemetryPlugin({\n * clickhouse: {\n * url: process.env.TELEM_CLICKHOUSE_URL ?? 'http://localhost:8123',\n * database: process.env.TELEM_CLICKHOUSE_DATABASE,\n * username: process.env.TELEM_CLICKHOUSE_USERNAME,\n * password: process.env.TELEM_CLICKHOUSE_PASSWORD,\n * },\n * }),\n * });\n */\n\nimport { initClickhouse, pingClickhouse, insertInvocation } from './clickhouse';\nimport type { McpResourceInvocation, TelemetryConfig } from './types';\n\n// ---------------------------------------------------------------------------\n// Minimal RouterPlugin types (inlined to avoid depending on @agentcash/router\n// at runtime — the router passes these shapes, we just consume them)\n// ---------------------------------------------------------------------------\n\ninterface RequestMeta {\n requestId: string;\n method: string;\n route: string;\n origin: string;\n referer: string | null;\n walletAddress: string | null;\n clientId: string | null;\n sessionId: string | null;\n contentType: string | null;\n headers: Record<string, string>;\n startTime: number;\n}\n\ninterface PluginContext {\n readonly requestId: string;\n readonly route: string;\n readonly walletAddress: string | null;\n readonly clientId: string | null;\n readonly sessionId: string | null;\n verifiedWallet: string | null;\n setVerifiedWallet(address: string): void;\n}\n\ninterface PaymentEvent {\n protocol: 'x402' | 'mpp';\n payer: string;\n amount: string;\n network: string;\n}\n\ninterface SettlementEvent {\n protocol: 'x402' | 'mpp';\n payer: string;\n transaction: string;\n network: string;\n}\n\ninterface ResponseMeta {\n statusCode: number;\n statusText: string;\n duration: number;\n contentType: string | null;\n headers: Record<string, string>;\n}\n\ninterface ErrorEvent {\n status: number;\n message: string;\n settled: boolean;\n}\n\ninterface AlertEvent {\n level: string;\n message: string;\n route: string;\n meta?: Record<string, unknown>;\n}\n\ninterface ProviderQuotaEvent {\n provider: string;\n route: string;\n remaining: number | null;\n limit: number | null;\n level: string;\n overage: string;\n message: string;\n}\n\n/** RouterPlugin interface — must match @agentcash/router's RouterPlugin */\ninterface RouterPlugin {\n init?(config: { origin?: string }): void | Promise<void>;\n onRequest?(meta: RequestMeta): PluginContext;\n onPaymentVerified?(ctx: PluginContext, payment: PaymentEvent): void;\n onPaymentSettled?(ctx: PluginContext, settlement: SettlementEvent): void;\n onResponse?(ctx: PluginContext, response: ResponseMeta): void;\n onError?(ctx: PluginContext, error: ErrorEvent): void;\n onAlert?(ctx: PluginContext, alert: AlertEvent): void;\n onProviderQuota?(ctx: PluginContext, event: ProviderQuotaEvent): void;\n}\n\n// ---------------------------------------------------------------------------\n// Extended context — carries request metadata through the lifecycle\n// ---------------------------------------------------------------------------\n\ninterface TelemetryPluginContext extends PluginContext {\n /** Stored from onRequest for use in onResponse */\n _meta: RequestMeta;\n /** Payment info captured between verify and response */\n _payment?: PaymentEvent;\n /** Settlement info captured between settle and response */\n _settlement?: SettlementEvent;\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\nexport interface TelemetryPluginConfig {\n clickhouse: TelemetryConfig['clickhouse'];\n /** If true, pings ClickHouse on init. */\n verify?: boolean;\n /** Console logging for dev. Default: false. */\n console?: boolean;\n}\n\nexport function createTelemetryPlugin(config: TelemetryPluginConfig): RouterPlugin {\n // Initialize ClickHouse synchronously (connection happens on first query)\n initClickhouse(config.clickhouse);\n if (config.verify) {\n pingClickhouse();\n }\n\n const log = config.console ?? false;\n\n return {\n onRequest(meta: RequestMeta): PluginContext {\n const ctx: TelemetryPluginContext = {\n requestId: meta.requestId,\n route: meta.route,\n walletAddress: meta.walletAddress,\n clientId: meta.clientId,\n sessionId: meta.sessionId,\n verifiedWallet: null,\n setVerifiedWallet(address: string) {\n ctx.verifiedWallet = address;\n },\n _meta: meta,\n };\n return ctx as PluginContext;\n },\n\n onPaymentVerified(ctx: PluginContext, payment: PaymentEvent) {\n (ctx as TelemetryPluginContext)._payment = payment;\n if (log) {\n console.log(`[telemetry] VERIFIED ${payment.protocol} ${payment.payer} ${payment.amount}`);\n }\n },\n\n onPaymentSettled(ctx: PluginContext, settlement: SettlementEvent) {\n (ctx as TelemetryPluginContext)._settlement = settlement;\n if (log) {\n console.log(`[telemetry] SETTLED ${settlement.protocol} tx=${settlement.transaction}`);\n }\n },\n\n onResponse(ctx: PluginContext, response: ResponseMeta) {\n const tCtx = ctx as TelemetryPluginContext;\n const meta = tCtx._meta;\n\n if (log) {\n const wallet = ctx.verifiedWallet ? ` wallet=${ctx.verifiedWallet}` : '';\n console.log(\n `[telemetry] ${meta.route} → ${response.statusCode} (${response.duration}ms)${wallet}`,\n );\n }\n\n const row: McpResourceInvocation = {\n id: meta.requestId,\n x_wallet_address: meta.walletAddress?.toLowerCase() ?? null,\n x_client_id: meta.clientId,\n session_id: meta.sessionId,\n verified_wallet_address: ctx.verifiedWallet?.toLowerCase() ?? null,\n\n method: meta.method,\n route: meta.route,\n origin: meta.origin,\n referer: meta.referer,\n request_content_type: meta.contentType,\n request_headers: JSON.stringify(meta.headers),\n request_body: null,\n\n status_code: response.statusCode,\n status_text: response.statusText,\n duration: response.duration,\n response_content_type: response.contentType,\n response_headers: JSON.stringify(response.headers),\n response_body: null,\n\n created_at: new Date(),\n };\n\n insertInvocation(row);\n },\n\n onError(ctx: PluginContext, error: ErrorEvent) {\n if (log) {\n console.error(`[telemetry] ERROR ${error.status}: ${error.message}`);\n }\n },\n\n onAlert(ctx: PluginContext, alert: AlertEvent) {\n if (log) {\n const logFn =\n alert.level === 'critical' || alert.level === 'error'\n ? console.error\n : alert.level === 'warn'\n ? console.warn\n : console.log;\n logFn(\n `[telemetry] ${alert.level.toUpperCase()} ${alert.route}: ${alert.message}`,\n alert.meta ?? '',\n );\n }\n },\n\n onProviderQuota(ctx: PluginContext, event: ProviderQuotaEvent) {\n if (log) {\n const logFn =\n event.level === 'critical'\n ? console.error\n : event.level === 'warn'\n ? console.warn\n : console.log;\n logFn(`[telemetry] QUOTA ${event.level.toUpperCase()} ${event.provider}: ${event.message}`);\n }\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/router-plugin.js","../src/router-plugin.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACF,sDAA4B;AAC5B;AACA;ACmIO,SAAS,qBAAA,CAAsB,MAAA,EAA6C;AAEjF,EAAA,6CAAA,MAAe,CAAO,UAAU,CAAA;AAChC,EAAA,GAAA,CAAI,MAAA,CAAO,MAAA,EAAQ;AACjB,IAAA,6CAAA,CAAe;AAAA,EACjB;AAEA,EAAA,MAAM,IAAA,mBAAM,MAAA,CAAO,OAAA,UAAW,OAAA;AAE9B,EAAA,OAAO;AAAA,IACL,SAAA,CAAU,IAAA,EAAkC;AAC1C,MAAA,MAAM,IAAA,EAA8B;AAAA,QAClC,SAAA,EAAW,IAAA,CAAK,SAAA;AAAA,QAChB,KAAA,EAAO,IAAA,CAAK,KAAA;AAAA,QACZ,aAAA,EAAe,IAAA,CAAK,aAAA;AAAA,QACpB,QAAA,EAAU,IAAA,CAAK,QAAA;AAAA,QACf,SAAA,EAAW,IAAA,CAAK,SAAA;AAAA,QAChB,cAAA,EAAgB,IAAA;AAAA,QAChB,iBAAA,CAAkB,OAAA,EAAiB;AACjC,UAAA,GAAA,CAAI,eAAA,EAAiB,OAAA;AAAA,QACvB,CAAA;AAAA,QACA,KAAA,EAAO;AAAA,MACT,CAAA;AACA,MAAA,OAAO,GAAA;AAAA,IACT,CAAA;AAAA,IAEA,iBAAA,CAAkB,GAAA,EAAoB,OAAA,EAAuB;AAC3D,MAAC,GAAA,CAA+B,SAAA,EAAW,OAAA;AAC3C,MAAA,GAAA,CAAI,GAAA,EAAK;AACP,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,qBAAA,EAAwB,OAAA,CAAQ,QAAQ,CAAA,CAAA,EAAI,OAAA,CAAQ,KAAK,CAAA,CAAA,EAAI,OAAA,CAAQ,MAAM,CAAA,CAAA;AACzF,MAAA;AACF,IAAA;AAEkE,IAAA;AAClB,MAAA;AACrC,MAAA;AAC8E,QAAA;AACvF,MAAA;AACF,IAAA;AAEuD,IAAA;AACxC,MAAA;AACK,MAAA;AAET,MAAA;AAC+D,QAAA;AAC9D,QAAA;AACwE,UAAA;AAChF,QAAA;AACF,MAAA;AAGiC,MAAA;AAC/B,QAAA;AACF,MAAA;AAEmC,MAAA;AACxB,QAAA;AAC8C,QAAA;AACrC,QAAA;AACD,QAAA;AAC6C,QAAA;AAEjD,QAAA;AACD,QAAA;AACC,QAAA;AACC,QAAA;AACa,QAAA;AACiB,QAAA;AAC9B,QAAA;AAEQ,QAAA;AACA,QAAA;AACH,QAAA;AACa,QAAA;AACiB,QAAA;AAClC,QAAA;AAEM,QAAA;AACvB,MAAA;AAEoB,MAAA;AACtB,IAAA;AAE+C,IAAA;AACpC,MAAA;AAC4D,QAAA;AACrE,MAAA;AACF,IAAA;AAE+C,IAAA;AACpC,MAAA;AAID,QAAA;AAGN,QAAA;AAC2E,UAAA;AAC3D,2BAAA;AAChB,QAAA;AACF,MAAA;AACF,IAAA;AAE+D,IAAA;AACpD,MAAA;AAKC,QAAA;AAEgF,QAAA;AAC1F,MAAA;AACF,IAAA;AACF,EAAA;AACF;AD5JgG;AACA;AACA","file":"/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/router-plugin.js","sourcesContent":[null,"/**\n * RouterPlugin adapter for @agentcash/router.\n *\n * Bridges the router's plugin hooks into ClickHouse telemetry.\n * Uses the same mcp_resource_invocations table as the legacy withTelemetry wrapper.\n *\n * Usage:\n * import { createRouter } from '@agentcash/router';\n * import { createTelemetryPlugin } from '@agentcash/telemetry/plugin';\n *\n * const router = createRouter({\n * payeeAddress: '...',\n * plugin: createTelemetryPlugin({\n * clickhouse: {\n * url: process.env.TELEM_CLICKHOUSE_URL ?? 'http://localhost:8123',\n * database: process.env.TELEM_CLICKHOUSE_DATABASE,\n * username: process.env.TELEM_CLICKHOUSE_USERNAME,\n * password: process.env.TELEM_CLICKHOUSE_PASSWORD,\n * },\n * }),\n * });\n */\n\nimport { initClickhouse, pingClickhouse, insertInvocation } from './clickhouse';\nimport type { McpResourceInvocation, TelemetryConfig } from './types';\n\n// ---------------------------------------------------------------------------\n// Minimal RouterPlugin types (inlined to avoid depending on @agentcash/router\n// at runtime — the router passes these shapes, we just consume them)\n// ---------------------------------------------------------------------------\n\ninterface RequestMeta {\n requestId: string;\n method: string;\n route: string;\n origin: string;\n referer: string | null;\n walletAddress: string | null;\n clientId: string | null;\n sessionId: string | null;\n contentType: string | null;\n headers: Record<string, string>;\n startTime: number;\n}\n\ninterface PluginContext {\n readonly requestId: string;\n readonly route: string;\n readonly walletAddress: string | null;\n readonly clientId: string | null;\n readonly sessionId: string | null;\n verifiedWallet: string | null;\n setVerifiedWallet(address: string): void;\n}\n\ninterface PaymentEvent {\n protocol: 'x402' | 'mpp';\n payer: string;\n amount: string;\n network: string;\n}\n\ninterface SettlementEvent {\n protocol: 'x402' | 'mpp';\n payer: string;\n transaction: string;\n network: string;\n}\n\ninterface ResponseMeta {\n statusCode: number;\n statusText: string;\n duration: number;\n contentType: string | null;\n headers: Record<string, string>;\n}\n\ninterface ErrorEvent {\n status: number;\n message: string;\n settled: boolean;\n}\n\ninterface AlertEvent {\n level: string;\n message: string;\n route: string;\n meta?: Record<string, unknown>;\n}\n\ninterface ProviderQuotaEvent {\n provider: string;\n route: string;\n remaining: number | null;\n limit: number | null;\n level: string;\n overage: string;\n message: string;\n}\n\n/** RouterPlugin interface — must match @agentcash/router's RouterPlugin */\ninterface RouterPlugin {\n init?(config: { origin?: string }): void | Promise<void>;\n onRequest?(meta: RequestMeta): PluginContext;\n onPaymentVerified?(ctx: PluginContext, payment: PaymentEvent): void;\n onPaymentSettled?(ctx: PluginContext, settlement: SettlementEvent): void;\n onResponse?(ctx: PluginContext, response: ResponseMeta): void;\n onError?(ctx: PluginContext, error: ErrorEvent): void;\n onAlert?(ctx: PluginContext, alert: AlertEvent): void;\n onProviderQuota?(ctx: PluginContext, event: ProviderQuotaEvent): void;\n}\n\n// ---------------------------------------------------------------------------\n// Extended context — carries request metadata through the lifecycle\n// ---------------------------------------------------------------------------\n\ninterface TelemetryPluginContext extends PluginContext {\n /** Stored from onRequest for use in onResponse */\n _meta: RequestMeta;\n /** Payment info captured between verify and response */\n _payment?: PaymentEvent;\n /** Settlement info captured between settle and response */\n _settlement?: SettlementEvent;\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\nexport interface TelemetryPluginConfig {\n clickhouse: TelemetryConfig['clickhouse'];\n /** If true, pings ClickHouse on init. */\n verify?: boolean;\n /** Console logging for dev. Default: false. */\n console?: boolean;\n}\n\nexport function createTelemetryPlugin(config: TelemetryPluginConfig): RouterPlugin {\n // Initialize ClickHouse synchronously (connection happens on first query)\n initClickhouse(config.clickhouse);\n if (config.verify) {\n pingClickhouse();\n }\n\n const log = config.console ?? false;\n\n return {\n onRequest(meta: RequestMeta): PluginContext {\n const ctx: TelemetryPluginContext = {\n requestId: meta.requestId,\n route: meta.route,\n walletAddress: meta.walletAddress,\n clientId: meta.clientId,\n sessionId: meta.sessionId,\n verifiedWallet: null,\n setVerifiedWallet(address: string) {\n ctx.verifiedWallet = address;\n },\n _meta: meta,\n };\n return ctx as PluginContext;\n },\n\n onPaymentVerified(ctx: PluginContext, payment: PaymentEvent) {\n (ctx as TelemetryPluginContext)._payment = payment;\n if (log) {\n console.log(`[telemetry] VERIFIED ${payment.protocol} ${payment.payer} ${payment.amount}`);\n }\n },\n\n onPaymentSettled(ctx: PluginContext, settlement: SettlementEvent) {\n (ctx as TelemetryPluginContext)._settlement = settlement;\n if (log) {\n console.log(`[telemetry] SETTLED ${settlement.protocol} tx=${settlement.transaction}`);\n }\n },\n\n onResponse(ctx: PluginContext, response: ResponseMeta) {\n const tCtx = ctx as TelemetryPluginContext;\n const meta = tCtx._meta;\n\n if (log) {\n const wallet = ctx.verifiedWallet ? ` wallet=${ctx.verifiedWallet}` : '';\n console.log(\n `[telemetry] ${meta.route} → ${response.statusCode} (${response.duration}ms)${wallet}`,\n );\n }\n\n // 402 is the x402/MPP payment challenge — not a real invocation, skip logging\n if (response.statusCode === 402) {\n return;\n }\n\n const row: McpResourceInvocation = {\n id: meta.requestId,\n x_wallet_address: meta.walletAddress?.toLowerCase() ?? null,\n x_client_id: meta.clientId,\n session_id: meta.sessionId,\n verified_wallet_address: ctx.verifiedWallet?.toLowerCase() ?? null,\n\n method: meta.method,\n route: meta.route,\n origin: meta.origin,\n referer: meta.referer,\n request_content_type: meta.contentType,\n request_headers: JSON.stringify(meta.headers),\n request_body: null,\n\n status_code: response.statusCode,\n status_text: response.statusText,\n duration: response.duration,\n response_content_type: response.contentType,\n response_headers: JSON.stringify(response.headers),\n response_body: null,\n\n created_at: new Date(),\n };\n\n insertInvocation(row);\n },\n\n onError(ctx: PluginContext, error: ErrorEvent) {\n if (log) {\n console.error(`[telemetry] ERROR ${error.status}: ${error.message}`);\n }\n },\n\n onAlert(ctx: PluginContext, alert: AlertEvent) {\n if (log) {\n const logFn =\n alert.level === 'critical' || alert.level === 'error'\n ? console.error\n : alert.level === 'warn'\n ? console.warn\n : console.log;\n logFn(\n `[telemetry] ${alert.level.toUpperCase()} ${alert.route}: ${alert.message}`,\n alert.meta ?? '',\n );\n }\n },\n\n onProviderQuota(ctx: PluginContext, event: ProviderQuotaEvent) {\n if (log) {\n const logFn =\n event.level === 'critical'\n ? console.error\n : event.level === 'warn'\n ? console.warn\n : console.log;\n logFn(`[telemetry] QUOTA ${event.level.toUpperCase()} ${event.provider}: ${event.message}`);\n }\n },\n };\n}\n"]}
@@ -2,7 +2,7 @@ import {
2
2
  initClickhouse,
3
3
  insertInvocation,
4
4
  pingClickhouse
5
- } from "./chunk-O2PCP6KV.mjs";
5
+ } from "./chunk-V553T6WE.mjs";
6
6
 
7
7
  // src/router-plugin.ts
8
8
  function createTelemetryPlugin(config) {
@@ -48,6 +48,9 @@ function createTelemetryPlugin(config) {
48
48
  `[telemetry] ${meta.route} \u2192 ${response.statusCode} (${response.duration}ms)${wallet}`
49
49
  );
50
50
  }
51
+ if (response.statusCode === 402) {
52
+ return;
53
+ }
51
54
  const row = {
52
55
  id: meta.requestId,
53
56
  x_wallet_address: meta.walletAddress?.toLowerCase() ?? null,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/router-plugin.ts"],"sourcesContent":["/**\n * RouterPlugin adapter for @agentcash/router.\n *\n * Bridges the router's plugin hooks into ClickHouse telemetry.\n * Uses the same mcp_resource_invocations table as the legacy withTelemetry wrapper.\n *\n * Usage:\n * import { createRouter } from '@agentcash/router';\n * import { createTelemetryPlugin } from '@agentcash/telemetry/plugin';\n *\n * const router = createRouter({\n * payeeAddress: '...',\n * plugin: createTelemetryPlugin({\n * clickhouse: {\n * url: process.env.TELEM_CLICKHOUSE_URL ?? 'http://localhost:8123',\n * database: process.env.TELEM_CLICKHOUSE_DATABASE,\n * username: process.env.TELEM_CLICKHOUSE_USERNAME,\n * password: process.env.TELEM_CLICKHOUSE_PASSWORD,\n * },\n * }),\n * });\n */\n\nimport { initClickhouse, pingClickhouse, insertInvocation } from './clickhouse';\nimport type { McpResourceInvocation, TelemetryConfig } from './types';\n\n// ---------------------------------------------------------------------------\n// Minimal RouterPlugin types (inlined to avoid depending on @agentcash/router\n// at runtime — the router passes these shapes, we just consume them)\n// ---------------------------------------------------------------------------\n\ninterface RequestMeta {\n requestId: string;\n method: string;\n route: string;\n origin: string;\n referer: string | null;\n walletAddress: string | null;\n clientId: string | null;\n sessionId: string | null;\n contentType: string | null;\n headers: Record<string, string>;\n startTime: number;\n}\n\ninterface PluginContext {\n readonly requestId: string;\n readonly route: string;\n readonly walletAddress: string | null;\n readonly clientId: string | null;\n readonly sessionId: string | null;\n verifiedWallet: string | null;\n setVerifiedWallet(address: string): void;\n}\n\ninterface PaymentEvent {\n protocol: 'x402' | 'mpp';\n payer: string;\n amount: string;\n network: string;\n}\n\ninterface SettlementEvent {\n protocol: 'x402' | 'mpp';\n payer: string;\n transaction: string;\n network: string;\n}\n\ninterface ResponseMeta {\n statusCode: number;\n statusText: string;\n duration: number;\n contentType: string | null;\n headers: Record<string, string>;\n}\n\ninterface ErrorEvent {\n status: number;\n message: string;\n settled: boolean;\n}\n\ninterface AlertEvent {\n level: string;\n message: string;\n route: string;\n meta?: Record<string, unknown>;\n}\n\ninterface ProviderQuotaEvent {\n provider: string;\n route: string;\n remaining: number | null;\n limit: number | null;\n level: string;\n overage: string;\n message: string;\n}\n\n/** RouterPlugin interface — must match @agentcash/router's RouterPlugin */\ninterface RouterPlugin {\n init?(config: { origin?: string }): void | Promise<void>;\n onRequest?(meta: RequestMeta): PluginContext;\n onPaymentVerified?(ctx: PluginContext, payment: PaymentEvent): void;\n onPaymentSettled?(ctx: PluginContext, settlement: SettlementEvent): void;\n onResponse?(ctx: PluginContext, response: ResponseMeta): void;\n onError?(ctx: PluginContext, error: ErrorEvent): void;\n onAlert?(ctx: PluginContext, alert: AlertEvent): void;\n onProviderQuota?(ctx: PluginContext, event: ProviderQuotaEvent): void;\n}\n\n// ---------------------------------------------------------------------------\n// Extended context — carries request metadata through the lifecycle\n// ---------------------------------------------------------------------------\n\ninterface TelemetryPluginContext extends PluginContext {\n /** Stored from onRequest for use in onResponse */\n _meta: RequestMeta;\n /** Payment info captured between verify and response */\n _payment?: PaymentEvent;\n /** Settlement info captured between settle and response */\n _settlement?: SettlementEvent;\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\nexport interface TelemetryPluginConfig {\n clickhouse: TelemetryConfig['clickhouse'];\n /** If true, pings ClickHouse on init. */\n verify?: boolean;\n /** Console logging for dev. Default: false. */\n console?: boolean;\n}\n\nexport function createTelemetryPlugin(config: TelemetryPluginConfig): RouterPlugin {\n // Initialize ClickHouse synchronously (connection happens on first query)\n initClickhouse(config.clickhouse);\n if (config.verify) {\n pingClickhouse();\n }\n\n const log = config.console ?? false;\n\n return {\n onRequest(meta: RequestMeta): PluginContext {\n const ctx: TelemetryPluginContext = {\n requestId: meta.requestId,\n route: meta.route,\n walletAddress: meta.walletAddress,\n clientId: meta.clientId,\n sessionId: meta.sessionId,\n verifiedWallet: null,\n setVerifiedWallet(address: string) {\n ctx.verifiedWallet = address;\n },\n _meta: meta,\n };\n return ctx as PluginContext;\n },\n\n onPaymentVerified(ctx: PluginContext, payment: PaymentEvent) {\n (ctx as TelemetryPluginContext)._payment = payment;\n if (log) {\n console.log(`[telemetry] VERIFIED ${payment.protocol} ${payment.payer} ${payment.amount}`);\n }\n },\n\n onPaymentSettled(ctx: PluginContext, settlement: SettlementEvent) {\n (ctx as TelemetryPluginContext)._settlement = settlement;\n if (log) {\n console.log(`[telemetry] SETTLED ${settlement.protocol} tx=${settlement.transaction}`);\n }\n },\n\n onResponse(ctx: PluginContext, response: ResponseMeta) {\n const tCtx = ctx as TelemetryPluginContext;\n const meta = tCtx._meta;\n\n if (log) {\n const wallet = ctx.verifiedWallet ? ` wallet=${ctx.verifiedWallet}` : '';\n console.log(\n `[telemetry] ${meta.route} → ${response.statusCode} (${response.duration}ms)${wallet}`,\n );\n }\n\n const row: McpResourceInvocation = {\n id: meta.requestId,\n x_wallet_address: meta.walletAddress?.toLowerCase() ?? null,\n x_client_id: meta.clientId,\n session_id: meta.sessionId,\n verified_wallet_address: ctx.verifiedWallet?.toLowerCase() ?? null,\n\n method: meta.method,\n route: meta.route,\n origin: meta.origin,\n referer: meta.referer,\n request_content_type: meta.contentType,\n request_headers: JSON.stringify(meta.headers),\n request_body: null,\n\n status_code: response.statusCode,\n status_text: response.statusText,\n duration: response.duration,\n response_content_type: response.contentType,\n response_headers: JSON.stringify(response.headers),\n response_body: null,\n\n created_at: new Date(),\n };\n\n insertInvocation(row);\n },\n\n onError(ctx: PluginContext, error: ErrorEvent) {\n if (log) {\n console.error(`[telemetry] ERROR ${error.status}: ${error.message}`);\n }\n },\n\n onAlert(ctx: PluginContext, alert: AlertEvent) {\n if (log) {\n const logFn =\n alert.level === 'critical' || alert.level === 'error'\n ? console.error\n : alert.level === 'warn'\n ? console.warn\n : console.log;\n logFn(\n `[telemetry] ${alert.level.toUpperCase()} ${alert.route}: ${alert.message}`,\n alert.meta ?? '',\n );\n }\n },\n\n onProviderQuota(ctx: PluginContext, event: ProviderQuotaEvent) {\n if (log) {\n const logFn =\n event.level === 'critical'\n ? console.error\n : event.level === 'warn'\n ? console.warn\n : console.log;\n logFn(`[telemetry] QUOTA ${event.level.toUpperCase()} ${event.provider}: ${event.message}`);\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/router-plugin.ts"],"sourcesContent":["/**\n * RouterPlugin adapter for @agentcash/router.\n *\n * Bridges the router's plugin hooks into ClickHouse telemetry.\n * Uses the same mcp_resource_invocations table as the legacy withTelemetry wrapper.\n *\n * Usage:\n * import { createRouter } from '@agentcash/router';\n * import { createTelemetryPlugin } from '@agentcash/telemetry/plugin';\n *\n * const router = createRouter({\n * payeeAddress: '...',\n * plugin: createTelemetryPlugin({\n * clickhouse: {\n * url: process.env.TELEM_CLICKHOUSE_URL ?? 'http://localhost:8123',\n * database: process.env.TELEM_CLICKHOUSE_DATABASE,\n * username: process.env.TELEM_CLICKHOUSE_USERNAME,\n * password: process.env.TELEM_CLICKHOUSE_PASSWORD,\n * },\n * }),\n * });\n */\n\nimport { initClickhouse, pingClickhouse, insertInvocation } from './clickhouse';\nimport type { McpResourceInvocation, TelemetryConfig } from './types';\n\n// ---------------------------------------------------------------------------\n// Minimal RouterPlugin types (inlined to avoid depending on @agentcash/router\n// at runtime — the router passes these shapes, we just consume them)\n// ---------------------------------------------------------------------------\n\ninterface RequestMeta {\n requestId: string;\n method: string;\n route: string;\n origin: string;\n referer: string | null;\n walletAddress: string | null;\n clientId: string | null;\n sessionId: string | null;\n contentType: string | null;\n headers: Record<string, string>;\n startTime: number;\n}\n\ninterface PluginContext {\n readonly requestId: string;\n readonly route: string;\n readonly walletAddress: string | null;\n readonly clientId: string | null;\n readonly sessionId: string | null;\n verifiedWallet: string | null;\n setVerifiedWallet(address: string): void;\n}\n\ninterface PaymentEvent {\n protocol: 'x402' | 'mpp';\n payer: string;\n amount: string;\n network: string;\n}\n\ninterface SettlementEvent {\n protocol: 'x402' | 'mpp';\n payer: string;\n transaction: string;\n network: string;\n}\n\ninterface ResponseMeta {\n statusCode: number;\n statusText: string;\n duration: number;\n contentType: string | null;\n headers: Record<string, string>;\n}\n\ninterface ErrorEvent {\n status: number;\n message: string;\n settled: boolean;\n}\n\ninterface AlertEvent {\n level: string;\n message: string;\n route: string;\n meta?: Record<string, unknown>;\n}\n\ninterface ProviderQuotaEvent {\n provider: string;\n route: string;\n remaining: number | null;\n limit: number | null;\n level: string;\n overage: string;\n message: string;\n}\n\n/** RouterPlugin interface — must match @agentcash/router's RouterPlugin */\ninterface RouterPlugin {\n init?(config: { origin?: string }): void | Promise<void>;\n onRequest?(meta: RequestMeta): PluginContext;\n onPaymentVerified?(ctx: PluginContext, payment: PaymentEvent): void;\n onPaymentSettled?(ctx: PluginContext, settlement: SettlementEvent): void;\n onResponse?(ctx: PluginContext, response: ResponseMeta): void;\n onError?(ctx: PluginContext, error: ErrorEvent): void;\n onAlert?(ctx: PluginContext, alert: AlertEvent): void;\n onProviderQuota?(ctx: PluginContext, event: ProviderQuotaEvent): void;\n}\n\n// ---------------------------------------------------------------------------\n// Extended context — carries request metadata through the lifecycle\n// ---------------------------------------------------------------------------\n\ninterface TelemetryPluginContext extends PluginContext {\n /** Stored from onRequest for use in onResponse */\n _meta: RequestMeta;\n /** Payment info captured between verify and response */\n _payment?: PaymentEvent;\n /** Settlement info captured between settle and response */\n _settlement?: SettlementEvent;\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\nexport interface TelemetryPluginConfig {\n clickhouse: TelemetryConfig['clickhouse'];\n /** If true, pings ClickHouse on init. */\n verify?: boolean;\n /** Console logging for dev. Default: false. */\n console?: boolean;\n}\n\nexport function createTelemetryPlugin(config: TelemetryPluginConfig): RouterPlugin {\n // Initialize ClickHouse synchronously (connection happens on first query)\n initClickhouse(config.clickhouse);\n if (config.verify) {\n pingClickhouse();\n }\n\n const log = config.console ?? false;\n\n return {\n onRequest(meta: RequestMeta): PluginContext {\n const ctx: TelemetryPluginContext = {\n requestId: meta.requestId,\n route: meta.route,\n walletAddress: meta.walletAddress,\n clientId: meta.clientId,\n sessionId: meta.sessionId,\n verifiedWallet: null,\n setVerifiedWallet(address: string) {\n ctx.verifiedWallet = address;\n },\n _meta: meta,\n };\n return ctx as PluginContext;\n },\n\n onPaymentVerified(ctx: PluginContext, payment: PaymentEvent) {\n (ctx as TelemetryPluginContext)._payment = payment;\n if (log) {\n console.log(`[telemetry] VERIFIED ${payment.protocol} ${payment.payer} ${payment.amount}`);\n }\n },\n\n onPaymentSettled(ctx: PluginContext, settlement: SettlementEvent) {\n (ctx as TelemetryPluginContext)._settlement = settlement;\n if (log) {\n console.log(`[telemetry] SETTLED ${settlement.protocol} tx=${settlement.transaction}`);\n }\n },\n\n onResponse(ctx: PluginContext, response: ResponseMeta) {\n const tCtx = ctx as TelemetryPluginContext;\n const meta = tCtx._meta;\n\n if (log) {\n const wallet = ctx.verifiedWallet ? ` wallet=${ctx.verifiedWallet}` : '';\n console.log(\n `[telemetry] ${meta.route} → ${response.statusCode} (${response.duration}ms)${wallet}`,\n );\n }\n\n // 402 is the x402/MPP payment challenge — not a real invocation, skip logging\n if (response.statusCode === 402) {\n return;\n }\n\n const row: McpResourceInvocation = {\n id: meta.requestId,\n x_wallet_address: meta.walletAddress?.toLowerCase() ?? null,\n x_client_id: meta.clientId,\n session_id: meta.sessionId,\n verified_wallet_address: ctx.verifiedWallet?.toLowerCase() ?? null,\n\n method: meta.method,\n route: meta.route,\n origin: meta.origin,\n referer: meta.referer,\n request_content_type: meta.contentType,\n request_headers: JSON.stringify(meta.headers),\n request_body: null,\n\n status_code: response.statusCode,\n status_text: response.statusText,\n duration: response.duration,\n response_content_type: response.contentType,\n response_headers: JSON.stringify(response.headers),\n response_body: null,\n\n created_at: new Date(),\n };\n\n insertInvocation(row);\n },\n\n onError(ctx: PluginContext, error: ErrorEvent) {\n if (log) {\n console.error(`[telemetry] ERROR ${error.status}: ${error.message}`);\n }\n },\n\n onAlert(ctx: PluginContext, alert: AlertEvent) {\n if (log) {\n const logFn =\n alert.level === 'critical' || alert.level === 'error'\n ? console.error\n : alert.level === 'warn'\n ? console.warn\n : console.log;\n logFn(\n `[telemetry] ${alert.level.toUpperCase()} ${alert.route}: ${alert.message}`,\n alert.meta ?? '',\n );\n }\n },\n\n onProviderQuota(ctx: PluginContext, event: ProviderQuotaEvent) {\n if (log) {\n const logFn =\n event.level === 'critical'\n ? console.error\n : event.level === 'warn'\n ? console.warn\n : console.log;\n logFn(`[telemetry] QUOTA ${event.level.toUpperCase()} ${event.provider}: ${event.message}`);\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
package/dist/siwx.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
2
 
3
- var _chunkFJ3YM6KOjs = require('./chunk-FJ3YM6KO.js');
4
- require('./chunk-GE7VBMQP.js');
5
- require('./chunk-P63MLKU3.js');
3
+ var _chunkCIAESB2Cjs = require('./chunk-CIAESB2C.js');
4
+ require('./chunk-2EFPFJCB.js');
5
+ require('./chunk-QEJ7ZGGH.js');
6
6
 
7
7
  // src/siwx.ts
8
8
  var _server = require('next/server');
@@ -15,7 +15,7 @@ var _crypto = require('crypto');
15
15
  var _signinwithx = require('@x402/extensions/sign-in-with-x');
16
16
  var _http = require('@x402/core/http');
17
17
  function withSiwxTelemetry(handler) {
18
- return _chunkFJ3YM6KOjs.withTelemetry.call(void 0, async (request, ctx) => {
18
+ return _chunkCIAESB2Cjs.withTelemetry.call(void 0, async (request, ctx) => {
19
19
  const header = _nullishCoalesce(request.headers.get("SIGN-IN-WITH-X"), () => ( request.headers.get("sign-in-with-x")));
20
20
  if (!header) {
21
21
  return buildSiwxChallengeResponse(request);
package/dist/siwx.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/siwx.js","../src/siwx.ts"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B;AACA;ACKA,qCAA+C;AAC/C,gCAA4B;AAC5B;AACE;AACA;AACA;AACA;AAAA,8DACK;AACP,uCAA4C;AAoBrC,SAAS,iBAAA,CAAkB,OAAA,EAAsB;AACtD,EAAA,OAAO,4CAAA,MAAc,CAAO,OAAA,EAAsB,GAAA,EAAA,GAA0B;AAC1E,IAAA,MAAM,OAAA,mBAAS,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA,UAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,gBAAgB,GAAA;AAE5F,IAAA,GAAA,CAAI,CAAC,MAAA,EAAQ;AACX,MAAA,OAAO,0BAAA,CAA2B,OAAO,CAAA;AAAA,IAC3C;AAEA,IAAA,MAAM,QAAA,EAAU,0CAAA,MAAsB,CAAA;AAEtC,IAAA,MAAM,WAAA,EAAa,MAAM,8CAAA,OAAoB,EAAS,OAAA,CAAQ,GAAG,CAAA;AACjE,IAAA,GAAA,CAAI,CAAC,UAAA,CAAW,KAAA,EAAO;AACrB,MAAA,OAAO,oBAAA,CAAa,IAAA;AAAA,QAClB,EAAE,OAAA,EAAS,KAAA,EAAO,KAAA,EAAO,CAAA,wBAAA,EAA2B,UAAA,CAAW,KAAK,CAAA,EAAA;AACtD,QAAA;AAChB,MAAA;AACF,IAAA;AAEsD,IAAA;AACJ,IAAA;AAC5B,MAAA;AAC4C,QAAA;AAChD,QAAA;AAChB,MAAA;AACF,IAAA;AAEuD,IAAA;AACpB,IAAA;AAEX,IAAA;AACnB,MAAA;AACa,MAAA;AACO,IAAA;AAC1B,EAAA;AACH;AAKwE;AACvC,EAAA;AACa,EAAA;AACJ,EAAA;AAC0B,EAAA;AAE1C,EAAA;AACT,IAAA;AACN,IAAA;AACG,IAAA;AACK,MAAA;AACA,MAAA;AACH,MAAA;AACZ,IAAA;AACS,IAAA;AACP,MAAA;AACU,QAAA;AACC,QAAA;AACF,QAAA;AACC,QAAA;AACD,QAAA;AACY,QAAA;AACX,QAAA;AACV,MAAA;AACF,IAAA;AACY,IAAA;AACQ,MAAA;AACV,QAAA;AACQ,UAAA;AACC,UAAA;AACJ,UAAA;AACT,UAAA;AACA,UAAA;AACA,UAAA;AACW,UAAA;AACY,UAAA;AACzB,QAAA;AAC4D,QAAA;AACpC,QAAA;AAC1B,MAAA;AACF,IAAA;AACF,EAAA;AAE2D,EAAA;AAEF,EAAA;AAC/C,IAAA;AACC,IAAA;AACS,MAAA;AACI,MAAA;AACtB,IAAA;AACD,EAAA;AACH;ADnC6E;AACA;AACA","file":"/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/siwx.js","sourcesContent":[null,"/**\n * SIWX telemetry wrapper — composes SIWX verification with telemetry.\n * One-liner for routes that need wallet auth + telemetry.\n *\n * Delegates SIWX verification to @x402/extensions/sign-in-with-x.\n * This package does NOT implement SIWX itself.\n *\n * Import from '@agentcash/telemetry/siwx'.\n * Requires peer dep: @x402/extensions\n */\n\nimport { type NextRequest, NextResponse } from 'next/server';\nimport { randomBytes } from 'crypto';\nimport {\n parseSIWxHeader,\n validateSIWxMessage,\n verifySIWxSignature,\n buildSIWxSchema,\n} from '@x402/extensions/sign-in-with-x';\nimport { encodePaymentRequiredHeader } from '@x402/core/http';\nimport type { TelemetryContext } from './types';\nimport { withTelemetry } from './telemetry';\n\nexport type { SiwxTelemetryContext };\n\ninterface SiwxTelemetryContext extends Omit<TelemetryContext, 'verifiedWallet'> {\n /** Verified wallet address from SIWX authentication */\n verifiedWallet: string;\n}\n\ntype SiwxHandler = (request: NextRequest, ctx: SiwxTelemetryContext) => Promise<NextResponse>;\n\n/**\n * Wrap a Next.js route handler with SIWX verification + telemetry.\n *\n * Verifies the SIGN-IN-WITH-X header and sets the verified wallet automatically.\n * If no SIWX header is present, returns a 402 with SIWX challenge.\n * If verification fails, returns a 402 (matching x402 protocol convention).\n */\nexport function withSiwxTelemetry(handler: SiwxHandler) {\n return withTelemetry(async (request: NextRequest, ctx: TelemetryContext) => {\n const header = request.headers.get('SIGN-IN-WITH-X') ?? request.headers.get('sign-in-with-x');\n\n if (!header) {\n return buildSiwxChallengeResponse(request);\n }\n\n const payload = parseSIWxHeader(header);\n\n const validation = await validateSIWxMessage(payload, request.url);\n if (!validation.valid) {\n return NextResponse.json(\n { success: false, error: `SIWX validation failed: ${validation.error}` },\n { status: 402 },\n );\n }\n\n const verification = await verifySIWxSignature(payload);\n if (!verification.valid || !verification.address) {\n return NextResponse.json(\n { success: false, error: 'SIWX signature verification failed' },\n { status: 402 },\n );\n }\n\n const walletAddress = verification.address.toLowerCase();\n ctx.setVerifiedWallet(walletAddress);\n\n return handler(request, {\n ...ctx,\n verifiedWallet: walletAddress,\n } as SiwxTelemetryContext);\n });\n}\n\n/**\n * Build a 402 response with SIWX challenge.\n */\nfunction buildSiwxChallengeResponse(request: NextRequest): NextResponse {\n const url = new URL(request.url);\n const nonce = randomBytes(16).toString('hex');\n const issuedAt = new Date().toISOString();\n const expirationTime = new Date(Date.now() + 300_000).toISOString();\n\n const paymentRequired = {\n x402Version: 2,\n error: 'SIWX authentication required',\n resource: {\n url: request.url,\n description: 'SIWX-protected endpoint',\n mimeType: 'application/json',\n },\n accepts: [\n {\n scheme: 'exact' as const,\n network: 'eip155:8453' as const,\n asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n amount: '0',\n payTo: '0x0000000000000000000000000000000000000000',\n maxTimeoutSeconds: 300,\n extra: {},\n },\n ],\n extensions: {\n 'sign-in-with-x': {\n info: {\n domain: url.hostname,\n uri: request.url,\n version: '1',\n nonce,\n issuedAt,\n expirationTime,\n statement: 'Sign in to verify your wallet identity',\n resources: [request.url],\n },\n supportedChains: [{ chainId: 'eip155:8453', type: 'eip191' }],\n schema: buildSIWxSchema(),\n },\n },\n };\n\n const encoded = encodePaymentRequiredHeader(paymentRequired);\n\n return new NextResponse(JSON.stringify(paymentRequired), {\n status: 402,\n headers: {\n 'Content-Type': 'application/json',\n 'PAYMENT-REQUIRED': encoded,\n },\n });\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/siwx.js","../src/siwx.ts"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B;AACA;ACKA,qCAA+C;AAC/C,gCAA4B;AAC5B;AACE;AACA;AACA;AACA;AAAA,8DACK;AACP,uCAA4C;AAoBrC,SAAS,iBAAA,CAAkB,OAAA,EAAsB;AACtD,EAAA,OAAO,4CAAA,MAAc,CAAO,OAAA,EAAsB,GAAA,EAAA,GAA0B;AAC1E,IAAA,MAAM,OAAA,mBAAS,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA,UAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,gBAAgB,GAAA;AAE5F,IAAA,GAAA,CAAI,CAAC,MAAA,EAAQ;AACX,MAAA,OAAO,0BAAA,CAA2B,OAAO,CAAA;AAAA,IAC3C;AAEA,IAAA,MAAM,QAAA,EAAU,0CAAA,MAAsB,CAAA;AAEtC,IAAA,MAAM,WAAA,EAAa,MAAM,8CAAA,OAAoB,EAAS,OAAA,CAAQ,GAAG,CAAA;AACjE,IAAA,GAAA,CAAI,CAAC,UAAA,CAAW,KAAA,EAAO;AACrB,MAAA,OAAO,oBAAA,CAAa,IAAA;AAAA,QAClB,EAAE,OAAA,EAAS,KAAA,EAAO,KAAA,EAAO,CAAA,wBAAA,EAA2B,UAAA,CAAW,KAAK,CAAA,EAAA;AACtD,QAAA;AAChB,MAAA;AACF,IAAA;AAEsD,IAAA;AACJ,IAAA;AAC5B,MAAA;AAC4C,QAAA;AAChD,QAAA;AAChB,MAAA;AACF,IAAA;AAEuD,IAAA;AACpB,IAAA;AAEX,IAAA;AACnB,MAAA;AACa,MAAA;AACO,IAAA;AAC1B,EAAA;AACH;AAKwE;AACvC,EAAA;AACa,EAAA;AACJ,EAAA;AAC0B,EAAA;AAE1C,EAAA;AACT,IAAA;AACN,IAAA;AACG,IAAA;AACK,MAAA;AACA,MAAA;AACH,MAAA;AACZ,IAAA;AACS,IAAA;AACP,MAAA;AACU,QAAA;AACC,QAAA;AACF,QAAA;AACC,QAAA;AACD,QAAA;AACY,QAAA;AACX,QAAA;AACV,MAAA;AACF,IAAA;AACY,IAAA;AACQ,MAAA;AACV,QAAA;AACQ,UAAA;AACC,UAAA;AACJ,UAAA;AACT,UAAA;AACA,UAAA;AACA,UAAA;AACW,UAAA;AACY,UAAA;AACzB,QAAA;AAC4D,QAAA;AACpC,QAAA;AAC1B,MAAA;AACF,IAAA;AACF,EAAA;AAE2D,EAAA;AAEF,EAAA;AAC/C,IAAA;AACC,IAAA;AACS,MAAA;AACI,MAAA;AACtB,IAAA;AACD,EAAA;AACH;ADnC6E;AACA;AACA","file":"/home/runner/work/agentcash-telemetry/agentcash-telemetry/dist/siwx.js","sourcesContent":[null,"/**\n * SIWX telemetry wrapper — composes SIWX verification with telemetry.\n * One-liner for routes that need wallet auth + telemetry.\n *\n * Delegates SIWX verification to @x402/extensions/sign-in-with-x.\n * This package does NOT implement SIWX itself.\n *\n * Import from '@agentcash/telemetry/siwx'.\n * Requires peer dep: @x402/extensions\n */\n\nimport { type NextRequest, NextResponse } from 'next/server';\nimport { randomBytes } from 'crypto';\nimport {\n parseSIWxHeader,\n validateSIWxMessage,\n verifySIWxSignature,\n buildSIWxSchema,\n} from '@x402/extensions/sign-in-with-x';\nimport { encodePaymentRequiredHeader } from '@x402/core/http';\nimport type { TelemetryContext } from './types';\nimport { withTelemetry } from './telemetry';\n\nexport type { SiwxTelemetryContext };\n\ninterface SiwxTelemetryContext extends Omit<TelemetryContext, 'verifiedWallet'> {\n /** Verified wallet address from SIWX authentication */\n verifiedWallet: string;\n}\n\ntype SiwxHandler = (request: NextRequest, ctx: SiwxTelemetryContext) => Promise<NextResponse>;\n\n/**\n * Wrap a Next.js route handler with SIWX verification + telemetry.\n *\n * Verifies the SIGN-IN-WITH-X header and sets the verified wallet automatically.\n * If no SIWX header is present, returns a 402 with SIWX challenge.\n * If verification fails, returns a 402 (matching x402 protocol convention).\n */\nexport function withSiwxTelemetry(handler: SiwxHandler) {\n return withTelemetry(async (request: NextRequest, ctx: TelemetryContext) => {\n const header = request.headers.get('SIGN-IN-WITH-X') ?? request.headers.get('sign-in-with-x');\n\n if (!header) {\n return buildSiwxChallengeResponse(request);\n }\n\n const payload = parseSIWxHeader(header);\n\n const validation = await validateSIWxMessage(payload, request.url);\n if (!validation.valid) {\n return NextResponse.json(\n { success: false, error: `SIWX validation failed: ${validation.error}` },\n { status: 402 },\n );\n }\n\n const verification = await verifySIWxSignature(payload);\n if (!verification.valid || !verification.address) {\n return NextResponse.json(\n { success: false, error: 'SIWX signature verification failed' },\n { status: 402 },\n );\n }\n\n const walletAddress = verification.address.toLowerCase();\n ctx.setVerifiedWallet(walletAddress);\n\n return handler(request, {\n ...ctx,\n verifiedWallet: walletAddress,\n } as SiwxTelemetryContext);\n });\n}\n\n/**\n * Build a 402 response with SIWX challenge.\n */\nfunction buildSiwxChallengeResponse(request: NextRequest): NextResponse {\n const url = new URL(request.url);\n const nonce = randomBytes(16).toString('hex');\n const issuedAt = new Date().toISOString();\n const expirationTime = new Date(Date.now() + 300_000).toISOString();\n\n const paymentRequired = {\n x402Version: 2,\n error: 'SIWX authentication required',\n resource: {\n url: request.url,\n description: 'SIWX-protected endpoint',\n mimeType: 'application/json',\n },\n accepts: [\n {\n scheme: 'exact' as const,\n network: 'eip155:8453' as const,\n asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n amount: '0',\n payTo: '0x0000000000000000000000000000000000000000',\n maxTimeoutSeconds: 300,\n extra: {},\n },\n ],\n extensions: {\n 'sign-in-with-x': {\n info: {\n domain: url.hostname,\n uri: request.url,\n version: '1',\n nonce,\n issuedAt,\n expirationTime,\n statement: 'Sign in to verify your wallet identity',\n resources: [request.url],\n },\n supportedChains: [{ chainId: 'eip155:8453', type: 'eip191' }],\n schema: buildSIWxSchema(),\n },\n },\n };\n\n const encoded = encodePaymentRequiredHeader(paymentRequired);\n\n return new NextResponse(JSON.stringify(paymentRequired), {\n status: 402,\n headers: {\n 'Content-Type': 'application/json',\n 'PAYMENT-REQUIRED': encoded,\n },\n });\n}\n"]}
package/dist/siwx.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  withTelemetry
3
- } from "./chunk-JVLKV7CX.mjs";
4
- import "./chunk-VOY67KA4.mjs";
5
- import "./chunk-O2PCP6KV.mjs";
3
+ } from "./chunk-YAHKVH4T.mjs";
4
+ import "./chunk-PZWW7EMH.mjs";
5
+ import "./chunk-V553T6WE.mjs";
6
6
 
7
7
  // src/siwx.ts
8
8
  import { NextResponse } from "next/server";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/telemetry",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "ClickHouse telemetry plugin for @agentcash/router. Logs request lifecycle, payments, settlements, and provider quota to ClickHouse.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -37,6 +37,10 @@
37
37
  "clickhouse",
38
38
  "payments"
39
39
  ],
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/Merit-Systems/agentcash-telemetry"
43
+ },
40
44
  "license": "MIT",
41
45
  "publishConfig": {
42
46
  "access": "public"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/chunk-FJ3YM6KO.js","../src/telemetry.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACF,sDAA4B;AAC5B;AACA;ACAA,qCAA+C;AAcxC,SAAS,aAAA,CAAc,OAAA,EAA2B;AACvD,EAAA,OAAO,MAAA,CAAO,OAAA,EAAA,GAAgD;AAC5D,IAAA,MAAM,KAAA,EAAO,iDAAA,OAA0B,CAAA;AACvC,IAAA,MAAM,IAAA,EAAM,oDAAA,IAA0B,CAAA;AAGtC,IAAA,IAAI,kBAAA,EAAmC,IAAA;AACvC,IAAA,GAAA,CAAI,IAAA,CAAK,OAAA,IAAW,OAAA,GAAU,IAAA,CAAK,OAAA,IAAW,MAAA,GAAS,IAAA,CAAK,OAAA,IAAW,OAAA,EAAS;AAC9E,MAAA,IAAI;AACF,QAAA,MAAM,KAAA,EAAO,MAAM,OAAA,CAAQ,KAAA,CAAM,CAAA,CAAE,IAAA,CAAK,CAAA;AACxC,QAAA,GAAA,CAAI,IAAA,EAAM,kBAAA,EAAoB,IAAA;AAAA,MAChC,EAAA,UAAQ;AAAA,MAER;AAAA,IACF;AAGA,IAAA,IAAI,QAAA;AACJ,IAAA,IAAI,aAAA,EAAwB,IAAA;AAE5B,IAAA,IAAI;AACF,MAAA,SAAA,EAAW,MAAM,OAAA,CAAQ,OAAA,EAAS,GAAG,CAAA;AAAA,IACvC,EAAA,MAAA,CAAS,KAAA,EAAgB;AACvB,MAAA,aAAA,EAAe,KAAA;AACf,MAAA,GAAA,CAAI,MAAA,WAAiB,oBAAA,EAAc;AACjC,QAAA,SAAA,EAAW,KAAA;AAAA,MACb,EAAA,KAAO;AACL,QAAA,MAAM,QAAA,EAAU,MAAA,WAAiB,MAAA,EAAQ,KAAA,CAAM,QAAA,EAAU,uBAAA;AACzD,QAAA,SAAA,EAAW,oBAAA,CAAa,IAAA,CAAK,EAAE,OAAA,EAAS,KAAA,EAAO,KAAA,EAAO,QAAQ,CAAA,EAAG,EAAE,MAAA,EAAQ,IAAI,CAAC,CAAA;AAAA,MAClF;AAAA,IACF;AAGA,IAAA,IAAI,mBAAA,EAAoC,IAAA;AACxC,IAAA,IAAI;AACF,MAAA,mBAAA,EAAqB,MAAM,QAAA,CAAS,KAAA,CAAM,CAAA,CAAE,IAAA,CAAK,CAAA;AAAA,IACnD,EAAA,WAAQ;AAAA,IAER;AAEA,IAAA,+CAAA,IAAiB,EAAM,iBAAA,EAAmB;AAAA,MACxC,MAAA,EAAQ,QAAA,CAAS,MAAA;AAAA,MACjB,IAAA,EAAM,kBAAA;AAAA,MACN,OAAA,EAAS,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,WAAA,CAAY,QAAA,CAAS,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAC,CAAA;AAAA,MACtE,WAAA,mBAAa,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,UAAK;AAAA,IACvD,CAAC,CAAA;AAGD,IAAA,GAAA,CAAI,aAAA,GAAgB,CAAA,CAAE,aAAA,WAAwB,oBAAA,CAAA,EAAe;AAC3D,MAAA,MAAM,YAAA;AAAA,IACR;AAEA,IAAA,OAAO,QAAA;AAAA,EACT,CAAA;AACF;ADxBA;AACA;AACE;AACF,sCAAC","file":"/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/chunk-FJ3YM6KO.js","sourcesContent":[null,"/**\n * Core telemetry wrapper for Next.js route handlers.\n * Extracts identity headers, logs to ClickHouse, extracts verified wallet.\n * This is a passive observer — it never influences the response.\n */\n\nimport { type NextRequest, NextResponse } from 'next/server';\nimport type { TelemetryContext } from './types';\nimport { extractRequestMeta, buildTelemetryContext, recordInvocation } from './telemetry-core';\n\ntype TelemetryHandler = (request: NextRequest, ctx: TelemetryContext) => Promise<NextResponse>;\n\n/**\n * Wrap a Next.js route handler with telemetry.\n * Extracts identity headers, logs the invocation to ClickHouse,\n * and auto-extracts verified wallet from x402 payment headers.\n *\n * The entire telemetry code path is wrapped in try/catch.\n * Telemetry failures never affect the response.\n */\nexport function withTelemetry(handler: TelemetryHandler) {\n return async (request: NextRequest): Promise<NextResponse> => {\n const meta = extractRequestMeta(request);\n const ctx = buildTelemetryContext(meta);\n\n // Capture request body for logging (only for methods with bodies)\n let requestBodyString: string | null = null;\n if (meta.method === 'POST' || meta.method === 'PUT' || meta.method === 'PATCH') {\n try {\n const body = await request.clone().text();\n if (body) requestBodyString = body;\n } catch {\n // Body read failed — that's fine\n }\n }\n\n // Execute the actual handler\n let response: NextResponse;\n let handlerError: unknown = null;\n\n try {\n response = await handler(request, ctx);\n } catch (error: unknown) {\n handlerError = error;\n if (error instanceof NextResponse) {\n response = error;\n } else {\n const message = error instanceof Error ? error.message : 'Internal server error';\n response = NextResponse.json({ success: false, error: message }, { status: 500 });\n }\n }\n\n // Log to ClickHouse (fire-and-forget)\n let responseBodyString: string | null = null;\n try {\n responseBodyString = await response.clone().text();\n } catch {\n // Response body read failed — that's fine\n }\n\n recordInvocation(meta, requestBodyString, {\n status: response.status,\n body: responseBodyString,\n headers: JSON.stringify(Object.fromEntries(response.headers.entries())),\n contentType: response.headers.get('content-type') ?? null,\n });\n\n // Re-throw the original error if it wasn't a NextResponse\n if (handlerError && !(handlerError instanceof NextResponse)) {\n throw handlerError;\n }\n\n return response;\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/chunk-GE7VBMQP.js","../src/init.ts","../src/extract-wallet.ts","../src/telemetry-core.ts"],"names":[],"mappings":"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","file":"/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/chunk-GE7VBMQP.js","sourcesContent":[null,"import type { TelemetryConfig } from './types';\nimport { initClickhouse, pingClickhouse } from './clickhouse';\n\nlet configuredOrigin: string | undefined;\n\n/**\n * Initialize the telemetry package. Call once at module level.\n *\n * This is synchronous — createClient() does not connect until first query.\n *\n * IMPORTANT: On Vercel, instrumentation.ts runs in a separate module scope\n * from route handlers. Call this in the same module that imports your route\n * wrappers (withTelemetry, createRouteBuilder, etc.), NOT in instrumentation.ts.\n *\n * ```typescript\n * import { initTelemetry, withTelemetry } from '@agentcash/telemetry';\n *\n * initTelemetry({\n * clickhouse: {\n * url: process.env.TELEM_CLICKHOUSE_URL!,\n * database: process.env.TELEM_CLICKHOUSE_DATABASE,\n * username: process.env.TELEM_CLICKHOUSE_USERNAME,\n * password: process.env.TELEM_CLICKHOUSE_PASSWORD,\n * },\n * });\n * ```\n */\nexport function initTelemetry(config: TelemetryConfig): void {\n initClickhouse(config.clickhouse);\n if (config.origin) {\n configuredOrigin = config.origin;\n }\n if (config.verify) {\n pingClickhouse();\n }\n}\n\n/** Get the configured origin, or undefined if not set. */\nexport function getOrigin(): string | undefined {\n return configuredOrigin;\n}\n","/**\n * Extract verified wallet address from x402 payment headers.\n *\n * Checks multiple sources in priority order:\n * 1. x-payer-address — injected by @x402/next's withX402 after verification (highest confidence)\n * 2. PAYMENT-SIGNATURE / X-PAYMENT — decode the payment header directly\n *\n * If the handler is executing, withX402 has already verified the payment signature.\n * For manual x402 flows, the app verifies before calling business logic.\n * Either way, the header content is trustworthy when this runs.\n */\nexport function extractVerifiedWallet(headers: Headers): string | null {\n try {\n // 1. x-payer-address: injected by @x402/next's withX402 after verification\n const payerAddress = headers.get('x-payer-address');\n if (payerAddress) {\n return payerAddress.toLowerCase();\n }\n\n // 2. Decode from PAYMENT-SIGNATURE or X-PAYMENT header\n const paymentHeader =\n headers.get('PAYMENT-SIGNATURE') ??\n headers.get('payment-signature') ??\n headers.get('X-PAYMENT') ??\n headers.get('x-payment');\n\n if (!paymentHeader) return null;\n\n // Decode the base64 payment header to extract the payer address.\n // The header is a base64-encoded JSON object with the structure:\n // { payload: { authorization: { from: \"0x...\" }, signature: \"0x...\" } }\n try {\n const decoded = JSON.parse(Buffer.from(paymentHeader, 'base64').toString()) as {\n payload?: { authorization?: { from?: string }; from?: string };\n };\n const from = decoded?.payload?.authorization?.from ?? decoded?.payload?.from;\n return typeof from === 'string' ? from.toLowerCase() : null;\n } catch {\n return null;\n }\n } catch {\n return null;\n }\n}\n","/**\n * Shared telemetry primitives used by withTelemetry and the route builder.\n * Extracts request metadata, builds telemetry context, and records invocations.\n */\n\nimport { type NextRequest } from 'next/server';\nimport { randomUUID } from 'crypto';\nimport type { TelemetryContext, McpResourceInvocation, RequestMeta } from './types';\nimport { insertInvocation } from './clickhouse';\nimport { extractVerifiedWallet } from './extract-wallet';\nimport { getOrigin } from './init';\n\n/**\n * Extract identity headers, route info, and verified wallet from a request.\n * All wrapped in try/catch — returns safe defaults on failure.\n */\nexport function extractRequestMeta(request: NextRequest): RequestMeta {\n const meta: RequestMeta = {\n requestId: randomUUID(),\n startTime: Date.now(),\n walletAddress: null,\n clientId: null,\n sessionId: null,\n verifiedWallet: null,\n route: '',\n method: '',\n origin: '',\n referer: null,\n requestContentType: null,\n requestHeadersJson: null,\n };\n\n try {\n meta.walletAddress = request.headers.get('X-Wallet-Address')?.toLowerCase() ?? null;\n meta.clientId = request.headers.get('X-Client-ID') ?? null;\n meta.sessionId = request.headers.get('X-Session-ID') ?? null;\n meta.referer = request.headers.get('Referer') ?? null;\n meta.requestContentType = request.headers.get('content-type') ?? null;\n meta.route = request.nextUrl.pathname;\n meta.method = request.method;\n meta.origin = getOrigin() ?? request.nextUrl.origin;\n meta.verifiedWallet = extractVerifiedWallet(request.headers);\n meta.requestHeadersJson = JSON.stringify(Object.fromEntries(request.headers.entries()));\n } catch {\n // Header extraction failed — continue with defaults\n }\n\n return meta;\n}\n\n/**\n * Build a TelemetryContext from extracted request metadata.\n * setVerifiedWallet mutates meta.verifiedWallet so recordInvocation sees the update.\n */\nexport function buildTelemetryContext(meta: RequestMeta): TelemetryContext {\n const ctx: TelemetryContext = {\n walletAddress: meta.walletAddress,\n clientId: meta.clientId,\n sessionId: meta.sessionId,\n verifiedWallet: meta.verifiedWallet,\n setVerifiedWallet: (address: string) => {\n meta.verifiedWallet = address.toLowerCase();\n ctx.verifiedWallet = meta.verifiedWallet;\n },\n };\n return ctx;\n}\n\n/**\n * Record an invocation to ClickHouse. Fire-and-forget, fully wrapped in try/catch.\n */\nexport function recordInvocation(\n meta: RequestMeta,\n requestBody: string | null,\n response: {\n status: number;\n body: string | null;\n headers: string | null;\n contentType: string | null;\n },\n): void {\n try {\n const invocation: McpResourceInvocation = {\n id: meta.requestId,\n x_wallet_address: meta.walletAddress,\n x_client_id: meta.clientId,\n session_id: meta.sessionId,\n verified_wallet_address: meta.verifiedWallet,\n method: meta.method,\n route: meta.route,\n origin: meta.origin,\n referer: meta.referer,\n request_content_type: meta.requestContentType,\n request_headers: meta.requestHeadersJson,\n request_body: requestBody,\n status_code: response.status,\n status_text: statusTextFromCode(response.status),\n duration: Date.now() - meta.startTime,\n response_content_type: response.contentType,\n response_headers: response.headers,\n response_body: response.body,\n created_at: new Date(),\n };\n insertInvocation(invocation);\n } catch {\n // Never affects the response\n }\n}\n\nfunction statusTextFromCode(code: number): string {\n switch (code) {\n case 200:\n return 'OK';\n case 201:\n return 'Created';\n case 204:\n return 'No Content';\n case 400:\n return 'Bad Request';\n case 401:\n return 'Unauthorized';\n case 402:\n return 'Payment Required';\n case 403:\n return 'Forbidden';\n case 404:\n return 'Not Found';\n case 500:\n return 'Internal Server Error';\n case 504:\n return 'Gateway Timeout';\n default:\n return String(code);\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/clickhouse.ts"],"sourcesContent":["import { createClient } from '@clickhouse/client';\nimport type { McpResourceInvocation, TelemetryConfig } from './types';\n\nlet clickhouseClient: ReturnType<typeof createClient> | null = null;\n\nconst TABLE = 'mcp_resource_invocations';\n\n/**\n * Initialize the ClickHouse client singleton.\n * createClient() is synchronous — no async needed.\n */\nexport function initClickhouse(config: TelemetryConfig['clickhouse']): void {\n clickhouseClient = createClient({\n url: config.url,\n database: config.database ?? 'default',\n username: config.username ?? 'default',\n password: config.password ?? '',\n });\n}\n\n/**\n * Ping ClickHouse to verify the connection. Fire-and-forget, logs result.\n */\nexport function pingClickhouse(): void {\n if (!clickhouseClient) {\n console.error('[telemetry] Cannot verify: ClickHouse client not initialized.');\n return;\n }\n clickhouseClient\n .ping()\n .then((result) => {\n if (result.success) {\n console.log('[telemetry] ClickHouse connected');\n } else {\n console.error('[telemetry] ClickHouse ping failed');\n }\n })\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error('[telemetry] ClickHouse ping failed:', message);\n });\n}\n\n/**\n * Fire-and-forget insert into mcp_resource_invocations.\n * Wrapped in try/catch — never throws, never blocks.\n */\nexport function insertInvocation(data: McpResourceInvocation): void {\n try {\n if (!clickhouseClient) {\n console.error('[telemetry] ClickHouse client not initialized. Call initTelemetry() first.');\n return;\n }\n\n // Fire and forget — do NOT await\n clickhouseClient\n .insert<McpResourceInvocation>({\n table: TABLE,\n values: [data],\n format: 'JSONEachRow',\n })\n .catch((error: unknown) => {\n try {\n const message = error instanceof Error ? error.message : String(error);\n console.error('[telemetry] ClickHouse insert failed:', message);\n } catch {\n // Absolutely nothing escapes\n }\n });\n } catch (error: unknown) {\n try {\n const message = error instanceof Error ? error.message : String(error);\n console.error('[telemetry] ClickHouse insert threw synchronously:', message);\n } catch {\n // Absolutely nothing escapes\n }\n }\n}\n"],"mappings":";AAAA,SAAS,oBAAoB;AAG7B,IAAI,mBAA2D;AAE/D,IAAM,QAAQ;AAMP,SAAS,eAAe,QAA6C;AAC1E,qBAAmB,aAAa;AAAA,IAC9B,KAAK,OAAO;AAAA,IACZ,UAAU,OAAO,YAAY;AAAA,IAC7B,UAAU,OAAO,YAAY;AAAA,IAC7B,UAAU,OAAO,YAAY;AAAA,EAC/B,CAAC;AACH;AAKO,SAAS,iBAAuB;AACrC,MAAI,CAAC,kBAAkB;AACrB,YAAQ,MAAM,+DAA+D;AAC7E;AAAA,EACF;AACA,mBACG,KAAK,EACL,KAAK,CAAC,WAAW;AAChB,QAAI,OAAO,SAAS;AAClB,cAAQ,IAAI,kCAAkC;AAAA,IAChD,OAAO;AACL,cAAQ,MAAM,oCAAoC;AAAA,IACpD;AAAA,EACF,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAQ,MAAM,uCAAuC,OAAO;AAAA,EAC9D,CAAC;AACL;AAMO,SAAS,iBAAiB,MAAmC;AAClE,MAAI;AACF,QAAI,CAAC,kBAAkB;AACrB,cAAQ,MAAM,4EAA4E;AAC1F;AAAA,IACF;AAGA,qBACG,OAA8B;AAAA,MAC7B,OAAO;AAAA,MACP,QAAQ,CAAC,IAAI;AAAA,MACb,QAAQ;AAAA,IACV,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,UAAI;AACF,cAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,gBAAQ,MAAM,yCAAyC,OAAO;AAAA,MAChE,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAAA,EACL,SAAS,OAAgB;AACvB,QAAI;AACF,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,cAAQ,MAAM,sDAAsD,OAAO;AAAA,IAC7E,QAAQ;AAAA,IAER;AAAA,EACF;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/chunk-P63MLKU3.js","../src/clickhouse.ts"],"names":[],"mappings":"AAAA;ACAA,4CAA6B;AAG7B,IAAI,iBAAA,EAA2D,IAAA;AAE/D,IAAM,MAAA,EAAQ,0BAAA;AAMP,SAAS,cAAA,CAAe,MAAA,EAA6C;AAC1E,EAAA,iBAAA,EAAmB,kCAAA;AAAa,IAC9B,GAAA,EAAK,MAAA,CAAO,GAAA;AAAA,IACZ,QAAA,mBAAU,MAAA,CAAO,QAAA,UAAY,WAAA;AAAA,IAC7B,QAAA,mBAAU,MAAA,CAAO,QAAA,UAAY,WAAA;AAAA,IAC7B,QAAA,mBAAU,MAAA,CAAO,QAAA,UAAY;AAAA,EAC/B,CAAC,CAAA;AACH;AAKO,SAAS,cAAA,CAAA,EAAuB;AACrC,EAAA,GAAA,CAAI,CAAC,gBAAA,EAAkB;AACrB,IAAA,OAAA,CAAQ,KAAA,CAAM,+DAA+D,CAAA;AAC7E,IAAA,MAAA;AAAA,EACF;AACA,EAAA,gBAAA,CACG,IAAA,CAAK,CAAA,CACL,IAAA,CAAK,CAAC,MAAA,EAAA,GAAW;AAChB,IAAA,GAAA,CAAI,MAAA,CAAO,OAAA,EAAS;AAClB,MAAA,OAAA,CAAQ,GAAA,CAAI,kCAAkC,CAAA;AAAA,IAChD,EAAA,KAAO;AACL,MAAA,OAAA,CAAQ,KAAA,CAAM,oCAAoC,CAAA;AAAA,IACpD;AAAA,EACF,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,KAAA,EAAA,GAAmB;AACzB,IAAA,MAAM,QAAA,EAAU,MAAA,WAAiB,MAAA,EAAQ,KAAA,CAAM,QAAA,EAAU,MAAA,CAAO,KAAK,CAAA;AACrE,IAAA,OAAA,CAAQ,KAAA,CAAM,qCAAA,EAAuC,OAAO,CAAA;AAAA,EAC9D,CAAC,CAAA;AACL;AAMO,SAAS,gBAAA,CAAiB,IAAA,EAAmC;AAClE,EAAA,IAAI;AACF,IAAA,GAAA,CAAI,CAAC,gBAAA,EAAkB;AACrB,MAAA,OAAA,CAAQ,KAAA,CAAM,4EAA4E,CAAA;AAC1F,MAAA,MAAA;AAAA,IACF;AAGA,IAAA,gBAAA,CACG,MAAA,CAA8B;AAAA,MAC7B,KAAA,EAAO,KAAA;AAAA,MACP,MAAA,EAAQ,CAAC,IAAI,CAAA;AAAA,MACb,MAAA,EAAQ;AAAA,IACV,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,KAAA,EAAA,GAAmB;AACzB,MAAA,IAAI;AACF,QAAA,MAAM,QAAA,EAAU,MAAA,WAAiB,MAAA,EAAQ,KAAA,CAAM,QAAA,EAAU,MAAA,CAAO,KAAK,CAAA;AACrE,QAAA,OAAA,CAAQ,KAAA,CAAM,uCAAA,EAAyC,OAAO,CAAA;AAAA,MAChE,EAAA,UAAQ;AAAA,MAER;AAAA,IACF,CAAC,CAAA;AAAA,EACL,EAAA,MAAA,CAAS,KAAA,EAAgB;AACvB,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,EAAU,MAAA,WAAiB,MAAA,EAAQ,KAAA,CAAM,QAAA,EAAU,MAAA,CAAO,KAAK,CAAA;AACrE,MAAA,OAAA,CAAQ,KAAA,CAAM,oDAAA,EAAsD,OAAO,CAAA;AAAA,IAC7E,EAAA,WAAQ;AAAA,IAER;AAAA,EACF;AACF;ADxBA;AACA;AACE;AACA;AACA;AACF,8HAAC","file":"/Users/samragsdale/Documents/Code/merit-systems/agentcash-telemetry/dist/chunk-P63MLKU3.js","sourcesContent":[null,"import { createClient } from '@clickhouse/client';\nimport type { McpResourceInvocation, TelemetryConfig } from './types';\n\nlet clickhouseClient: ReturnType<typeof createClient> | null = null;\n\nconst TABLE = 'mcp_resource_invocations';\n\n/**\n * Initialize the ClickHouse client singleton.\n * createClient() is synchronous — no async needed.\n */\nexport function initClickhouse(config: TelemetryConfig['clickhouse']): void {\n clickhouseClient = createClient({\n url: config.url,\n database: config.database ?? 'default',\n username: config.username ?? 'default',\n password: config.password ?? '',\n });\n}\n\n/**\n * Ping ClickHouse to verify the connection. Fire-and-forget, logs result.\n */\nexport function pingClickhouse(): void {\n if (!clickhouseClient) {\n console.error('[telemetry] Cannot verify: ClickHouse client not initialized.');\n return;\n }\n clickhouseClient\n .ping()\n .then((result) => {\n if (result.success) {\n console.log('[telemetry] ClickHouse connected');\n } else {\n console.error('[telemetry] ClickHouse ping failed');\n }\n })\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error('[telemetry] ClickHouse ping failed:', message);\n });\n}\n\n/**\n * Fire-and-forget insert into mcp_resource_invocations.\n * Wrapped in try/catch — never throws, never blocks.\n */\nexport function insertInvocation(data: McpResourceInvocation): void {\n try {\n if (!clickhouseClient) {\n console.error('[telemetry] ClickHouse client not initialized. Call initTelemetry() first.');\n return;\n }\n\n // Fire and forget — do NOT await\n clickhouseClient\n .insert<McpResourceInvocation>({\n table: TABLE,\n values: [data],\n format: 'JSONEachRow',\n })\n .catch((error: unknown) => {\n try {\n const message = error instanceof Error ? error.message : String(error);\n console.error('[telemetry] ClickHouse insert failed:', message);\n } catch {\n // Absolutely nothing escapes\n }\n });\n } catch (error: unknown) {\n try {\n const message = error instanceof Error ? error.message : String(error);\n console.error('[telemetry] ClickHouse insert threw synchronously:', message);\n } catch {\n // Absolutely nothing escapes\n }\n }\n}\n"]}