@vornrun/connector-anthropic 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@vornrun/connector-anthropic`.
4
+
5
+ ## 0.1.0
6
+
7
+ First release.
8
+
9
+ Trigger a workflow when an Anthropic message batch finishes processing or a
10
+ new model becomes available, and let a workflow step create a message, count
11
+ tokens, list or read models, and create, read, collect or cancel a message
12
+ batch.
13
+
14
+ - **Triggers:** `batchEnded`, `newModel`.
15
+ - **Actions:** `createMessage`, `countTokens`, `listModels`, `getModel`,
16
+ `createMessageBatch`, `getMessageBatch`, `getBatchResults`,
17
+ `cancelMessageBatch`.
18
+ - **Signing in:** an API key from console.anthropic.com/settings/keys, sent as
19
+ `x-api-key` with `anthropic-version: 2023-06-01`. There is no CLI to borrow
20
+ a login from.
21
+
22
+ Every action and both polls go through one small client rather than declared
23
+ SDK requests, because the documented rate-limit behaviour needs more than a
24
+ declared request can express: a `429` is retried once after `retry-after` and
25
+ reported at once when the header is missing, since that is the spend cap; a
26
+ `529` or `5xx` is retried once; and a reply that says no requests remain makes
27
+ the next call wait for `anthropic-ratelimit-requests-reset`. Every failure is
28
+ reported as `<error.type>: <error.message>` with the status and request id,
29
+ and every message, model and batch read returns the reply as `raw` beside the
30
+ named fields. Batch results are the JSONL stream parsed into an array.
31
+
32
+ Both triggers poll on the SDK's timestamp dedupe strategy, walking `after_id`
33
+ pages newest first. Ended batches are stamped with `ended_at` and
34
+ the walk stops 24 hours before the cursor, the batch lifetime, so a batch
35
+ created before one poll and ended after it still fires once. New models are
36
+ stamped with `created_at`; the first poll looks 30 days back and an epoch
37
+ release date never fires.
38
+
39
+ Ships as a pack with a conformance receipt covering the dedupe replay of both
40
+ triggers and the mock run of every action. No runtime dependencies: `fetch`
41
+ and `JSON` cover the client, the pager is a loop on `after_id`, and JSONL is
42
+ a split on newlines.
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # @vornrun/connector-anthropic
2
+
3
+ Trigger Vorn workflows when an Anthropic message batch finishes processing or
4
+ a new model becomes available, and create messages, count tokens, list models
5
+ and run message batches from a workflow step. Talks to the Claude API at
6
+ `https://api.anthropic.com/v1`.
7
+
8
+ ## Signing in
9
+
10
+ Paste an API key into the **API key** field. There is no CLI to borrow a login
11
+ from. Create one at https://console.anthropic.com/settings/keys (the console
12
+ also answers at https://platform.claude.com/settings/keys). Keys have no
13
+ scopes: choose the key's type and expiry when you create it. Use a
14
+ **single-workspace** key; a multi-workspace key also needs an
15
+ `anthropic-workspace-id` header, which this connector does not send.
16
+
17
+ The key is sent as `x-api-key` together with `anthropic-version: 2023-06-01`
18
+ on every call. A malformed, revoked or expired key answers
19
+ `401 authentication_error`; a key without access to a resource answers
20
+ `403 permission_error`. The connector surfaces every API error as
21
+ `<error.type>: <error.message>` with the HTTP status and the `request-id`,
22
+ such as `not_found_error: The requested resource could not be found. (HTTP 404,
23
+ request req_…)`. Keep the key out of shared configuration: it bills against
24
+ your organization.
25
+
26
+ ## Settings
27
+
28
+ | Setting | Environment | Required | What it does |
29
+ | --- | --- | --- | --- |
30
+ | API key | `ANTHROPIC_API_KEY` | yes | Sent as `x-api-key` on every call |
31
+
32
+ ## Rate limits
33
+
34
+ Limits are per organization and per model, in requests, input tokens and
35
+ output tokens per minute; batches have their own request limit shared across
36
+ models. Beyond a limit the API answers `429 rate_limit_error` with a
37
+ `retry-after` header. The connector:
38
+
39
+ - waits `retry-after` (capped at 60 seconds) and sends once more on a `429`;
40
+ a second `429` is reported. A `429` **without** `retry-after` is the monthly
41
+ spend cap and is reported at once, since no wait lifts it;
42
+ - retries once after `retry-after`, or a jittered one to two seconds, on a
43
+ `529 overloaded_error` or any `5xx`;
44
+ - reads `anthropic-ratelimit-requests-remaining` on every reply, and when it
45
+ reached `0` waits until `anthropic-ratelimit-requests-reset` (capped at 60
46
+ seconds) before the next call, so a poll that walks pages does not trip the
47
+ limit by itself.
48
+
49
+ Nothing else is retried. Every action goes through the same client, so the
50
+ same waits, retries and error text apply to all of them.
51
+
52
+ The connector does not stream, so `createMessage` defaults `maxTokens` to
53
+ 1024. A long generation or a large job belongs in a message batch.
54
+
55
+ ## Triggers
56
+
57
+ Both poll with `limit=100` and walk `after_id` pages, at most ten per poll,
58
+ and deliver oldest first.
59
+
60
+ ### `batchEnded` — a message batch finished
61
+
62
+ Polls `GET messages/batches`, newest first, and fires once for each batch
63
+ whose `processing_status` is `ended`. Processing ends when every request has
64
+ succeeded, errored, been canceled or expired. The list is ordered by creation,
65
+ and a batch created before the last poll can end after it, so the poll walks
66
+ back until it meets a batch created more than 24 hours before the cursor:
67
+ batches expire 24 hours after creation, so anything older ended before the
68
+ previous poll. The first poll looks 24 hours back. Each item carries the
69
+ batch as `data`, `ended_at` as its time, and a title such as
70
+ `Batch msgbatch_… ended: 50 succeeded, 30 errored`. The docs give no console
71
+ URL for a batch, so there is no `url`. Read the results with
72
+ `getBatchResults`.
73
+
74
+ ### `newModel` — a model became available
75
+
76
+ Polls `GET models`, newest first, and fires once for each model whose
77
+ `created_at` is at or after the cursor. The first poll looks 30 days back so
78
+ an old catalog does not fire wholesale. A model whose release date is unknown
79
+ carries an epoch `created_at` and never fires. Each item carries the model as
80
+ `data`, its `display_name` as the title and the models overview page as `url`.
81
+
82
+ ## Actions
83
+
84
+ Inputs typed JSON are checked before any call is made. `model` defaults to
85
+ `claude-sonnet-5`; `messages` is either prompt text, sent as one user turn, or
86
+ a JSON array of `{ "role", "content" }` turns passed through as is.
87
+
88
+ | Action | Idempotent | What it does |
89
+ | --- | --- | --- |
90
+ | `createMessage` | no | `POST messages` with `model`, `messages`, optional `system`, `maxTokens` (default 1024), `temperature`, `tools` and `stopSequences`. Returns `id`, `model`, `text` (the first text block, empty on a tool call alone), `stopReason`, `usage`, `raw`. Every call bills a generation. |
91
+ | `countTokens` | yes | `POST messages/count_tokens` with `model`, `messages`, optional `system`. Returns `inputTokens`. |
92
+ | `listModels` | yes | `GET models`, every page. Returns `models`, `count`. |
93
+ | `getModel` | yes | `GET models/{modelId}`; resolves an alias to its id. Returns `id`, `displayName`, `createdAt`, `maxInputTokens`, `maxTokens`, `capabilities`, `raw`. |
94
+ | `createMessageBatch` | no | `POST messages/batches` with `requests`, an array of `{ "custom_id", "params" }` where `params` is a full message body; a single object is a batch of one. Returns `id`, `processingStatus`, `requestCounts`, `createdAt`, `endedAt`, `expiresAt`, `cancelInitiatedAt`, `resultsUrl`, `raw`. |
95
+ | `getMessageBatch` | yes | `GET messages/batches/{batchId}`; poll it until `processingStatus` is `ended`. Same outputs. |
96
+ | `getBatchResults` | yes | `GET messages/batches/{batchId}/results`, the JSONL parsed into `results` (`{ custom_id, result }`, matched by `custom_id`, not by order) and `count`. Before processing ends the API's own error is reported. |
97
+ | `cancelMessageBatch` | no | `POST messages/batches/{batchId}/cancel`. Same outputs, with `cancelInitiatedAt` set. Requests already running may still finish. |
98
+
99
+ Models released after Claude Opus 4.6, the default included, accept only a
100
+ `temperature` of 1.0 and reject other values with a `400`. `stopSequences`
101
+ takes a JSON array of strings, or one string as a single sequence; `tools`
102
+ takes an array of `{ name, description, input_schema }`, and a tool call comes
103
+ back as a `tool_use` block in `raw.content` with `stopReason` `tool_use`.
104
+
105
+ ## Checks
106
+
107
+ From the repository root:
108
+
109
+ ```sh
110
+ yarn typecheck && yarn test && yarn build
111
+ node node_modules/@vornrun/connector-sdk/dist/cli.js check packages/anthropic/dist/index.js --mock --receipt packages/anthropic/verified.json
112
+ ```
113
+
114
+ `packages/anthropic/scripts/check.sh` runs exactly this. Tests make no network
115
+ calls: the client takes an injected `fetch`, clock and sleep.
116
+
117
+ `packages/anthropic/scripts/check-live.sh` exits 0 with a note when
118
+ `ANTHROPIC_API_KEY` is unset. With a key it lists models, reads
119
+ `claude-sonnet-5`, counts the tokens of `hello`, lists five batches, then the
120
+ batch named by `ANTHROPIC_BATCH_ID` and its results when that is set, and
121
+ finally runs `vorn-connector check --live` against the built package. The
122
+ same variable fills the live samples of `getMessageBatch` and
123
+ `getBatchResults`. Nothing is generated, queued or canceled.
124
+
125
+ ## Built from
126
+
127
+ The API reference was the only source. Every `docs.anthropic.com/en/api/…`
128
+ page now redirects to the same path under `platform.claude.com/docs/en/api/`.
129
+
130
+ - API overview: https://platform.claude.com/docs/en/api/overview
131
+ - Get started (headers, `ANTHROPIC_API_KEY`): https://platform.claude.com/docs/en/get-started
132
+ - Versions: https://platform.claude.com/docs/en/api/versioning
133
+ - Create a message: https://platform.claude.com/docs/en/api/messages
134
+ - Count tokens: https://platform.claude.com/docs/en/api/messages-count-tokens
135
+ - List models: https://platform.claude.com/docs/en/api/models-list
136
+ - Get a model: https://platform.claude.com/docs/en/api/models
137
+ - Create a message batch: https://platform.claude.com/docs/en/api/creating-message-batches
138
+ - List message batches: https://platform.claude.com/docs/en/api/listing-message-batches
139
+ - Retrieve a message batch: https://platform.claude.com/docs/en/api/retrieving-message-batches
140
+ - Retrieve batch results: https://platform.claude.com/docs/en/api/retrieving-message-batch-results
141
+ - Cancel a message batch: https://platform.claude.com/docs/en/api/canceling-message-batches
142
+ - Errors: https://platform.claude.com/docs/en/api/errors
143
+ - Rate limits: https://platform.claude.com/docs/en/api/rate-limits
144
+ - Create an API key: https://console.anthropic.com/settings/keys
@@ -0,0 +1,138 @@
1
+ import * as _vornrun_connector_sdk from '@vornrun/connector-sdk';
2
+ import { ConnectorItem } from '@vornrun/connector-sdk';
3
+
4
+ interface AnthropicConnectorOptions {
5
+ version?: string;
6
+ /** Where live samples are read from; defaults to the process environment. */
7
+ env?: NodeJS.ProcessEnv;
8
+ /** Replaced in tests so a rate-limit wait costs no real time. */
9
+ sleep?: (ms: number) => Promise<void>;
10
+ /** The clock retry waits are measured on, in milliseconds. */
11
+ now?: () => number;
12
+ random?: () => number;
13
+ }
14
+ declare const BATCH_LOOKBACK_MS: number;
15
+ declare const MODEL_LOOKBACK_MS: number;
16
+ declare function createAnthropicConnector(options?: AnthropicConnectorOptions): _vornrun_connector_sdk.Connector;
17
+ declare const connector: _vornrun_connector_sdk.Connector;
18
+
19
+ declare const API_ROOT = "https://api.anthropic.com/v1";
20
+ declare const ANTHROPIC_VERSION = "2023-06-01";
21
+ declare const PAGE_LIMIT = 100;
22
+ declare const MAX_LIST_PAGES = 10;
23
+ declare const MAX_WAIT_MS = 60000;
24
+ type Params = Record<string, unknown>;
25
+ interface AnthropicModel {
26
+ id: string;
27
+ type?: string;
28
+ display_name?: string;
29
+ created_at?: string;
30
+ max_input_tokens?: number;
31
+ max_tokens?: number;
32
+ capabilities?: Record<string, unknown>;
33
+ }
34
+ interface RequestCounts {
35
+ processing?: number;
36
+ succeeded?: number;
37
+ errored?: number;
38
+ canceled?: number;
39
+ expired?: number;
40
+ }
41
+ interface MessageBatch {
42
+ id: string;
43
+ type?: string;
44
+ processing_status?: string;
45
+ request_counts?: RequestCounts;
46
+ created_at?: string;
47
+ ended_at?: string | null;
48
+ expires_at?: string;
49
+ archived_at?: string | null;
50
+ cancel_initiated_at?: string | null;
51
+ results_url?: string | null;
52
+ }
53
+ interface Page<T> {
54
+ data?: T[];
55
+ has_more?: boolean;
56
+ first_id?: string | null;
57
+ last_id?: string | null;
58
+ }
59
+ interface BatchResult {
60
+ custom_id?: string;
61
+ result?: Record<string, unknown>;
62
+ }
63
+ declare class AnthropicApiError extends Error {
64
+ readonly status: number;
65
+ readonly type: string | undefined;
66
+ readonly requestId: string | undefined;
67
+ constructor(status: number, detail: {
68
+ type?: string;
69
+ message: string;
70
+ requestId?: string;
71
+ });
72
+ }
73
+ declare function retryAfterMs(header: string | null, now: number): number | undefined;
74
+ interface RateGateOptions {
75
+ now?: () => number;
76
+ sleep?: (ms: number) => Promise<void>;
77
+ }
78
+ declare function createRateGate(options?: RateGateOptions): {
79
+ acquire: () => Promise<void>;
80
+ observe: (headers: Headers) => void;
81
+ };
82
+ type RateGate = ReturnType<typeof createRateGate>;
83
+ declare function parseJsonl(text: string): BatchResult[];
84
+ interface AnthropicClientOptions {
85
+ apiKey: string;
86
+ fetch: typeof fetch;
87
+ gate?: RateGate;
88
+ /** Replaced in tests so a wait costs no real time. */
89
+ sleep?: (ms: number) => Promise<void>;
90
+ now?: () => number;
91
+ random?: () => number;
92
+ }
93
+ interface CallOptions {
94
+ method?: 'GET' | 'POST';
95
+ query?: Params;
96
+ body?: Params;
97
+ /** Read the body as text rather than JSON, for the JSONL results stream. */
98
+ text?: boolean;
99
+ }
100
+ interface ListOptions<T> {
101
+ maxPages?: number;
102
+ /** Stops the walk once a page holds an entry this says is past the window. */
103
+ until?: (entry: T) => boolean;
104
+ }
105
+ declare function createAnthropicClient(options: AnthropicClientOptions): {
106
+ call: <T>(path: string, callOptions?: CallOptions) => Promise<T>;
107
+ list: <T>(path: string, listOptions?: ListOptions<T>) => Promise<T[]>;
108
+ createMessage: (body: Params) => Promise<Params>;
109
+ countTokens: (body: Params) => Promise<{
110
+ input_tokens?: number;
111
+ }>;
112
+ listModels: (listOptions?: ListOptions<AnthropicModel>) => Promise<AnthropicModel[]>;
113
+ getModel: (id: string) => Promise<AnthropicModel>;
114
+ createBatch: (requests: unknown[]) => Promise<MessageBatch>;
115
+ listBatches: (listOptions?: ListOptions<MessageBatch>) => Promise<MessageBatch[]>;
116
+ getBatch: (id: string) => Promise<MessageBatch>;
117
+ getBatchResults: (id: string) => Promise<BatchResult[]>;
118
+ cancelBatch: (id: string) => Promise<MessageBatch>;
119
+ };
120
+ type AnthropicClient = ReturnType<typeof createAnthropicClient>;
121
+
122
+ declare const DEFAULT_MODEL = "claude-sonnet-5";
123
+ declare const DEFAULT_MAX_TOKENS = 1024;
124
+ declare const MODEL_IDS: readonly ["claude-fable-5-1", "claude-sonnet-5", "claude-fable-5", "claude-opus-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5", "claude-opus-4-5", "claude-sonnet-4-5"];
125
+ declare const SAMPLE_BATCH: MessageBatch;
126
+ declare const SAMPLE_MODEL: AnthropicModel;
127
+ declare function messagesArg(value: unknown): Array<Record<string, unknown>>;
128
+ declare function listArg(value: unknown, key: string): unknown[] | undefined;
129
+ declare function stopSequencesArg(value: unknown): string[] | undefined;
130
+ declare function numberArg(value: unknown, key: string): number | undefined;
131
+ declare function firstText(content: unknown): string;
132
+ declare function messageOutput(message: Params): Record<string, unknown>;
133
+ declare function modelOutput(model: AnthropicModel): Record<string, unknown>;
134
+ declare function batchOutput(batch: MessageBatch): Record<string, unknown>;
135
+ declare function batchToItem(batch: MessageBatch): ConnectorItem;
136
+ declare function modelToItem(model: AnthropicModel): ConnectorItem;
137
+
138
+ export { ANTHROPIC_VERSION, API_ROOT, AnthropicApiError, type AnthropicClient, type AnthropicConnectorOptions, type AnthropicModel, BATCH_LOOKBACK_MS, type BatchResult, DEFAULT_MAX_TOKENS, DEFAULT_MODEL, MAX_LIST_PAGES, MAX_WAIT_MS, MODEL_IDS, MODEL_LOOKBACK_MS, type MessageBatch, PAGE_LIMIT, type Page, type RateGate, SAMPLE_BATCH, SAMPLE_MODEL, batchOutput, batchToItem, connector, createAnthropicClient, createAnthropicConnector, createRateGate, connector as default, firstText, listArg, messageOutput, messagesArg, modelOutput, modelToItem, numberArg, parseJsonl, retryAfterMs, stopSequencesArg };
package/dist/index.js ADDED
@@ -0,0 +1,753 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/connector.ts
4
+ import {
5
+ defineConnector
6
+ } from "@vornrun/connector-sdk";
7
+
8
+ // src/client.ts
9
+ var API_ROOT = "https://api.anthropic.com/v1";
10
+ var ANTHROPIC_VERSION = "2023-06-01";
11
+ var PAGE_LIMIT = 100;
12
+ var MAX_LIST_PAGES = 10;
13
+ var MAX_WAIT_MS = 6e4;
14
+ var SERVER_ERROR_BACKOFF_MS = 1e3;
15
+ var MAX_ERROR_BODY = 300;
16
+ var AnthropicApiError = class extends Error {
17
+ status;
18
+ type;
19
+ requestId;
20
+ constructor(status, detail) {
21
+ const head = detail.type ? `${detail.type}: ${detail.message}` : detail.message;
22
+ super(detail.requestId ? `${head} (HTTP ${status}, request ${detail.requestId})` : `${head} (HTTP ${status})`);
23
+ this.name = "AnthropicApiError";
24
+ this.status = status;
25
+ this.type = detail.type;
26
+ this.requestId = detail.requestId;
27
+ }
28
+ };
29
+ var wait = (ms) => new Promise((resolve) => {
30
+ setTimeout(resolve, ms);
31
+ });
32
+ function retryAfterMs(header, now) {
33
+ const trimmed = (header ?? "").trim();
34
+ if (trimmed === "") return void 0;
35
+ const seconds = Number(trimmed);
36
+ const ms = Number.isFinite(seconds) ? seconds * 1e3 : Date.parse(trimmed) - now;
37
+ return Number.isNaN(ms) ? void 0 : Math.min(MAX_WAIT_MS, Math.max(0, ms));
38
+ }
39
+ function createRateGate(options = {}) {
40
+ const now = options.now ?? Date.now;
41
+ const sleep = options.sleep ?? wait;
42
+ let resetAt;
43
+ async function acquire() {
44
+ if (resetAt === void 0) return;
45
+ const delay = Math.min(MAX_WAIT_MS, resetAt - now());
46
+ resetAt = void 0;
47
+ if (delay > 0) await sleep(delay);
48
+ }
49
+ function observe(headers) {
50
+ const remaining = headers.get("anthropic-ratelimit-requests-remaining");
51
+ const reset = headers.get("anthropic-ratelimit-requests-reset");
52
+ if (remaining === null || Number(remaining) > 0 || reset === null) return;
53
+ const at = Date.parse(reset);
54
+ if (!Number.isNaN(at)) resetAt = at;
55
+ }
56
+ return { acquire, observe };
57
+ }
58
+ async function describeFailure(response) {
59
+ const requestId = response.headers.get("request-id") ?? void 0;
60
+ const text2 = await response.text().catch(() => "");
61
+ try {
62
+ const parsed2 = JSON.parse(text2);
63
+ if (parsed2.error && typeof parsed2.error === "object") {
64
+ return new AnthropicApiError(response.status, {
65
+ ...parsed2.error.type && { type: parsed2.error.type },
66
+ message: parsed2.error.message ?? `Anthropic API ${response.status}`,
67
+ requestId: parsed2.request_id ?? requestId
68
+ });
69
+ }
70
+ } catch {
71
+ }
72
+ const quoted = text2.length > MAX_ERROR_BODY ? `${text2.slice(0, MAX_ERROR_BODY)}\u2026` : text2;
73
+ return new AnthropicApiError(response.status, {
74
+ message: `Anthropic API ${response.status}${quoted ? `: ${quoted}` : ""}`,
75
+ requestId
76
+ });
77
+ }
78
+ function compact(params) {
79
+ const out = {};
80
+ for (const [key, value] of Object.entries(params)) {
81
+ if (value !== void 0 && value !== null && value !== "") out[key] = value;
82
+ }
83
+ return out;
84
+ }
85
+ function parseJsonl(text2) {
86
+ return text2.split("\n").map((line) => line.trim()).filter((line) => line !== "").map((line, index) => {
87
+ try {
88
+ return JSON.parse(line);
89
+ } catch {
90
+ throw new Error(`Batch results line ${index + 1} is not JSON`);
91
+ }
92
+ });
93
+ }
94
+ function createAnthropicClient(options) {
95
+ const apiKey = options.apiKey.trim();
96
+ if (!apiKey) throw new Error("ANTHROPIC_API_KEY is required");
97
+ const gate = options.gate ?? createRateGate(options);
98
+ const sleep = options.sleep ?? wait;
99
+ const now = options.now ?? Date.now;
100
+ const random = options.random ?? Math.random;
101
+ async function call(path, callOptions = {}) {
102
+ const url = new URL(`${API_ROOT}/${path}`);
103
+ for (const [key, value] of Object.entries(compact(callOptions.query ?? {}))) {
104
+ url.searchParams.set(key, String(value));
105
+ }
106
+ const init = {
107
+ method: callOptions.method ?? "GET",
108
+ headers: {
109
+ "x-api-key": apiKey,
110
+ "anthropic-version": ANTHROPIC_VERSION,
111
+ ...callOptions.body !== void 0 && { "content-type": "application/json" }
112
+ },
113
+ ...callOptions.body !== void 0 && { body: JSON.stringify(compact(callOptions.body)) }
114
+ };
115
+ for (let attempt = 0; ; attempt++) {
116
+ await gate.acquire();
117
+ const response = await options.fetch(url.toString(), init);
118
+ gate.observe(response.headers);
119
+ const retryAfter = retryAfterMs(response.headers.get("retry-after"), now());
120
+ if (attempt === 0 && response.status === 429 && retryAfter !== void 0) {
121
+ await sleep(retryAfter);
122
+ continue;
123
+ }
124
+ if (attempt === 0 && response.status >= 500) {
125
+ await sleep(retryAfter ?? SERVER_ERROR_BACKOFF_MS * (1 + random()));
126
+ continue;
127
+ }
128
+ if (!response.ok) throw await describeFailure(response);
129
+ const text2 = await response.text();
130
+ if (callOptions.text) return text2;
131
+ return text2 === "" ? {} : JSON.parse(text2);
132
+ }
133
+ }
134
+ async function list(path, listOptions = {}) {
135
+ const maxPages = listOptions.maxPages ?? MAX_LIST_PAGES;
136
+ const collected = [];
137
+ let afterId;
138
+ for (let index = 0; index < maxPages; index++) {
139
+ const page = await call(path, { query: { limit: PAGE_LIMIT, after_id: afterId } });
140
+ const entries = page.data ?? [];
141
+ collected.push(...entries);
142
+ if (listOptions.until && entries.some(listOptions.until)) break;
143
+ if (!page.has_more || !page.last_id) break;
144
+ afterId = page.last_id;
145
+ }
146
+ return collected;
147
+ }
148
+ const encoded = (id) => encodeURIComponent(id);
149
+ const createMessage = (body) => call("messages", { method: "POST", body });
150
+ const countTokens = (body) => call("messages/count_tokens", { method: "POST", body });
151
+ const listModels = (listOptions) => list("models", listOptions);
152
+ const getModel = (id) => call(`models/${encoded(id)}`);
153
+ const createBatch = (requests) => call("messages/batches", { method: "POST", body: { requests } });
154
+ const listBatches = (listOptions) => list("messages/batches", listOptions);
155
+ const getBatch = (id) => call(`messages/batches/${encoded(id)}`);
156
+ const getBatchResults = async (id) => parseJsonl(await call(`messages/batches/${encoded(id)}/results`, { text: true }));
157
+ const cancelBatch = (id) => call(`messages/batches/${encoded(id)}/cancel`, { method: "POST" });
158
+ return {
159
+ call,
160
+ list,
161
+ createMessage,
162
+ countTokens,
163
+ listModels,
164
+ getModel,
165
+ createBatch,
166
+ listBatches,
167
+ getBatch,
168
+ getBatchResults,
169
+ cancelBatch
170
+ };
171
+ }
172
+
173
+ // src/items.ts
174
+ var DEFAULT_MODEL = "claude-sonnet-5";
175
+ var DEFAULT_MAX_TOKENS = 1024;
176
+ var MODEL_IDS = [
177
+ "claude-fable-5-1",
178
+ "claude-sonnet-5",
179
+ "claude-fable-5",
180
+ "claude-opus-5",
181
+ "claude-opus-4-8",
182
+ "claude-opus-4-7",
183
+ "claude-opus-4-6",
184
+ "claude-sonnet-4-6",
185
+ "claude-haiku-4-5",
186
+ "claude-opus-4-5",
187
+ "claude-sonnet-4-5"
188
+ ];
189
+ var MODELS_DOC_URL = "https://platform.claude.com/docs/en/about-claude/models/overview";
190
+ var SAMPLE_BATCH = {
191
+ id: "msgbatch_013Zva2CMHLNnXjNJJKqJ2EF",
192
+ type: "message_batch",
193
+ processing_status: "ended",
194
+ request_counts: { processing: 0, succeeded: 50, errored: 30, canceled: 10, expired: 10 },
195
+ created_at: "2024-08-20T18:37:24.100435Z",
196
+ ended_at: "2024-08-20T18:37:24.100435Z",
197
+ expires_at: "2024-08-21T18:37:24.100435Z",
198
+ archived_at: null,
199
+ cancel_initiated_at: null,
200
+ results_url: "https://api.anthropic.com/v1/messages/batches/msgbatch_013Zva2CMHLNnXjNJJKqJ2EF/results"
201
+ };
202
+ var SAMPLE_MODEL = {
203
+ id: "claude-opus-5",
204
+ type: "model",
205
+ display_name: "Claude Opus 5",
206
+ created_at: "2026-07-24T00:00:00Z",
207
+ max_input_tokens: 1e6,
208
+ max_tokens: 128e3,
209
+ capabilities: {
210
+ batch: { supported: true },
211
+ thinking: { supported: true, types: { adaptive: { supported: true }, enabled: { supported: true } } }
212
+ }
213
+ };
214
+ function isRecord(value) {
215
+ return typeof value === "object" && value !== null && !Array.isArray(value);
216
+ }
217
+ function parsed(value, key) {
218
+ if (typeof value !== "string") return value;
219
+ try {
220
+ return JSON.parse(value);
221
+ } catch {
222
+ throw new Error(`${key} must be JSON`);
223
+ }
224
+ }
225
+ function messagesArg(value) {
226
+ if (value === void 0 || value === null || value === "") throw new Error("messages is required");
227
+ const raw = typeof value === "string" && value.trimStart().startsWith("[") ? parsed(value, "messages") : value;
228
+ if (typeof raw === "string") return [{ role: "user", content: raw }];
229
+ const turns = Array.isArray(raw) ? raw : [raw];
230
+ if (!turns.every(isRecord)) throw new Error("messages must be prompt text or a JSON array of { role, content } turns");
231
+ return turns;
232
+ }
233
+ function listArg(value, key) {
234
+ if (value === void 0 || value === null || value === "") return void 0;
235
+ const raw = parsed(value, key);
236
+ return Array.isArray(raw) ? raw : [raw];
237
+ }
238
+ function stopSequencesArg(value) {
239
+ if (value === void 0 || value === null || value === "") return void 0;
240
+ const raw = typeof value === "string" && value.trimStart().startsWith("[") ? parsed(value, "stopSequences") : value;
241
+ if (typeof raw === "string") return [raw];
242
+ if (!Array.isArray(raw)) return void 0;
243
+ if (raw.some((entry) => typeof entry !== "string")) throw new Error("stopSequences must be strings");
244
+ const sequences = raw.filter((entry) => entry !== "");
245
+ return sequences.length > 0 ? sequences : void 0;
246
+ }
247
+ function numberArg(value, key) {
248
+ if (value === void 0 || value === null || value === "") return void 0;
249
+ const number = Number(value);
250
+ if (!Number.isFinite(number)) throw new Error(`${key} must be a number, got "${String(value)}"`);
251
+ return number;
252
+ }
253
+ function firstText(content) {
254
+ if (!Array.isArray(content)) return "";
255
+ const block = content.find((entry) => isRecord(entry) && entry.type === "text" && typeof entry.text === "string");
256
+ return block ? String(block.text) : "";
257
+ }
258
+ function messageOutput(message) {
259
+ return {
260
+ id: message.id ?? "",
261
+ model: message.model ?? "",
262
+ text: firstText(message.content),
263
+ stopReason: message.stop_reason ?? "",
264
+ usage: message.usage ?? {},
265
+ raw: message
266
+ };
267
+ }
268
+ function modelOutput(model) {
269
+ return {
270
+ id: model.id ?? "",
271
+ displayName: model.display_name ?? "",
272
+ createdAt: model.created_at ?? "",
273
+ maxInputTokens: model.max_input_tokens ?? null,
274
+ maxTokens: model.max_tokens ?? null,
275
+ capabilities: model.capabilities ?? {},
276
+ raw: model
277
+ };
278
+ }
279
+ function batchOutput(batch) {
280
+ return {
281
+ id: batch.id ?? "",
282
+ processingStatus: batch.processing_status ?? "",
283
+ requestCounts: batch.request_counts ?? {},
284
+ createdAt: batch.created_at ?? "",
285
+ endedAt: batch.ended_at ?? null,
286
+ expiresAt: batch.expires_at ?? "",
287
+ cancelInitiatedAt: batch.cancel_initiated_at ?? null,
288
+ resultsUrl: batch.results_url ?? null,
289
+ raw: batch
290
+ };
291
+ }
292
+ function countsSummary(counts) {
293
+ const parts = ["succeeded", "errored", "canceled", "expired"].filter((key) => (counts?.[key] ?? 0) > 0).map((key) => `${counts?.[key]} ${key}`);
294
+ return parts.length > 0 ? parts.join(", ") : "no requests";
295
+ }
296
+ function batchToItem(batch) {
297
+ return {
298
+ externalId: batch.id,
299
+ title: `Batch ${batch.id} ended: ${countsSummary(batch.request_counts)}`,
300
+ status: batch.processing_status ?? "",
301
+ ...batch.ended_at && { updatedAt: batch.ended_at },
302
+ data: { ...batch }
303
+ };
304
+ }
305
+ function modelToItem(model) {
306
+ return {
307
+ externalId: model.id,
308
+ title: model.display_name ?? model.id,
309
+ url: MODELS_DOC_URL,
310
+ ...model.created_at && { updatedAt: model.created_at },
311
+ data: { ...model }
312
+ };
313
+ }
314
+
315
+ // package.json
316
+ var package_default = {
317
+ name: "@vornrun/connector-anthropic",
318
+ version: "0.1.0",
319
+ description: "Trigger workflows when an Anthropic message batch finishes or a model becomes available, and create messages, count tokens, list models and run message batches from a step.",
320
+ type: "module",
321
+ license: "MIT",
322
+ repository: {
323
+ type: "git",
324
+ url: "git+https://github.com/vorn-run/connectors.git",
325
+ directory: "packages/anthropic"
326
+ },
327
+ keywords: [
328
+ "vorn",
329
+ "connector",
330
+ "anthropic",
331
+ "claude",
332
+ "mcp"
333
+ ],
334
+ bin: {
335
+ "vorn-connector-anthropic": "dist/index.js"
336
+ },
337
+ main: "./dist/index.js",
338
+ types: "./dist/index.d.ts",
339
+ exports: {
340
+ ".": {
341
+ types: "./dist/index.d.ts",
342
+ default: "./dist/index.js"
343
+ }
344
+ },
345
+ files: [
346
+ "dist",
347
+ "README.md",
348
+ "CHANGELOG.md"
349
+ ],
350
+ scripts: {
351
+ build: "tsup",
352
+ typecheck: "tsc --noEmit",
353
+ test: "vitest run"
354
+ },
355
+ dependencies: {
356
+ "@vornrun/connector-sdk": "^0.7.0-beta.14"
357
+ },
358
+ devDependencies: {
359
+ "@types/node": "^22.10.2",
360
+ "@vitest/coverage-v8": "^4.1.10",
361
+ tsup: "^8.5.1",
362
+ typescript: "^6.0.3",
363
+ vitest: "^4.1.10"
364
+ },
365
+ vorn: {
366
+ category: "AI",
367
+ keywords: [
368
+ "anthropic",
369
+ "claude",
370
+ "messages",
371
+ "llm",
372
+ "batches",
373
+ "tokens",
374
+ "models"
375
+ ],
376
+ auth: "Paste an Anthropic API key from console.anthropic.com/settings/keys for a single workspace. There is no CLI to sign in with.",
377
+ packs: true
378
+ }
379
+ };
380
+
381
+ // src/connector.ts
382
+ var BATCH_LOOKBACK_MS = 24 * 60 * 6e4;
383
+ var MODEL_LOOKBACK_MS = 30 * 24 * 60 * 6e4;
384
+ var PLACEHOLDER_BATCH_ID = "msgbatch_placeholder";
385
+ function text(value) {
386
+ const trimmed = String(value ?? "").trim();
387
+ return trimmed || void 0;
388
+ }
389
+ function required(config, key, env) {
390
+ const value = text(config[key]);
391
+ if (value === void 0) throw new Error(`${env} is required`);
392
+ return value;
393
+ }
394
+ function millis(value) {
395
+ const at = Date.parse(value ?? "");
396
+ return Number.isNaN(at) ? 0 : at;
397
+ }
398
+ var MODEL_INPUT = {
399
+ key: "model",
400
+ label: "Model",
401
+ description: `The model that will complete your prompt. Defaults to ${DEFAULT_MODEL}.`,
402
+ builderHint: `Sent as model. Ids the reference lists today: ${MODEL_IDS.join(", ")}; listModels returns the live set and getModel resolves an alias.`
403
+ };
404
+ var MESSAGES_INPUT = {
405
+ key: "messages",
406
+ label: "Messages",
407
+ required: true,
408
+ description: 'Prompt text, sent as one user turn, or a JSON array of { "role", "content" } turns.',
409
+ builderHint: 'Text becomes [{ role: "user", content: text }]; a value starting with [ is parsed as the messages array and passed through, so multi-turn and content blocks work. Consecutive turns of one role are merged by the API.'
410
+ };
411
+ var SYSTEM_INPUT = {
412
+ key: "system",
413
+ label: "System prompt",
414
+ description: "Context and instructions for the model, kept apart from the conversation.",
415
+ builderHint: "Sent as the top-level system string only when set; the API takes no system role inside messages."
416
+ };
417
+ var BATCH_ID_INPUT = {
418
+ key: "batchId",
419
+ label: "Batch",
420
+ required: true,
421
+ description: "The message batch id, msgbatch_\u2026",
422
+ builderHint: "Sent URL-encoded as the path segment; an unknown id answers 404 not_found_error."
423
+ };
424
+ var BATCH_OUTPUTS = [
425
+ { key: "id", description: "Batch id, msgbatch_\u2026" },
426
+ { key: "processingStatus", description: "in_progress, canceling or ended" },
427
+ { key: "requestCounts", description: "{ processing, succeeded, errored, canceled, expired }" },
428
+ { key: "createdAt", description: "When the batch was created, RFC 3339" },
429
+ { key: "endedAt", description: "When processing ended; null until then" },
430
+ { key: "expiresAt", description: "24 hours after creation, when unfinished requests expire" },
431
+ { key: "cancelInitiatedAt", description: "When cancellation was asked for, RFC 3339; null when it never was" },
432
+ { key: "resultsUrl", description: "Where the results stream from once processing has ended; null until then" },
433
+ { key: "raw", description: "The whole batch as the API returned it" }
434
+ ];
435
+ function createAnthropicConnector(options = {}) {
436
+ const env = options.env ?? process.env;
437
+ const clock = options.now ?? Date.now;
438
+ const gate = createRateGate({ now: clock, ...options.sleep && { sleep: options.sleep } });
439
+ function client(context) {
440
+ return createAnthropicClient({
441
+ apiKey: required(context.config, "apiKey", "ANTHROPIC_API_KEY"),
442
+ fetch: context.fetch,
443
+ gate,
444
+ now: clock,
445
+ ...options.sleep && { sleep: options.sleep },
446
+ ...options.random && { random: options.random }
447
+ });
448
+ }
449
+ async function fetchEndedBatches(context) {
450
+ const floor = (context.since ? Date.parse(context.since) : Date.parse(context.now())) - BATCH_LOOKBACK_MS;
451
+ const batches = await client(context).listBatches({
452
+ until: (batch) => millis(batch.created_at) < floor
453
+ });
454
+ return batches.filter((batch) => batch.processing_status === "ended" && millis(batch.created_at) >= floor).sort((left, right) => millis(left.ended_at) - millis(right.ended_at)).map(batchToItem);
455
+ }
456
+ async function fetchNewModels(context) {
457
+ const floor = context.since ? Date.parse(context.since) : Date.parse(context.now()) - MODEL_LOOKBACK_MS;
458
+ const models = await client(context).listModels({
459
+ until: (model) => millis(model.created_at) < floor
460
+ });
461
+ return models.filter((model) => millis(model.created_at) >= floor).sort((left, right) => millis(left.created_at) - millis(right.created_at)).map(modelToItem);
462
+ }
463
+ const batchId = text(env.ANTHROPIC_BATCH_ID) ?? PLACEHOLDER_BATCH_ID;
464
+ return defineConnector({
465
+ id: "anthropic",
466
+ name: "Anthropic",
467
+ version: options.version ?? package_default.version,
468
+ description: "Trigger workflows when a message batch finishes or a model becomes available, and create messages, count tokens, list models and run message batches from a step.",
469
+ // Anthropic's crossbar-less A: the full-height right leg and the shorter left leg meeting below its apex.
470
+ icon: {
471
+ viewBox: "0 0 24 24",
472
+ paths: ["M9 3 15 3 22.5 21 16.5 21z", "M1.5 21 7.5 21 12.5 8.5 9.5 3.8z"]
473
+ },
474
+ auth: { rung: "key", keys: ["apiKey"] },
475
+ config: [
476
+ {
477
+ key: "apiKey",
478
+ env: "ANTHROPIC_API_KEY",
479
+ label: "API key",
480
+ secret: true,
481
+ required: true,
482
+ description: "An API key from console.anthropic.com/settings/keys, for a single workspace.",
483
+ builderHint: "Sent as x-api-key with anthropic-version: 2023-06-01 on every call. Keys have no scopes: a key without access answers 403 permission_error, a bad one 401 authentication_error. A multi-workspace key also needs anthropic-workspace-id, which is not sent, so use a single-workspace key. There is no CLI to borrow a login from."
484
+ }
485
+ ],
486
+ triggers: [
487
+ {
488
+ type: "batchEnded",
489
+ label: "A message batch finished",
490
+ description: "Fires once for each message batch whose processing has ended since the last poll, oldest first. Read the results with getBatchResults.",
491
+ dedupe: "timestamp",
492
+ fetch: fetchEndedBatches,
493
+ defaultWorkflow: { name: "Anthropic: finished batches", defaultCronFromMinutes: 5 },
494
+ sample: [batchToItem(SAMPLE_BATCH)]
495
+ },
496
+ {
497
+ type: "newModel",
498
+ label: "A model became available",
499
+ description: "Fires once for each model released since the last poll, oldest first. The first poll looks 30 days back.",
500
+ dedupe: "timestamp",
501
+ fetch: fetchNewModels,
502
+ defaultWorkflow: { name: "Anthropic: new models", defaultCronFromMinutes: 60 },
503
+ sample: [modelToItem(SAMPLE_MODEL)]
504
+ }
505
+ ],
506
+ actions: [
507
+ {
508
+ type: "createMessage",
509
+ label: "Create a message",
510
+ description: "Send a conversation to a model and get its reply. Every call bills a generation.",
511
+ idempotent: false,
512
+ inputs: [
513
+ MODEL_INPUT,
514
+ MESSAGES_INPUT,
515
+ SYSTEM_INPUT,
516
+ {
517
+ key: "maxTokens",
518
+ label: "Maximum tokens",
519
+ type: "number",
520
+ description: `The most tokens to generate before stopping; the model may stop sooner. Defaults to ${DEFAULT_MAX_TOKENS}.`,
521
+ builderHint: "Sent as max_tokens. The connector does not stream, so keep it modest; a large job belongs in createMessageBatch."
522
+ },
523
+ {
524
+ key: "temperature",
525
+ label: "Temperature",
526
+ type: "number",
527
+ description: "Randomness from 0.0 to 1.0. Leave unset for the default.",
528
+ builderHint: "Sent only when set. Models released after Claude Opus 4.6, the default model included, accept only 1.0 and reject other values with a 400."
529
+ },
530
+ {
531
+ key: "tools",
532
+ label: "Tools",
533
+ type: "json",
534
+ description: 'A JSON array of tool definitions, each { "name", "description", "input_schema" }.',
535
+ builderHint: "Sent as tools; a single object is taken as one tool. input_schema is a JSON schema for the tool input; a tool call comes back as a tool_use block in raw.content with stopReason tool_use."
536
+ },
537
+ {
538
+ key: "stopSequences",
539
+ label: "Stop sequences",
540
+ type: "json",
541
+ description: "A JSON array of strings that end generation when the model emits one.",
542
+ builderHint: 'Sent as stop_sequences; one JSON string such as "END" is taken as a single sequence. A hit sets stopReason to stop_sequence and raw.stop_sequence names it.'
543
+ }
544
+ ],
545
+ outputs: [
546
+ { key: "id", description: "Message id, msg_\u2026" },
547
+ { key: "model", description: "The model that answered" },
548
+ { key: "text", description: "The text of the first text block; empty when the model answered with a tool call alone" },
549
+ {
550
+ key: "stopReason",
551
+ description: "end_turn, max_tokens, stop_sequence, tool_use, pause_turn, refusal or model_context_window_exceeded"
552
+ },
553
+ { key: "usage", description: "{ input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens }" },
554
+ { key: "raw", description: "The whole response, with every content block and stop_details" }
555
+ ],
556
+ async run(args, context) {
557
+ const message = await client(context).createMessage({
558
+ model: text(args.model) ?? DEFAULT_MODEL,
559
+ messages: messagesArg(args.messages),
560
+ system: text(args.system),
561
+ max_tokens: numberArg(args.maxTokens, "maxTokens") ?? DEFAULT_MAX_TOKENS,
562
+ temperature: numberArg(args.temperature, "temperature"),
563
+ tools: listArg(args.tools, "tools"),
564
+ stop_sequences: stopSequencesArg(args.stopSequences)
565
+ });
566
+ return messageOutput(message);
567
+ }
568
+ },
569
+ {
570
+ type: "countTokens",
571
+ label: "Count tokens",
572
+ description: "Count the tokens a conversation and system prompt would use, without creating a message.",
573
+ idempotent: true,
574
+ inputs: [MODEL_INPUT, MESSAGES_INPUT, SYSTEM_INPUT],
575
+ outputs: [
576
+ { key: "inputTokens", type: "number", description: "Tokens across the messages, system prompt and tools" }
577
+ ],
578
+ sample: { model: DEFAULT_MODEL, messages: "hello" },
579
+ async run(args, context) {
580
+ const counted = await client(context).countTokens({
581
+ model: text(args.model) ?? DEFAULT_MODEL,
582
+ messages: messagesArg(args.messages),
583
+ system: text(args.system)
584
+ });
585
+ return { inputTokens: counted.input_tokens ?? 0 };
586
+ }
587
+ },
588
+ {
589
+ type: "listModels",
590
+ label: "List models",
591
+ description: "The models the key can use, newest release first.",
592
+ idempotent: true,
593
+ inputs: [],
594
+ outputs: [
595
+ { key: "models", description: "One entry per model: id, display_name, created_at, max_input_tokens, max_tokens, capabilities" },
596
+ { key: "count", type: "number", description: "How many models came back" }
597
+ ],
598
+ sample: {},
599
+ async run(_args, context) {
600
+ const models = await client(context).listModels();
601
+ return { models, count: models.length };
602
+ }
603
+ },
604
+ {
605
+ type: "getModel",
606
+ label: "Get a model",
607
+ description: "Read one model by id, or resolve an alias to its model id.",
608
+ idempotent: true,
609
+ inputs: [
610
+ {
611
+ key: "modelId",
612
+ label: "Model",
613
+ required: true,
614
+ description: "A model id or alias, such as claude-sonnet-5.",
615
+ builderHint: "Sent URL-encoded as the path segment of GET models/{modelId}; an unknown id answers 404 not_found_error."
616
+ }
617
+ ],
618
+ outputs: [
619
+ { key: "id", description: "The resolved model id" },
620
+ { key: "displayName", description: "Human-readable name" },
621
+ { key: "createdAt", description: "Release time, RFC 3339; an epoch when unknown" },
622
+ { key: "maxInputTokens", type: "number", description: "Context window in tokens" },
623
+ { key: "maxTokens", type: "number", description: "Most output tokens per message" },
624
+ { key: "capabilities", description: "What the model supports, such as batch and thinking" },
625
+ { key: "raw", description: "The whole model as the API returned it" }
626
+ ],
627
+ sample: { modelId: DEFAULT_MODEL },
628
+ async run(args, context) {
629
+ return modelOutput(await client(context).getModel(String(args.modelId)));
630
+ }
631
+ },
632
+ {
633
+ type: "createMessageBatch",
634
+ label: "Create a message batch",
635
+ description: "Queue up to 100,000 message requests for asynchronous processing. Two calls queue two batches.",
636
+ idempotent: false,
637
+ inputs: [
638
+ {
639
+ key: "requests",
640
+ label: "Requests",
641
+ type: "json",
642
+ required: true,
643
+ description: 'A JSON array of { "custom_id", "params" } where params is a full createMessage body: model, max_tokens, messages, \u2026.',
644
+ builderHint: "Sent as requests; a single object is taken as a batch of one. custom_id matches ^[a-zA-Z0-9_-]{1,64}$ and must be unique within the batch; results come back keyed by it, not in order."
645
+ }
646
+ ],
647
+ outputs: BATCH_OUTPUTS,
648
+ async run(args, context) {
649
+ const batch = await client(context).createBatch(listArg(args.requests, "requests") ?? []);
650
+ return batchOutput(batch);
651
+ }
652
+ },
653
+ {
654
+ type: "getMessageBatch",
655
+ label: "Get a message batch",
656
+ description: "Read a batch and its request counts; poll it until processingStatus is ended.",
657
+ idempotent: true,
658
+ inputs: [BATCH_ID_INPUT],
659
+ outputs: BATCH_OUTPUTS,
660
+ sample: { batchId },
661
+ async run(args, context) {
662
+ return batchOutput(await client(context).getBatch(String(args.batchId)));
663
+ }
664
+ },
665
+ {
666
+ type: "getBatchResults",
667
+ label: "Get batch results",
668
+ description: "Read the results of an ended batch, one entry per request, matched by custom_id rather than by order.",
669
+ idempotent: true,
670
+ inputs: [BATCH_ID_INPUT],
671
+ outputs: [
672
+ {
673
+ key: "results",
674
+ description: "One entry per request: { custom_id, result } where result.type is succeeded (with result.message), errored (with result.error), canceled or expired"
675
+ },
676
+ { key: "count", type: "number", description: "How many results came back" }
677
+ ],
678
+ sample: { batchId },
679
+ async run(args, context) {
680
+ const results = await client(context).getBatchResults(String(args.batchId));
681
+ return { results, count: results.length };
682
+ }
683
+ },
684
+ {
685
+ type: "cancelMessageBatch",
686
+ label: "Cancel a message batch",
687
+ description: "Ask for a batch to stop; it enters canceling and requests already running may still finish.",
688
+ idempotent: false,
689
+ inputs: [BATCH_ID_INPUT],
690
+ outputs: BATCH_OUTPUTS,
691
+ async run(args, context) {
692
+ return batchOutput(await client(context).cancelBatch(String(args.batchId)));
693
+ }
694
+ }
695
+ ]
696
+ });
697
+ }
698
+ var connector = createAnthropicConnector();
699
+
700
+ // src/entry.ts
701
+ import { realpathSync } from "fs";
702
+ import { fileURLToPath } from "url";
703
+ import { serveConnector } from "@vornrun/connector-sdk";
704
+ function isEntryPoint(moduleUrl, argv = process.argv) {
705
+ const invoked = argv[1];
706
+ if (invoked === void 0) return false;
707
+ try {
708
+ return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(invoked);
709
+ } catch {
710
+ return false;
711
+ }
712
+ }
713
+ async function serveIfEntryPoint(moduleUrl, serve = serveConnector) {
714
+ if (!isEntryPoint(moduleUrl)) return false;
715
+ await serve(connector);
716
+ return true;
717
+ }
718
+
719
+ // src/index.ts
720
+ var index_default = connector;
721
+ await serveIfEntryPoint(import.meta.url);
722
+ export {
723
+ ANTHROPIC_VERSION,
724
+ API_ROOT,
725
+ AnthropicApiError,
726
+ BATCH_LOOKBACK_MS,
727
+ DEFAULT_MAX_TOKENS,
728
+ DEFAULT_MODEL,
729
+ MAX_LIST_PAGES,
730
+ MAX_WAIT_MS,
731
+ MODEL_IDS,
732
+ MODEL_LOOKBACK_MS,
733
+ PAGE_LIMIT,
734
+ SAMPLE_BATCH,
735
+ SAMPLE_MODEL,
736
+ batchOutput,
737
+ batchToItem,
738
+ connector,
739
+ createAnthropicClient,
740
+ createAnthropicConnector,
741
+ createRateGate,
742
+ index_default as default,
743
+ firstText,
744
+ listArg,
745
+ messageOutput,
746
+ messagesArg,
747
+ modelOutput,
748
+ modelToItem,
749
+ numberArg,
750
+ parseJsonl,
751
+ retryAfterMs,
752
+ stopSequencesArg
753
+ };
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@vornrun/connector-anthropic",
3
+ "version": "0.1.0",
4
+ "description": "Trigger workflows when an Anthropic message batch finishes or a model becomes available, and create messages, count tokens, list models and run message batches from a step.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/vorn-run/connectors.git",
10
+ "directory": "packages/anthropic"
11
+ },
12
+ "keywords": [
13
+ "vorn",
14
+ "connector",
15
+ "anthropic",
16
+ "claude",
17
+ "mcp"
18
+ ],
19
+ "bin": {
20
+ "vorn-connector-anthropic": "dist/index.js"
21
+ },
22
+ "main": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "README.md",
33
+ "CHANGELOG.md"
34
+ ],
35
+ "scripts": {
36
+ "build": "tsup",
37
+ "typecheck": "tsc --noEmit",
38
+ "test": "vitest run"
39
+ },
40
+ "dependencies": {
41
+ "@vornrun/connector-sdk": "^0.7.0-beta.14"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^22.10.2",
45
+ "@vitest/coverage-v8": "^4.1.10",
46
+ "tsup": "^8.5.1",
47
+ "typescript": "^6.0.3",
48
+ "vitest": "^4.1.10"
49
+ },
50
+ "vorn": {
51
+ "category": "AI",
52
+ "keywords": [
53
+ "anthropic",
54
+ "claude",
55
+ "messages",
56
+ "llm",
57
+ "batches",
58
+ "tokens",
59
+ "models"
60
+ ],
61
+ "auth": "Paste an Anthropic API key from console.anthropic.com/settings/keys for a single workspace. There is no CLI to sign in with.",
62
+ "packs": true
63
+ }
64
+ }