@rawdash/connector-anthropic 0.0.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/dist/index.js ADDED
@@ -0,0 +1,628 @@
1
+ // ../../connector-shared/dist/index.js
2
+ var HTTP_CLIENT_VERSION = "0.0.0";
3
+ var DEFAULT_USER_AGENT = `rawdash-connector/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;
4
+ function connectorUserAgent(connectorId) {
5
+ return `rawdash-connector-${connectorId}/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;
6
+ }
7
+ function parseEpoch(value, unit) {
8
+ if (value === null || value === void 0) {
9
+ return null;
10
+ }
11
+ if (unit === "iso") {
12
+ if (typeof value !== "string") {
13
+ return null;
14
+ }
15
+ const ms = new Date(value).getTime();
16
+ return Number.isFinite(ms) ? ms : null;
17
+ }
18
+ if (typeof value === "string" && value.trim() === "") {
19
+ return null;
20
+ }
21
+ const n = typeof value === "number" ? value : Number(value);
22
+ if (!Number.isFinite(n)) {
23
+ return null;
24
+ }
25
+ const result = unit === "s" ? n * 1e3 : n;
26
+ return Number.isFinite(result) ? result : null;
27
+ }
28
+
29
+ // src/anthropic.ts
30
+ import {
31
+ BaseConnector,
32
+ defineConfigFields,
33
+ defineConnectorDoc,
34
+ defineResources,
35
+ makeChunkedCursorGuard,
36
+ schemasFromResources,
37
+ selectActivePhases
38
+ } from "@rawdash/core";
39
+ import { z } from "zod";
40
+ var ANTHROPIC_API_HOST = "api.anthropic.com";
41
+ var ANTHROPIC_API_BASE = `https://${ANTHROPIC_API_HOST}`;
42
+ var ANTHROPIC_API_VERSION = "2023-06-01";
43
+ var USAGE_PAGE_LIMIT = 31;
44
+ var COSTS_PAGE_LIMIT = 31;
45
+ var MS_PER_DAY = 864e5;
46
+ var DEFAULT_LOOKBACK_DAYS = 30;
47
+ var INCREMENTAL_LOOKBACK_DAYS = 2;
48
+ var COST_AMOUNT_DIVISOR = 100;
49
+ var configFields = defineConfigFields(
50
+ z.object({
51
+ adminApiKey: z.object({ $secret: z.string().min(1) }).meta({
52
+ label: "Admin API key",
53
+ description: "Anthropic organization admin API key (starts with sk-ant-admin-). Create one at console.anthropic.com -> Settings -> Admin keys. Regular API keys (sk-ant-api-) cannot read the Usage and Cost reports.",
54
+ placeholder: "ANTHROPIC_ADMIN_API_KEY",
55
+ secret: true
56
+ }),
57
+ workspaceIds: z.array(z.string().min(1)).nonempty().optional().meta({
58
+ label: "Workspace IDs (optional)",
59
+ description: "Restrict usage and cost queries to specific Anthropic workspace ids (wrkspc_...). Omit to aggregate every workspace the admin key can see."
60
+ }),
61
+ resources: z.array(
62
+ z.enum([
63
+ "anthropic_input_tokens",
64
+ "anthropic_output_tokens",
65
+ "anthropic_cache_read_tokens",
66
+ "anthropic_cache_creation_tokens",
67
+ "anthropic_web_search_requests",
68
+ "anthropic_cost_usd"
69
+ ])
70
+ ).nonempty().optional().meta({
71
+ label: "Resources",
72
+ description: "Which Anthropic metric series to sync. Omit to sync all of them. The five usage metrics share one upstream call to the Messages Usage Report; enabling any one of them fetches the report and writes all five."
73
+ }),
74
+ lookbackDays: z.number().int().positive().max(180).optional().meta({
75
+ label: "Backfill window (days)",
76
+ description: "How many days of usage history to fetch on a full sync. Defaults to 30. The Usage Report returns at most 31 buckets per page, so longer windows paginate.",
77
+ placeholder: "30"
78
+ })
79
+ })
80
+ );
81
+ var doc = defineConnectorDoc({
82
+ displayName: "Anthropic",
83
+ category: "engineering",
84
+ brandColor: "#D97757",
85
+ tagline: "Track Anthropic spend, daily token usage across Claude models, cache hit volumes, and web-search tool requests from the Anthropic Admin API.",
86
+ vendor: {
87
+ name: "Anthropic",
88
+ domain: "anthropic.com",
89
+ apiDocs: "https://docs.claude.com/en/api/admin-api/usage-cost/get-messages-usage-report",
90
+ website: "https://anthropic.com"
91
+ },
92
+ auth: {
93
+ summary: "Authenticates with an Anthropic organization admin API key (sk-ant-admin-). Admin keys are the only key class that can read the Usage and Cost reports; regular API keys return 403.",
94
+ setup: [
95
+ "Open console.anthropic.com -> Settings -> Admin Keys and create a new admin key. Admin keys are organization-scoped, so create the key from the organization whose usage you want to read.",
96
+ "Store the key as a secret (e.g. ANTHROPIC_ADMIN_API_KEY).",
97
+ 'Reference it from config as `adminApiKey: secret("ANTHROPIC_ADMIN_API_KEY")`.',
98
+ "Optionally set `workspaceIds` to restrict the query to a subset of workspaces."
99
+ ]
100
+ },
101
+ rateLimit: "The Admin API returns 429 with a Retry-After header on burst; the shared HTTP client honors it automatically. Daily syncs against the Usage and Cost reports are well below the per-organization Admin API budget.",
102
+ limitations: [
103
+ "Only the organization Messages Usage Report and Cost Report endpoints are synced. Per-request logs and individual message bodies are not exposed by the Admin API.",
104
+ "All samples are bucketed daily (1d bucket_width). The Usage Report also supports hourly and per-minute granularity but those are not exposed here in v1.",
105
+ "The Cost Report only supports 1d bucket_width and reports cost in USD; non-USD billing currencies are not converted.",
106
+ "Admin API keys are required - regular sk-ant-api- keys do not have access to the organization Usage and Cost reports."
107
+ ]
108
+ });
109
+ var PHASE_ORDER = ["usage_messages", "cost_report"];
110
+ var isAnthropicSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);
111
+ var RESOURCES_BY_PHASE = {
112
+ usage_messages: [
113
+ "anthropic_input_tokens",
114
+ "anthropic_output_tokens",
115
+ "anthropic_cache_read_tokens",
116
+ "anthropic_cache_creation_tokens",
117
+ "anthropic_web_search_requests"
118
+ ],
119
+ cost_report: ["anthropic_cost_usd"]
120
+ };
121
+ var PHASE_ENDPOINT_PATH = {
122
+ usage_messages: "/v1/organizations/usage_report/messages",
123
+ cost_report: "/v1/organizations/cost_report"
124
+ };
125
+ var usageCacheCreationSchema = z.object({
126
+ ephemeral_1h_input_tokens: z.number().nonnegative(),
127
+ ephemeral_5m_input_tokens: z.number().nonnegative()
128
+ });
129
+ var usageServerToolUseSchema = z.object({
130
+ web_search_requests: z.number().int().nonnegative()
131
+ });
132
+ var usageResultSchema = z.object({
133
+ account_id: z.string().nullish(),
134
+ api_key_id: z.string().nullish(),
135
+ cache_creation: usageCacheCreationSchema.nullish(),
136
+ cache_read_input_tokens: z.number().nonnegative(),
137
+ context_window: z.string().nullish(),
138
+ inference_geo: z.string().nullish(),
139
+ model: z.string().nullish(),
140
+ output_tokens: z.number().nonnegative(),
141
+ server_tool_use: usageServerToolUseSchema.nullish(),
142
+ service_account_id: z.string().nullish(),
143
+ service_tier: z.string().nullish(),
144
+ uncached_input_tokens: z.number().nonnegative(),
145
+ workspace_id: z.string().nullish()
146
+ });
147
+ var costResultSchema = z.object({
148
+ amount: z.string(),
149
+ context_window: z.string().nullish(),
150
+ cost_type: z.string().nullish(),
151
+ currency: z.string(),
152
+ description: z.string().nullish(),
153
+ inference_geo: z.string().nullish(),
154
+ model: z.string().nullish(),
155
+ service_tier: z.string().nullish(),
156
+ token_type: z.string().nullish(),
157
+ workspace_id: z.string().nullish()
158
+ });
159
+ function bucketResponseSchema(resultSchema) {
160
+ return z.object({
161
+ data: z.array(
162
+ z.object({
163
+ starting_at: z.string(),
164
+ ending_at: z.string(),
165
+ results: z.array(resultSchema)
166
+ })
167
+ ),
168
+ has_more: z.boolean(),
169
+ next_page: z.string().nullish()
170
+ });
171
+ }
172
+ var usageResponseSchema = bucketResponseSchema(usageResultSchema);
173
+ var costResponseSchema = bucketResponseSchema(costResultSchema);
174
+ var USAGE_DIMENSIONS = [
175
+ {
176
+ name: "model",
177
+ description: "Claude model id reported by Anthropic (or null)."
178
+ },
179
+ {
180
+ name: "workspace_id",
181
+ description: "Anthropic workspace id the usage is attributed to (or null for the default workspace)."
182
+ },
183
+ {
184
+ name: "api_key_id",
185
+ description: "API key id the usage is attributed to (or null)."
186
+ },
187
+ {
188
+ name: "service_tier",
189
+ description: "Service tier the request ran under (standard, batch, priority, flex, etc.), or null."
190
+ },
191
+ {
192
+ name: "context_window",
193
+ description: "Context window bucket the request used (0-200k or 200k-1M), or null."
194
+ },
195
+ {
196
+ name: "inference_geo",
197
+ description: "Inference geo the request ran in (global, us, not_available), or null."
198
+ }
199
+ ];
200
+ var COST_DIMENSIONS = [
201
+ {
202
+ name: "workspace_id",
203
+ description: "Anthropic workspace id the cost is attributed to (or null for the default workspace)."
204
+ },
205
+ {
206
+ name: "description",
207
+ description: 'Human-readable cost line item label (e.g. "Claude Sonnet 4 Usage - Input Tokens"), or null when ungrouped.'
208
+ },
209
+ {
210
+ name: "cost_type",
211
+ description: "Cost category (tokens, web_search, code_execution, session_usage), or null."
212
+ },
213
+ {
214
+ name: "model",
215
+ description: "Claude model the cost is attributed to (or null for non-token costs)."
216
+ },
217
+ {
218
+ name: "token_type",
219
+ description: "Token category for token costs (uncached_input_tokens, output_tokens, cache_read_input_tokens, cache_creation.ephemeral_*_input_tokens), or null."
220
+ },
221
+ {
222
+ name: "service_tier",
223
+ description: "Service tier the cost is attributed to (standard or batch), or null."
224
+ },
225
+ {
226
+ name: "context_window",
227
+ description: "Context window the cost is attributed to (0-200k or 200k-1M), or null."
228
+ },
229
+ {
230
+ name: "currency",
231
+ description: "Billing currency reported by Anthropic (currently always USD)."
232
+ }
233
+ ];
234
+ var anthropicResources = defineResources({
235
+ anthropic_input_tokens: {
236
+ shape: "metric",
237
+ description: "Daily uncached input tokens processed by the Anthropic Messages API, grouped by model and workspace.",
238
+ endpoint: "GET /v1/organizations/usage_report/messages",
239
+ unit: "tokens",
240
+ granularity: "daily",
241
+ dimensions: [...USAGE_DIMENSIONS],
242
+ notes: "Sample value is uncached_input_tokens. Cache-read and cache-creation token volumes are mirrored on their own metrics so a cache hit ratio can be computed at query time.",
243
+ responses: { usage_messages: usageResponseSchema }
244
+ },
245
+ anthropic_output_tokens: {
246
+ shape: "metric",
247
+ description: "Daily output tokens generated by the Anthropic Messages API, grouped by model and workspace.",
248
+ endpoint: "GET /v1/organizations/usage_report/messages",
249
+ unit: "tokens",
250
+ granularity: "daily",
251
+ dimensions: [...USAGE_DIMENSIONS],
252
+ notes: "Written alongside anthropic_input_tokens from the same usage_messages API call."
253
+ },
254
+ anthropic_cache_read_tokens: {
255
+ shape: "metric",
256
+ description: "Daily input tokens read from the prompt cache, grouped by model and workspace.",
257
+ endpoint: "GET /v1/organizations/usage_report/messages",
258
+ unit: "tokens",
259
+ granularity: "daily",
260
+ dimensions: [...USAGE_DIMENSIONS],
261
+ notes: "Cache hits are charged at a fraction of the uncached rate, so this metric paired with anthropic_input_tokens gives the cache hit ratio."
262
+ },
263
+ anthropic_cache_creation_tokens: {
264
+ shape: "metric",
265
+ description: "Daily input tokens written into the prompt cache (sum of the 1h and 5m ephemeral caches), grouped by model and workspace.",
266
+ endpoint: "GET /v1/organizations/usage_report/messages",
267
+ unit: "tokens",
268
+ granularity: "daily",
269
+ dimensions: [...USAGE_DIMENSIONS],
270
+ notes: "The per-cache-bucket counts (ephemeral_1h_input_tokens, ephemeral_5m_input_tokens) are mirrored in attributes for finer-grained widgets."
271
+ },
272
+ anthropic_web_search_requests: {
273
+ shape: "metric",
274
+ description: "Daily count of web-search tool requests executed server-side by Claude, grouped by model and workspace.",
275
+ endpoint: "GET /v1/organizations/usage_report/messages",
276
+ unit: "requests",
277
+ granularity: "daily",
278
+ dimensions: [...USAGE_DIMENSIONS],
279
+ notes: 'Sourced from server_tool_use.web_search_requests on each usage bucket. Zero rows are still written so a "no usage today" widget renders correctly.'
280
+ },
281
+ anthropic_cost_usd: {
282
+ shape: "metric",
283
+ description: "Daily organization spend in USD, broken down by workspace and cost line item, pulled from the Anthropic Cost Report.",
284
+ endpoint: "GET /v1/organizations/cost_report",
285
+ unit: "USD",
286
+ granularity: "daily",
287
+ dimensions: [...COST_DIMENSIONS],
288
+ notes: "The Cost Report returns amounts as a decimal string in the lowest currency unit (cents for USD). The connector divides by 100 so the stored metric value is dollars. Costs can be revised for a couple of days after the fact; incremental syncs refetch a short trailing window to pick up adjustments.",
289
+ responses: { cost_report: costResponseSchema }
290
+ }
291
+ });
292
+ var anthropicCredentials = {
293
+ adminApiKey: {
294
+ description: "Anthropic organization admin API key (sk-ant-admin-...)",
295
+ auth: "required"
296
+ }
297
+ };
298
+ var id = "anthropic";
299
+ function getUsageWindow(options, lookbackDays, now = Date.now()) {
300
+ const todayStart = Math.floor(now / MS_PER_DAY) * MS_PER_DAY;
301
+ const endMs = todayStart + MS_PER_DAY;
302
+ let days = lookbackDays;
303
+ if (options.mode === "latest") {
304
+ days = INCREMENTAL_LOOKBACK_DAYS;
305
+ } else if (options.since !== void 0) {
306
+ const sinceMs = parseEpoch(options.since, "iso");
307
+ if (sinceMs !== null) {
308
+ const elapsed = Math.ceil((now - sinceMs) / MS_PER_DAY);
309
+ days = Math.min(
310
+ Math.max(elapsed + INCREMENTAL_LOOKBACK_DAYS, 1),
311
+ lookbackDays
312
+ );
313
+ }
314
+ }
315
+ const startMs = endMs - days * MS_PER_DAY;
316
+ return {
317
+ startingAt: new Date(startMs).toISOString(),
318
+ endingAt: new Date(endMs).toISOString()
319
+ };
320
+ }
321
+ function resourceToPhase(resource) {
322
+ for (const phase of PHASE_ORDER) {
323
+ if (RESOURCES_BY_PHASE[phase].includes(resource)) {
324
+ return phase;
325
+ }
326
+ }
327
+ throw new Error(`anthropic: unmapped resource ${resource}`);
328
+ }
329
+ function nullableString(value) {
330
+ return value === void 0 || value === null ? null : value;
331
+ }
332
+ function usageDimensionAttributes(row) {
333
+ return {
334
+ model: nullableString(row.model),
335
+ workspace_id: nullableString(row.workspace_id),
336
+ api_key_id: nullableString(row.api_key_id),
337
+ service_tier: nullableString(row.service_tier),
338
+ context_window: nullableString(row.context_window),
339
+ inference_geo: nullableString(row.inference_geo),
340
+ account_id: nullableString(row.account_id),
341
+ service_account_id: nullableString(row.service_account_id)
342
+ };
343
+ }
344
+ function cacheCreationTotal(row) {
345
+ const c = row.cache_creation;
346
+ if (!c) {
347
+ return 0;
348
+ }
349
+ return c.ephemeral_1h_input_tokens + c.ephemeral_5m_input_tokens;
350
+ }
351
+ function tsFromBucket(bucket) {
352
+ return parseEpoch(bucket.starting_at, "iso");
353
+ }
354
+ function buildUsageSamples(buckets) {
355
+ const inputTokens = [];
356
+ const outputTokens = [];
357
+ const cacheReadTokens = [];
358
+ const cacheCreationTokens = [];
359
+ const webSearchRequests = [];
360
+ for (const bucket of buckets) {
361
+ const ts = tsFromBucket(bucket);
362
+ if (ts === null) {
363
+ continue;
364
+ }
365
+ for (const row of bucket.results) {
366
+ const common = usageDimensionAttributes(row);
367
+ const cacheCreation = row.cache_creation;
368
+ inputTokens.push({
369
+ name: "anthropic_input_tokens",
370
+ ts,
371
+ value: row.uncached_input_tokens,
372
+ attributes: { ...common }
373
+ });
374
+ outputTokens.push({
375
+ name: "anthropic_output_tokens",
376
+ ts,
377
+ value: row.output_tokens,
378
+ attributes: { ...common }
379
+ });
380
+ cacheReadTokens.push({
381
+ name: "anthropic_cache_read_tokens",
382
+ ts,
383
+ value: row.cache_read_input_tokens,
384
+ attributes: { ...common }
385
+ });
386
+ cacheCreationTokens.push({
387
+ name: "anthropic_cache_creation_tokens",
388
+ ts,
389
+ value: cacheCreationTotal(row),
390
+ attributes: {
391
+ ...common,
392
+ ephemeral_1h_input_tokens: cacheCreation?.ephemeral_1h_input_tokens ?? 0,
393
+ ephemeral_5m_input_tokens: cacheCreation?.ephemeral_5m_input_tokens ?? 0
394
+ }
395
+ });
396
+ webSearchRequests.push({
397
+ name: "anthropic_web_search_requests",
398
+ ts,
399
+ value: row.server_tool_use?.web_search_requests ?? 0,
400
+ attributes: { ...common }
401
+ });
402
+ }
403
+ }
404
+ return {
405
+ inputTokens,
406
+ outputTokens,
407
+ cacheReadTokens,
408
+ cacheCreationTokens,
409
+ webSearchRequests
410
+ };
411
+ }
412
+ function buildCostSamples(buckets) {
413
+ const samples = [];
414
+ for (const bucket of buckets) {
415
+ const ts = tsFromBucket(bucket);
416
+ if (ts === null) {
417
+ continue;
418
+ }
419
+ for (const row of bucket.results) {
420
+ const rawAmount = Number.parseFloat(row.amount);
421
+ const value = Number.isFinite(rawAmount) ? rawAmount / COST_AMOUNT_DIVISOR : 0;
422
+ samples.push({
423
+ name: "anthropic_cost_usd",
424
+ ts,
425
+ value,
426
+ attributes: {
427
+ workspace_id: nullableString(row.workspace_id),
428
+ description: nullableString(row.description),
429
+ cost_type: nullableString(row.cost_type),
430
+ model: nullableString(row.model),
431
+ token_type: nullableString(row.token_type),
432
+ service_tier: nullableString(row.service_tier),
433
+ context_window: nullableString(row.context_window),
434
+ inference_geo: nullableString(row.inference_geo),
435
+ currency: row.currency
436
+ }
437
+ });
438
+ }
439
+ }
440
+ return samples;
441
+ }
442
+ var AnthropicConnector = class _AnthropicConnector extends BaseConnector {
443
+ static id = id;
444
+ static resources = anthropicResources;
445
+ static schemas = schemasFromResources(anthropicResources);
446
+ static create(input, ctx) {
447
+ const parsed = configFields.parse(input);
448
+ return new _AnthropicConnector(
449
+ {
450
+ workspaceIds: parsed.workspaceIds,
451
+ resources: parsed.resources,
452
+ lookbackDays: parsed.lookbackDays
453
+ },
454
+ { adminApiKey: parsed.adminApiKey },
455
+ ctx
456
+ );
457
+ }
458
+ id = id;
459
+ credentials = anthropicCredentials;
460
+ buildHeaders() {
461
+ return {
462
+ "X-Api-Key": String(this.creds.adminApiKey),
463
+ "anthropic-version": ANTHROPIC_API_VERSION,
464
+ "User-Agent": connectorUserAgent(this.id)
465
+ };
466
+ }
467
+ fetch(url, resource, signal) {
468
+ return this.get(url, {
469
+ resource,
470
+ headers: this.buildHeaders(),
471
+ signal
472
+ });
473
+ }
474
+ buildInitialUrl(phase, window) {
475
+ const url = new URL(`${ANTHROPIC_API_BASE}${PHASE_ENDPOINT_PATH[phase]}`);
476
+ url.searchParams.set("starting_at", window.startingAt);
477
+ url.searchParams.set("ending_at", window.endingAt);
478
+ url.searchParams.set("bucket_width", "1d");
479
+ if (phase === "usage_messages") {
480
+ url.searchParams.set("limit", String(USAGE_PAGE_LIMIT));
481
+ url.searchParams.append("group_by", "model");
482
+ url.searchParams.append("group_by", "workspace_id");
483
+ url.searchParams.append("group_by", "api_key_id");
484
+ url.searchParams.append("group_by", "service_tier");
485
+ url.searchParams.append("group_by", "context_window");
486
+ url.searchParams.append("group_by", "inference_geo");
487
+ for (const workspaceId of this.settings.workspaceIds ?? []) {
488
+ url.searchParams.append("workspace_ids", workspaceId);
489
+ }
490
+ } else {
491
+ url.searchParams.set("limit", String(COSTS_PAGE_LIMIT));
492
+ url.searchParams.append("group_by", "workspace_id");
493
+ url.searchParams.append("group_by", "description");
494
+ }
495
+ return url.toString();
496
+ }
497
+ buildNextUrl(currentUrl, nextPage) {
498
+ const url = new URL(currentUrl);
499
+ url.searchParams.set("page", nextPage);
500
+ return url.toString();
501
+ }
502
+ async fetchPhasePage(phase, schema, initialUrl, page, signal) {
503
+ const url = page ?? initialUrl;
504
+ const res = await this.fetch(url, phase, signal);
505
+ const parsed = schema.parse(res.body);
506
+ const body = parsed;
507
+ const nextPage = body.has_more === true && typeof body.next_page === "string" && body.next_page.length > 0 ? body.next_page : null;
508
+ const nextUrl = nextPage ? this.buildNextUrl(url, nextPage) : null;
509
+ return { url, parsed, nextUrl };
510
+ }
511
+ async sync(options, storage, signal) {
512
+ const cursor = isAnthropicSyncCursor(options.cursor) ? options.cursor : void 0;
513
+ const lookbackDays = this.settings.lookbackDays ?? DEFAULT_LOOKBACK_DAYS;
514
+ const window = getUsageWindow(options, lookbackDays);
515
+ const phases = selectActivePhases(
516
+ resourceToPhase,
517
+ PHASE_ORDER,
518
+ this.settings.resources
519
+ );
520
+ const startIdx = cursor ? phases.indexOf(cursor.phase) : 0;
521
+ const resumeIdx = startIdx >= 0 ? startIdx : 0;
522
+ for (let i = resumeIdx; i < phases.length; i++) {
523
+ const phase = phases[i];
524
+ if (signal?.aborted) {
525
+ return { done: false, cursor: { phase, page: null } };
526
+ }
527
+ const phaseStart = Date.now();
528
+ const initialUrl = this.buildInitialUrl(phase, window);
529
+ let pageUrl = null;
530
+ let pageCount = 0;
531
+ const buckets = [];
532
+ while (true) {
533
+ if (signal?.aborted) {
534
+ return { done: false, cursor: { phase, page: null } };
535
+ }
536
+ pageCount += 1;
537
+ const { parsed, nextUrl } = await this.fetchAnyPhasePage(
538
+ phase,
539
+ initialUrl,
540
+ pageUrl,
541
+ signal
542
+ );
543
+ const data = parsed.data;
544
+ buckets.push(...data);
545
+ this.logger.info("fetched page", {
546
+ resource: phase,
547
+ page: pageCount,
548
+ items: data.length
549
+ });
550
+ if (nextUrl === null) {
551
+ break;
552
+ }
553
+ pageUrl = nextUrl;
554
+ }
555
+ await this.writePhase(storage, phase, buckets);
556
+ this.logger.info("resource done", {
557
+ resource: phase,
558
+ pages: pageCount,
559
+ items: buckets.length,
560
+ duration_ms: Date.now() - phaseStart
561
+ });
562
+ }
563
+ return { done: true };
564
+ }
565
+ async fetchAnyPhasePage(phase, initialUrl, page, signal) {
566
+ switch (phase) {
567
+ case "usage_messages":
568
+ return this.fetchPhasePage(
569
+ phase,
570
+ usageResponseSchema,
571
+ initialUrl,
572
+ page,
573
+ signal
574
+ );
575
+ case "cost_report":
576
+ return this.fetchPhasePage(
577
+ phase,
578
+ costResponseSchema,
579
+ initialUrl,
580
+ page,
581
+ signal
582
+ );
583
+ }
584
+ }
585
+ async writePhase(storage, phase, buckets) {
586
+ switch (phase) {
587
+ case "usage_messages": {
588
+ const samples = buildUsageSamples(buckets);
589
+ await storage.metrics(samples.inputTokens, {
590
+ names: ["anthropic_input_tokens"]
591
+ });
592
+ await storage.metrics(samples.outputTokens, {
593
+ names: ["anthropic_output_tokens"]
594
+ });
595
+ await storage.metrics(samples.cacheReadTokens, {
596
+ names: ["anthropic_cache_read_tokens"]
597
+ });
598
+ await storage.metrics(samples.cacheCreationTokens, {
599
+ names: ["anthropic_cache_creation_tokens"]
600
+ });
601
+ await storage.metrics(samples.webSearchRequests, {
602
+ names: ["anthropic_web_search_requests"]
603
+ });
604
+ return;
605
+ }
606
+ case "cost_report": {
607
+ const samples = buildCostSamples(buckets);
608
+ await storage.metrics(samples, { names: ["anthropic_cost_usd"] });
609
+ return;
610
+ }
611
+ }
612
+ }
613
+ };
614
+
615
+ // src/index.ts
616
+ var index_default = AnthropicConnector;
617
+ export {
618
+ AnthropicConnector,
619
+ buildCostSamples,
620
+ buildUsageSamples,
621
+ configFields,
622
+ index_default as default,
623
+ doc,
624
+ getUsageWindow,
625
+ id,
626
+ anthropicResources as resources
627
+ };
628
+ //# sourceMappingURL=index.js.map