@rawdash/connector-anthropic 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -134,7 +134,7 @@ The Admin API returns 429 with a Retry-After header on burst; the shared HTTP cl
|
|
|
134
134
|
|
|
135
135
|
## Links
|
|
136
136
|
|
|
137
|
-
- [Rawdash docs](https://rawdash.dev/docs/connectors
|
|
137
|
+
- [Rawdash docs](https://rawdash.dev/docs/connectors)
|
|
138
138
|
- [Anthropic API docs](https://docs.claude.com/en/api/admin-api/usage-cost/get-messages-usage-report)
|
|
139
139
|
- [GitHub](https://github.com/rawdash/rawdash)
|
|
140
140
|
|
package/dist/index.d.ts
CHANGED
|
@@ -22,8 +22,8 @@ declare const usageResultSchema: z.ZodObject<{
|
|
|
22
22
|
account_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
23
23
|
api_key_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
24
24
|
cache_creation: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
25
|
-
ephemeral_1h_input_tokens: z.ZodNumber
|
|
26
|
-
ephemeral_5m_input_tokens: z.ZodNumber
|
|
25
|
+
ephemeral_1h_input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
26
|
+
ephemeral_5m_input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
27
27
|
}, z.core.$strip>>>;
|
|
28
28
|
cache_read_input_tokens: z.ZodNumber;
|
|
29
29
|
context_window: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -31,7 +31,7 @@ declare const usageResultSchema: z.ZodObject<{
|
|
|
31
31
|
model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
32
32
|
output_tokens: z.ZodNumber;
|
|
33
33
|
server_tool_use: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
34
|
-
web_search_requests: z.ZodNumber
|
|
34
|
+
web_search_requests: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
35
35
|
}, z.core.$strip>>>;
|
|
36
36
|
service_account_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
37
37
|
service_tier: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -97,8 +97,8 @@ declare const anthropicResources: {
|
|
|
97
97
|
account_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
98
98
|
api_key_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
99
99
|
cache_creation: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
100
|
-
ephemeral_1h_input_tokens: z.ZodNumber
|
|
101
|
-
ephemeral_5m_input_tokens: z.ZodNumber
|
|
100
|
+
ephemeral_1h_input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
101
|
+
ephemeral_5m_input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
102
102
|
}, z.core.$strip>>>;
|
|
103
103
|
cache_read_input_tokens: z.ZodNumber;
|
|
104
104
|
context_window: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -106,7 +106,7 @@ declare const anthropicResources: {
|
|
|
106
106
|
model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
107
107
|
output_tokens: z.ZodNumber;
|
|
108
108
|
server_tool_use: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
109
|
-
web_search_requests: z.ZodNumber
|
|
109
|
+
web_search_requests: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
110
110
|
}, z.core.$strip>>>;
|
|
111
111
|
service_account_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
112
112
|
service_tier: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -347,8 +347,8 @@ declare class AnthropicConnector extends BaseConnector<AnthropicSettings, Anthro
|
|
|
347
347
|
account_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
348
348
|
api_key_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
349
349
|
cache_creation: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
350
|
-
ephemeral_1h_input_tokens: z.ZodNumber
|
|
351
|
-
ephemeral_5m_input_tokens: z.ZodNumber
|
|
350
|
+
ephemeral_1h_input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
351
|
+
ephemeral_5m_input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
352
352
|
}, z.core.$strip>>>;
|
|
353
353
|
cache_read_input_tokens: z.ZodNumber;
|
|
354
354
|
context_window: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -356,7 +356,7 @@ declare class AnthropicConnector extends BaseConnector<AnthropicSettings, Anthro
|
|
|
356
356
|
model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
357
357
|
output_tokens: z.ZodNumber;
|
|
358
358
|
server_tool_use: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
359
|
-
web_search_requests: z.ZodNumber
|
|
359
|
+
web_search_requests: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
360
360
|
}, z.core.$strip>>>;
|
|
361
361
|
service_account_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
362
362
|
service_tier: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -542,8 +542,8 @@ declare class AnthropicConnector extends BaseConnector<AnthropicSettings, Anthro
|
|
|
542
542
|
account_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
543
543
|
api_key_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
544
544
|
cache_creation: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
545
|
-
ephemeral_1h_input_tokens: z.ZodNumber
|
|
546
|
-
ephemeral_5m_input_tokens: z.ZodNumber
|
|
545
|
+
ephemeral_1h_input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
546
|
+
ephemeral_5m_input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
547
547
|
}, z.core.$strip>>>;
|
|
548
548
|
cache_read_input_tokens: z.ZodNumber;
|
|
549
549
|
context_window: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -551,7 +551,7 @@ declare class AnthropicConnector extends BaseConnector<AnthropicSettings, Anthro
|
|
|
551
551
|
model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
552
552
|
output_tokens: z.ZodNumber;
|
|
553
553
|
server_tool_use: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
554
|
-
web_search_requests: z.ZodNumber
|
|
554
|
+
web_search_requests: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
555
555
|
}, z.core.$strip>>>;
|
|
556
556
|
service_account_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
557
557
|
service_tier: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
package/dist/index.js
CHANGED
|
@@ -123,11 +123,11 @@ var PHASE_ENDPOINT_PATH = {
|
|
|
123
123
|
cost_report: "/v1/organizations/cost_report"
|
|
124
124
|
};
|
|
125
125
|
var usageCacheCreationSchema = z.object({
|
|
126
|
-
ephemeral_1h_input_tokens: z.number().nonnegative(),
|
|
127
|
-
ephemeral_5m_input_tokens: z.number().nonnegative()
|
|
126
|
+
ephemeral_1h_input_tokens: z.number().nonnegative().nullish(),
|
|
127
|
+
ephemeral_5m_input_tokens: z.number().nonnegative().nullish()
|
|
128
128
|
});
|
|
129
129
|
var usageServerToolUseSchema = z.object({
|
|
130
|
-
web_search_requests: z.number().int().nonnegative()
|
|
130
|
+
web_search_requests: z.number().int().nonnegative().nullish()
|
|
131
131
|
});
|
|
132
132
|
var usageResultSchema = z.object({
|
|
133
133
|
account_id: z.string().nullish(),
|
|
@@ -346,7 +346,7 @@ function cacheCreationTotal(row) {
|
|
|
346
346
|
if (!c) {
|
|
347
347
|
return 0;
|
|
348
348
|
}
|
|
349
|
-
return c.ephemeral_1h_input_tokens + c.ephemeral_5m_input_tokens;
|
|
349
|
+
return (c.ephemeral_1h_input_tokens ?? 0) + (c.ephemeral_5m_input_tokens ?? 0);
|
|
350
350
|
}
|
|
351
351
|
function tsFromBucket(bucket) {
|
|
352
352
|
return parseEpoch(bucket.starting_at, "iso");
|
|
@@ -431,7 +431,6 @@ function buildCostSamples(buckets) {
|
|
|
431
431
|
token_type: nullableString(row.token_type),
|
|
432
432
|
service_tier: nullableString(row.service_tier),
|
|
433
433
|
context_window: nullableString(row.context_window),
|
|
434
|
-
inference_geo: nullableString(row.inference_geo),
|
|
435
434
|
currency: row.currency
|
|
436
435
|
}
|
|
437
436
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../connector-shared/src/errors.ts","../../../connector-shared/src/retry.ts","../../../connector-shared/src/version.ts","../../../connector-shared/src/request.ts","../../../connector-shared/src/rate-limit.ts","../../../connector-shared/src/map-concurrent.ts","../../../connector-shared/src/sanitize.ts","../../../connector-shared/src/epoch.ts","../../../connector-shared/src/pagination.ts","../../../connector-shared/src/logger.ts","../src/anthropic.ts","../src/index.ts"],"sourcesContent":["import type { HttpResponse } from './types';\n\nexport type HttpErrorKind =\n | 'transient'\n | 'rate_limit'\n | 'auth'\n | 'upstream_bug'\n | 'client_bug';\n\nexport abstract class HttpClientError extends Error {\n abstract readonly kind: HttpErrorKind;\n readonly response?: HttpResponse;\n\n constructor(message: string, response?: HttpResponse) {\n super(message);\n this.name = new.target.name;\n this.response = response;\n }\n}\n\nexport class TransientError extends HttpClientError {\n readonly kind = 'transient' as const;\n}\n\nexport class RateLimitError extends HttpClientError {\n readonly kind = 'rate_limit' as const;\n readonly retryAfter?: Date;\n\n constructor(message: string, response?: HttpResponse, retryAfter?: Date) {\n super(message, response);\n this.retryAfter = retryAfter;\n }\n}\n\nexport class AuthError extends HttpClientError {\n readonly kind = 'auth' as const;\n}\n\nexport class UpstreamBugError extends HttpClientError {\n readonly kind = 'upstream_bug' as const;\n}\n\nexport class ClientBugError extends HttpClientError {\n readonly kind = 'client_bug' as const;\n}\n\nexport function classifyStatus(status: number): HttpErrorKind {\n if (status === 429) {\n return 'rate_limit';\n }\n if (status === 401 || status === 403) {\n return 'auth';\n }\n if (status === 408) {\n return 'transient';\n }\n if (status >= 500) {\n return 'upstream_bug';\n }\n if (status >= 400) {\n return 'client_bug';\n }\n return 'client_bug';\n}\n\nexport function errorForStatus(\n message: string,\n response: HttpResponse,\n retryAfter?: Date,\n): HttpClientError {\n const kind = classifyStatus(response.status);\n switch (kind) {\n case 'rate_limit':\n return new RateLimitError(message, response, retryAfter);\n case 'auth':\n return new AuthError(message, response);\n case 'transient':\n return new TransientError(message, response);\n case 'upstream_bug':\n return new UpstreamBugError(message, response);\n case 'client_bug':\n return new ClientBugError(message, response);\n }\n}\n","import { HttpClientError, RateLimitError, TransientError } from './errors';\n\nexport interface RetryPolicy {\n maxAttempts?: number;\n initialDelayMs?: number;\n maxDelayMs?: number;\n retryOn?: (status: number | null, err?: Error) => boolean;\n}\n\nexport const defaultRetryOn = (status: number | null, err?: Error): boolean => {\n if (err instanceof RateLimitError) {\n return true;\n }\n if (err instanceof TransientError) {\n return true;\n }\n if (status === null) {\n return err instanceof Error && !(err instanceof HttpClientError);\n }\n if (status === 408 || status === 429) {\n return true;\n }\n if (status >= 500) {\n return true;\n }\n return false;\n};\n\nexport function backoffDelayMs(\n attempt: number,\n policy: Required<Pick<RetryPolicy, 'initialDelayMs' | 'maxDelayMs'>>,\n): number {\n const base = policy.initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, policy.maxDelayMs);\n}\n\nexport function parseRetryAfter(\n headerValue: string | null,\n now: Date = new Date(),\n): Date | undefined {\n if (!headerValue) {\n return undefined;\n }\n const trimmed = headerValue.trim();\n if (/^\\d+$/.test(trimmed)) {\n return new Date(now.getTime() + Number(trimmed) * 1000);\n }\n const parsed = Date.parse(trimmed);\n if (Number.isNaN(parsed)) {\n return undefined;\n }\n return new Date(parsed);\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(signal.reason ?? new Error('Aborted'));\n }\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal!.reason ?? new Error('Aborted'));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","export const HTTP_CLIENT_VERSION = '0.0.0';\n\nexport const DEFAULT_USER_AGENT = `rawdash-connector/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n\nexport function connectorUserAgent(connectorId: string): string {\n return `rawdash-connector-${connectorId}/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n}\n","import {\n AuthError,\n ClientBugError,\n HttpClientError,\n RateLimitError,\n TransientError,\n UpstreamBugError,\n errorForStatus,\n} from './errors';\nimport { defaultRetryOn, parseRetryAfter, sleep } from './retry';\nimport type { FetchLike, HttpMethod, HttpRequest, HttpResponse } from './types';\nimport { DEFAULT_USER_AGENT } from './version';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_ATTEMPTS = 3;\nconst DEFAULT_INITIAL_DELAY_MS = 1000;\nconst DEFAULT_MAX_DELAY_MS = 60_000;\nconst OBSERVER_TIMEOUT_MS = 250;\n\nexport interface RequestObservation {\n url: string;\n method: HttpMethod;\n status: number;\n resource: string;\n requestId: string;\n body: unknown;\n}\n\nexport type RequestObserver = (\n event: RequestObservation,\n) => void | Promise<void>;\n\nexport interface RequestOptions {\n fetch?: FetchLike;\n observer?: RequestObserver;\n resource: string;\n requestId?: string;\n}\n\nasync function notifyObserver(\n observer: RequestObserver,\n event: RequestObservation,\n): Promise<void> {\n let result: void | Promise<void>;\n try {\n result = observer(event);\n } catch (err) {\n console.warn('[connector-shared] request observer threw:', err);\n return;\n }\n if (!(result instanceof Promise)) {\n return;\n }\n const guarded = result.catch((err) => {\n console.warn('[connector-shared] request observer rejected:', err);\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, OBSERVER_TIMEOUT_MS);\n });\n try {\n await Promise.race([guarded, timeout]);\n } finally {\n if (timer) {\n clearTimeout(timer);\n }\n }\n}\n\nfunction newRequestId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction mergeHeaders(\n defaults: Record<string, string>,\n overrides: Record<string, string> | undefined,\n): Record<string, string> {\n const merged: Record<string, string> = {};\n for (const [k, v] of Object.entries(defaults)) {\n merged[k.toLowerCase()] = v;\n }\n if (overrides) {\n for (const [k, v] of Object.entries(overrides)) {\n merged[k.toLowerCase()] = v;\n }\n }\n return merged;\n}\n\nfunction linkTimeoutSignal(\n parent: AbortSignal | undefined,\n timeoutMs: number,\n): { signal: AbortSignal; cancel: () => void } {\n const controller = new AbortController();\n const onParentAbort = () => {\n controller.abort(parent?.reason);\n };\n if (parent) {\n if (parent.aborted) {\n controller.abort(parent.reason);\n } else {\n parent.addEventListener('abort', onParentAbort, { once: true });\n }\n }\n const timer = setTimeout(() => {\n controller.abort(new Error(`Request timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n return {\n signal: controller.signal,\n cancel: () => {\n clearTimeout(timer);\n if (parent) {\n parent.removeEventListener('abort', onParentAbort);\n }\n },\n };\n}\n\nasync function readBody(res: Response, parseJson: boolean): Promise<unknown> {\n if (res.status === 204 || res.status === 205) {\n return null;\n }\n const contentType = res.headers.get('content-type') ?? '';\n if (parseJson && contentType.includes('application/json')) {\n const text = await res.text();\n if (text.length === 0) {\n return null;\n }\n return JSON.parse(text);\n }\n return res.text();\n}\n\nexport async function request<T = unknown>(\n req: HttpRequest,\n options: RequestOptions,\n): Promise<HttpResponse<T>> {\n const fetchImpl: FetchLike = options.fetch ?? (globalThis.fetch as FetchLike);\n const retry = req.retry ?? {};\n const maxAttempts = retry.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n const initialDelayMs = retry.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;\n const maxDelayMs = retry.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;\n const retryOn = retry.retryOn ?? defaultRetryOn;\n const timeoutMs = req.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const parseJson = req.parseJson ?? true;\n\n const headers = mergeHeaders(\n {\n 'User-Agent': DEFAULT_USER_AGENT,\n Accept: 'application/json',\n },\n req.headers,\n );\n\n let lastErr: Error | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n req.signal?.throwIfAborted();\n\n const { signal, cancel } = linkTimeoutSignal(req.signal, timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(req.url, {\n method: req.method ?? 'GET',\n headers,\n body: req.body as RequestInit['body'],\n signal,\n });\n } catch (err) {\n cancel();\n if (req.signal?.aborted) {\n throw req.signal.reason ?? err;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n lastErr = error;\n if (attempt < maxAttempts - 1 && retryOn(null, error)) {\n const delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n await sleep(delay, req.signal);\n continue;\n }\n throw new TransientError(error.message);\n }\n cancel();\n\n const body = await readBody(res, parseJson);\n const httpResponse: HttpResponse<T> = {\n status: res.status,\n headers: res.headers,\n body: body as T,\n };\n if (req.rateLimit) {\n const state = req.rateLimit.parse(res.headers);\n if (state) {\n httpResponse.rateLimitState = state;\n }\n }\n\n if (options.observer) {\n await notifyObserver(options.observer, {\n url: req.url,\n method: req.method ?? 'GET',\n status: res.status,\n resource: options.resource,\n requestId: options.requestId ?? newRequestId(),\n body,\n });\n }\n\n if (res.ok) {\n return httpResponse;\n }\n\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\n const message = `HTTP ${res.status} ${res.statusText} for ${req.method ?? 'GET'} ${req.url}`;\n const err = errorForStatus(message, httpResponse, retryAfter);\n\n if (\n attempt < maxAttempts - 1 &&\n retryOn(res.status, err) &&\n !(err instanceof AuthError) &&\n !(err instanceof ClientBugError)\n ) {\n lastErr = err;\n let delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n if (err instanceof RateLimitError && retryAfter) {\n const wait = retryAfter.getTime() - Date.now();\n if (wait > 0) {\n delay = Math.min(wait, maxDelayMs);\n }\n }\n await sleep(delay, req.signal);\n continue;\n }\n\n throw err;\n }\n\n throw lastErr ?? new UpstreamBugError('Exhausted retry attempts');\n}\n\nfunction computeDelay(\n attempt: number,\n initialDelayMs: number,\n maxDelayMs: number,\n): number {\n const base = initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, maxDelayMs);\n}\n\nexport { HttpClientError };\n","export interface RateLimitState {\n remaining: number;\n resetAt: Date;\n}\n\nexport interface RateLimitPolicy {\n parse(headers: Headers): RateLimitState | null;\n}\n\nexport interface StandardRateLimitPolicyConfig {\n remainingHeader: string;\n resetHeader: string;\n resetUnit: 's' | 'ms';\n resetFallbackMs?: number;\n}\n\nexport function standardRateLimitPolicy(\n config: StandardRateLimitPolicyConfig,\n): RateLimitPolicy {\n const { remainingHeader, resetHeader, resetUnit, resetFallbackMs } = config;\n const multiplier = resetUnit === 's' ? 1000 : 1;\n return {\n parse(h) {\n const remainingRaw = h.get(remainingHeader);\n if (remainingRaw === null || remainingRaw.trim() === '') {\n return null;\n }\n const remaining = Number(remainingRaw);\n if (!Number.isFinite(remaining)) {\n return null;\n }\n const resetRaw = h.get(resetHeader);\n if (resetRaw === null) {\n if (resetFallbackMs === undefined) {\n return null;\n }\n return {\n remaining,\n resetAt: new Date(Date.now() + resetFallbackMs),\n };\n }\n if (resetRaw.trim() === '') {\n return null;\n }\n const reset = Number(resetRaw);\n if (!Number.isFinite(reset) || reset < 0) {\n return null;\n }\n const resetMs = reset * multiplier;\n if (!Number.isFinite(resetMs)) {\n return null;\n }\n return { remaining, resetAt: new Date(resetMs) };\n },\n };\n}\n","export async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n if (items.length === 0) {\n return results;\n }\n const normalized = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;\n const limit = Math.max(1, Math.min(normalized, items.length));\n let next = 0;\n let failed = false;\n\n async function worker(): Promise<void> {\n while (!failed) {\n const i = next++;\n if (i >= items.length) {\n return;\n }\n try {\n results[i] = await fn(items[i]!, i);\n } catch (err) {\n failed = true;\n throw err;\n }\n }\n }\n\n const workers: Promise<void>[] = [];\n for (let w = 0; w < limit; w++) {\n workers.push(worker());\n }\n await Promise.all(workers);\n return results;\n}\n","export interface SanitizeAllowedUrlOptions {\n url: string | null;\n host: string;\n pathname: string;\n protocol?: 'https:' | 'http:';\n}\n\nexport function sanitizeAllowedUrl(\n options: SanitizeAllowedUrlOptions,\n): string | null {\n const { url, host, pathname, protocol = 'https:' } = options;\n if (url === null) {\n return null;\n }\n try {\n const u = new URL(url);\n if (u.protocol !== protocol || u.host !== host || u.pathname !== pathname) {\n return null;\n }\n return u.toString();\n } catch {\n return null;\n }\n}\n","export type EpochUnit = 'ms' | 's' | 'iso';\n\nexport function parseEpoch(\n value: number | string | null | undefined,\n unit: EpochUnit,\n): number | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (unit === 'iso') {\n if (typeof value !== 'string') {\n return null;\n }\n const ms = new Date(value).getTime();\n return Number.isFinite(ms) ? ms : null;\n }\n if (typeof value === 'string' && value.trim() === '') {\n return null;\n }\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n)) {\n return null;\n }\n const result = unit === 's' ? n * 1000 : n;\n return Number.isFinite(result) ? result : null;\n}\n","import { request } from './request';\nimport type { HttpRequest } from './types';\n\nexport function parseLinkHeader(header: string | null): Record<string, string> {\n if (!header) {\n return {};\n }\n const result: Record<string, string> = {};\n for (const part of header.split(',')) {\n const match = part.match(/<([^>]+)>\\s*;\\s*rel=\"([^\"]+)\"/);\n if (match) {\n result[match[2]!] = match[1]!;\n }\n }\n return result;\n}\n\nexport async function* paginateLink<T>(\n initial: HttpRequest,\n parse: (body: unknown) => T[],\n options: { resource: string },\n): AsyncIterable<T> {\n let next: string | null = initial.url;\n while (next) {\n const res: Awaited<ReturnType<typeof request>> = await request(\n {\n ...initial,\n url: next,\n },\n { resource: options.resource },\n );\n for (const item of parse(res.body)) {\n yield item;\n }\n const links = parseLinkHeader(res.headers.get('link'));\n next = links['next'] ?? null;\n }\n}\n\nexport async function* paginateCursor<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; nextCursor: string | null },\n buildNext: (req: HttpRequest, cursor: string) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let req: HttpRequest = initial;\n while (true) {\n const res = await request(req, { resource: options.resource });\n const { items, nextCursor } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!nextCursor) {\n return;\n }\n req = buildNext(req, nextCursor);\n }\n}\n\nexport async function* paginatePage<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; hasMore: boolean },\n buildPage: (req: HttpRequest, page: number) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let page = 1;\n while (true) {\n const req = page === 1 ? initial : buildPage(initial, page);\n const res = await request(req, { resource: options.resource });\n const { items, hasMore } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!hasMore || items.length === 0) {\n return;\n }\n page++;\n }\n}\n","export type LogFields = Record<string, unknown>;\n\nexport interface ConnectorLogger {\n info(event: string, fields?: LogFields): void;\n warn(event: string, fields?: LogFields): void;\n}\n\nexport interface ConnectorLoggerOptions {\n scope: string;\n}\n\nconst MAX_VALUE_LEN = 120;\n\nfunction truncate(s: string, max = MAX_VALUE_LEN): string {\n if (s.length <= max) {\n return s;\n }\n return `${s.slice(0, max - 1)}…`;\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return '';\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'string') {\n const t = truncate(value);\n if (/[\\s\"=]/.test(t)) {\n return JSON.stringify(t);\n }\n return t;\n }\n if (typeof value === 'bigint') {\n return value.toString();\n }\n let json: string | undefined;\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n return truncate(json ?? String(value));\n}\n\nexport function formatLogFields(fields?: LogFields): string {\n if (!fields) {\n return '';\n }\n const parts: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (v === undefined) {\n continue;\n }\n parts.push(`${k}=${formatValue(v)}`);\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nexport function formatLogLine(\n scope: string,\n event: string,\n fields?: LogFields,\n): string {\n return `[${scope}] ${event}${formatLogFields(fields)}`;\n}\n\nexport function createDefaultConnectorLogger(\n opts: ConnectorLoggerOptions,\n): ConnectorLogger {\n return {\n info(event, fields) {\n console.info(formatLogLine(opts.scope, event, fields));\n },\n warn(event, fields) {\n console.warn(formatLogLine(opts.scope, event, fields));\n },\n };\n}\n\nconst NOOP_LOGGER: ConnectorLogger = {\n info() {},\n warn() {},\n};\n\nexport function noopConnectorLogger(): ConnectorLogger {\n return NOOP_LOGGER;\n}\n","import {\n type HttpResponse,\n connectorUserAgent,\n parseEpoch,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type ConnectorContext,\n type ConnectorDoc,\n type CredentialsSchema,\n type JSONValue,\n type MetricSample,\n type StorageHandle,\n type SyncOptions,\n type SyncResult,\n defineConfigFields,\n defineConnectorDoc,\n defineResources,\n makeChunkedCursorGuard,\n schemasFromResources,\n selectActivePhases,\n} from '@rawdash/core';\nimport { z } from 'zod';\n\nconst ANTHROPIC_API_HOST = 'api.anthropic.com';\nconst ANTHROPIC_API_BASE = `https://${ANTHROPIC_API_HOST}`;\nconst ANTHROPIC_API_VERSION = '2023-06-01';\nconst USAGE_PAGE_LIMIT = 31;\nconst COSTS_PAGE_LIMIT = 31;\nconst MS_PER_DAY = 86_400_000;\nconst DEFAULT_LOOKBACK_DAYS = 30;\nconst INCREMENTAL_LOOKBACK_DAYS = 2;\n// Cost report `amount` is a decimal string in the lowest currency unit (cents\n// for USD): e.g. \"123.45\" represents $1.2345. Divide by 100 to get dollars.\nconst COST_AMOUNT_DIVISOR = 100;\n\nexport const configFields = defineConfigFields(\n z.object({\n adminApiKey: z.object({ $secret: z.string().min(1) }).meta({\n label: 'Admin API key',\n description:\n '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.',\n placeholder: 'ANTHROPIC_ADMIN_API_KEY',\n secret: true,\n }),\n workspaceIds: z.array(z.string().min(1)).nonempty().optional().meta({\n label: 'Workspace IDs (optional)',\n description:\n 'Restrict usage and cost queries to specific Anthropic workspace ids (wrkspc_...). Omit to aggregate every workspace the admin key can see.',\n }),\n resources: z\n .array(\n z.enum([\n 'anthropic_input_tokens',\n 'anthropic_output_tokens',\n 'anthropic_cache_read_tokens',\n 'anthropic_cache_creation_tokens',\n 'anthropic_web_search_requests',\n 'anthropic_cost_usd',\n ]),\n )\n .nonempty()\n .optional()\n .meta({\n label: 'Resources',\n description:\n '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.',\n }),\n lookbackDays: z.number().int().positive().max(180).optional().meta({\n label: 'Backfill window (days)',\n description:\n '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.',\n placeholder: '30',\n }),\n }),\n);\n\nexport const doc: ConnectorDoc = defineConnectorDoc({\n displayName: 'Anthropic',\n category: 'engineering',\n brandColor: '#D97757',\n tagline:\n 'Track Anthropic spend, daily token usage across Claude models, cache hit volumes, and web-search tool requests from the Anthropic Admin API.',\n vendor: {\n name: 'Anthropic',\n domain: 'anthropic.com',\n apiDocs:\n 'https://docs.claude.com/en/api/admin-api/usage-cost/get-messages-usage-report',\n website: 'https://anthropic.com',\n },\n auth: {\n summary:\n '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.',\n setup: [\n '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.',\n 'Store the key as a secret (e.g. ANTHROPIC_ADMIN_API_KEY).',\n 'Reference it from config as `adminApiKey: secret(\"ANTHROPIC_ADMIN_API_KEY\")`.',\n 'Optionally set `workspaceIds` to restrict the query to a subset of workspaces.',\n ],\n },\n rateLimit:\n '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.',\n limitations: [\n '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.',\n '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.',\n 'The Cost Report only supports 1d bucket_width and reports cost in USD; non-USD billing currencies are not converted.',\n 'Admin API keys are required - regular sk-ant-api- keys do not have access to the organization Usage and Cost reports.',\n ],\n});\n\nconst PHASE_ORDER = ['usage_messages', 'cost_report'] as const;\n\ntype AnthropicPhase = (typeof PHASE_ORDER)[number];\n\nexport type AnthropicResource =\n | 'anthropic_input_tokens'\n | 'anthropic_output_tokens'\n | 'anthropic_cache_read_tokens'\n | 'anthropic_cache_creation_tokens'\n | 'anthropic_web_search_requests'\n | 'anthropic_cost_usd';\n\nconst isAnthropicSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);\n\nconst RESOURCES_BY_PHASE: Record<AnthropicPhase, readonly AnthropicResource[]> =\n {\n usage_messages: [\n 'anthropic_input_tokens',\n 'anthropic_output_tokens',\n 'anthropic_cache_read_tokens',\n 'anthropic_cache_creation_tokens',\n 'anthropic_web_search_requests',\n ],\n cost_report: ['anthropic_cost_usd'],\n };\n\nconst PHASE_ENDPOINT_PATH: Record<AnthropicPhase, string> = {\n usage_messages: '/v1/organizations/usage_report/messages',\n cost_report: '/v1/organizations/cost_report',\n};\n\nconst usageCacheCreationSchema = z.object({\n ephemeral_1h_input_tokens: z.number().nonnegative(),\n ephemeral_5m_input_tokens: z.number().nonnegative(),\n});\n\nconst usageServerToolUseSchema = z.object({\n web_search_requests: z.number().int().nonnegative(),\n});\n\nconst usageResultSchema = z.object({\n account_id: z.string().nullish(),\n api_key_id: z.string().nullish(),\n cache_creation: usageCacheCreationSchema.nullish(),\n cache_read_input_tokens: z.number().nonnegative(),\n context_window: z.string().nullish(),\n inference_geo: z.string().nullish(),\n model: z.string().nullish(),\n output_tokens: z.number().nonnegative(),\n server_tool_use: usageServerToolUseSchema.nullish(),\n service_account_id: z.string().nullish(),\n service_tier: z.string().nullish(),\n uncached_input_tokens: z.number().nonnegative(),\n workspace_id: z.string().nullish(),\n});\n\nconst costResultSchema = z.object({\n amount: z.string(),\n context_window: z.string().nullish(),\n cost_type: z.string().nullish(),\n currency: z.string(),\n description: z.string().nullish(),\n inference_geo: z.string().nullish(),\n model: z.string().nullish(),\n service_tier: z.string().nullish(),\n token_type: z.string().nullish(),\n workspace_id: z.string().nullish(),\n});\n\nfunction bucketResponseSchema<T extends z.ZodTypeAny>(resultSchema: T) {\n return z.object({\n data: z.array(\n z.object({\n starting_at: z.string(),\n ending_at: z.string(),\n results: z.array(resultSchema),\n }),\n ),\n has_more: z.boolean(),\n next_page: z.string().nullish(),\n });\n}\n\nconst usageResponseSchema = bucketResponseSchema(usageResultSchema);\nconst costResponseSchema = bucketResponseSchema(costResultSchema);\n\ntype UsageResult = z.infer<typeof usageResultSchema>;\ntype CostResult = z.infer<typeof costResultSchema>;\n\ninterface BucketPage<TResult> {\n starting_at: string;\n ending_at: string;\n results: TResult[];\n}\n\ninterface PageResponse<TResult> {\n buckets: BucketPage<TResult>[];\n nextPage: string | null;\n}\n\nconst USAGE_DIMENSIONS = [\n {\n name: 'model',\n description: 'Claude model id reported by Anthropic (or null).',\n },\n {\n name: 'workspace_id',\n description:\n 'Anthropic workspace id the usage is attributed to (or null for the default workspace).',\n },\n {\n name: 'api_key_id',\n description: 'API key id the usage is attributed to (or null).',\n },\n {\n name: 'service_tier',\n description:\n 'Service tier the request ran under (standard, batch, priority, flex, etc.), or null.',\n },\n {\n name: 'context_window',\n description:\n 'Context window bucket the request used (0-200k or 200k-1M), or null.',\n },\n {\n name: 'inference_geo',\n description:\n 'Inference geo the request ran in (global, us, not_available), or null.',\n },\n] as const;\n\nconst COST_DIMENSIONS = [\n {\n name: 'workspace_id',\n description:\n 'Anthropic workspace id the cost is attributed to (or null for the default workspace).',\n },\n {\n name: 'description',\n description:\n 'Human-readable cost line item label (e.g. \"Claude Sonnet 4 Usage - Input Tokens\"), or null when ungrouped.',\n },\n {\n name: 'cost_type',\n description:\n 'Cost category (tokens, web_search, code_execution, session_usage), or null.',\n },\n {\n name: 'model',\n description:\n 'Claude model the cost is attributed to (or null for non-token costs).',\n },\n {\n name: 'token_type',\n description:\n 'Token category for token costs (uncached_input_tokens, output_tokens, cache_read_input_tokens, cache_creation.ephemeral_*_input_tokens), or null.',\n },\n {\n name: 'service_tier',\n description:\n 'Service tier the cost is attributed to (standard or batch), or null.',\n },\n {\n name: 'context_window',\n description:\n 'Context window the cost is attributed to (0-200k or 200k-1M), or null.',\n },\n {\n name: 'currency',\n description:\n 'Billing currency reported by Anthropic (currently always USD).',\n },\n] as const;\n\nexport const anthropicResources = defineResources({\n anthropic_input_tokens: {\n shape: 'metric',\n description:\n 'Daily uncached input tokens processed by the Anthropic Messages API, grouped by model and workspace.',\n endpoint: 'GET /v1/organizations/usage_report/messages',\n unit: 'tokens',\n granularity: 'daily',\n dimensions: [...USAGE_DIMENSIONS],\n notes:\n '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.',\n responses: { usage_messages: usageResponseSchema },\n },\n anthropic_output_tokens: {\n shape: 'metric',\n description:\n 'Daily output tokens generated by the Anthropic Messages API, grouped by model and workspace.',\n endpoint: 'GET /v1/organizations/usage_report/messages',\n unit: 'tokens',\n granularity: 'daily',\n dimensions: [...USAGE_DIMENSIONS],\n notes:\n 'Written alongside anthropic_input_tokens from the same usage_messages API call.',\n },\n anthropic_cache_read_tokens: {\n shape: 'metric',\n description:\n 'Daily input tokens read from the prompt cache, grouped by model and workspace.',\n endpoint: 'GET /v1/organizations/usage_report/messages',\n unit: 'tokens',\n granularity: 'daily',\n dimensions: [...USAGE_DIMENSIONS],\n notes:\n 'Cache hits are charged at a fraction of the uncached rate, so this metric paired with anthropic_input_tokens gives the cache hit ratio.',\n },\n anthropic_cache_creation_tokens: {\n shape: 'metric',\n description:\n 'Daily input tokens written into the prompt cache (sum of the 1h and 5m ephemeral caches), grouped by model and workspace.',\n endpoint: 'GET /v1/organizations/usage_report/messages',\n unit: 'tokens',\n granularity: 'daily',\n dimensions: [...USAGE_DIMENSIONS],\n notes:\n 'The per-cache-bucket counts (ephemeral_1h_input_tokens, ephemeral_5m_input_tokens) are mirrored in attributes for finer-grained widgets.',\n },\n anthropic_web_search_requests: {\n shape: 'metric',\n description:\n 'Daily count of web-search tool requests executed server-side by Claude, grouped by model and workspace.',\n endpoint: 'GET /v1/organizations/usage_report/messages',\n unit: 'requests',\n granularity: 'daily',\n dimensions: [...USAGE_DIMENSIONS],\n notes:\n '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.',\n },\n anthropic_cost_usd: {\n shape: 'metric',\n description:\n 'Daily organization spend in USD, broken down by workspace and cost line item, pulled from the Anthropic Cost Report.',\n endpoint: 'GET /v1/organizations/cost_report',\n unit: 'USD',\n granularity: 'daily',\n dimensions: [...COST_DIMENSIONS],\n notes:\n '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.',\n responses: { cost_report: costResponseSchema },\n },\n});\n\nexport interface AnthropicSettings {\n workspaceIds?: readonly string[];\n resources?: readonly AnthropicResource[];\n lookbackDays?: number;\n}\n\nconst anthropicCredentials = {\n adminApiKey: {\n description: 'Anthropic organization admin API key (sk-ant-admin-...)',\n auth: 'required' as const,\n },\n} satisfies CredentialsSchema;\n\ntype AnthropicCredentials = typeof anthropicCredentials;\n\nexport const id = 'anthropic';\n\ninterface UsageWindow {\n startingAt: string;\n endingAt: string;\n}\n\nexport function getUsageWindow(\n options: SyncOptions,\n lookbackDays: number,\n now: number = Date.now(),\n): UsageWindow {\n const todayStart = Math.floor(now / MS_PER_DAY) * MS_PER_DAY;\n const endMs = todayStart + MS_PER_DAY;\n\n let days = lookbackDays;\n if (options.mode === 'latest') {\n days = INCREMENTAL_LOOKBACK_DAYS;\n } else if (options.since !== undefined) {\n const sinceMs = parseEpoch(options.since, 'iso');\n if (sinceMs !== null) {\n const elapsed = Math.ceil((now - sinceMs) / MS_PER_DAY);\n days = Math.min(\n Math.max(elapsed + INCREMENTAL_LOOKBACK_DAYS, 1),\n lookbackDays,\n );\n }\n }\n const startMs = endMs - days * MS_PER_DAY;\n return {\n startingAt: new Date(startMs).toISOString(),\n endingAt: new Date(endMs).toISOString(),\n };\n}\n\nfunction resourceToPhase(resource: AnthropicResource): AnthropicPhase {\n for (const phase of PHASE_ORDER) {\n if ((RESOURCES_BY_PHASE[phase] as readonly string[]).includes(resource)) {\n return phase;\n }\n }\n // unreachable - RESOURCES_BY_PHASE covers every AnthropicResource\n throw new Error(`anthropic: unmapped resource ${resource}`);\n}\n\nfunction nullableString(value: string | null | undefined): string | null {\n return value === undefined || value === null ? null : value;\n}\n\nfunction usageDimensionAttributes(row: UsageResult): Record<string, JSONValue> {\n return {\n model: nullableString(row.model),\n workspace_id: nullableString(row.workspace_id),\n api_key_id: nullableString(row.api_key_id),\n service_tier: nullableString(row.service_tier),\n context_window: nullableString(row.context_window),\n inference_geo: nullableString(row.inference_geo),\n account_id: nullableString(row.account_id),\n service_account_id: nullableString(row.service_account_id),\n };\n}\n\nfunction cacheCreationTotal(row: UsageResult): number {\n const c = row.cache_creation;\n if (!c) {\n return 0;\n }\n return c.ephemeral_1h_input_tokens + c.ephemeral_5m_input_tokens;\n}\n\nfunction tsFromBucket(bucket: BucketPage<unknown>): number | null {\n return parseEpoch(bucket.starting_at, 'iso');\n}\n\nexport function buildUsageSamples(\n buckets: readonly BucketPage<UsageResult>[],\n): {\n inputTokens: MetricSample[];\n outputTokens: MetricSample[];\n cacheReadTokens: MetricSample[];\n cacheCreationTokens: MetricSample[];\n webSearchRequests: MetricSample[];\n} {\n const inputTokens: MetricSample[] = [];\n const outputTokens: MetricSample[] = [];\n const cacheReadTokens: MetricSample[] = [];\n const cacheCreationTokens: MetricSample[] = [];\n const webSearchRequests: MetricSample[] = [];\n for (const bucket of buckets) {\n const ts = tsFromBucket(bucket);\n if (ts === null) {\n continue;\n }\n for (const row of bucket.results) {\n const common = usageDimensionAttributes(row);\n const cacheCreation = row.cache_creation;\n inputTokens.push({\n name: 'anthropic_input_tokens',\n ts,\n value: row.uncached_input_tokens,\n attributes: { ...common },\n });\n outputTokens.push({\n name: 'anthropic_output_tokens',\n ts,\n value: row.output_tokens,\n attributes: { ...common },\n });\n cacheReadTokens.push({\n name: 'anthropic_cache_read_tokens',\n ts,\n value: row.cache_read_input_tokens,\n attributes: { ...common },\n });\n cacheCreationTokens.push({\n name: 'anthropic_cache_creation_tokens',\n ts,\n value: cacheCreationTotal(row),\n attributes: {\n ...common,\n ephemeral_1h_input_tokens:\n cacheCreation?.ephemeral_1h_input_tokens ?? 0,\n ephemeral_5m_input_tokens:\n cacheCreation?.ephemeral_5m_input_tokens ?? 0,\n },\n });\n webSearchRequests.push({\n name: 'anthropic_web_search_requests',\n ts,\n value: row.server_tool_use?.web_search_requests ?? 0,\n attributes: { ...common },\n });\n }\n }\n return {\n inputTokens,\n outputTokens,\n cacheReadTokens,\n cacheCreationTokens,\n webSearchRequests,\n };\n}\n\nexport function buildCostSamples(\n buckets: readonly BucketPage<CostResult>[],\n): MetricSample[] {\n const samples: MetricSample[] = [];\n for (const bucket of buckets) {\n const ts = tsFromBucket(bucket);\n if (ts === null) {\n continue;\n }\n for (const row of bucket.results) {\n const rawAmount = Number.parseFloat(row.amount);\n const value = Number.isFinite(rawAmount)\n ? rawAmount / COST_AMOUNT_DIVISOR\n : 0;\n samples.push({\n name: 'anthropic_cost_usd',\n ts,\n value,\n attributes: {\n workspace_id: nullableString(row.workspace_id),\n description: nullableString(row.description),\n cost_type: nullableString(row.cost_type),\n model: nullableString(row.model),\n token_type: nullableString(row.token_type),\n service_tier: nullableString(row.service_tier),\n context_window: nullableString(row.context_window),\n inference_geo: nullableString(row.inference_geo),\n currency: row.currency,\n },\n });\n }\n }\n return samples;\n}\n\nexport class AnthropicConnector extends BaseConnector<\n AnthropicSettings,\n AnthropicCredentials\n> {\n static readonly id = id;\n\n static readonly resources = anthropicResources;\n\n static readonly schemas = schemasFromResources(anthropicResources);\n\n static create(input: unknown, ctx?: ConnectorContext): AnthropicConnector {\n const parsed = configFields.parse(input);\n return new AnthropicConnector(\n {\n workspaceIds: parsed.workspaceIds,\n resources: parsed.resources,\n lookbackDays: parsed.lookbackDays,\n },\n { adminApiKey: parsed.adminApiKey },\n ctx,\n );\n }\n\n readonly id = id;\n override readonly credentials = anthropicCredentials;\n\n private buildHeaders(): Record<string, string> {\n return {\n 'X-Api-Key': String(this.creds.adminApiKey),\n 'anthropic-version': ANTHROPIC_API_VERSION,\n 'User-Agent': connectorUserAgent(this.id),\n };\n }\n\n private fetch<T>(\n url: string,\n resource: string,\n signal?: AbortSignal,\n ): Promise<HttpResponse<T>> {\n return this.get<T>(url, {\n resource,\n headers: this.buildHeaders(),\n signal,\n });\n }\n\n private buildInitialUrl(phase: AnthropicPhase, window: UsageWindow): string {\n const url = new URL(`${ANTHROPIC_API_BASE}${PHASE_ENDPOINT_PATH[phase]}`);\n url.searchParams.set('starting_at', window.startingAt);\n url.searchParams.set('ending_at', window.endingAt);\n url.searchParams.set('bucket_width', '1d');\n if (phase === 'usage_messages') {\n url.searchParams.set('limit', String(USAGE_PAGE_LIMIT));\n url.searchParams.append('group_by', 'model');\n url.searchParams.append('group_by', 'workspace_id');\n url.searchParams.append('group_by', 'api_key_id');\n url.searchParams.append('group_by', 'service_tier');\n url.searchParams.append('group_by', 'context_window');\n url.searchParams.append('group_by', 'inference_geo');\n for (const workspaceId of this.settings.workspaceIds ?? []) {\n url.searchParams.append('workspace_ids', workspaceId);\n }\n } else {\n url.searchParams.set('limit', String(COSTS_PAGE_LIMIT));\n url.searchParams.append('group_by', 'workspace_id');\n url.searchParams.append('group_by', 'description');\n }\n return url.toString();\n }\n\n private buildNextUrl(currentUrl: string, nextPage: string): string {\n const url = new URL(currentUrl);\n url.searchParams.set('page', nextPage);\n return url.toString();\n }\n\n private async fetchPhasePage<T>(\n phase: AnthropicPhase,\n schema: z.ZodType<T>,\n initialUrl: string,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<{ url: string; parsed: T; nextUrl: string | null }> {\n const url = page ?? initialUrl;\n const res = await this.fetch<unknown>(url, phase, signal);\n const parsed = schema.parse(res.body);\n const body = parsed as unknown as {\n next_page?: string | null;\n has_more?: boolean;\n };\n const nextPage =\n body.has_more === true &&\n typeof body.next_page === 'string' &&\n body.next_page.length > 0\n ? body.next_page\n : null;\n const nextUrl = nextPage ? this.buildNextUrl(url, nextPage) : null;\n return { url, parsed, nextUrl };\n }\n\n async sync(\n options: SyncOptions,\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<SyncResult> {\n const cursor = isAnthropicSyncCursor(options.cursor)\n ? options.cursor\n : undefined;\n const lookbackDays = this.settings.lookbackDays ?? DEFAULT_LOOKBACK_DAYS;\n const window = getUsageWindow(options, lookbackDays);\n\n const phases = selectActivePhases<AnthropicResource, AnthropicPhase>(\n resourceToPhase,\n PHASE_ORDER,\n this.settings.resources,\n );\n\n const startIdx = cursor ? phases.indexOf(cursor.phase) : 0;\n const resumeIdx = startIdx >= 0 ? startIdx : 0;\n\n for (let i = resumeIdx; i < phases.length; i++) {\n const phase = phases[i]!;\n if (signal?.aborted) {\n return { done: false, cursor: { phase, page: null } };\n }\n const phaseStart = Date.now();\n const initialUrl = this.buildInitialUrl(phase, window);\n let pageUrl: string | null = null;\n let pageCount = 0;\n const buckets: BucketPage<unknown>[] = [];\n\n while (true) {\n if (signal?.aborted) {\n return { done: false, cursor: { phase, page: null } };\n }\n pageCount += 1;\n const { parsed, nextUrl } = await this.fetchAnyPhasePage(\n phase,\n initialUrl,\n pageUrl,\n signal,\n );\n const data = parsed.data as BucketPage<unknown>[];\n buckets.push(...data);\n this.logger.info('fetched page', {\n resource: phase,\n page: pageCount,\n items: data.length,\n });\n if (nextUrl === null) {\n break;\n }\n pageUrl = nextUrl;\n }\n\n await this.writePhase(storage, phase, buckets);\n this.logger.info('resource done', {\n resource: phase,\n pages: pageCount,\n items: buckets.length,\n duration_ms: Date.now() - phaseStart,\n });\n }\n\n return { done: true };\n }\n\n private async fetchAnyPhasePage(\n phase: AnthropicPhase,\n initialUrl: string,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<{\n parsed: { data: BucketPage<unknown>[] };\n nextUrl: string | null;\n }> {\n switch (phase) {\n case 'usage_messages':\n return this.fetchPhasePage(\n phase,\n usageResponseSchema,\n initialUrl,\n page,\n signal,\n );\n case 'cost_report':\n return this.fetchPhasePage(\n phase,\n costResponseSchema,\n initialUrl,\n page,\n signal,\n );\n }\n }\n\n private async writePhase(\n storage: StorageHandle,\n phase: AnthropicPhase,\n buckets: BucketPage<unknown>[],\n ): Promise<void> {\n switch (phase) {\n case 'usage_messages': {\n const samples = buildUsageSamples(buckets as BucketPage<UsageResult>[]);\n await storage.metrics(samples.inputTokens, {\n names: ['anthropic_input_tokens'],\n });\n await storage.metrics(samples.outputTokens, {\n names: ['anthropic_output_tokens'],\n });\n await storage.metrics(samples.cacheReadTokens, {\n names: ['anthropic_cache_read_tokens'],\n });\n await storage.metrics(samples.cacheCreationTokens, {\n names: ['anthropic_cache_creation_tokens'],\n });\n await storage.metrics(samples.webSearchRequests, {\n names: ['anthropic_web_search_requests'],\n });\n return;\n }\n case 'cost_report': {\n const samples = buildCostSamples(buckets as BucketPage<CostResult>[]);\n await storage.metrics(samples, { names: ['anthropic_cost_usd'] });\n return;\n }\n }\n }\n}\n\nexport type { PageResponse, BucketPage, UsageResult, CostResult };\n","import { AnthropicConnector } from './anthropic';\n\nexport {\n AnthropicConnector,\n anthropicResources as resources,\n buildCostSamples,\n buildUsageSamples,\n configFields,\n doc,\n getUsageWindow,\n id,\n} from './anthropic';\nexport type {\n AnthropicResource,\n AnthropicSettings,\n BucketPage,\n CostResult,\n PageResponse,\n UsageResult,\n} from './anthropic';\nexport default AnthropicConnector;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../connector-shared/src/errors.ts","../../../connector-shared/src/retry.ts","../../../connector-shared/src/version.ts","../../../connector-shared/src/request.ts","../../../connector-shared/src/rate-limit.ts","../../../connector-shared/src/map-concurrent.ts","../../../connector-shared/src/sanitize.ts","../../../connector-shared/src/epoch.ts","../../../connector-shared/src/pagination.ts","../../../connector-shared/src/logger.ts","../src/anthropic.ts","../src/index.ts"],"sourcesContent":["import type { HttpResponse } from './types';\n\nexport type HttpErrorKind =\n | 'transient'\n | 'rate_limit'\n | 'auth'\n | 'upstream_bug'\n | 'client_bug';\n\nexport abstract class HttpClientError extends Error {\n abstract readonly kind: HttpErrorKind;\n readonly response?: HttpResponse;\n\n constructor(message: string, response?: HttpResponse) {\n super(message);\n this.name = new.target.name;\n this.response = response;\n }\n}\n\nexport class TransientError extends HttpClientError {\n readonly kind = 'transient' as const;\n}\n\nexport class RateLimitError extends HttpClientError {\n readonly kind = 'rate_limit' as const;\n readonly retryAfter?: Date;\n\n constructor(message: string, response?: HttpResponse, retryAfter?: Date) {\n super(message, response);\n this.retryAfter = retryAfter;\n }\n}\n\nexport class AuthError extends HttpClientError {\n readonly kind = 'auth' as const;\n}\n\nexport class UpstreamBugError extends HttpClientError {\n readonly kind = 'upstream_bug' as const;\n}\n\nexport class ClientBugError extends HttpClientError {\n readonly kind = 'client_bug' as const;\n}\n\nexport function classifyStatus(status: number): HttpErrorKind {\n if (status === 429) {\n return 'rate_limit';\n }\n if (status === 401 || status === 403) {\n return 'auth';\n }\n if (status === 408) {\n return 'transient';\n }\n if (status >= 500) {\n return 'upstream_bug';\n }\n if (status >= 400) {\n return 'client_bug';\n }\n return 'client_bug';\n}\n\nexport function errorForStatus(\n message: string,\n response: HttpResponse,\n retryAfter?: Date,\n): HttpClientError {\n const kind = classifyStatus(response.status);\n switch (kind) {\n case 'rate_limit':\n return new RateLimitError(message, response, retryAfter);\n case 'auth':\n return new AuthError(message, response);\n case 'transient':\n return new TransientError(message, response);\n case 'upstream_bug':\n return new UpstreamBugError(message, response);\n case 'client_bug':\n return new ClientBugError(message, response);\n }\n}\n","import { HttpClientError, RateLimitError, TransientError } from './errors';\n\nexport interface RetryPolicy {\n maxAttempts?: number;\n initialDelayMs?: number;\n maxDelayMs?: number;\n retryOn?: (status: number | null, err?: Error) => boolean;\n}\n\nexport const defaultRetryOn = (status: number | null, err?: Error): boolean => {\n if (err instanceof RateLimitError) {\n return true;\n }\n if (err instanceof TransientError) {\n return true;\n }\n if (status === null) {\n return err instanceof Error && !(err instanceof HttpClientError);\n }\n if (status === 408 || status === 429) {\n return true;\n }\n if (status >= 500) {\n return true;\n }\n return false;\n};\n\nexport function backoffDelayMs(\n attempt: number,\n policy: Required<Pick<RetryPolicy, 'initialDelayMs' | 'maxDelayMs'>>,\n): number {\n const base = policy.initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, policy.maxDelayMs);\n}\n\nexport function parseRetryAfter(\n headerValue: string | null,\n now: Date = new Date(),\n): Date | undefined {\n if (!headerValue) {\n return undefined;\n }\n const trimmed = headerValue.trim();\n if (/^\\d+$/.test(trimmed)) {\n return new Date(now.getTime() + Number(trimmed) * 1000);\n }\n const parsed = Date.parse(trimmed);\n if (Number.isNaN(parsed)) {\n return undefined;\n }\n return new Date(parsed);\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(signal.reason ?? new Error('Aborted'));\n }\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal!.reason ?? new Error('Aborted'));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","export const HTTP_CLIENT_VERSION = '0.0.0';\n\nexport const DEFAULT_USER_AGENT = `rawdash-connector/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n\nexport function connectorUserAgent(connectorId: string): string {\n return `rawdash-connector-${connectorId}/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n}\n","import {\n AuthError,\n ClientBugError,\n HttpClientError,\n RateLimitError,\n TransientError,\n UpstreamBugError,\n errorForStatus,\n} from './errors';\nimport { defaultRetryOn, parseRetryAfter, sleep } from './retry';\nimport type { FetchLike, HttpMethod, HttpRequest, HttpResponse } from './types';\nimport { DEFAULT_USER_AGENT } from './version';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_ATTEMPTS = 3;\nconst DEFAULT_INITIAL_DELAY_MS = 1000;\nconst DEFAULT_MAX_DELAY_MS = 60_000;\nconst OBSERVER_TIMEOUT_MS = 250;\n\nexport interface RequestObservation {\n url: string;\n method: HttpMethod;\n status: number;\n resource: string;\n requestId: string;\n body: unknown;\n}\n\nexport type RequestObserver = (\n event: RequestObservation,\n) => void | Promise<void>;\n\nexport interface RequestOptions {\n fetch?: FetchLike;\n observer?: RequestObserver;\n resource: string;\n requestId?: string;\n}\n\nasync function notifyObserver(\n observer: RequestObserver,\n event: RequestObservation,\n): Promise<void> {\n let result: void | Promise<void>;\n try {\n result = observer(event);\n } catch (err) {\n console.warn('[connector-shared] request observer threw:', err);\n return;\n }\n if (!(result instanceof Promise)) {\n return;\n }\n const guarded = result.catch((err) => {\n console.warn('[connector-shared] request observer rejected:', err);\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, OBSERVER_TIMEOUT_MS);\n });\n try {\n await Promise.race([guarded, timeout]);\n } finally {\n if (timer) {\n clearTimeout(timer);\n }\n }\n}\n\nfunction newRequestId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction mergeHeaders(\n defaults: Record<string, string>,\n overrides: Record<string, string> | undefined,\n): Record<string, string> {\n const merged: Record<string, string> = {};\n for (const [k, v] of Object.entries(defaults)) {\n merged[k.toLowerCase()] = v;\n }\n if (overrides) {\n for (const [k, v] of Object.entries(overrides)) {\n merged[k.toLowerCase()] = v;\n }\n }\n return merged;\n}\n\nfunction linkTimeoutSignal(\n parent: AbortSignal | undefined,\n timeoutMs: number,\n): { signal: AbortSignal; cancel: () => void } {\n const controller = new AbortController();\n const onParentAbort = () => {\n controller.abort(parent?.reason);\n };\n if (parent) {\n if (parent.aborted) {\n controller.abort(parent.reason);\n } else {\n parent.addEventListener('abort', onParentAbort, { once: true });\n }\n }\n const timer = setTimeout(() => {\n controller.abort(new Error(`Request timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n return {\n signal: controller.signal,\n cancel: () => {\n clearTimeout(timer);\n if (parent) {\n parent.removeEventListener('abort', onParentAbort);\n }\n },\n };\n}\n\nasync function readBody(res: Response, parseJson: boolean): Promise<unknown> {\n if (res.status === 204 || res.status === 205) {\n return null;\n }\n const contentType = res.headers.get('content-type') ?? '';\n if (parseJson && contentType.includes('application/json')) {\n const text = await res.text();\n if (text.length === 0) {\n return null;\n }\n return JSON.parse(text);\n }\n return res.text();\n}\n\nexport async function request<T = unknown>(\n req: HttpRequest,\n options: RequestOptions,\n): Promise<HttpResponse<T>> {\n const fetchImpl: FetchLike = options.fetch ?? (globalThis.fetch as FetchLike);\n const retry = req.retry ?? {};\n const maxAttempts = retry.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n const initialDelayMs = retry.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;\n const maxDelayMs = retry.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;\n const retryOn = retry.retryOn ?? defaultRetryOn;\n const timeoutMs = req.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const parseJson = req.parseJson ?? true;\n\n const headers = mergeHeaders(\n {\n 'User-Agent': DEFAULT_USER_AGENT,\n Accept: 'application/json',\n },\n req.headers,\n );\n\n let lastErr: Error | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n req.signal?.throwIfAborted();\n\n const { signal, cancel } = linkTimeoutSignal(req.signal, timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(req.url, {\n method: req.method ?? 'GET',\n headers,\n body: req.body as RequestInit['body'],\n signal,\n });\n } catch (err) {\n cancel();\n if (req.signal?.aborted) {\n throw req.signal.reason ?? err;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n lastErr = error;\n if (attempt < maxAttempts - 1 && retryOn(null, error)) {\n const delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n await sleep(delay, req.signal);\n continue;\n }\n throw new TransientError(error.message);\n }\n cancel();\n\n const body = await readBody(res, parseJson);\n const httpResponse: HttpResponse<T> = {\n status: res.status,\n headers: res.headers,\n body: body as T,\n };\n if (req.rateLimit) {\n const state = req.rateLimit.parse(res.headers);\n if (state) {\n httpResponse.rateLimitState = state;\n }\n }\n\n if (options.observer) {\n await notifyObserver(options.observer, {\n url: req.url,\n method: req.method ?? 'GET',\n status: res.status,\n resource: options.resource,\n requestId: options.requestId ?? newRequestId(),\n body,\n });\n }\n\n if (res.ok) {\n return httpResponse;\n }\n\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\n const message = `HTTP ${res.status} ${res.statusText} for ${req.method ?? 'GET'} ${req.url}`;\n const err = errorForStatus(message, httpResponse, retryAfter);\n\n if (\n attempt < maxAttempts - 1 &&\n retryOn(res.status, err) &&\n !(err instanceof AuthError) &&\n !(err instanceof ClientBugError)\n ) {\n lastErr = err;\n let delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n if (err instanceof RateLimitError && retryAfter) {\n const wait = retryAfter.getTime() - Date.now();\n if (wait > 0) {\n delay = Math.min(wait, maxDelayMs);\n }\n }\n await sleep(delay, req.signal);\n continue;\n }\n\n throw err;\n }\n\n throw lastErr ?? new UpstreamBugError('Exhausted retry attempts');\n}\n\nfunction computeDelay(\n attempt: number,\n initialDelayMs: number,\n maxDelayMs: number,\n): number {\n const base = initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, maxDelayMs);\n}\n\nexport { HttpClientError };\n","export interface RateLimitState {\n remaining: number;\n resetAt: Date;\n}\n\nexport interface RateLimitPolicy {\n parse(headers: Headers): RateLimitState | null;\n}\n\nexport interface StandardRateLimitPolicyConfig {\n remainingHeader: string;\n resetHeader: string;\n resetUnit: 's' | 'ms';\n resetFallbackMs?: number;\n}\n\nexport function standardRateLimitPolicy(\n config: StandardRateLimitPolicyConfig,\n): RateLimitPolicy {\n const { remainingHeader, resetHeader, resetUnit, resetFallbackMs } = config;\n const multiplier = resetUnit === 's' ? 1000 : 1;\n return {\n parse(h) {\n const remainingRaw = h.get(remainingHeader);\n if (remainingRaw === null || remainingRaw.trim() === '') {\n return null;\n }\n const remaining = Number(remainingRaw);\n if (!Number.isFinite(remaining)) {\n return null;\n }\n const resetRaw = h.get(resetHeader);\n if (resetRaw === null) {\n if (resetFallbackMs === undefined) {\n return null;\n }\n return {\n remaining,\n resetAt: new Date(Date.now() + resetFallbackMs),\n };\n }\n if (resetRaw.trim() === '') {\n return null;\n }\n const reset = Number(resetRaw);\n if (!Number.isFinite(reset) || reset < 0) {\n return null;\n }\n const resetMs = reset * multiplier;\n if (!Number.isFinite(resetMs)) {\n return null;\n }\n return { remaining, resetAt: new Date(resetMs) };\n },\n };\n}\n","export async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n if (items.length === 0) {\n return results;\n }\n const normalized = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;\n const limit = Math.max(1, Math.min(normalized, items.length));\n let next = 0;\n let failed = false;\n\n async function worker(): Promise<void> {\n while (!failed) {\n const i = next++;\n if (i >= items.length) {\n return;\n }\n try {\n results[i] = await fn(items[i]!, i);\n } catch (err) {\n failed = true;\n throw err;\n }\n }\n }\n\n const workers: Promise<void>[] = [];\n for (let w = 0; w < limit; w++) {\n workers.push(worker());\n }\n await Promise.all(workers);\n return results;\n}\n","export interface SanitizeAllowedUrlOptions {\n url: string | null;\n host: string;\n pathname: string;\n protocol?: 'https:' | 'http:';\n}\n\nexport function sanitizeAllowedUrl(\n options: SanitizeAllowedUrlOptions,\n): string | null {\n const { url, host, pathname, protocol = 'https:' } = options;\n if (url === null) {\n return null;\n }\n try {\n const u = new URL(url);\n if (u.protocol !== protocol || u.host !== host || u.pathname !== pathname) {\n return null;\n }\n return u.toString();\n } catch {\n return null;\n }\n}\n","export type EpochUnit = 'ms' | 's' | 'iso';\n\nexport function parseEpoch(\n value: number | string | null | undefined,\n unit: EpochUnit,\n): number | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (unit === 'iso') {\n if (typeof value !== 'string') {\n return null;\n }\n const ms = new Date(value).getTime();\n return Number.isFinite(ms) ? ms : null;\n }\n if (typeof value === 'string' && value.trim() === '') {\n return null;\n }\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n)) {\n return null;\n }\n const result = unit === 's' ? n * 1000 : n;\n return Number.isFinite(result) ? result : null;\n}\n","import { request } from './request';\nimport type { HttpRequest } from './types';\n\nexport function parseLinkHeader(header: string | null): Record<string, string> {\n if (!header) {\n return {};\n }\n const result: Record<string, string> = {};\n for (const part of header.split(',')) {\n const match = part.match(/<([^>]+)>\\s*;\\s*rel=\"([^\"]+)\"/);\n if (match) {\n result[match[2]!] = match[1]!;\n }\n }\n return result;\n}\n\nexport async function* paginateLink<T>(\n initial: HttpRequest,\n parse: (body: unknown) => T[],\n options: { resource: string },\n): AsyncIterable<T> {\n let next: string | null = initial.url;\n while (next) {\n const res: Awaited<ReturnType<typeof request>> = await request(\n {\n ...initial,\n url: next,\n },\n { resource: options.resource },\n );\n for (const item of parse(res.body)) {\n yield item;\n }\n const links = parseLinkHeader(res.headers.get('link'));\n next = links['next'] ?? null;\n }\n}\n\nexport async function* paginateCursor<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; nextCursor: string | null },\n buildNext: (req: HttpRequest, cursor: string) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let req: HttpRequest = initial;\n while (true) {\n const res = await request(req, { resource: options.resource });\n const { items, nextCursor } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!nextCursor) {\n return;\n }\n req = buildNext(req, nextCursor);\n }\n}\n\nexport async function* paginatePage<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; hasMore: boolean },\n buildPage: (req: HttpRequest, page: number) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let page = 1;\n while (true) {\n const req = page === 1 ? initial : buildPage(initial, page);\n const res = await request(req, { resource: options.resource });\n const { items, hasMore } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!hasMore || items.length === 0) {\n return;\n }\n page++;\n }\n}\n","export type LogFields = Record<string, unknown>;\n\nexport interface ConnectorLogger {\n info(event: string, fields?: LogFields): void;\n warn(event: string, fields?: LogFields): void;\n}\n\nexport interface ConnectorLoggerOptions {\n scope: string;\n}\n\nconst MAX_VALUE_LEN = 120;\n\nfunction truncate(s: string, max = MAX_VALUE_LEN): string {\n if (s.length <= max) {\n return s;\n }\n return `${s.slice(0, max - 1)}…`;\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return '';\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'string') {\n const t = truncate(value);\n if (/[\\s\"=]/.test(t)) {\n return JSON.stringify(t);\n }\n return t;\n }\n if (typeof value === 'bigint') {\n return value.toString();\n }\n let json: string | undefined;\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n return truncate(json ?? String(value));\n}\n\nexport function formatLogFields(fields?: LogFields): string {\n if (!fields) {\n return '';\n }\n const parts: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (v === undefined) {\n continue;\n }\n parts.push(`${k}=${formatValue(v)}`);\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nexport function formatLogLine(\n scope: string,\n event: string,\n fields?: LogFields,\n): string {\n return `[${scope}] ${event}${formatLogFields(fields)}`;\n}\n\nexport function createDefaultConnectorLogger(\n opts: ConnectorLoggerOptions,\n): ConnectorLogger {\n return {\n info(event, fields) {\n console.info(formatLogLine(opts.scope, event, fields));\n },\n warn(event, fields) {\n console.warn(formatLogLine(opts.scope, event, fields));\n },\n };\n}\n\nconst NOOP_LOGGER: ConnectorLogger = {\n info() {},\n warn() {},\n};\n\nexport function noopConnectorLogger(): ConnectorLogger {\n return NOOP_LOGGER;\n}\n","import {\n type HttpResponse,\n connectorUserAgent,\n parseEpoch,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type ConnectorContext,\n type ConnectorDoc,\n type CredentialsSchema,\n type JSONValue,\n type MetricSample,\n type StorageHandle,\n type SyncOptions,\n type SyncResult,\n defineConfigFields,\n defineConnectorDoc,\n defineResources,\n makeChunkedCursorGuard,\n schemasFromResources,\n selectActivePhases,\n} from '@rawdash/core';\nimport { z } from 'zod';\n\nconst ANTHROPIC_API_HOST = 'api.anthropic.com';\nconst ANTHROPIC_API_BASE = `https://${ANTHROPIC_API_HOST}`;\nconst ANTHROPIC_API_VERSION = '2023-06-01';\nconst USAGE_PAGE_LIMIT = 31;\nconst COSTS_PAGE_LIMIT = 31;\nconst MS_PER_DAY = 86_400_000;\nconst DEFAULT_LOOKBACK_DAYS = 30;\nconst INCREMENTAL_LOOKBACK_DAYS = 2;\n// Cost report `amount` is a decimal string in the lowest currency unit (cents\n// for USD): e.g. \"123.45\" represents $1.2345. Divide by 100 to get dollars.\nconst COST_AMOUNT_DIVISOR = 100;\n\nexport const configFields = defineConfigFields(\n z.object({\n adminApiKey: z.object({ $secret: z.string().min(1) }).meta({\n label: 'Admin API key',\n description:\n '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.',\n placeholder: 'ANTHROPIC_ADMIN_API_KEY',\n secret: true,\n }),\n workspaceIds: z.array(z.string().min(1)).nonempty().optional().meta({\n label: 'Workspace IDs (optional)',\n description:\n 'Restrict usage and cost queries to specific Anthropic workspace ids (wrkspc_...). Omit to aggregate every workspace the admin key can see.',\n }),\n resources: z\n .array(\n z.enum([\n 'anthropic_input_tokens',\n 'anthropic_output_tokens',\n 'anthropic_cache_read_tokens',\n 'anthropic_cache_creation_tokens',\n 'anthropic_web_search_requests',\n 'anthropic_cost_usd',\n ]),\n )\n .nonempty()\n .optional()\n .meta({\n label: 'Resources',\n description:\n '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.',\n }),\n lookbackDays: z.number().int().positive().max(180).optional().meta({\n label: 'Backfill window (days)',\n description:\n '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.',\n placeholder: '30',\n }),\n }),\n);\n\nexport const doc: ConnectorDoc = defineConnectorDoc({\n displayName: 'Anthropic',\n category: 'engineering',\n brandColor: '#D97757',\n tagline:\n 'Track Anthropic spend, daily token usage across Claude models, cache hit volumes, and web-search tool requests from the Anthropic Admin API.',\n vendor: {\n name: 'Anthropic',\n domain: 'anthropic.com',\n apiDocs:\n 'https://docs.claude.com/en/api/admin-api/usage-cost/get-messages-usage-report',\n website: 'https://anthropic.com',\n },\n auth: {\n summary:\n '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.',\n setup: [\n '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.',\n 'Store the key as a secret (e.g. ANTHROPIC_ADMIN_API_KEY).',\n 'Reference it from config as `adminApiKey: secret(\"ANTHROPIC_ADMIN_API_KEY\")`.',\n 'Optionally set `workspaceIds` to restrict the query to a subset of workspaces.',\n ],\n },\n rateLimit:\n '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.',\n limitations: [\n '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.',\n '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.',\n 'The Cost Report only supports 1d bucket_width and reports cost in USD; non-USD billing currencies are not converted.',\n 'Admin API keys are required - regular sk-ant-api- keys do not have access to the organization Usage and Cost reports.',\n ],\n});\n\nconst PHASE_ORDER = ['usage_messages', 'cost_report'] as const;\n\ntype AnthropicPhase = (typeof PHASE_ORDER)[number];\n\nexport type AnthropicResource =\n | 'anthropic_input_tokens'\n | 'anthropic_output_tokens'\n | 'anthropic_cache_read_tokens'\n | 'anthropic_cache_creation_tokens'\n | 'anthropic_web_search_requests'\n | 'anthropic_cost_usd';\n\nconst isAnthropicSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);\n\nconst RESOURCES_BY_PHASE: Record<AnthropicPhase, readonly AnthropicResource[]> =\n {\n usage_messages: [\n 'anthropic_input_tokens',\n 'anthropic_output_tokens',\n 'anthropic_cache_read_tokens',\n 'anthropic_cache_creation_tokens',\n 'anthropic_web_search_requests',\n ],\n cost_report: ['anthropic_cost_usd'],\n };\n\nconst PHASE_ENDPOINT_PATH: Record<AnthropicPhase, string> = {\n usage_messages: '/v1/organizations/usage_report/messages',\n cost_report: '/v1/organizations/cost_report',\n};\n\nconst usageCacheCreationSchema = z.object({\n ephemeral_1h_input_tokens: z.number().nonnegative().nullish(),\n ephemeral_5m_input_tokens: z.number().nonnegative().nullish(),\n});\n\nconst usageServerToolUseSchema = z.object({\n web_search_requests: z.number().int().nonnegative().nullish(),\n});\n\nconst usageResultSchema = z.object({\n account_id: z.string().nullish(),\n api_key_id: z.string().nullish(),\n cache_creation: usageCacheCreationSchema.nullish(),\n cache_read_input_tokens: z.number().nonnegative(),\n context_window: z.string().nullish(),\n inference_geo: z.string().nullish(),\n model: z.string().nullish(),\n output_tokens: z.number().nonnegative(),\n server_tool_use: usageServerToolUseSchema.nullish(),\n service_account_id: z.string().nullish(),\n service_tier: z.string().nullish(),\n uncached_input_tokens: z.number().nonnegative(),\n workspace_id: z.string().nullish(),\n});\n\nconst costResultSchema = z.object({\n amount: z.string(),\n context_window: z.string().nullish(),\n cost_type: z.string().nullish(),\n currency: z.string(),\n description: z.string().nullish(),\n inference_geo: z.string().nullish(),\n model: z.string().nullish(),\n service_tier: z.string().nullish(),\n token_type: z.string().nullish(),\n workspace_id: z.string().nullish(),\n});\n\nfunction bucketResponseSchema<T extends z.ZodTypeAny>(resultSchema: T) {\n return z.object({\n data: z.array(\n z.object({\n starting_at: z.string(),\n ending_at: z.string(),\n results: z.array(resultSchema),\n }),\n ),\n has_more: z.boolean(),\n next_page: z.string().nullish(),\n });\n}\n\nconst usageResponseSchema = bucketResponseSchema(usageResultSchema);\nconst costResponseSchema = bucketResponseSchema(costResultSchema);\n\ntype UsageResult = z.infer<typeof usageResultSchema>;\ntype CostResult = z.infer<typeof costResultSchema>;\n\ninterface BucketPage<TResult> {\n starting_at: string;\n ending_at: string;\n results: TResult[];\n}\n\ninterface PageResponse<TResult> {\n buckets: BucketPage<TResult>[];\n nextPage: string | null;\n}\n\nconst USAGE_DIMENSIONS = [\n {\n name: 'model',\n description: 'Claude model id reported by Anthropic (or null).',\n },\n {\n name: 'workspace_id',\n description:\n 'Anthropic workspace id the usage is attributed to (or null for the default workspace).',\n },\n {\n name: 'api_key_id',\n description: 'API key id the usage is attributed to (or null).',\n },\n {\n name: 'service_tier',\n description:\n 'Service tier the request ran under (standard, batch, priority, flex, etc.), or null.',\n },\n {\n name: 'context_window',\n description:\n 'Context window bucket the request used (0-200k or 200k-1M), or null.',\n },\n {\n name: 'inference_geo',\n description:\n 'Inference geo the request ran in (global, us, not_available), or null.',\n },\n] as const;\n\nconst COST_DIMENSIONS = [\n {\n name: 'workspace_id',\n description:\n 'Anthropic workspace id the cost is attributed to (or null for the default workspace).',\n },\n {\n name: 'description',\n description:\n 'Human-readable cost line item label (e.g. \"Claude Sonnet 4 Usage - Input Tokens\"), or null when ungrouped.',\n },\n {\n name: 'cost_type',\n description:\n 'Cost category (tokens, web_search, code_execution, session_usage), or null.',\n },\n {\n name: 'model',\n description:\n 'Claude model the cost is attributed to (or null for non-token costs).',\n },\n {\n name: 'token_type',\n description:\n 'Token category for token costs (uncached_input_tokens, output_tokens, cache_read_input_tokens, cache_creation.ephemeral_*_input_tokens), or null.',\n },\n {\n name: 'service_tier',\n description:\n 'Service tier the cost is attributed to (standard or batch), or null.',\n },\n {\n name: 'context_window',\n description:\n 'Context window the cost is attributed to (0-200k or 200k-1M), or null.',\n },\n {\n name: 'currency',\n description:\n 'Billing currency reported by Anthropic (currently always USD).',\n },\n] as const;\n\nexport const anthropicResources = defineResources({\n anthropic_input_tokens: {\n shape: 'metric',\n description:\n 'Daily uncached input tokens processed by the Anthropic Messages API, grouped by model and workspace.',\n endpoint: 'GET /v1/organizations/usage_report/messages',\n unit: 'tokens',\n granularity: 'daily',\n dimensions: [...USAGE_DIMENSIONS],\n notes:\n '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.',\n responses: { usage_messages: usageResponseSchema },\n },\n anthropic_output_tokens: {\n shape: 'metric',\n description:\n 'Daily output tokens generated by the Anthropic Messages API, grouped by model and workspace.',\n endpoint: 'GET /v1/organizations/usage_report/messages',\n unit: 'tokens',\n granularity: 'daily',\n dimensions: [...USAGE_DIMENSIONS],\n notes:\n 'Written alongside anthropic_input_tokens from the same usage_messages API call.',\n },\n anthropic_cache_read_tokens: {\n shape: 'metric',\n description:\n 'Daily input tokens read from the prompt cache, grouped by model and workspace.',\n endpoint: 'GET /v1/organizations/usage_report/messages',\n unit: 'tokens',\n granularity: 'daily',\n dimensions: [...USAGE_DIMENSIONS],\n notes:\n 'Cache hits are charged at a fraction of the uncached rate, so this metric paired with anthropic_input_tokens gives the cache hit ratio.',\n },\n anthropic_cache_creation_tokens: {\n shape: 'metric',\n description:\n 'Daily input tokens written into the prompt cache (sum of the 1h and 5m ephemeral caches), grouped by model and workspace.',\n endpoint: 'GET /v1/organizations/usage_report/messages',\n unit: 'tokens',\n granularity: 'daily',\n dimensions: [...USAGE_DIMENSIONS],\n notes:\n 'The per-cache-bucket counts (ephemeral_1h_input_tokens, ephemeral_5m_input_tokens) are mirrored in attributes for finer-grained widgets.',\n },\n anthropic_web_search_requests: {\n shape: 'metric',\n description:\n 'Daily count of web-search tool requests executed server-side by Claude, grouped by model and workspace.',\n endpoint: 'GET /v1/organizations/usage_report/messages',\n unit: 'requests',\n granularity: 'daily',\n dimensions: [...USAGE_DIMENSIONS],\n notes:\n '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.',\n },\n anthropic_cost_usd: {\n shape: 'metric',\n description:\n 'Daily organization spend in USD, broken down by workspace and cost line item, pulled from the Anthropic Cost Report.',\n endpoint: 'GET /v1/organizations/cost_report',\n unit: 'USD',\n granularity: 'daily',\n dimensions: [...COST_DIMENSIONS],\n notes:\n '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.',\n responses: { cost_report: costResponseSchema },\n },\n});\n\nexport interface AnthropicSettings {\n workspaceIds?: readonly string[];\n resources?: readonly AnthropicResource[];\n lookbackDays?: number;\n}\n\nconst anthropicCredentials = {\n adminApiKey: {\n description: 'Anthropic organization admin API key (sk-ant-admin-...)',\n auth: 'required' as const,\n },\n} satisfies CredentialsSchema;\n\ntype AnthropicCredentials = typeof anthropicCredentials;\n\nexport const id = 'anthropic';\n\ninterface UsageWindow {\n startingAt: string;\n endingAt: string;\n}\n\nexport function getUsageWindow(\n options: SyncOptions,\n lookbackDays: number,\n now: number = Date.now(),\n): UsageWindow {\n const todayStart = Math.floor(now / MS_PER_DAY) * MS_PER_DAY;\n const endMs = todayStart + MS_PER_DAY;\n\n let days = lookbackDays;\n if (options.mode === 'latest') {\n days = INCREMENTAL_LOOKBACK_DAYS;\n } else if (options.since !== undefined) {\n const sinceMs = parseEpoch(options.since, 'iso');\n if (sinceMs !== null) {\n const elapsed = Math.ceil((now - sinceMs) / MS_PER_DAY);\n days = Math.min(\n Math.max(elapsed + INCREMENTAL_LOOKBACK_DAYS, 1),\n lookbackDays,\n );\n }\n }\n const startMs = endMs - days * MS_PER_DAY;\n return {\n startingAt: new Date(startMs).toISOString(),\n endingAt: new Date(endMs).toISOString(),\n };\n}\n\nfunction resourceToPhase(resource: AnthropicResource): AnthropicPhase {\n for (const phase of PHASE_ORDER) {\n if ((RESOURCES_BY_PHASE[phase] as readonly string[]).includes(resource)) {\n return phase;\n }\n }\n // unreachable - RESOURCES_BY_PHASE covers every AnthropicResource\n throw new Error(`anthropic: unmapped resource ${resource}`);\n}\n\nfunction nullableString(value: string | null | undefined): string | null {\n return value === undefined || value === null ? null : value;\n}\n\nfunction usageDimensionAttributes(row: UsageResult): Record<string, JSONValue> {\n return {\n model: nullableString(row.model),\n workspace_id: nullableString(row.workspace_id),\n api_key_id: nullableString(row.api_key_id),\n service_tier: nullableString(row.service_tier),\n context_window: nullableString(row.context_window),\n inference_geo: nullableString(row.inference_geo),\n account_id: nullableString(row.account_id),\n service_account_id: nullableString(row.service_account_id),\n };\n}\n\nfunction cacheCreationTotal(row: UsageResult): number {\n const c = row.cache_creation;\n if (!c) {\n return 0;\n }\n return (\n (c.ephemeral_1h_input_tokens ?? 0) + (c.ephemeral_5m_input_tokens ?? 0)\n );\n}\n\nfunction tsFromBucket(bucket: BucketPage<unknown>): number | null {\n return parseEpoch(bucket.starting_at, 'iso');\n}\n\nexport function buildUsageSamples(\n buckets: readonly BucketPage<UsageResult>[],\n): {\n inputTokens: MetricSample[];\n outputTokens: MetricSample[];\n cacheReadTokens: MetricSample[];\n cacheCreationTokens: MetricSample[];\n webSearchRequests: MetricSample[];\n} {\n const inputTokens: MetricSample[] = [];\n const outputTokens: MetricSample[] = [];\n const cacheReadTokens: MetricSample[] = [];\n const cacheCreationTokens: MetricSample[] = [];\n const webSearchRequests: MetricSample[] = [];\n for (const bucket of buckets) {\n const ts = tsFromBucket(bucket);\n if (ts === null) {\n continue;\n }\n for (const row of bucket.results) {\n const common = usageDimensionAttributes(row);\n const cacheCreation = row.cache_creation;\n inputTokens.push({\n name: 'anthropic_input_tokens',\n ts,\n value: row.uncached_input_tokens,\n attributes: { ...common },\n });\n outputTokens.push({\n name: 'anthropic_output_tokens',\n ts,\n value: row.output_tokens,\n attributes: { ...common },\n });\n cacheReadTokens.push({\n name: 'anthropic_cache_read_tokens',\n ts,\n value: row.cache_read_input_tokens,\n attributes: { ...common },\n });\n cacheCreationTokens.push({\n name: 'anthropic_cache_creation_tokens',\n ts,\n value: cacheCreationTotal(row),\n attributes: {\n ...common,\n ephemeral_1h_input_tokens:\n cacheCreation?.ephemeral_1h_input_tokens ?? 0,\n ephemeral_5m_input_tokens:\n cacheCreation?.ephemeral_5m_input_tokens ?? 0,\n },\n });\n webSearchRequests.push({\n name: 'anthropic_web_search_requests',\n ts,\n value: row.server_tool_use?.web_search_requests ?? 0,\n attributes: { ...common },\n });\n }\n }\n return {\n inputTokens,\n outputTokens,\n cacheReadTokens,\n cacheCreationTokens,\n webSearchRequests,\n };\n}\n\nexport function buildCostSamples(\n buckets: readonly BucketPage<CostResult>[],\n): MetricSample[] {\n const samples: MetricSample[] = [];\n for (const bucket of buckets) {\n const ts = tsFromBucket(bucket);\n if (ts === null) {\n continue;\n }\n for (const row of bucket.results) {\n const rawAmount = Number.parseFloat(row.amount);\n const value = Number.isFinite(rawAmount)\n ? rawAmount / COST_AMOUNT_DIVISOR\n : 0;\n samples.push({\n name: 'anthropic_cost_usd',\n ts,\n value,\n attributes: {\n workspace_id: nullableString(row.workspace_id),\n description: nullableString(row.description),\n cost_type: nullableString(row.cost_type),\n model: nullableString(row.model),\n token_type: nullableString(row.token_type),\n service_tier: nullableString(row.service_tier),\n context_window: nullableString(row.context_window),\n currency: row.currency,\n },\n });\n }\n }\n return samples;\n}\n\nexport class AnthropicConnector extends BaseConnector<\n AnthropicSettings,\n AnthropicCredentials\n> {\n static readonly id = id;\n\n static readonly resources = anthropicResources;\n\n static readonly schemas = schemasFromResources(anthropicResources);\n\n static create(input: unknown, ctx?: ConnectorContext): AnthropicConnector {\n const parsed = configFields.parse(input);\n return new AnthropicConnector(\n {\n workspaceIds: parsed.workspaceIds,\n resources: parsed.resources,\n lookbackDays: parsed.lookbackDays,\n },\n { adminApiKey: parsed.adminApiKey },\n ctx,\n );\n }\n\n readonly id = id;\n override readonly credentials = anthropicCredentials;\n\n private buildHeaders(): Record<string, string> {\n return {\n 'X-Api-Key': String(this.creds.adminApiKey),\n 'anthropic-version': ANTHROPIC_API_VERSION,\n 'User-Agent': connectorUserAgent(this.id),\n };\n }\n\n private fetch<T>(\n url: string,\n resource: string,\n signal?: AbortSignal,\n ): Promise<HttpResponse<T>> {\n return this.get<T>(url, {\n resource,\n headers: this.buildHeaders(),\n signal,\n });\n }\n\n private buildInitialUrl(phase: AnthropicPhase, window: UsageWindow): string {\n const url = new URL(`${ANTHROPIC_API_BASE}${PHASE_ENDPOINT_PATH[phase]}`);\n url.searchParams.set('starting_at', window.startingAt);\n url.searchParams.set('ending_at', window.endingAt);\n url.searchParams.set('bucket_width', '1d');\n if (phase === 'usage_messages') {\n url.searchParams.set('limit', String(USAGE_PAGE_LIMIT));\n url.searchParams.append('group_by', 'model');\n url.searchParams.append('group_by', 'workspace_id');\n url.searchParams.append('group_by', 'api_key_id');\n url.searchParams.append('group_by', 'service_tier');\n url.searchParams.append('group_by', 'context_window');\n url.searchParams.append('group_by', 'inference_geo');\n for (const workspaceId of this.settings.workspaceIds ?? []) {\n url.searchParams.append('workspace_ids', workspaceId);\n }\n } else {\n url.searchParams.set('limit', String(COSTS_PAGE_LIMIT));\n url.searchParams.append('group_by', 'workspace_id');\n url.searchParams.append('group_by', 'description');\n }\n return url.toString();\n }\n\n private buildNextUrl(currentUrl: string, nextPage: string): string {\n const url = new URL(currentUrl);\n url.searchParams.set('page', nextPage);\n return url.toString();\n }\n\n private async fetchPhasePage<T>(\n phase: AnthropicPhase,\n schema: z.ZodType<T>,\n initialUrl: string,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<{ url: string; parsed: T; nextUrl: string | null }> {\n const url = page ?? initialUrl;\n const res = await this.fetch<unknown>(url, phase, signal);\n const parsed = schema.parse(res.body);\n const body = parsed as unknown as {\n next_page?: string | null;\n has_more?: boolean;\n };\n const nextPage =\n body.has_more === true &&\n typeof body.next_page === 'string' &&\n body.next_page.length > 0\n ? body.next_page\n : null;\n const nextUrl = nextPage ? this.buildNextUrl(url, nextPage) : null;\n return { url, parsed, nextUrl };\n }\n\n async sync(\n options: SyncOptions,\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<SyncResult> {\n const cursor = isAnthropicSyncCursor(options.cursor)\n ? options.cursor\n : undefined;\n const lookbackDays = this.settings.lookbackDays ?? DEFAULT_LOOKBACK_DAYS;\n const window = getUsageWindow(options, lookbackDays);\n\n const phases = selectActivePhases<AnthropicResource, AnthropicPhase>(\n resourceToPhase,\n PHASE_ORDER,\n this.settings.resources,\n );\n\n const startIdx = cursor ? phases.indexOf(cursor.phase) : 0;\n const resumeIdx = startIdx >= 0 ? startIdx : 0;\n\n for (let i = resumeIdx; i < phases.length; i++) {\n const phase = phases[i]!;\n if (signal?.aborted) {\n return { done: false, cursor: { phase, page: null } };\n }\n const phaseStart = Date.now();\n const initialUrl = this.buildInitialUrl(phase, window);\n let pageUrl: string | null = null;\n let pageCount = 0;\n const buckets: BucketPage<unknown>[] = [];\n\n while (true) {\n if (signal?.aborted) {\n return { done: false, cursor: { phase, page: null } };\n }\n pageCount += 1;\n const { parsed, nextUrl } = await this.fetchAnyPhasePage(\n phase,\n initialUrl,\n pageUrl,\n signal,\n );\n const data = parsed.data as BucketPage<unknown>[];\n buckets.push(...data);\n this.logger.info('fetched page', {\n resource: phase,\n page: pageCount,\n items: data.length,\n });\n if (nextUrl === null) {\n break;\n }\n pageUrl = nextUrl;\n }\n\n await this.writePhase(storage, phase, buckets);\n this.logger.info('resource done', {\n resource: phase,\n pages: pageCount,\n items: buckets.length,\n duration_ms: Date.now() - phaseStart,\n });\n }\n\n return { done: true };\n }\n\n private async fetchAnyPhasePage(\n phase: AnthropicPhase,\n initialUrl: string,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<{\n parsed: { data: BucketPage<unknown>[] };\n nextUrl: string | null;\n }> {\n switch (phase) {\n case 'usage_messages':\n return this.fetchPhasePage(\n phase,\n usageResponseSchema,\n initialUrl,\n page,\n signal,\n );\n case 'cost_report':\n return this.fetchPhasePage(\n phase,\n costResponseSchema,\n initialUrl,\n page,\n signal,\n );\n }\n }\n\n private async writePhase(\n storage: StorageHandle,\n phase: AnthropicPhase,\n buckets: BucketPage<unknown>[],\n ): Promise<void> {\n switch (phase) {\n case 'usage_messages': {\n const samples = buildUsageSamples(buckets as BucketPage<UsageResult>[]);\n await storage.metrics(samples.inputTokens, {\n names: ['anthropic_input_tokens'],\n });\n await storage.metrics(samples.outputTokens, {\n names: ['anthropic_output_tokens'],\n });\n await storage.metrics(samples.cacheReadTokens, {\n names: ['anthropic_cache_read_tokens'],\n });\n await storage.metrics(samples.cacheCreationTokens, {\n names: ['anthropic_cache_creation_tokens'],\n });\n await storage.metrics(samples.webSearchRequests, {\n names: ['anthropic_web_search_requests'],\n });\n return;\n }\n case 'cost_report': {\n const samples = buildCostSamples(buckets as BucketPage<CostResult>[]);\n await storage.metrics(samples, { names: ['anthropic_cost_usd'] });\n return;\n }\n }\n }\n}\n\nexport type { PageResponse, BucketPage, UsageResult, CostResult };\n","import { AnthropicConnector } from './anthropic';\n\nexport {\n AnthropicConnector,\n anthropicResources as resources,\n buildCostSamples,\n buildUsageSamples,\n configFields,\n doc,\n getUsageWindow,\n id,\n} from './anthropic';\nexport type {\n AnthropicResource,\n AnthropicSettings,\n BucketPage,\n CostResult,\n PageResponse,\n UsageResult,\n} from './anthropic';\nexport default AnthropicConnector;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rawdash/connector-anthropic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Rawdash connector for Anthropic — daily token usage and spend from the Anthropic Admin API (usage and cost reports)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -24,15 +24,15 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"zod": "^4.4.3",
|
|
27
|
-
"@rawdash/core": "0.
|
|
27
|
+
"@rawdash/core": "0.25.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"fast-check": "^4.8.0",
|
|
31
31
|
"tsup": "^8.0.0",
|
|
32
32
|
"typescript": "^5.7.2",
|
|
33
33
|
"vitest": "^4.1.4",
|
|
34
|
-
"@rawdash/connector-
|
|
35
|
-
"@rawdash/connector-
|
|
34
|
+
"@rawdash/connector-test-utils": "0.0.10",
|
|
35
|
+
"@rawdash/connector-shared": "0.3.1"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "tsup",
|